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

     1  package upstream
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"math"
     7  	"path"
     8  	"path/filepath"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/pkg/errors"
    15  	"github.com/rclone/rclone/fs"
    16  	"github.com/rclone/rclone/fs/cache"
    17  )
    18  
    19  var (
    20  	// ErrUsageFieldNotSupported stats the usage field is not supported by the backend
    21  	ErrUsageFieldNotSupported = errors.New("this usage field is not supported")
    22  )
    23  
    24  // Fs is a wrap of any fs and its configs
    25  type Fs struct {
    26  	fs.Fs
    27  	RootFs      fs.Fs
    28  	RootPath    string
    29  	writable    bool
    30  	creatable   bool
    31  	usage       *fs.Usage     // Cache the usage
    32  	cacheTime   time.Duration // cache duration
    33  	cacheExpiry int64         // usage cache expiry time
    34  	cacheMutex  sync.RWMutex
    35  	cacheOnce   sync.Once
    36  	cacheUpdate bool // if the cache is updating
    37  }
    38  
    39  // Directory describes a wrapped Directory
    40  //
    41  // This is a wrapped Directory which contains the upstream Fs
    42  type Directory struct {
    43  	fs.Directory
    44  	f *Fs
    45  }
    46  
    47  // Object describes a wrapped Object
    48  //
    49  // This is a wrapped Object which contains the upstream Fs
    50  type Object struct {
    51  	fs.Object
    52  	f *Fs
    53  }
    54  
    55  // Entry describe a warpped fs.DirEntry interface with the
    56  // information of upstream Fs
    57  type Entry interface {
    58  	fs.DirEntry
    59  	UpstreamFs() *Fs
    60  }
    61  
    62  // New creates a new Fs based on the
    63  // string formatted `type:root_path(:ro/:nc)`
    64  func New(remote, root string, cacheTime time.Duration) (*Fs, error) {
    65  	_, configName, fsPath, err := fs.ParseRemote(remote)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	f := &Fs{
    70  		RootPath:    root,
    71  		writable:    true,
    72  		creatable:   true,
    73  		cacheExpiry: time.Now().Unix(),
    74  		cacheTime:   cacheTime,
    75  		usage:       &fs.Usage{},
    76  	}
    77  	if strings.HasSuffix(fsPath, ":ro") {
    78  		f.writable = false
    79  		f.creatable = false
    80  		fsPath = fsPath[0 : len(fsPath)-3]
    81  	} else if strings.HasSuffix(fsPath, ":nc") {
    82  		f.writable = true
    83  		f.creatable = false
    84  		fsPath = fsPath[0 : len(fsPath)-3]
    85  	}
    86  	if configName != "local" {
    87  		fsPath = configName + ":" + fsPath
    88  	}
    89  	rFs, err := cache.Get(fsPath)
    90  	if err != nil && err != fs.ErrorIsFile {
    91  		return nil, err
    92  	}
    93  	f.RootFs = rFs
    94  	rootString := path.Join(fsPath, filepath.ToSlash(root))
    95  	myFs, err := cache.Get(rootString)
    96  	if err != nil && err != fs.ErrorIsFile {
    97  		return nil, err
    98  	}
    99  	f.Fs = myFs
   100  	return f, err
   101  }
   102  
   103  // WrapDirectory wraps an fs.Directory to include the info
   104  // of the upstream Fs
   105  func (f *Fs) WrapDirectory(e fs.Directory) *Directory {
   106  	if e == nil {
   107  		return nil
   108  	}
   109  	return &Directory{
   110  		Directory: e,
   111  		f:         f,
   112  	}
   113  }
   114  
   115  // WrapObject wraps an fs.Object to include the info
   116  // of the upstream Fs
   117  func (f *Fs) WrapObject(o fs.Object) *Object {
   118  	if o == nil {
   119  		return nil
   120  	}
   121  	return &Object{
   122  		Object: o,
   123  		f:      f,
   124  	}
   125  }
   126  
   127  // WrapEntry wraps an fs.DirEntry to include the info
   128  // of the upstream Fs
   129  func (f *Fs) WrapEntry(e fs.DirEntry) (Entry, error) {
   130  	switch e.(type) {
   131  	case fs.Object:
   132  		return f.WrapObject(e.(fs.Object)), nil
   133  	case fs.Directory:
   134  		return f.WrapDirectory(e.(fs.Directory)), nil
   135  	default:
   136  		return nil, errors.Errorf("unknown object type %T", e)
   137  	}
   138  }
   139  
   140  // UpstreamFs get the upstream Fs the entry is stored in
   141  func (e *Directory) UpstreamFs() *Fs {
   142  	return e.f
   143  }
   144  
   145  // UpstreamFs get the upstream Fs the entry is stored in
   146  func (o *Object) UpstreamFs() *Fs {
   147  	return o.f
   148  }
   149  
   150  // UnWrap returns the Object that this Object is wrapping or
   151  // nil if it isn't wrapping anything
   152  func (o *Object) UnWrap() fs.Object {
   153  	return o.Object
   154  }
   155  
   156  // IsCreatable return if the fs is allowed to create new objects
   157  func (f *Fs) IsCreatable() bool {
   158  	return f.creatable
   159  }
   160  
   161  // IsWritable return if the fs is allowed to write
   162  func (f *Fs) IsWritable() bool {
   163  	return f.writable
   164  }
   165  
   166  // Put in to the remote path with the modTime given of the given size
   167  //
   168  // May create the object even if it returns an error - if so
   169  // will return the object and the error, otherwise will return
   170  // nil and the error
   171  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   172  	o, err := f.Fs.Put(ctx, in, src, options...)
   173  	if err != nil {
   174  		return o, err
   175  	}
   176  	f.cacheMutex.Lock()
   177  	defer f.cacheMutex.Unlock()
   178  	size := src.Size()
   179  	if f.usage.Used != nil {
   180  		*f.usage.Used += size
   181  	}
   182  	if f.usage.Free != nil {
   183  		*f.usage.Free -= size
   184  	}
   185  	if f.usage.Objects != nil {
   186  		*f.usage.Objects++
   187  	}
   188  	return o, nil
   189  }
   190  
   191  // PutStream uploads to the remote path with the modTime given of indeterminate size
   192  //
   193  // May create the object even if it returns an error - if so
   194  // will return the object and the error, otherwise will return
   195  // nil and the error
   196  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   197  	do := f.Features().PutStream
   198  	if do == nil {
   199  		return nil, fs.ErrorNotImplemented
   200  	}
   201  	o, err := do(ctx, in, src, options...)
   202  	if err != nil {
   203  		return o, err
   204  	}
   205  	f.cacheMutex.Lock()
   206  	defer f.cacheMutex.Unlock()
   207  	size := o.Size()
   208  	if f.usage.Used != nil {
   209  		*f.usage.Used += size
   210  	}
   211  	if f.usage.Free != nil {
   212  		*f.usage.Free -= size
   213  	}
   214  	if f.usage.Objects != nil {
   215  		*f.usage.Objects++
   216  	}
   217  	return o, nil
   218  }
   219  
   220  // Update in to the object with the modTime given of the given size
   221  //
   222  // When called from outside an Fs by rclone, src.Size() will always be >= 0.
   223  // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
   224  // return an error or update the object properly (rather than e.g. calling panic).
   225  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
   226  	size := o.Size()
   227  	err := o.Object.Update(ctx, in, src, options...)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	o.f.cacheMutex.Lock()
   232  	defer o.f.cacheMutex.Unlock()
   233  	delta := o.Size() - size
   234  	if delta <= 0 {
   235  		return nil
   236  	}
   237  	if o.f.usage.Used != nil {
   238  		*o.f.usage.Used += size
   239  	}
   240  	if o.f.usage.Free != nil {
   241  		*o.f.usage.Free -= size
   242  	}
   243  	return nil
   244  }
   245  
   246  // About gets quota information from the Fs
   247  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   248  	if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() {
   249  		err := f.updateUsage()
   250  		if err != nil {
   251  			return nil, ErrUsageFieldNotSupported
   252  		}
   253  	}
   254  	f.cacheMutex.RLock()
   255  	defer f.cacheMutex.RUnlock()
   256  	return f.usage, nil
   257  }
   258  
   259  // GetFreeSpace get the free space of the fs
   260  func (f *Fs) GetFreeSpace() (int64, error) {
   261  	if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() {
   262  		err := f.updateUsage()
   263  		if err != nil {
   264  			return math.MaxInt64, ErrUsageFieldNotSupported
   265  		}
   266  	}
   267  	f.cacheMutex.RLock()
   268  	defer f.cacheMutex.RUnlock()
   269  	if f.usage.Free == nil {
   270  		return math.MaxInt64, ErrUsageFieldNotSupported
   271  	}
   272  	return *f.usage.Free, nil
   273  }
   274  
   275  // GetUsedSpace get the used space of the fs
   276  func (f *Fs) GetUsedSpace() (int64, error) {
   277  	if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() {
   278  		err := f.updateUsage()
   279  		if err != nil {
   280  			return 0, ErrUsageFieldNotSupported
   281  		}
   282  	}
   283  	f.cacheMutex.RLock()
   284  	defer f.cacheMutex.RUnlock()
   285  	if f.usage.Used == nil {
   286  		return 0, ErrUsageFieldNotSupported
   287  	}
   288  	return *f.usage.Used, nil
   289  }
   290  
   291  // GetNumObjects get the number of objects of the fs
   292  func (f *Fs) GetNumObjects() (int64, error) {
   293  	if atomic.LoadInt64(&f.cacheExpiry) <= time.Now().Unix() {
   294  		err := f.updateUsage()
   295  		if err != nil {
   296  			return 0, ErrUsageFieldNotSupported
   297  		}
   298  	}
   299  	f.cacheMutex.RLock()
   300  	defer f.cacheMutex.RUnlock()
   301  	if f.usage.Objects == nil {
   302  		return 0, ErrUsageFieldNotSupported
   303  	}
   304  	return *f.usage.Objects, nil
   305  }
   306  
   307  func (f *Fs) updateUsage() (err error) {
   308  	if do := f.RootFs.Features().About; do == nil {
   309  		return ErrUsageFieldNotSupported
   310  	}
   311  	done := false
   312  	f.cacheOnce.Do(func() {
   313  		f.cacheMutex.Lock()
   314  		err = f.updateUsageCore(false)
   315  		f.cacheMutex.Unlock()
   316  		done = true
   317  	})
   318  	if done {
   319  		return err
   320  	}
   321  	if !f.cacheUpdate {
   322  		f.cacheUpdate = true
   323  		go func() {
   324  			_ = f.updateUsageCore(true)
   325  			f.cacheUpdate = false
   326  		}()
   327  	}
   328  	return nil
   329  }
   330  
   331  func (f *Fs) updateUsageCore(lock bool) error {
   332  	// Run in background, should not be cancelled by user
   333  	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
   334  	defer cancel()
   335  	usage, err := f.RootFs.Features().About(ctx)
   336  	if err != nil {
   337  		f.cacheUpdate = false
   338  		return err
   339  	}
   340  	if lock {
   341  		f.cacheMutex.Lock()
   342  		defer f.cacheMutex.Unlock()
   343  	}
   344  	// Store usage
   345  	atomic.StoreInt64(&f.cacheExpiry, time.Now().Add(f.cacheTime).Unix())
   346  	f.usage = usage
   347  	return nil
   348  }