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  }