github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/memory/memory.go (about)

     1  // Package memory provides an interface to an in memory object storage system
     2  package memory
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"crypto/md5"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"path"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/pkg/errors"
    18  	"github.com/rclone/rclone/fs"
    19  	"github.com/rclone/rclone/fs/config/configmap"
    20  	"github.com/rclone/rclone/fs/config/configstruct"
    21  	"github.com/rclone/rclone/fs/hash"
    22  	"github.com/rclone/rclone/fs/walk"
    23  	"github.com/rclone/rclone/lib/bucket"
    24  )
    25  
    26  var (
    27  	hashType = hash.MD5
    28  	// the object storage is persistent
    29  	buckets = newBucketsInfo()
    30  )
    31  
    32  // Register with Fs
    33  func init() {
    34  	fs.Register(&fs.RegInfo{
    35  		Name:        "memory",
    36  		Description: "In memory object storage system.",
    37  		NewFs:       NewFs,
    38  		Options:     []fs.Option{},
    39  	})
    40  }
    41  
    42  // Options defines the configuration for this backend
    43  type Options struct {
    44  }
    45  
    46  // Fs represents a remote memory server
    47  type Fs struct {
    48  	name          string       // name of this remote
    49  	root          string       // the path we are working on if any
    50  	opt           Options      // parsed config options
    51  	rootBucket    string       // bucket part of root (if any)
    52  	rootDirectory string       // directory part of root (if any)
    53  	features      *fs.Features // optional features
    54  }
    55  
    56  // bucketsInfo holds info about all the buckets
    57  type bucketsInfo struct {
    58  	mu      sync.RWMutex
    59  	buckets map[string]*bucketInfo
    60  }
    61  
    62  func newBucketsInfo() *bucketsInfo {
    63  	return &bucketsInfo{
    64  		buckets: make(map[string]*bucketInfo, 16),
    65  	}
    66  }
    67  
    68  // getBucket gets a names bucket or nil
    69  func (bi *bucketsInfo) getBucket(name string) (b *bucketInfo) {
    70  	bi.mu.RLock()
    71  	b = bi.buckets[name]
    72  	bi.mu.RUnlock()
    73  	return b
    74  }
    75  
    76  // makeBucket returns the bucket or makes it
    77  func (bi *bucketsInfo) makeBucket(name string) (b *bucketInfo) {
    78  	bi.mu.Lock()
    79  	defer bi.mu.Unlock()
    80  	b = bi.buckets[name]
    81  	if b != nil {
    82  		return b
    83  	}
    84  	b = newBucketInfo()
    85  	bi.buckets[name] = b
    86  	return b
    87  }
    88  
    89  // deleteBucket deleted the bucket or returns an error
    90  func (bi *bucketsInfo) deleteBucket(name string) error {
    91  	bi.mu.Lock()
    92  	defer bi.mu.Unlock()
    93  	b := bi.buckets[name]
    94  	if b == nil {
    95  		return fs.ErrorDirNotFound
    96  	}
    97  	if !b.isEmpty() {
    98  		return fs.ErrorDirectoryNotEmpty
    99  	}
   100  	delete(bi.buckets, name)
   101  	return nil
   102  }
   103  
   104  // getObjectData gets an object from (bucketName, bucketPath) or nil
   105  func (bi *bucketsInfo) getObjectData(bucketName, bucketPath string) (od *objectData) {
   106  	b := bi.getBucket(bucketName)
   107  	if b == nil {
   108  		return nil
   109  	}
   110  	return b.getObjectData(bucketPath)
   111  }
   112  
   113  // updateObjectData updates an object from (bucketName, bucketPath)
   114  func (bi *bucketsInfo) updateObjectData(bucketName, bucketPath string, od *objectData) {
   115  	b := bi.makeBucket(bucketName)
   116  	b.mu.Lock()
   117  	b.objects[bucketPath] = od
   118  	b.mu.Unlock()
   119  }
   120  
   121  // removeObjectData removes an object from (bucketName, bucketPath) returning true if removed
   122  func (bi *bucketsInfo) removeObjectData(bucketName, bucketPath string) (removed bool) {
   123  	b := bi.getBucket(bucketName)
   124  	if b != nil {
   125  		b.mu.Lock()
   126  		od := b.objects[bucketPath]
   127  		if od != nil {
   128  			delete(b.objects, bucketPath)
   129  			removed = true
   130  		}
   131  		b.mu.Unlock()
   132  	}
   133  	return removed
   134  }
   135  
   136  // bucketInfo holds info about a single bucket
   137  type bucketInfo struct {
   138  	mu      sync.RWMutex
   139  	objects map[string]*objectData
   140  }
   141  
   142  func newBucketInfo() *bucketInfo {
   143  	return &bucketInfo{
   144  		objects: make(map[string]*objectData, 16),
   145  	}
   146  }
   147  
   148  // getBucket gets a names bucket or nil
   149  func (bi *bucketInfo) getObjectData(name string) (od *objectData) {
   150  	bi.mu.RLock()
   151  	od = bi.objects[name]
   152  	bi.mu.RUnlock()
   153  	return od
   154  }
   155  
   156  // getBucket gets a names bucket or nil
   157  func (bi *bucketInfo) isEmpty() (empty bool) {
   158  	bi.mu.RLock()
   159  	empty = len(bi.objects) == 0
   160  	bi.mu.RUnlock()
   161  	return empty
   162  }
   163  
   164  // the object data and metadata
   165  type objectData struct {
   166  	modTime  time.Time
   167  	hash     string
   168  	mimeType string
   169  	data     []byte
   170  }
   171  
   172  // Object describes a memory object
   173  type Object struct {
   174  	fs     *Fs         // what this object is part of
   175  	remote string      // The remote path
   176  	od     *objectData // the object data
   177  }
   178  
   179  // ------------------------------------------------------------
   180  
   181  // Name of the remote (as passed into NewFs)
   182  func (f *Fs) Name() string {
   183  	return f.name
   184  }
   185  
   186  // Root of the remote (as passed into NewFs)
   187  func (f *Fs) Root() string {
   188  	return f.root
   189  }
   190  
   191  // String converts this Fs to a string
   192  func (f *Fs) String() string {
   193  	return fmt.Sprintf("Memory root '%s'", f.root)
   194  }
   195  
   196  // Features returns the optional features of this Fs
   197  func (f *Fs) Features() *fs.Features {
   198  	return f.features
   199  }
   200  
   201  // parsePath parses a remote 'url'
   202  func parsePath(path string) (root string) {
   203  	root = strings.Trim(path, "/")
   204  	return
   205  }
   206  
   207  // split returns bucket and bucketPath from the rootRelativePath
   208  // relative to f.root
   209  func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
   210  	return bucket.Split(path.Join(f.root, rootRelativePath))
   211  }
   212  
   213  // split returns bucket and bucketPath from the object
   214  func (o *Object) split() (bucket, bucketPath string) {
   215  	return o.fs.split(o.remote)
   216  }
   217  
   218  // setRoot changes the root of the Fs
   219  func (f *Fs) setRoot(root string) {
   220  	f.root = parsePath(root)
   221  	f.rootBucket, f.rootDirectory = bucket.Split(f.root)
   222  }
   223  
   224  // NewFs contstructs an Fs from the path, bucket:path
   225  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   226  	// Parse config into Options struct
   227  	opt := new(Options)
   228  	err := configstruct.Set(m, opt)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	root = strings.Trim(root, "/")
   233  	f := &Fs{
   234  		name: name,
   235  		root: root,
   236  		opt:  *opt,
   237  	}
   238  	f.setRoot(root)
   239  	f.features = (&fs.Features{
   240  		ReadMimeType:      true,
   241  		WriteMimeType:     true,
   242  		BucketBased:       true,
   243  		BucketBasedRootOK: true,
   244  	}).Fill(f)
   245  	if f.rootBucket != "" && f.rootDirectory != "" {
   246  		od := buckets.getObjectData(f.rootBucket, f.rootDirectory)
   247  		if od != nil {
   248  			newRoot := path.Dir(f.root)
   249  			if newRoot == "." {
   250  				newRoot = ""
   251  			}
   252  			f.setRoot(newRoot)
   253  			// return an error with an fs which points to the parent
   254  			err = fs.ErrorIsFile
   255  		}
   256  	}
   257  	return f, err
   258  }
   259  
   260  // newObject makes an object from a remote and an objectData
   261  func (f *Fs) newObject(remote string, od *objectData) *Object {
   262  	return &Object{fs: f, remote: remote, od: od}
   263  }
   264  
   265  // NewObject finds the Object at remote.  If it can't be found
   266  // it returns the error fs.ErrorObjectNotFound.
   267  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   268  	bucket, bucketPath := f.split(remote)
   269  	od := buckets.getObjectData(bucket, bucketPath)
   270  	if od == nil {
   271  		return nil, fs.ErrorObjectNotFound
   272  	}
   273  	return f.newObject(remote, od), nil
   274  }
   275  
   276  // listFn is called from list to handle an object.
   277  type listFn func(remote string, entry fs.DirEntry, isDirectory bool) error
   278  
   279  // list the buckets to fn
   280  func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBucket bool, recurse bool, fn listFn) (err error) {
   281  	if prefix != "" {
   282  		prefix += "/"
   283  	}
   284  	if directory != "" {
   285  		directory += "/"
   286  	}
   287  	b := buckets.getBucket(bucket)
   288  	if b == nil {
   289  		return fs.ErrorDirNotFound
   290  	}
   291  	b.mu.RLock()
   292  	defer b.mu.RUnlock()
   293  	dirs := make(map[string]struct{})
   294  	for absPath, od := range b.objects {
   295  		if strings.HasPrefix(absPath, directory) {
   296  			remote := absPath[len(prefix):]
   297  			if !recurse {
   298  				localPath := absPath[len(directory):]
   299  				slash := strings.IndexRune(localPath, '/')
   300  				if slash >= 0 {
   301  					// send a directory if have a slash
   302  					dir := directory + localPath[:slash]
   303  					if addBucket {
   304  						dir = path.Join(bucket, dir)
   305  					}
   306  					_, found := dirs[dir]
   307  					if !found {
   308  						err = fn(dir, fs.NewDir(dir, time.Time{}), true)
   309  						if err != nil {
   310  							return err
   311  						}
   312  						dirs[dir] = struct{}{}
   313  					}
   314  					continue // don't send this file if not recursing
   315  				}
   316  			}
   317  			// send an object
   318  			if addBucket {
   319  				remote = path.Join(bucket, remote)
   320  			}
   321  			err = fn(remote, f.newObject(remote, od), false)
   322  			if err != nil {
   323  				return err
   324  			}
   325  		}
   326  	}
   327  	return nil
   328  }
   329  
   330  // listDir lists the bucket to the entries
   331  func (f *Fs) listDir(ctx context.Context, bucket, directory, prefix string, addBucket bool) (entries fs.DirEntries, err error) {
   332  	// List the objects and directories
   333  	err = f.list(ctx, bucket, directory, prefix, addBucket, false, func(remote string, entry fs.DirEntry, isDirectory bool) error {
   334  		entries = append(entries, entry)
   335  		return nil
   336  	})
   337  	return entries, err
   338  }
   339  
   340  // listBuckets lists the buckets to entries
   341  func (f *Fs) listBuckets(ctx context.Context) (entries fs.DirEntries, err error) {
   342  	buckets.mu.RLock()
   343  	defer buckets.mu.RUnlock()
   344  	for name := range buckets.buckets {
   345  		entries = append(entries, fs.NewDir(name, time.Time{}))
   346  	}
   347  	return entries, nil
   348  }
   349  
   350  // List the objects and directories in dir into entries.  The
   351  // entries can be returned in any order but should be for a
   352  // complete directory.
   353  //
   354  // dir should be "" to list the root, and should not have
   355  // trailing slashes.
   356  //
   357  // This should return ErrDirNotFound if the directory isn't
   358  // found.
   359  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   360  	// defer fslog.Trace(dir, "")("entries = %q, err = %v", &entries, &err)
   361  	bucket, directory := f.split(dir)
   362  	if bucket == "" {
   363  		if directory != "" {
   364  			return nil, fs.ErrorListBucketRequired
   365  		}
   366  		return f.listBuckets(ctx)
   367  	}
   368  	return f.listDir(ctx, bucket, directory, f.rootDirectory, f.rootBucket == "")
   369  }
   370  
   371  // ListR lists the objects and directories of the Fs starting
   372  // from dir recursively into out.
   373  //
   374  // dir should be "" to start from the root, and should not
   375  // have trailing slashes.
   376  //
   377  // This should return ErrDirNotFound if the directory isn't
   378  // found.
   379  //
   380  // It should call callback for each tranche of entries read.
   381  // These need not be returned in any particular order.  If
   382  // callback returns an error then the listing will stop
   383  // immediately.
   384  //
   385  // Don't implement this unless you have a more efficient way
   386  // of listing recursively that doing a directory traversal.
   387  func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) {
   388  	bucket, directory := f.split(dir)
   389  	list := walk.NewListRHelper(callback)
   390  	listR := func(bucket, directory, prefix string, addBucket bool) error {
   391  		return f.list(ctx, bucket, directory, prefix, addBucket, true, func(remote string, entry fs.DirEntry, isDirectory bool) error {
   392  			return list.Add(entry)
   393  		})
   394  	}
   395  	if bucket == "" {
   396  		entries, err := f.listBuckets(ctx)
   397  		if err != nil {
   398  			return err
   399  		}
   400  		for _, entry := range entries {
   401  			err = list.Add(entry)
   402  			if err != nil {
   403  				return err
   404  			}
   405  			bucket := entry.Remote()
   406  			err = listR(bucket, "", f.rootDirectory, true)
   407  			if err != nil {
   408  				return err
   409  			}
   410  		}
   411  	} else {
   412  		err = listR(bucket, directory, f.rootDirectory, f.rootBucket == "")
   413  		if err != nil {
   414  			return err
   415  		}
   416  	}
   417  	return list.Flush()
   418  }
   419  
   420  // Put the object into the bucket
   421  //
   422  // Copy the reader in to the new object which is returned
   423  //
   424  // The new object may have been created if an error is returned
   425  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   426  	// Temporary Object under construction
   427  	fs := &Object{
   428  		fs:     f,
   429  		remote: src.Remote(),
   430  		od: &objectData{
   431  			modTime: src.ModTime(ctx),
   432  		},
   433  	}
   434  	return fs, fs.Update(ctx, in, src, options...)
   435  }
   436  
   437  // PutStream uploads to the remote path with the modTime given of indeterminate size
   438  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   439  	return f.Put(ctx, in, src, options...)
   440  }
   441  
   442  // Mkdir creates the bucket if it doesn't exist
   443  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   444  	bucket, _ := f.split(dir)
   445  	buckets.makeBucket(bucket)
   446  	return nil
   447  }
   448  
   449  // Rmdir deletes the bucket if the fs is at the root
   450  //
   451  // Returns an error if it isn't empty
   452  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   453  	bucket, directory := f.split(dir)
   454  	if bucket == "" || directory != "" {
   455  		return nil
   456  	}
   457  	return buckets.deleteBucket(bucket)
   458  }
   459  
   460  // Precision of the remote
   461  func (f *Fs) Precision() time.Duration {
   462  	return time.Nanosecond
   463  }
   464  
   465  // Copy src to this remote using server side copy operations.
   466  //
   467  // This is stored with the remote path given
   468  //
   469  // It returns the destination Object and a possible error
   470  //
   471  // Will only be called if src.Fs().Name() == f.Name()
   472  //
   473  // If it isn't possible then return fs.ErrorCantCopy
   474  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   475  	dstBucket, dstPath := f.split(remote)
   476  	_ = buckets.makeBucket(dstBucket)
   477  	srcObj, ok := src.(*Object)
   478  	if !ok {
   479  		fs.Debugf(src, "Can't copy - not same remote type")
   480  		return nil, fs.ErrorCantCopy
   481  	}
   482  	srcBucket, srcPath := srcObj.split()
   483  	od := buckets.getObjectData(srcBucket, srcPath)
   484  	if od == nil {
   485  		return nil, fs.ErrorObjectNotFound
   486  	}
   487  	buckets.updateObjectData(dstBucket, dstPath, od)
   488  	return f.NewObject(ctx, remote)
   489  }
   490  
   491  // Hashes returns the supported hash sets.
   492  func (f *Fs) Hashes() hash.Set {
   493  	return hash.Set(hashType)
   494  }
   495  
   496  // ------------------------------------------------------------
   497  
   498  // Fs returns the parent Fs
   499  func (o *Object) Fs() fs.Info {
   500  	return o.fs
   501  }
   502  
   503  // Return a string version
   504  func (o *Object) String() string {
   505  	if o == nil {
   506  		return "<nil>"
   507  	}
   508  	return o.Remote()
   509  }
   510  
   511  // Remote returns the remote path
   512  func (o *Object) Remote() string {
   513  	return o.remote
   514  }
   515  
   516  // Hash returns the hash of an object returning a lowercase hex string
   517  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
   518  	if t != hashType {
   519  		return "", hash.ErrUnsupported
   520  	}
   521  	if o.od.hash == "" {
   522  		sum := md5.Sum(o.od.data)
   523  		o.od.hash = hex.EncodeToString(sum[:])
   524  	}
   525  	return o.od.hash, nil
   526  }
   527  
   528  // Size returns the size of an object in bytes
   529  func (o *Object) Size() int64 {
   530  	return int64(len(o.od.data))
   531  }
   532  
   533  // ModTime returns the modification time of the object
   534  //
   535  // It attempts to read the objects mtime and if that isn't present the
   536  // LastModified returned in the http headers
   537  //
   538  // SHA-1 will also be updated once the request has completed.
   539  func (o *Object) ModTime(ctx context.Context) (result time.Time) {
   540  	return o.od.modTime
   541  }
   542  
   543  // SetModTime sets the modification time of the local fs object
   544  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
   545  	o.od.modTime = modTime
   546  	return nil
   547  }
   548  
   549  // Storable returns if this object is storable
   550  func (o *Object) Storable() bool {
   551  	return true
   552  }
   553  
   554  // Open an object for read
   555  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
   556  	var offset, limit int64 = 0, -1
   557  	for _, option := range options {
   558  		switch x := option.(type) {
   559  		case *fs.RangeOption:
   560  			offset, limit = x.Decode(int64(len(o.od.data)))
   561  		case *fs.SeekOption:
   562  			offset = x.Offset
   563  		default:
   564  			if option.Mandatory() {
   565  				fs.Logf(o, "Unsupported mandatory option: %v", option)
   566  			}
   567  		}
   568  	}
   569  	if offset > int64(len(o.od.data)) {
   570  		offset = int64(len(o.od.data))
   571  	}
   572  	data := o.od.data[offset:]
   573  	if limit >= 0 {
   574  		if limit > int64(len(data)) {
   575  			limit = int64(len(data))
   576  		}
   577  		data = data[:limit]
   578  	}
   579  	return ioutil.NopCloser(bytes.NewBuffer(data)), nil
   580  }
   581  
   582  // Update the object with the contents of the io.Reader, modTime and size
   583  //
   584  // The new object may have been created if an error is returned
   585  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
   586  	bucket, bucketPath := o.split()
   587  	data, err := ioutil.ReadAll(in)
   588  	if err != nil {
   589  		return errors.Wrap(err, "failed to update memory object")
   590  	}
   591  	o.od = &objectData{
   592  		data:     data,
   593  		hash:     "",
   594  		modTime:  src.ModTime(ctx),
   595  		mimeType: fs.MimeType(ctx, o),
   596  	}
   597  	buckets.updateObjectData(bucket, bucketPath, o.od)
   598  	return nil
   599  }
   600  
   601  // Remove an object
   602  func (o *Object) Remove(ctx context.Context) error {
   603  	bucket, bucketPath := o.split()
   604  	removed := buckets.removeObjectData(bucket, bucketPath)
   605  	if !removed {
   606  		return fs.ErrorObjectNotFound
   607  	}
   608  	return nil
   609  }
   610  
   611  // MimeType of an Object if known, "" otherwise
   612  func (o *Object) MimeType(ctx context.Context) string {
   613  	return o.od.mimeType
   614  }
   615  
   616  // Check the interfaces are satisfied
   617  var (
   618  	_ fs.Fs          = &Fs{}
   619  	_ fs.Copier      = &Fs{}
   620  	_ fs.PutStreamer = &Fs{}
   621  	_ fs.ListRer     = &Fs{}
   622  	_ fs.Object      = &Object{}
   623  	_ fs.MimeTyper   = &Object{}
   624  )