github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/union/upstream/upstream.go (about)

     1  // Package upstream provides utility functionality to union.
     2  package upstream
     3  
     4  import (
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"github.com/rclone/rclone/backend/union/common"
    16  	"github.com/rclone/rclone/fs"
    17  	"github.com/rclone/rclone/fs/cache"
    18  	"github.com/rclone/rclone/fs/fspath"
    19  	"github.com/rclone/rclone/fs/operations"
    20  )
    21  
    22  var (
    23  	// ErrUsageFieldNotSupported stats the usage field is not supported by the backend
    24  	ErrUsageFieldNotSupported = errors.New("this usage field is not supported")
    25  )
    26  
    27  // Fs is a wrap of any fs and its configs
    28  type Fs struct {
    29  	fs.Fs
    30  	RootFs      fs.Fs
    31  	RootPath    string
    32  	Opt         *common.Options
    33  	writable    bool
    34  	creatable   bool
    35  	usage       *fs.Usage     // Cache the usage
    36  	cacheTime   time.Duration // cache duration
    37  	cacheExpiry atomic.Int64  // usage cache expiry time
    38  	cacheMutex  sync.RWMutex
    39  	cacheOnce   sync.Once
    40  	cacheUpdate bool // if the cache is updating
    41  	writeback   bool // writeback to this upstream
    42  	writebackFs *Fs  // if non zero, writeback to this upstream
    43  }
    44  
    45  // Directory describes a wrapped Directory
    46  //
    47  // This is a wrapped Directory which contains the upstream Fs
    48  type Directory struct {
    49  	fs.Directory
    50  	f *Fs
    51  }
    52  
    53  // Object describes a wrapped Object
    54  //
    55  // This is a wrapped Object which contains the upstream Fs
    56  type Object struct {
    57  	fs.Object
    58  	f *Fs
    59  }
    60  
    61  // Entry describe a wrapped fs.DirEntry interface with the
    62  // information of upstream Fs
    63  type Entry interface {
    64  	fs.DirEntry
    65  	UpstreamFs() *Fs
    66  }
    67  
    68  // New creates a new Fs based on the
    69  // string formatted `type:root_path(:ro/:nc)`
    70  func New(ctx context.Context, remote, root string, opt *common.Options) (*Fs, error) {
    71  	configName, fsPath, err := fspath.SplitFs(remote)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	f := &Fs{
    76  		RootPath:  strings.TrimRight(root, "/"),
    77  		Opt:       opt,
    78  		writable:  true,
    79  		creatable: true,
    80  		cacheTime: time.Duration(opt.CacheTime) * time.Second,
    81  		usage:     &fs.Usage{},
    82  	}
    83  	f.cacheExpiry.Store(time.Now().Unix())
    84  	if strings.HasSuffix(fsPath, ":ro") {
    85  		f.writable = false
    86  		f.creatable = false
    87  		fsPath = fsPath[0 : len(fsPath)-3]
    88  	} else if strings.HasSuffix(fsPath, ":nc") {
    89  		f.writable = true
    90  		f.creatable = false
    91  		fsPath = fsPath[0 : len(fsPath)-3]
    92  	} else if strings.HasSuffix(fsPath, ":writeback") {
    93  		f.writeback = true
    94  		fsPath = fsPath[0 : len(fsPath)-len(":writeback")]
    95  	}
    96  	remote = configName + fsPath
    97  	rFs, err := cache.Get(ctx, remote)
    98  	if err != nil && err != fs.ErrorIsFile {
    99  		return nil, err
   100  	}
   101  	f.RootFs = rFs
   102  	rootString := fspath.JoinRootPath(remote, root)
   103  	myFs, err := cache.Get(ctx, rootString)
   104  	if err != nil && err != fs.ErrorIsFile {
   105  		return nil, err
   106  	}
   107  	f.Fs = myFs
   108  	cache.PinUntilFinalized(f.Fs, f)
   109  	return f, err
   110  }
   111  
   112  // Prepare the configured upstreams as a group
   113  func Prepare(fses []*Fs) error {
   114  	writebacks := 0
   115  	var writebackFs *Fs
   116  	for _, f := range fses {
   117  		if f.writeback {
   118  			writebackFs = f
   119  			writebacks++
   120  		}
   121  	}
   122  	if writebacks == 0 {
   123  		return nil
   124  	} else if writebacks > 1 {
   125  		return fmt.Errorf("can only have 1 :writeback not %d", writebacks)
   126  	}
   127  	for _, f := range fses {
   128  		if !f.writeback {
   129  			f.writebackFs = writebackFs
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  // WrapDirectory wraps an fs.Directory to include the info
   136  // of the upstream Fs
   137  func (f *Fs) WrapDirectory(e fs.Directory) *Directory {
   138  	if e == nil {
   139  		return nil
   140  	}
   141  	return &Directory{
   142  		Directory: e,
   143  		f:         f,
   144  	}
   145  }
   146  
   147  // WrapObject wraps an fs.Object to include the info
   148  // of the upstream Fs
   149  func (f *Fs) WrapObject(o fs.Object) *Object {
   150  	if o == nil {
   151  		return nil
   152  	}
   153  	return &Object{
   154  		Object: o,
   155  		f:      f,
   156  	}
   157  }
   158  
   159  // WrapEntry wraps an fs.DirEntry to include the info
   160  // of the upstream Fs
   161  func (f *Fs) WrapEntry(e fs.DirEntry) (Entry, error) {
   162  	switch e := e.(type) {
   163  	case fs.Object:
   164  		return f.WrapObject(e), nil
   165  	case fs.Directory:
   166  		return f.WrapDirectory(e), nil
   167  	default:
   168  		return nil, fmt.Errorf("unknown object type %T", e)
   169  	}
   170  }
   171  
   172  // UpstreamFs get the upstream Fs the entry is stored in
   173  func (e *Directory) UpstreamFs() *Fs {
   174  	return e.f
   175  }
   176  
   177  // UpstreamFs get the upstream Fs the entry is stored in
   178  func (o *Object) UpstreamFs() *Fs {
   179  	return o.f
   180  }
   181  
   182  // UnWrap returns the Object that this Object is wrapping or
   183  // nil if it isn't wrapping anything
   184  func (o *Object) UnWrap() fs.Object {
   185  	return o.Object
   186  }
   187  
   188  // IsCreatable return if the fs is allowed to create new objects
   189  func (f *Fs) IsCreatable() bool {
   190  	return f.creatable
   191  }
   192  
   193  // IsWritable return if the fs is allowed to write
   194  func (f *Fs) IsWritable() bool {
   195  	return f.writable
   196  }
   197  
   198  // Put in to the remote path with the modTime given of the given size
   199  //
   200  // May create the object even if it returns an error - if so
   201  // will return the object and the error, otherwise will return
   202  // nil and the error
   203  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   204  	o, err := f.Fs.Put(ctx, in, src, options...)
   205  	if err != nil {
   206  		return o, err
   207  	}
   208  	f.cacheMutex.Lock()
   209  	defer f.cacheMutex.Unlock()
   210  	size := src.Size()
   211  	if f.usage.Used != nil {
   212  		*f.usage.Used += size
   213  	}
   214  	if f.usage.Free != nil {
   215  		*f.usage.Free -= size
   216  	}
   217  	if f.usage.Objects != nil {
   218  		*f.usage.Objects++
   219  	}
   220  	return o, nil
   221  }
   222  
   223  // PutStream uploads to the remote path with the modTime given of indeterminate size
   224  //
   225  // May create the object even if it returns an error - if so
   226  // will return the object and the error, otherwise will return
   227  // nil and the error
   228  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   229  	do := f.Features().PutStream
   230  	if do == nil {
   231  		return nil, fs.ErrorNotImplemented
   232  	}
   233  	o, err := do(ctx, in, src, options...)
   234  	if err != nil {
   235  		return o, err
   236  	}
   237  	f.cacheMutex.Lock()
   238  	defer f.cacheMutex.Unlock()
   239  	size := o.Size()
   240  	if f.usage.Used != nil {
   241  		*f.usage.Used += size
   242  	}
   243  	if f.usage.Free != nil {
   244  		*f.usage.Free -= size
   245  	}
   246  	if f.usage.Objects != nil {
   247  		*f.usage.Objects++
   248  	}
   249  	return o, nil
   250  }
   251  
   252  // Update in to the object with the modTime given of the given size
   253  //
   254  // When called from outside an Fs by rclone, src.Size() will always be >= 0.
   255  // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
   256  // return an error or update the object properly (rather than e.g. calling panic).
   257  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
   258  	size := o.Size()
   259  	err := o.Object.Update(ctx, in, src, options...)
   260  	if err != nil {
   261  		return err
   262  	}
   263  	o.f.cacheMutex.Lock()
   264  	defer o.f.cacheMutex.Unlock()
   265  	delta := o.Size() - size
   266  	if delta <= 0 {
   267  		return nil
   268  	}
   269  	if o.f.usage.Used != nil {
   270  		*o.f.usage.Used += size
   271  	}
   272  	if o.f.usage.Free != nil {
   273  		*o.f.usage.Free -= size
   274  	}
   275  	return nil
   276  }
   277  
   278  // GetTier returns storage tier or class of the Object
   279  func (o *Object) GetTier() string {
   280  	do, ok := o.Object.(fs.GetTierer)
   281  	if !ok {
   282  		return ""
   283  	}
   284  	return do.GetTier()
   285  }
   286  
   287  // ID returns the ID of the Object if known, or "" if not
   288  func (o *Object) ID() string {
   289  	do, ok := o.Object.(fs.IDer)
   290  	if !ok {
   291  		return ""
   292  	}
   293  	return do.ID()
   294  }
   295  
   296  // MimeType returns the content type of the Object if known
   297  func (o *Object) MimeType(ctx context.Context) (mimeType string) {
   298  	if do, ok := o.Object.(fs.MimeTyper); ok {
   299  		mimeType = do.MimeType(ctx)
   300  	}
   301  	return mimeType
   302  }
   303  
   304  // SetTier performs changing storage tier of the Object if
   305  // multiple storage classes supported
   306  func (o *Object) SetTier(tier string) error {
   307  	do, ok := o.Object.(fs.SetTierer)
   308  	if !ok {
   309  		return errors.New("underlying remote does not support SetTier")
   310  	}
   311  	return do.SetTier(tier)
   312  }
   313  
   314  // Metadata returns metadata for an object
   315  //
   316  // It should return nil if there is no Metadata
   317  func (o *Object) Metadata(ctx context.Context) (fs.Metadata, error) {
   318  	do, ok := o.Object.(fs.Metadataer)
   319  	if !ok {
   320  		return nil, nil
   321  	}
   322  	return do.Metadata(ctx)
   323  }
   324  
   325  // SetMetadata sets metadata for an Object
   326  //
   327  // It should return fs.ErrorNotImplemented if it can't set metadata
   328  func (o *Object) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
   329  	do, ok := o.Object.(fs.SetMetadataer)
   330  	if !ok {
   331  		return fs.ErrorNotImplemented
   332  	}
   333  	return do.SetMetadata(ctx, metadata)
   334  }
   335  
   336  // Metadata returns metadata for an DirEntry
   337  //
   338  // It should return nil if there is no Metadata
   339  func (e *Directory) Metadata(ctx context.Context) (fs.Metadata, error) {
   340  	do, ok := e.Directory.(fs.Metadataer)
   341  	if !ok {
   342  		return nil, nil
   343  	}
   344  	return do.Metadata(ctx)
   345  }
   346  
   347  // SetMetadata sets metadata for an DirEntry
   348  //
   349  // It should return fs.ErrorNotImplemented if it can't set metadata
   350  func (e *Directory) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
   351  	do, ok := e.Directory.(fs.SetMetadataer)
   352  	if !ok {
   353  		return fs.ErrorNotImplemented
   354  	}
   355  	return do.SetMetadata(ctx, metadata)
   356  }
   357  
   358  // SetModTime sets the metadata on the DirEntry to set the modification date
   359  //
   360  // If there is any other metadata it does not overwrite it.
   361  func (e *Directory) SetModTime(ctx context.Context, t time.Time) error {
   362  	do, ok := e.Directory.(fs.SetModTimer)
   363  	if !ok {
   364  		return fs.ErrorNotImplemented
   365  	}
   366  	return do.SetModTime(ctx, t)
   367  }
   368  
   369  // Writeback writes the object back and returns a new object
   370  //
   371  // If it returns nil, nil then the original object is OK
   372  func (o *Object) Writeback(ctx context.Context) (*Object, error) {
   373  	if o.f.writebackFs == nil {
   374  		return nil, nil
   375  	}
   376  	newObj, err := operations.Copy(ctx, o.f.writebackFs.Fs, nil, o.Object.Remote(), o.Object)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	// newObj could be nil here
   381  	if newObj == nil {
   382  		fs.Errorf(o, "nil Object returned from operations.Copy")
   383  		return nil, nil
   384  	}
   385  	return &Object{
   386  		Object: newObj,
   387  		f:      o.f,
   388  	}, err
   389  }
   390  
   391  // About gets quota information from the Fs
   392  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   393  	if f.cacheExpiry.Load() <= time.Now().Unix() {
   394  		err := f.updateUsage()
   395  		if err != nil {
   396  			return nil, ErrUsageFieldNotSupported
   397  		}
   398  	}
   399  	f.cacheMutex.RLock()
   400  	defer f.cacheMutex.RUnlock()
   401  	return f.usage, nil
   402  }
   403  
   404  // GetFreeSpace get the free space of the fs
   405  //
   406  // This is returned as 0..math.MaxInt64-1 leaving math.MaxInt64 as a sentinel
   407  func (f *Fs) GetFreeSpace() (int64, error) {
   408  	if f.cacheExpiry.Load() <= time.Now().Unix() {
   409  		err := f.updateUsage()
   410  		if err != nil {
   411  			return math.MaxInt64 - 1, ErrUsageFieldNotSupported
   412  		}
   413  	}
   414  	f.cacheMutex.RLock()
   415  	defer f.cacheMutex.RUnlock()
   416  	if f.usage.Free == nil {
   417  		return math.MaxInt64 - 1, ErrUsageFieldNotSupported
   418  	}
   419  	return *f.usage.Free, nil
   420  }
   421  
   422  // GetUsedSpace get the used space of the fs
   423  //
   424  // This is returned as 0..math.MaxInt64-1 leaving math.MaxInt64 as a sentinel
   425  func (f *Fs) GetUsedSpace() (int64, error) {
   426  	if f.cacheExpiry.Load() <= time.Now().Unix() {
   427  		err := f.updateUsage()
   428  		if err != nil {
   429  			return 0, ErrUsageFieldNotSupported
   430  		}
   431  	}
   432  	f.cacheMutex.RLock()
   433  	defer f.cacheMutex.RUnlock()
   434  	if f.usage.Used == nil {
   435  		return 0, ErrUsageFieldNotSupported
   436  	}
   437  	return *f.usage.Used, nil
   438  }
   439  
   440  // GetNumObjects get the number of objects of the fs
   441  func (f *Fs) GetNumObjects() (int64, error) {
   442  	if f.cacheExpiry.Load() <= time.Now().Unix() {
   443  		err := f.updateUsage()
   444  		if err != nil {
   445  			return 0, ErrUsageFieldNotSupported
   446  		}
   447  	}
   448  	f.cacheMutex.RLock()
   449  	defer f.cacheMutex.RUnlock()
   450  	if f.usage.Objects == nil {
   451  		return 0, ErrUsageFieldNotSupported
   452  	}
   453  	return *f.usage.Objects, nil
   454  }
   455  
   456  func (f *Fs) updateUsage() (err error) {
   457  	if do := f.RootFs.Features().About; do == nil {
   458  		return ErrUsageFieldNotSupported
   459  	}
   460  	done := false
   461  	f.cacheOnce.Do(func() {
   462  		f.cacheMutex.Lock()
   463  		err = f.updateUsageCore(false)
   464  		f.cacheMutex.Unlock()
   465  		done = true
   466  	})
   467  	if done {
   468  		return err
   469  	}
   470  	if !f.cacheUpdate {
   471  		f.cacheUpdate = true
   472  		go func() {
   473  			_ = f.updateUsageCore(true)
   474  			f.cacheUpdate = false
   475  		}()
   476  	}
   477  	return nil
   478  }
   479  
   480  func (f *Fs) updateUsageCore(lock bool) error {
   481  	// Run in background, should not be cancelled by user
   482  	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
   483  	defer cancel()
   484  	usage, err := f.RootFs.Features().About(ctx)
   485  	if err != nil {
   486  		f.cacheUpdate = false
   487  		if errors.Is(err, fs.ErrorDirNotFound) {
   488  			err = nil
   489  		}
   490  		return err
   491  	}
   492  	if lock {
   493  		f.cacheMutex.Lock()
   494  		defer f.cacheMutex.Unlock()
   495  	}
   496  	// Store usage
   497  	f.cacheExpiry.Store(time.Now().Add(f.cacheTime).Unix())
   498  	f.usage = usage
   499  	return nil
   500  }
   501  
   502  // Check the interfaces are satisfied
   503  var (
   504  	_ fs.FullObject    = (*Object)(nil)
   505  	_ fs.FullDirectory = (*Directory)(nil)
   506  )