github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/spaceidindex/spaceidindex.go (about) 1 package spaceidindex 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 "time" 8 9 "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/mtimesyncedcache" 10 "github.com/pkg/errors" 11 "github.com/rogpeppe/go-internal/lockedfile" 12 "github.com/shamaton/msgpack/v2" 13 ) 14 15 // Index holds space id indexes 16 type Index struct { 17 root string 18 name string 19 cache mtimesyncedcache.Cache[string, map[string]string] 20 } 21 22 type readWriteCloseSeekTruncater interface { 23 io.ReadWriteCloser 24 io.Seeker 25 Truncate(int64) error 26 } 27 28 // New returns a new index instance 29 func New(root, name string) *Index { 30 return &Index{ 31 root: root, 32 name: name, 33 } 34 } 35 36 // Init initializes the index and makes sure it can be used 37 func (i *Index) Init() error { 38 // Make sure to work on an existing tree 39 return os.MkdirAll(filepath.Join(i.root, i.name), 0700) 40 } 41 42 // Load returns the content of an index 43 func (i *Index) Load(index string) (map[string]string, error) { 44 indexPath := filepath.Join(i.root, i.name, index+".mpk") 45 fi, err := os.Stat(indexPath) 46 if err != nil { 47 return nil, err 48 } 49 return i.readSpaceIndex(indexPath, i.name+":"+index, fi.ModTime()) 50 } 51 52 // Add adds an entry to an index 53 // Consider calling AddAll() when trying to add multiple entries as every Add call has to lock the index 54 func (i *Index) Add(index, key string, value string) error { 55 return i.updateIndex(index, map[string]string{key: value}, []string{}) 56 } 57 58 // AddAll adds multiple entries to the index 59 func (i *Index) AddAll(index string, m map[string]string) error { 60 return i.updateIndex(index, m, []string{}) 61 } 62 63 // Remove removes an entry from the index 64 func (i *Index) Remove(index, key string) error { 65 return i.updateIndex(index, map[string]string{}, []string{key}) 66 } 67 68 func (i *Index) updateIndex(index string, addLinks map[string]string, removeLinks []string) error { 69 indexPath := filepath.Join(i.root, i.name, index+".mpk") 70 71 var err error 72 // acquire writelock 73 var f readWriteCloseSeekTruncater 74 f, err = lockedfile.OpenFile(indexPath, os.O_RDWR|os.O_CREATE, 0600) 75 if err != nil { 76 return errors.Wrap(err, "unable to lock index to write") 77 } 78 defer func() { 79 rerr := f.Close() 80 81 // if err is non nil we do not overwrite that 82 if err == nil { 83 err = rerr 84 } 85 }() 86 87 // Read current state 88 msgBytes, err := io.ReadAll(f) 89 if err != nil { 90 return err 91 } 92 links := map[string]string{} 93 if len(msgBytes) > 0 { 94 err = msgpack.Unmarshal(msgBytes, &links) 95 if err != nil { 96 return err 97 } 98 } 99 100 // set new metadata 101 for key, val := range addLinks { 102 links[key] = val 103 } 104 for _, key := range removeLinks { 105 delete(links, key) 106 } 107 // Truncate file 108 _, err = f.Seek(0, io.SeekStart) 109 if err != nil { 110 return err 111 } 112 err = f.Truncate(0) 113 if err != nil { 114 return err 115 } 116 117 // Write new metadata to file 118 d, err := msgpack.Marshal(links) 119 if err != nil { 120 return errors.Wrap(err, "unable to marshal index") 121 } 122 _, err = f.Write(d) 123 if err != nil { 124 return errors.Wrap(err, "unable to write index") 125 } 126 return nil 127 } 128 129 func (i *Index) readSpaceIndex(indexPath, cacheKey string, mtime time.Time) (map[string]string, error) { 130 return i.cache.LoadOrStore(cacheKey, mtime, func() (map[string]string, error) { 131 // Acquire a read log on the index file 132 f, err := lockedfile.Open(indexPath) 133 if err != nil { 134 return nil, errors.Wrap(err, "unable to lock index to read") 135 } 136 defer func() { 137 rerr := f.Close() 138 139 // if err is non nil we do not overwrite that 140 if err == nil { 141 err = rerr 142 } 143 }() 144 145 // Read current state 146 msgBytes, err := io.ReadAll(f) 147 if err != nil { 148 return nil, errors.Wrap(err, "unable to read index") 149 } 150 links := map[string]string{} 151 if len(msgBytes) > 0 { 152 err = msgpack.Unmarshal(msgBytes, &links) 153 if err != nil { 154 return nil, errors.Wrap(err, "unable to parse index") 155 } 156 } 157 return links, nil 158 }) 159 }