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

     1  // Package vfs provides a virtual filing system layer over rclone's
     2  // native objects.
     3  //
     4  // It attempts to behave in a similar way to Go's filing system
     5  // manipulation code in the os package.  The same named function
     6  // should behave in an identical fashion.  The objects also obey Go's
     7  // standard interfaces.
     8  //
     9  // Note that paths don't start or end with /, so the root directory
    10  // may be referred to as "".  However Stat strips slashes so you can
    11  // use paths with slashes in.
    12  //
    13  // It also includes directory caching
    14  //
    15  // The vfs package returns Error values to signal precisely which
    16  // error conditions have ocurred.  It may also return general errors
    17  // it receives.  It tries to use os Error values (eg os.ErrExist)
    18  // where possible.
    19  
    20  //go:generate sh -c "go run make_open_tests.go | gofmt > open_test.go"
    21  
    22  package vfs
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"os"
    29  	"path"
    30  	"sort"
    31  	"strings"
    32  	"sync"
    33  	"sync/atomic"
    34  	"time"
    35  
    36  	"github.com/rclone/rclone/fs"
    37  	"github.com/rclone/rclone/fs/cache"
    38  	"github.com/rclone/rclone/fs/log"
    39  	"github.com/rclone/rclone/vfs/vfscache"
    40  	"github.com/rclone/rclone/vfs/vfscommon"
    41  )
    42  
    43  // Node represents either a directory (*Dir) or a file (*File)
    44  type Node interface {
    45  	os.FileInfo
    46  	IsFile() bool
    47  	Inode() uint64
    48  	SetModTime(modTime time.Time) error
    49  	Sync() error
    50  	Remove() error
    51  	RemoveAll() error
    52  	DirEntry() fs.DirEntry
    53  	VFS() *VFS
    54  	Open(flags int) (Handle, error)
    55  	Truncate(size int64) error
    56  	Path() string
    57  	SetSys(interface{})
    58  }
    59  
    60  // Check interfaces
    61  var (
    62  	_ Node = (*File)(nil)
    63  	_ Node = (*Dir)(nil)
    64  )
    65  
    66  // Nodes is a slice of Node
    67  type Nodes []Node
    68  
    69  // Sort functions
    70  func (ns Nodes) Len() int           { return len(ns) }
    71  func (ns Nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
    72  func (ns Nodes) Less(i, j int) bool { return ns[i].Path() < ns[j].Path() }
    73  
    74  // Noder represents something which can return a node
    75  type Noder interface {
    76  	fmt.Stringer
    77  	Node() Node
    78  }
    79  
    80  // Check interfaces
    81  var (
    82  	_ Noder = (*File)(nil)
    83  	_ Noder = (*Dir)(nil)
    84  	_ Noder = (*ReadFileHandle)(nil)
    85  	_ Noder = (*WriteFileHandle)(nil)
    86  	_ Noder = (*RWFileHandle)(nil)
    87  	_ Noder = (*DirHandle)(nil)
    88  )
    89  
    90  // OsFiler is the methods on *os.File
    91  type OsFiler interface {
    92  	Chdir() error
    93  	Chmod(mode os.FileMode) error
    94  	Chown(uid, gid int) error
    95  	Close() error
    96  	Fd() uintptr
    97  	Name() string
    98  	Read(b []byte) (n int, err error)
    99  	ReadAt(b []byte, off int64) (n int, err error)
   100  	Readdir(n int) ([]os.FileInfo, error)
   101  	Readdirnames(n int) (names []string, err error)
   102  	Seek(offset int64, whence int) (ret int64, err error)
   103  	Stat() (os.FileInfo, error)
   104  	Sync() error
   105  	Truncate(size int64) error
   106  	Write(b []byte) (n int, err error)
   107  	WriteAt(b []byte, off int64) (n int, err error)
   108  	WriteString(s string) (n int, err error)
   109  }
   110  
   111  // Handle is the interface statisified by open files or directories.
   112  // It is the methods on *os.File, plus a few more useful for FUSE
   113  // filingsystems.  Not all of them are supported.
   114  type Handle interface {
   115  	OsFiler
   116  	// Additional methods useful for FUSE filesystems
   117  	Flush() error
   118  	Release() error
   119  	Node() Node
   120  	//	Size() int64
   121  }
   122  
   123  // baseHandle implements all the missing methods
   124  type baseHandle struct{}
   125  
   126  func (h baseHandle) Chdir() error                                         { return ENOSYS }
   127  func (h baseHandle) Chmod(mode os.FileMode) error                         { return ENOSYS }
   128  func (h baseHandle) Chown(uid, gid int) error                             { return ENOSYS }
   129  func (h baseHandle) Close() error                                         { return ENOSYS }
   130  func (h baseHandle) Fd() uintptr                                          { return 0 }
   131  func (h baseHandle) Name() string                                         { return "" }
   132  func (h baseHandle) Read(b []byte) (n int, err error)                     { return 0, ENOSYS }
   133  func (h baseHandle) ReadAt(b []byte, off int64) (n int, err error)        { return 0, ENOSYS }
   134  func (h baseHandle) Readdir(n int) ([]os.FileInfo, error)                 { return nil, ENOSYS }
   135  func (h baseHandle) Readdirnames(n int) (names []string, err error)       { return nil, ENOSYS }
   136  func (h baseHandle) Seek(offset int64, whence int) (ret int64, err error) { return 0, ENOSYS }
   137  func (h baseHandle) Stat() (os.FileInfo, error)                           { return nil, ENOSYS }
   138  func (h baseHandle) Sync() error                                          { return nil }
   139  func (h baseHandle) Truncate(size int64) error                            { return ENOSYS }
   140  func (h baseHandle) Write(b []byte) (n int, err error)                    { return 0, ENOSYS }
   141  func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error)       { return 0, ENOSYS }
   142  func (h baseHandle) WriteString(s string) (n int, err error)              { return 0, ENOSYS }
   143  func (h baseHandle) Flush() (err error)                                   { return ENOSYS }
   144  func (h baseHandle) Release() (err error)                                 { return ENOSYS }
   145  func (h baseHandle) Node() Node                                           { return nil }
   146  
   147  //func (h baseHandle) Size() int64                                          { return 0 }
   148  
   149  // Check interfaces
   150  var (
   151  	_ OsFiler = (*os.File)(nil)
   152  	_ Handle  = (*baseHandle)(nil)
   153  	_ Handle  = (*ReadFileHandle)(nil)
   154  	_ Handle  = (*WriteFileHandle)(nil)
   155  	_ Handle  = (*DirHandle)(nil)
   156  )
   157  
   158  // VFS represents the top level filing system
   159  type VFS struct {
   160  	f         fs.Fs
   161  	root      *Dir
   162  	Opt       vfscommon.Options
   163  	cache     *vfscache.Cache
   164  	cancel    context.CancelFunc
   165  	usageMu   sync.Mutex
   166  	usageTime time.Time
   167  	usage     *fs.Usage
   168  	pollChan  chan time.Duration
   169  }
   170  
   171  // New creates a new VFS and root directory.  If opt is nil, then
   172  // DefaultOpt will be used
   173  func New(f fs.Fs, opt *vfscommon.Options) *VFS {
   174  	fsDir := fs.NewDir("", time.Now())
   175  	vfs := &VFS{
   176  		f: f,
   177  	}
   178  
   179  	// Make a copy of the options
   180  	if opt != nil {
   181  		vfs.Opt = *opt
   182  	} else {
   183  		vfs.Opt = vfscommon.DefaultOpt
   184  	}
   185  
   186  	// Mask the permissions with the umask
   187  	vfs.Opt.DirPerms &= ^os.FileMode(vfs.Opt.Umask)
   188  	vfs.Opt.FilePerms &= ^os.FileMode(vfs.Opt.Umask)
   189  
   190  	// Make sure directories are returned as directories
   191  	vfs.Opt.DirPerms |= os.ModeDir
   192  
   193  	// Create root directory
   194  	vfs.root = newDir(vfs, f, nil, fsDir)
   195  
   196  	// Start polling function
   197  	if do := vfs.f.Features().ChangeNotify; do != nil {
   198  		vfs.pollChan = make(chan time.Duration)
   199  		do(context.TODO(), vfs.root.changeNotify, vfs.pollChan)
   200  		vfs.pollChan <- vfs.Opt.PollInterval
   201  	} else {
   202  		fs.Infof(f, "poll-interval is not supported by this remote")
   203  	}
   204  
   205  	vfs.SetCacheMode(vfs.Opt.CacheMode)
   206  
   207  	// add the remote control
   208  	vfs.addRC()
   209  
   210  	// Pin the Fs into the cache so that when we use cache.NewFs
   211  	// with the same remote string we get this one. The Pin is
   212  	// removed by Shutdown
   213  	cache.Pin(f)
   214  	return vfs
   215  }
   216  
   217  // Fs returns the Fs passed into the New call
   218  func (vfs *VFS) Fs() fs.Fs {
   219  	return vfs.f
   220  }
   221  
   222  // SetCacheMode change the cache mode
   223  func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) {
   224  	vfs.Shutdown()
   225  	vfs.cache = nil
   226  	if cacheMode > vfscommon.CacheModeOff {
   227  		ctx, cancel := context.WithCancel(context.Background())
   228  		cache, err := vfscache.New(ctx, vfs.f, &vfs.Opt) // FIXME pass on context or get from Opt?
   229  		if err != nil {
   230  			fs.Errorf(nil, "Failed to create vfs cache - disabling: %v", err)
   231  			vfs.Opt.CacheMode = vfscommon.CacheModeOff
   232  			cancel()
   233  			return
   234  		}
   235  		vfs.Opt.CacheMode = cacheMode
   236  		vfs.cancel = cancel
   237  		vfs.cache = cache
   238  	}
   239  }
   240  
   241  // Shutdown stops any background go-routines
   242  func (vfs *VFS) Shutdown() {
   243  	// Unpin the Fs from the cache
   244  	cache.Unpin(vfs.f)
   245  	if vfs.cancel != nil {
   246  		vfs.cancel()
   247  		vfs.cancel = nil
   248  	}
   249  }
   250  
   251  // CleanUp deletes the contents of the on disk cache
   252  func (vfs *VFS) CleanUp() error {
   253  	if vfs.Opt.CacheMode == vfscommon.CacheModeOff {
   254  		return nil
   255  	}
   256  	return vfs.cache.CleanUp()
   257  }
   258  
   259  // FlushDirCache empties the directory cache
   260  func (vfs *VFS) FlushDirCache() {
   261  	vfs.root.ForgetAll()
   262  }
   263  
   264  // WaitForWriters sleeps until all writers have finished or
   265  // time.Duration has elapsed
   266  func (vfs *VFS) WaitForWriters(timeout time.Duration) {
   267  	defer log.Trace(nil, "timeout=%v", timeout)("")
   268  	const tickTime = 1 * time.Second
   269  	deadline := time.NewTimer(timeout)
   270  	defer deadline.Stop()
   271  	tick := time.NewTimer(tickTime)
   272  	defer tick.Stop()
   273  	tick.Stop()
   274  	for {
   275  		writers := vfs.root.countActiveWriters()
   276  		if writers == 0 {
   277  			return
   278  		}
   279  		fs.Debugf(nil, "Still %d writers active, waiting %v", writers, tickTime)
   280  		tick.Reset(tickTime)
   281  		select {
   282  		case <-tick.C:
   283  			break
   284  		case <-deadline.C:
   285  			fs.Errorf(nil, "Exiting even though %d writers are active after %v", writers, timeout)
   286  			return
   287  		}
   288  	}
   289  }
   290  
   291  // Root returns the root node
   292  func (vfs *VFS) Root() (*Dir, error) {
   293  	// fs.Debugf(vfs.f, "Root()")
   294  	return vfs.root, nil
   295  }
   296  
   297  var inodeCount uint64
   298  
   299  // newInode creates a new unique inode number
   300  func newInode() (inode uint64) {
   301  	return atomic.AddUint64(&inodeCount, 1)
   302  }
   303  
   304  // Stat finds the Node by path starting from the root
   305  //
   306  // It is the equivalent of os.Stat - Node contains the os.FileInfo
   307  // interface.
   308  func (vfs *VFS) Stat(path string) (node Node, err error) {
   309  	path = strings.Trim(path, "/")
   310  	node = vfs.root
   311  	for path != "" {
   312  		i := strings.IndexRune(path, '/')
   313  		var name string
   314  		if i < 0 {
   315  			name, path = path, ""
   316  		} else {
   317  			name, path = path[:i], path[i+1:]
   318  		}
   319  		if name == "" {
   320  			continue
   321  		}
   322  		dir, ok := node.(*Dir)
   323  		if !ok {
   324  			// We need to look in a directory, but found a file
   325  			return nil, ENOENT
   326  		}
   327  		node, err = dir.Stat(name)
   328  		if err != nil {
   329  			return nil, err
   330  		}
   331  	}
   332  	return
   333  }
   334  
   335  // StatParent finds the parent directory and the leaf name of a path
   336  func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) {
   337  	name = strings.Trim(name, "/")
   338  	parent, leaf := path.Split(name)
   339  	node, err := vfs.Stat(parent)
   340  	if err != nil {
   341  		return nil, "", err
   342  	}
   343  	if node.IsFile() {
   344  		return nil, "", os.ErrExist
   345  	}
   346  	dir = node.(*Dir)
   347  	return dir, leaf, nil
   348  }
   349  
   350  // decodeOpenFlags returns a string representing the open flags
   351  func decodeOpenFlags(flags int) string {
   352  	var out []string
   353  	rdwrMode := flags & accessModeMask
   354  	switch rdwrMode {
   355  	case os.O_RDONLY:
   356  		out = append(out, "O_RDONLY")
   357  	case os.O_WRONLY:
   358  		out = append(out, "O_WRONLY")
   359  	case os.O_RDWR:
   360  		out = append(out, "O_RDWR")
   361  	default:
   362  		out = append(out, fmt.Sprintf("0x%X", rdwrMode))
   363  	}
   364  	if flags&os.O_APPEND != 0 {
   365  		out = append(out, "O_APPEND")
   366  	}
   367  	if flags&os.O_CREATE != 0 {
   368  		out = append(out, "O_CREATE")
   369  	}
   370  	if flags&os.O_EXCL != 0 {
   371  		out = append(out, "O_EXCL")
   372  	}
   373  	if flags&os.O_SYNC != 0 {
   374  		out = append(out, "O_SYNC")
   375  	}
   376  	if flags&os.O_TRUNC != 0 {
   377  		out = append(out, "O_TRUNC")
   378  	}
   379  	flags &^= accessModeMask | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC
   380  	if flags != 0 {
   381  		out = append(out, fmt.Sprintf("0x%X", flags))
   382  	}
   383  	return strings.Join(out, "|")
   384  }
   385  
   386  // OpenFile a file according to the flags and perm provided
   387  func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) {
   388  	defer log.Trace(name, "flags=%s, perm=%v", decodeOpenFlags(flags), perm)("fd=%v, err=%v", &fd, &err)
   389  
   390  	// http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html
   391  	// The result of using O_TRUNC with O_RDONLY is undefined.
   392  	// Linux seems to truncate the file, but we prefer to return EINVAL
   393  	if flags&accessModeMask == os.O_RDONLY && flags&os.O_TRUNC != 0 {
   394  		return nil, EINVAL
   395  	}
   396  
   397  	node, err := vfs.Stat(name)
   398  	if err != nil {
   399  		if err != ENOENT || flags&os.O_CREATE == 0 {
   400  			return nil, err
   401  		}
   402  		// If not found and O_CREATE then create the file
   403  		dir, leaf, err := vfs.StatParent(name)
   404  		if err != nil {
   405  			return nil, err
   406  		}
   407  		node, err = dir.Create(leaf, flags)
   408  		if err != nil {
   409  			return nil, err
   410  		}
   411  	}
   412  	return node.Open(flags)
   413  }
   414  
   415  // Open opens the named file for reading. If successful, methods on
   416  // the returned file can be used for reading; the associated file
   417  // descriptor has mode O_RDONLY.
   418  func (vfs *VFS) Open(name string) (Handle, error) {
   419  	return vfs.OpenFile(name, os.O_RDONLY, 0)
   420  }
   421  
   422  // Create creates the named file with mode 0666 (before umask), truncating
   423  // it if it already exists. If successful, methods on the returned
   424  // File can be used for I/O; the associated file descriptor has mode
   425  // O_RDWR.
   426  func (vfs *VFS) Create(name string) (Handle, error) {
   427  	return vfs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
   428  }
   429  
   430  // Rename oldName to newName
   431  func (vfs *VFS) Rename(oldName, newName string) error {
   432  	// find the parent directories
   433  	oldDir, oldLeaf, err := vfs.StatParent(oldName)
   434  	if err != nil {
   435  		return err
   436  	}
   437  	newDir, newLeaf, err := vfs.StatParent(newName)
   438  	if err != nil {
   439  		return err
   440  	}
   441  	err = oldDir.Rename(oldLeaf, newLeaf, newDir)
   442  	if err != nil {
   443  		return err
   444  	}
   445  	return nil
   446  }
   447  
   448  // This works out the missing values from (total, used, free) using
   449  // unknownFree as the intended free space
   450  func fillInMissingSizes(total, used, free, unknownFree int64) (newTotal, newUsed, newFree int64) {
   451  	if total < 0 {
   452  		if free >= 0 {
   453  			total = free
   454  		} else {
   455  			total = unknownFree
   456  		}
   457  		if used >= 0 {
   458  			total += used
   459  		}
   460  	}
   461  	// total is now defined
   462  	if used < 0 {
   463  		if free >= 0 {
   464  			used = total - free
   465  		} else {
   466  			used = 0
   467  		}
   468  	}
   469  	// used is now defined
   470  	if free < 0 {
   471  		free = total - used
   472  	}
   473  	return total, used, free
   474  }
   475  
   476  // If the total size isn't known then we will aim for this many bytes free (1PB)
   477  const unknownFreeBytes = 1 << 50
   478  
   479  // Statfs returns into about the filing system if known
   480  //
   481  // The values will be -1 if they aren't known
   482  //
   483  // This information is cached for the DirCacheTime interval
   484  func (vfs *VFS) Statfs() (total, used, free int64) {
   485  	// defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free)
   486  	vfs.usageMu.Lock()
   487  	defer vfs.usageMu.Unlock()
   488  	total, used, free = -1, -1, -1
   489  	doAbout := vfs.f.Features().About
   490  	if doAbout != nil && (vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime) {
   491  		var err error
   492  		vfs.usage, err = doAbout(context.TODO())
   493  		vfs.usageTime = time.Now()
   494  		if err != nil {
   495  			fs.Errorf(vfs.f, "Statfs failed: %v", err)
   496  			return
   497  		}
   498  	}
   499  	if u := vfs.usage; u != nil {
   500  		if u.Total != nil {
   501  			total = *u.Total
   502  		}
   503  		if u.Free != nil {
   504  			free = *u.Free
   505  		}
   506  		if u.Used != nil {
   507  			used = *u.Used
   508  		}
   509  	}
   510  	total, used, free = fillInMissingSizes(total, used, free, unknownFreeBytes)
   511  	return
   512  }
   513  
   514  // Remove removes the named file or (empty) directory.
   515  func (vfs *VFS) Remove(name string) error {
   516  	node, err := vfs.Stat(name)
   517  	if err != nil {
   518  		return err
   519  	}
   520  	err = node.Remove()
   521  	if err != nil {
   522  		return err
   523  	}
   524  	return nil
   525  }
   526  
   527  // Chtimes changes the access and modification times of the named file, similar
   528  // to the Unix utime() or utimes() functions.
   529  //
   530  // The underlying filesystem may truncate or round the values to a less precise
   531  // time unit.
   532  func (vfs *VFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
   533  	node, err := vfs.Stat(name)
   534  	if err != nil {
   535  		return err
   536  	}
   537  	err = node.SetModTime(mtime)
   538  	if err != nil {
   539  		return err
   540  	}
   541  	return nil
   542  }
   543  
   544  // Mkdir creates a new directory with the specified name and permission bits
   545  // (before umask).
   546  func (vfs *VFS) Mkdir(name string, perm os.FileMode) error {
   547  	dir, leaf, err := vfs.StatParent(name)
   548  	if err != nil {
   549  		return err
   550  	}
   551  	_, err = dir.Mkdir(leaf)
   552  	if err != nil {
   553  		return err
   554  	}
   555  	return nil
   556  }
   557  
   558  // ReadDir reads the directory named by dirname and returns
   559  // a list of directory entries sorted by filename.
   560  func (vfs *VFS) ReadDir(dirname string) ([]os.FileInfo, error) {
   561  	f, err := vfs.Open(dirname)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  	list, err := f.Readdir(-1)
   566  	closeErr := f.Close()
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	if closeErr != nil {
   571  		return nil, closeErr
   572  	}
   573  	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
   574  	return list, nil
   575  }
   576  
   577  // ReadFile reads the file named by filename and returns the contents.
   578  // A successful call returns err == nil, not err == EOF. Because ReadFile
   579  // reads the whole file, it does not treat an EOF from Read as an error
   580  // to be reported.
   581  func (vfs *VFS) ReadFile(filename string) (b []byte, err error) {
   582  	f, err := vfs.Open(filename)
   583  	if err != nil {
   584  		return nil, err
   585  	}
   586  	defer fs.CheckClose(f, &err)
   587  	return ioutil.ReadAll(f)
   588  }