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

     1  // Package local provides a filesystem interface
     2  package local
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"regexp"
    14  	"runtime"
    15  	"strings"
    16  	"sync"
    17  	"time"
    18  	"unicode/utf8"
    19  
    20  	"github.com/pkg/errors"
    21  	"github.com/rclone/rclone/fs"
    22  	"github.com/rclone/rclone/fs/accounting"
    23  	"github.com/rclone/rclone/fs/config"
    24  	"github.com/rclone/rclone/fs/config/configmap"
    25  	"github.com/rclone/rclone/fs/config/configstruct"
    26  	"github.com/rclone/rclone/fs/fserrors"
    27  	"github.com/rclone/rclone/fs/hash"
    28  	"github.com/rclone/rclone/lib/encoder"
    29  	"github.com/rclone/rclone/lib/file"
    30  	"github.com/rclone/rclone/lib/readers"
    31  )
    32  
    33  // Constants
    34  const devUnset = 0xdeadbeefcafebabe                                       // a device id meaning it is unset
    35  const linkSuffix = ".rclonelink"                                          // The suffix added to a translated symbolic link
    36  const useReadDir = (runtime.GOOS == "windows" || runtime.GOOS == "plan9") // these OSes read FileInfos directly
    37  
    38  // Register with Fs
    39  func init() {
    40  	fsi := &fs.RegInfo{
    41  		Name:        "local",
    42  		Description: "Local Disk",
    43  		NewFs:       NewFs,
    44  		CommandHelp: commandHelp,
    45  		Options: []fs.Option{{
    46  			Name: "nounc",
    47  			Help: "Disable UNC (long path names) conversion on Windows",
    48  			Examples: []fs.OptionExample{{
    49  				Value: "true",
    50  				Help:  "Disables long file names",
    51  			}},
    52  		}, {
    53  			Name:     "copy_links",
    54  			Help:     "Follow symlinks and copy the pointed to item.",
    55  			Default:  false,
    56  			NoPrefix: true,
    57  			ShortOpt: "L",
    58  			Advanced: true,
    59  		}, {
    60  			Name:     "links",
    61  			Help:     "Translate symlinks to/from regular files with a '" + linkSuffix + "' extension",
    62  			Default:  false,
    63  			NoPrefix: true,
    64  			ShortOpt: "l",
    65  			Advanced: true,
    66  		}, {
    67  			Name: "skip_links",
    68  			Help: `Don't warn about skipped symlinks.
    69  This flag disables warning messages on skipped symlinks or junction
    70  points, as you explicitly acknowledge that they should be skipped.`,
    71  			Default:  false,
    72  			NoPrefix: true,
    73  			Advanced: true,
    74  		}, {
    75  			Name: "no_unicode_normalization",
    76  			Help: `Don't apply unicode normalization to paths and filenames (Deprecated)
    77  
    78  This flag is deprecated now.  Rclone no longer normalizes unicode file
    79  names, but it compares them with unicode normalization in the sync
    80  routine instead.`,
    81  			Default:  false,
    82  			Advanced: true,
    83  		}, {
    84  			Name: "no_check_updated",
    85  			Help: `Don't check to see if the files change during upload
    86  
    87  Normally rclone checks the size and modification time of files as they
    88  are being uploaded and aborts with a message which starts "can't copy
    89  - source file is being updated" if the file changes during upload.
    90  
    91  However on some file systems this modification time check may fail (eg
    92  [Glusterfs #2206](https://github.com/rclone/rclone/issues/2206)) so this
    93  check can be disabled with this flag.`,
    94  			Default:  false,
    95  			Advanced: true,
    96  		}, {
    97  			Name:     "one_file_system",
    98  			Help:     "Don't cross filesystem boundaries (unix/macOS only).",
    99  			Default:  false,
   100  			NoPrefix: true,
   101  			ShortOpt: "x",
   102  			Advanced: true,
   103  		}, {
   104  			Name: "case_sensitive",
   105  			Help: `Force the filesystem to report itself as case sensitive.
   106  
   107  Normally the local backend declares itself as case insensitive on
   108  Windows/macOS and case sensitive for everything else.  Use this flag
   109  to override the default choice.`,
   110  			Default:  false,
   111  			Advanced: true,
   112  		}, {
   113  			Name: "case_insensitive",
   114  			Help: `Force the filesystem to report itself as case insensitive
   115  
   116  Normally the local backend declares itself as case insensitive on
   117  Windows/macOS and case sensitive for everything else.  Use this flag
   118  to override the default choice.`,
   119  			Default:  false,
   120  			Advanced: true,
   121  		}, {
   122  			Name: "no_sparse",
   123  			Help: `Disable sparse files for multi-thread downloads
   124  
   125  On Windows platforms rclone will make sparse files when doing
   126  multi-thread downloads. This avoids long pauses on large files where
   127  the OS zeros the file. However sparse files may be undesirable as they
   128  cause disk fragmentation and can be slow to work with.`,
   129  			Default:  false,
   130  			Advanced: true,
   131  		}, {
   132  			Name:     config.ConfigEncoding,
   133  			Help:     config.ConfigEncodingHelp,
   134  			Advanced: true,
   135  			Default:  defaultEnc,
   136  		}},
   137  	}
   138  	fs.Register(fsi)
   139  }
   140  
   141  // Options defines the configuration for this backend
   142  type Options struct {
   143  	FollowSymlinks    bool                 `config:"copy_links"`
   144  	TranslateSymlinks bool                 `config:"links"`
   145  	SkipSymlinks      bool                 `config:"skip_links"`
   146  	NoUTFNorm         bool                 `config:"no_unicode_normalization"`
   147  	NoCheckUpdated    bool                 `config:"no_check_updated"`
   148  	NoUNC             bool                 `config:"nounc"`
   149  	OneFileSystem     bool                 `config:"one_file_system"`
   150  	CaseSensitive     bool                 `config:"case_sensitive"`
   151  	CaseInsensitive   bool                 `config:"case_insensitive"`
   152  	NoSparse          bool                 `config:"no_sparse"`
   153  	Enc               encoder.MultiEncoder `config:"encoding"`
   154  }
   155  
   156  // Fs represents a local filesystem rooted at root
   157  type Fs struct {
   158  	name        string              // the name of the remote
   159  	root        string              // The root directory (OS path)
   160  	opt         Options             // parsed config options
   161  	features    *fs.Features        // optional features
   162  	dev         uint64              // device number of root node
   163  	precisionOk sync.Once           // Whether we need to read the precision
   164  	precision   time.Duration       // precision of local filesystem
   165  	warnedMu    sync.Mutex          // used for locking access to 'warned'.
   166  	warned      map[string]struct{} // whether we have warned about this string
   167  
   168  	// do os.Lstat or os.Stat
   169  	lstat          func(name string) (os.FileInfo, error)
   170  	objectHashesMu sync.Mutex // global lock for Object.hashes
   171  }
   172  
   173  // Object represents a local filesystem object
   174  type Object struct {
   175  	fs             *Fs    // The Fs this object is part of
   176  	remote         string // The remote path (encoded path)
   177  	path           string // The local path (OS path)
   178  	size           int64  // file metadata - always present
   179  	mode           os.FileMode
   180  	modTime        time.Time
   181  	hashes         map[hash.Type]string // Hashes
   182  	translatedLink bool                 // Is this object a translated link
   183  }
   184  
   185  // ------------------------------------------------------------
   186  
   187  var errLinksAndCopyLinks = errors.New("can't use -l/--links with -L/--copy-links")
   188  
   189  // NewFs constructs an Fs from the path
   190  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   191  	// Parse config into Options struct
   192  	opt := new(Options)
   193  	err := configstruct.Set(m, opt)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	if opt.TranslateSymlinks && opt.FollowSymlinks {
   198  		return nil, errLinksAndCopyLinks
   199  	}
   200  
   201  	if opt.NoUTFNorm {
   202  		fs.Errorf(nil, "The --local-no-unicode-normalization flag is deprecated and will be removed")
   203  	}
   204  
   205  	f := &Fs{
   206  		name:   name,
   207  		opt:    *opt,
   208  		warned: make(map[string]struct{}),
   209  		dev:    devUnset,
   210  		lstat:  os.Lstat,
   211  	}
   212  	f.root = cleanRootPath(root, f.opt.NoUNC, f.opt.Enc)
   213  	f.features = (&fs.Features{
   214  		CaseInsensitive:         f.caseInsensitive(),
   215  		CanHaveEmptyDirectories: true,
   216  		IsLocal:                 true,
   217  	}).Fill(f)
   218  	if opt.FollowSymlinks {
   219  		f.lstat = os.Stat
   220  	}
   221  
   222  	// Check to see if this points to a file
   223  	fi, err := f.lstat(f.root)
   224  	if err == nil {
   225  		f.dev = readDevice(fi, f.opt.OneFileSystem)
   226  	}
   227  	if err == nil && f.isRegular(fi.Mode()) {
   228  		// It is a file, so use the parent as the root
   229  		f.root = filepath.Dir(f.root)
   230  		// return an error with an fs which points to the parent
   231  		return f, fs.ErrorIsFile
   232  	}
   233  	return f, nil
   234  }
   235  
   236  // Determine whether a file is a 'regular' file,
   237  // Symlinks are regular files, only if the TranslateSymlink
   238  // option is in-effect
   239  func (f *Fs) isRegular(mode os.FileMode) bool {
   240  	if !f.opt.TranslateSymlinks {
   241  		return mode.IsRegular()
   242  	}
   243  
   244  	// fi.Mode().IsRegular() tests that all mode bits are zero
   245  	// Since symlinks are accepted, test that all other bits are zero,
   246  	// except the symlink bit
   247  	return mode&os.ModeType&^os.ModeSymlink == 0
   248  }
   249  
   250  // Name of the remote (as passed into NewFs)
   251  func (f *Fs) Name() string {
   252  	return f.name
   253  }
   254  
   255  // Root of the remote (as passed into NewFs)
   256  func (f *Fs) Root() string {
   257  	return f.opt.Enc.ToStandardPath(filepath.ToSlash(f.root))
   258  }
   259  
   260  // String converts this Fs to a string
   261  func (f *Fs) String() string {
   262  	return fmt.Sprintf("Local file system at %s", f.Root())
   263  }
   264  
   265  // Features returns the optional features of this Fs
   266  func (f *Fs) Features() *fs.Features {
   267  	return f.features
   268  }
   269  
   270  // caseInsensitive returns whether the remote is case insensitive or not
   271  func (f *Fs) caseInsensitive() bool {
   272  	if f.opt.CaseSensitive {
   273  		return false
   274  	}
   275  	if f.opt.CaseInsensitive {
   276  		return true
   277  	}
   278  	// FIXME not entirely accurate since you can have case
   279  	// sensitive Fses on darwin and case insensitive Fses on linux.
   280  	// Should probably check but that would involve creating a
   281  	// file in the remote to be most accurate which probably isn't
   282  	// desirable.
   283  	return runtime.GOOS == "windows" || runtime.GOOS == "darwin"
   284  }
   285  
   286  // translateLink checks whether the remote is a translated link
   287  // and returns a new path, removing the suffix as needed,
   288  // It also returns whether this is a translated link at all
   289  //
   290  // for regular files, localPath is returned unchanged
   291  func translateLink(remote, localPath string) (newLocalPath string, isTranslatedLink bool) {
   292  	isTranslatedLink = strings.HasSuffix(remote, linkSuffix)
   293  	newLocalPath = strings.TrimSuffix(localPath, linkSuffix)
   294  	return newLocalPath, isTranslatedLink
   295  }
   296  
   297  // newObject makes a half completed Object
   298  func (f *Fs) newObject(remote string) *Object {
   299  	translatedLink := false
   300  	localPath := f.localPath(remote)
   301  
   302  	if f.opt.TranslateSymlinks {
   303  		// Possibly receive a new name for localPath
   304  		localPath, translatedLink = translateLink(remote, localPath)
   305  	}
   306  
   307  	return &Object{
   308  		fs:             f,
   309  		remote:         remote,
   310  		path:           localPath,
   311  		translatedLink: translatedLink,
   312  	}
   313  }
   314  
   315  // Return an Object from a path
   316  //
   317  // May return nil if an error occurred
   318  func (f *Fs) newObjectWithInfo(remote string, info os.FileInfo) (fs.Object, error) {
   319  	o := f.newObject(remote)
   320  	if info != nil {
   321  		o.setMetadata(info)
   322  	} else {
   323  		err := o.lstat()
   324  		if err != nil {
   325  			if os.IsNotExist(err) {
   326  				return nil, fs.ErrorObjectNotFound
   327  			}
   328  			if os.IsPermission(err) {
   329  				return nil, fs.ErrorPermissionDenied
   330  			}
   331  			return nil, err
   332  		}
   333  		// Handle the odd case, that a symlink was specified by name without the link suffix
   334  		if o.fs.opt.TranslateSymlinks && o.mode&os.ModeSymlink != 0 && !o.translatedLink {
   335  			return nil, fs.ErrorObjectNotFound
   336  		}
   337  
   338  	}
   339  	if o.mode.IsDir() {
   340  		return nil, errors.Wrapf(fs.ErrorNotAFile, "%q", remote)
   341  	}
   342  	return o, nil
   343  }
   344  
   345  // NewObject finds the Object at remote.  If it can't be found
   346  // it returns the error ErrorObjectNotFound.
   347  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   348  	return f.newObjectWithInfo(remote, nil)
   349  }
   350  
   351  // List the objects and directories in dir into entries.  The
   352  // entries can be returned in any order but should be for a
   353  // complete directory.
   354  //
   355  // dir should be "" to list the root, and should not have
   356  // trailing slashes.
   357  //
   358  // This should return ErrDirNotFound if the directory isn't
   359  // found.
   360  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   361  	fsDirPath := f.localPath(dir)
   362  	_, err = os.Stat(fsDirPath)
   363  	if err != nil {
   364  		return nil, fs.ErrorDirNotFound
   365  	}
   366  
   367  	fd, err := os.Open(fsDirPath)
   368  	if err != nil {
   369  		isPerm := os.IsPermission(err)
   370  		err = errors.Wrapf(err, "failed to open directory %q", dir)
   371  		fs.Errorf(dir, "%v", err)
   372  		if isPerm {
   373  			_ = accounting.Stats(ctx).Error(fserrors.NoRetryError(err))
   374  			err = nil // ignore error but fail sync
   375  		}
   376  		return nil, err
   377  	}
   378  	defer func() {
   379  		cerr := fd.Close()
   380  		if cerr != nil && err == nil {
   381  			err = errors.Wrapf(cerr, "failed to close directory %q:", dir)
   382  		}
   383  	}()
   384  
   385  	for {
   386  		var fis []os.FileInfo
   387  		if useReadDir {
   388  			// Windows and Plan9 read the directory entries with the stat information in which
   389  			// shouldn't fail because of unreadable entries.
   390  			fis, err = fd.Readdir(1024)
   391  			if err == io.EOF && len(fis) == 0 {
   392  				break
   393  			}
   394  		} else {
   395  			// For other OSes we read the names only (which shouldn't fail) then stat the
   396  			// individual ourselves so we can log errors but not fail the directory read.
   397  			var names []string
   398  			names, err = fd.Readdirnames(1024)
   399  			if err == io.EOF && len(names) == 0 {
   400  				break
   401  			}
   402  			if err == nil {
   403  				for _, name := range names {
   404  					namepath := filepath.Join(fsDirPath, name)
   405  					fi, fierr := os.Lstat(namepath)
   406  					if fierr != nil {
   407  						err = errors.Wrapf(err, "failed to read directory %q", namepath)
   408  						fs.Errorf(dir, "%v", fierr)
   409  						_ = accounting.Stats(ctx).Error(fserrors.NoRetryError(fierr)) // fail the sync
   410  						continue
   411  					}
   412  					fis = append(fis, fi)
   413  				}
   414  			}
   415  		}
   416  		if err != nil {
   417  			return nil, errors.Wrap(err, "failed to read directory entry")
   418  		}
   419  
   420  		for _, fi := range fis {
   421  			name := fi.Name()
   422  			mode := fi.Mode()
   423  			newRemote := f.cleanRemote(dir, name)
   424  			// Follow symlinks if required
   425  			if f.opt.FollowSymlinks && (mode&os.ModeSymlink) != 0 {
   426  				localPath := filepath.Join(fsDirPath, name)
   427  				fi, err = os.Stat(localPath)
   428  				if os.IsNotExist(err) {
   429  					// Skip bad symlinks
   430  					err = fserrors.NoRetryError(errors.Wrap(err, "symlink"))
   431  					fs.Errorf(newRemote, "Listing error: %v", err)
   432  					err = accounting.Stats(ctx).Error(err)
   433  					continue
   434  				}
   435  				if err != nil {
   436  					return nil, err
   437  				}
   438  				mode = fi.Mode()
   439  			}
   440  			if fi.IsDir() {
   441  				// Ignore directories which are symlinks.  These are junction points under windows which
   442  				// are kind of a souped up symlink. Unix doesn't have directories which are symlinks.
   443  				if (mode&os.ModeSymlink) == 0 && f.dev == readDevice(fi, f.opt.OneFileSystem) {
   444  					d := fs.NewDir(newRemote, fi.ModTime())
   445  					entries = append(entries, d)
   446  				}
   447  			} else {
   448  				// Check whether this link should be translated
   449  				if f.opt.TranslateSymlinks && fi.Mode()&os.ModeSymlink != 0 {
   450  					newRemote += linkSuffix
   451  				}
   452  				fso, err := f.newObjectWithInfo(newRemote, fi)
   453  				if err != nil {
   454  					return nil, err
   455  				}
   456  				if fso.Storable() {
   457  					entries = append(entries, fso)
   458  				}
   459  			}
   460  		}
   461  	}
   462  	return entries, nil
   463  }
   464  
   465  func (f *Fs) cleanRemote(dir, filename string) (remote string) {
   466  	remote = path.Join(dir, f.opt.Enc.ToStandardName(filename))
   467  
   468  	if !utf8.ValidString(filename) {
   469  		f.warnedMu.Lock()
   470  		if _, ok := f.warned[remote]; !ok {
   471  			fs.Logf(f, "Replacing invalid UTF-8 characters in %q", remote)
   472  			f.warned[remote] = struct{}{}
   473  		}
   474  		f.warnedMu.Unlock()
   475  	}
   476  	return
   477  }
   478  
   479  func (f *Fs) localPath(name string) string {
   480  	return filepath.Join(f.root, filepath.FromSlash(f.opt.Enc.FromStandardPath(name)))
   481  }
   482  
   483  // Put the Object to the local filesystem
   484  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   485  	// Temporary Object under construction - info filled in by Update()
   486  	o := f.newObject(src.Remote())
   487  	err := o.Update(ctx, in, src, options...)
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  	return o, nil
   492  }
   493  
   494  // PutStream uploads to the remote path with the modTime given of indeterminate size
   495  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   496  	return f.Put(ctx, in, src, options...)
   497  }
   498  
   499  // Mkdir creates the directory if it doesn't exist
   500  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   501  	// FIXME: https://github.com/syncthing/syncthing/blob/master/lib/osutil/mkdirall_windows.go
   502  	localPath := f.localPath(dir)
   503  	err := os.MkdirAll(localPath, 0777)
   504  	if err != nil {
   505  		return err
   506  	}
   507  	if dir == "" {
   508  		fi, err := f.lstat(localPath)
   509  		if err != nil {
   510  			return err
   511  		}
   512  		f.dev = readDevice(fi, f.opt.OneFileSystem)
   513  	}
   514  	return nil
   515  }
   516  
   517  // Rmdir removes the directory
   518  //
   519  // If it isn't empty it will return an error
   520  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   521  	return os.Remove(f.localPath(dir))
   522  }
   523  
   524  // Precision of the file system
   525  func (f *Fs) Precision() (precision time.Duration) {
   526  	f.precisionOk.Do(func() {
   527  		f.precision = f.readPrecision()
   528  	})
   529  	return f.precision
   530  }
   531  
   532  // Read the precision
   533  func (f *Fs) readPrecision() (precision time.Duration) {
   534  	// Default precision of 1s
   535  	precision = time.Second
   536  
   537  	// Create temporary file and test it
   538  	fd, err := ioutil.TempFile("", "rclone")
   539  	if err != nil {
   540  		// If failed return 1s
   541  		// fmt.Println("Failed to create temp file", err)
   542  		return time.Second
   543  	}
   544  	path := fd.Name()
   545  	// fmt.Println("Created temp file", path)
   546  	err = fd.Close()
   547  	if err != nil {
   548  		return time.Second
   549  	}
   550  
   551  	// Delete it on return
   552  	defer func() {
   553  		// fmt.Println("Remove temp file")
   554  		_ = os.Remove(path) // ignore error
   555  	}()
   556  
   557  	// Find the minimum duration we can detect
   558  	for duration := time.Duration(1); duration < time.Second; duration *= 10 {
   559  		// Current time with delta
   560  		t := time.Unix(time.Now().Unix(), int64(duration))
   561  		err := os.Chtimes(path, t, t)
   562  		if err != nil {
   563  			// fmt.Println("Failed to Chtimes", err)
   564  			break
   565  		}
   566  
   567  		// Read the actual time back
   568  		fi, err := os.Stat(path)
   569  		if err != nil {
   570  			// fmt.Println("Failed to Stat", err)
   571  			break
   572  		}
   573  
   574  		// If it matches - have found the precision
   575  		// fmt.Println("compare", fi.ModTime(ctx), t)
   576  		if fi.ModTime().Equal(t) {
   577  			// fmt.Println("Precision detected as", duration)
   578  			return duration
   579  		}
   580  	}
   581  	return
   582  }
   583  
   584  // Purge deletes all the files and directories
   585  //
   586  // Optional interface: Only implement this if you have a way of
   587  // deleting all the files quicker than just running Remove() on the
   588  // result of List()
   589  func (f *Fs) Purge(ctx context.Context) error {
   590  	fi, err := f.lstat(f.root)
   591  	if err != nil {
   592  		return err
   593  	}
   594  	if !fi.Mode().IsDir() {
   595  		return errors.Errorf("can't purge non directory: %q", f.root)
   596  	}
   597  	return os.RemoveAll(f.root)
   598  }
   599  
   600  // Move src to this remote using server side move operations.
   601  //
   602  // This is stored with the remote path given
   603  //
   604  // It returns the destination Object and a possible error
   605  //
   606  // Will only be called if src.Fs().Name() == f.Name()
   607  //
   608  // If it isn't possible then return fs.ErrorCantMove
   609  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   610  	srcObj, ok := src.(*Object)
   611  	if !ok {
   612  		fs.Debugf(src, "Can't move - not same remote type")
   613  		return nil, fs.ErrorCantMove
   614  	}
   615  
   616  	// Temporary Object under construction
   617  	dstObj := f.newObject(remote)
   618  
   619  	// Check it is a file if it exists
   620  	err := dstObj.lstat()
   621  	if os.IsNotExist(err) {
   622  		// OK
   623  	} else if err != nil {
   624  		return nil, err
   625  	} else if !dstObj.fs.isRegular(dstObj.mode) {
   626  		// It isn't a file
   627  		return nil, errors.New("can't move file onto non-file")
   628  	}
   629  
   630  	// Create destination
   631  	err = dstObj.mkdirAll()
   632  	if err != nil {
   633  		return nil, err
   634  	}
   635  
   636  	// Do the move
   637  	err = os.Rename(srcObj.path, dstObj.path)
   638  	if os.IsNotExist(err) {
   639  		// race condition, source was deleted in the meantime
   640  		return nil, err
   641  	} else if os.IsPermission(err) {
   642  		// not enough rights to write to dst
   643  		return nil, err
   644  	} else if err != nil {
   645  		// not quite clear, but probably trying to move a file across file system
   646  		// boundaries. Copying might still work.
   647  		fs.Debugf(src, "Can't move: %v: trying copy", err)
   648  		return nil, fs.ErrorCantMove
   649  	}
   650  
   651  	// Update the info
   652  	err = dstObj.lstat()
   653  	if err != nil {
   654  		return nil, err
   655  	}
   656  
   657  	return dstObj, nil
   658  }
   659  
   660  // DirMove moves src, srcRemote to this remote at dstRemote
   661  // using server side move operations.
   662  //
   663  // Will only be called if src.Fs().Name() == f.Name()
   664  //
   665  // If it isn't possible then return fs.ErrorCantDirMove
   666  //
   667  // If destination exists then return fs.ErrorDirExists
   668  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   669  	srcFs, ok := src.(*Fs)
   670  	if !ok {
   671  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   672  		return fs.ErrorCantDirMove
   673  	}
   674  	srcPath := srcFs.localPath(srcRemote)
   675  	dstPath := f.localPath(dstRemote)
   676  
   677  	// Check if destination exists
   678  	_, err := os.Lstat(dstPath)
   679  	if !os.IsNotExist(err) {
   680  		return fs.ErrorDirExists
   681  	}
   682  
   683  	// Create parent of destination
   684  	dstParentPath := filepath.Dir(dstPath)
   685  	err = os.MkdirAll(dstParentPath, 0777)
   686  	if err != nil {
   687  		return err
   688  	}
   689  
   690  	// Do the move
   691  	err = os.Rename(srcPath, dstPath)
   692  	if os.IsNotExist(err) {
   693  		// race condition, source was deleted in the meantime
   694  		return err
   695  	} else if os.IsPermission(err) {
   696  		// not enough rights to write to dst
   697  		return err
   698  	} else if err != nil {
   699  		// not quite clear, but probably trying to move directory across file system
   700  		// boundaries. Copying might still work.
   701  		fs.Debugf(src, "Can't move dir: %v: trying copy", err)
   702  		return fs.ErrorCantDirMove
   703  	}
   704  	return nil
   705  }
   706  
   707  // Hashes returns the supported hash sets.
   708  func (f *Fs) Hashes() hash.Set {
   709  	return hash.Supported()
   710  }
   711  
   712  var commandHelp = []fs.CommandHelp{
   713  	{
   714  		Name:  "noop",
   715  		Short: "A null operation for testing backend commands",
   716  		Long: `This is a test command which has some options
   717  you can try to change the output.`,
   718  		Opts: map[string]string{
   719  			"echo":  "echo the input arguments",
   720  			"error": "return an error based on option value",
   721  		},
   722  	},
   723  }
   724  
   725  // Command the backend to run a named command
   726  //
   727  // The command run is name
   728  // args may be used to read arguments from
   729  // opts may be used to read optional arguments from
   730  //
   731  // The result should be capable of being JSON encoded
   732  // If it is a string or a []string it will be shown to the user
   733  // otherwise it will be JSON encoded and shown to the user like that
   734  func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[string]string) (interface{}, error) {
   735  	switch name {
   736  	case "noop":
   737  		if txt, ok := opt["error"]; ok {
   738  			if txt == "" {
   739  				txt = "unspecified error"
   740  			}
   741  			return nil, errors.New(txt)
   742  		}
   743  		if _, ok := opt["echo"]; ok {
   744  			out := map[string]interface{}{}
   745  			out["name"] = name
   746  			out["arg"] = arg
   747  			out["opt"] = opt
   748  			return out, nil
   749  		}
   750  		return nil, nil
   751  	default:
   752  		return nil, fs.ErrorCommandNotFound
   753  	}
   754  }
   755  
   756  // ------------------------------------------------------------
   757  
   758  // Fs returns the parent Fs
   759  func (o *Object) Fs() fs.Info {
   760  	return o.fs
   761  }
   762  
   763  // Return a string version
   764  func (o *Object) String() string {
   765  	if o == nil {
   766  		return "<nil>"
   767  	}
   768  	return o.remote
   769  }
   770  
   771  // Remote returns the remote path
   772  func (o *Object) Remote() string {
   773  	return o.remote
   774  }
   775  
   776  // Hash returns the requested hash of a file as a lowercase hex string
   777  func (o *Object) Hash(ctx context.Context, r hash.Type) (string, error) {
   778  	// Check that the underlying file hasn't changed
   779  	oldtime := o.modTime
   780  	oldsize := o.size
   781  	err := o.lstat()
   782  	var changed bool
   783  	if err != nil {
   784  		if os.IsNotExist(errors.Cause(err)) {
   785  			// If file not found then we assume any accumulated
   786  			// hashes are OK - this will error on Open
   787  			changed = true
   788  		} else {
   789  			return "", errors.Wrap(err, "hash: failed to stat")
   790  		}
   791  	} else {
   792  		changed = !o.modTime.Equal(oldtime) || oldsize != o.size
   793  	}
   794  
   795  	o.fs.objectHashesMu.Lock()
   796  	hashes := o.hashes
   797  	hashValue, hashFound := o.hashes[r]
   798  	o.fs.objectHashesMu.Unlock()
   799  
   800  	if changed || hashes == nil || !hashFound {
   801  		var in io.ReadCloser
   802  
   803  		if !o.translatedLink {
   804  			var fd *os.File
   805  			fd, err = file.Open(o.path)
   806  			if fd != nil {
   807  				in = newFadviseReadCloser(o, fd, 0, 0)
   808  			}
   809  		} else {
   810  			in, err = o.openTranslatedLink(0, -1)
   811  		}
   812  		if err != nil {
   813  			return "", errors.Wrap(err, "hash: failed to open")
   814  		}
   815  		hashes, err = hash.StreamTypes(in, hash.NewHashSet(r))
   816  		closeErr := in.Close()
   817  		if err != nil {
   818  			return "", errors.Wrap(err, "hash: failed to read")
   819  		}
   820  		if closeErr != nil {
   821  			return "", errors.Wrap(closeErr, "hash: failed to close")
   822  		}
   823  		hashValue = hashes[r]
   824  		o.fs.objectHashesMu.Lock()
   825  		if o.hashes == nil {
   826  			o.hashes = hashes
   827  		} else {
   828  			o.hashes[r] = hashValue
   829  		}
   830  		o.fs.objectHashesMu.Unlock()
   831  	}
   832  	return hashValue, nil
   833  }
   834  
   835  // Size returns the size of an object in bytes
   836  func (o *Object) Size() int64 {
   837  	return o.size
   838  }
   839  
   840  // ModTime returns the modification time of the object
   841  func (o *Object) ModTime(ctx context.Context) time.Time {
   842  	return o.modTime
   843  }
   844  
   845  // SetModTime sets the modification time of the local fs object
   846  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
   847  	var err error
   848  	if o.translatedLink {
   849  		err = lChtimes(o.path, modTime, modTime)
   850  	} else {
   851  		err = os.Chtimes(o.path, modTime, modTime)
   852  	}
   853  	if err != nil {
   854  		return err
   855  	}
   856  	// Re-read metadata
   857  	return o.lstat()
   858  }
   859  
   860  // Storable returns a boolean showing if this object is storable
   861  func (o *Object) Storable() bool {
   862  	mode := o.mode
   863  	if mode&os.ModeSymlink != 0 && !o.fs.opt.TranslateSymlinks {
   864  		if !o.fs.opt.SkipSymlinks {
   865  			fs.Logf(o, "Can't follow symlink without -L/--copy-links")
   866  		}
   867  		return false
   868  	} else if mode&(os.ModeNamedPipe|os.ModeSocket|os.ModeDevice) != 0 {
   869  		fs.Logf(o, "Can't transfer non file/directory")
   870  		return false
   871  	} else if mode&os.ModeDir != 0 {
   872  		// fs.Debugf(o, "Skipping directory")
   873  		return false
   874  	}
   875  	return true
   876  }
   877  
   878  // localOpenFile wraps an io.ReadCloser and updates the md5sum of the
   879  // object that is read
   880  type localOpenFile struct {
   881  	o    *Object           // object that is open
   882  	in   io.ReadCloser     // handle we are wrapping
   883  	hash *hash.MultiHasher // currently accumulating hashes
   884  	fd   *os.File          // file object reference
   885  }
   886  
   887  // Read bytes from the object - see io.Reader
   888  func (file *localOpenFile) Read(p []byte) (n int, err error) {
   889  	if !file.o.fs.opt.NoCheckUpdated {
   890  		// Check if file has the same size and modTime
   891  		fi, err := file.fd.Stat()
   892  		if err != nil {
   893  			return 0, errors.Wrap(err, "can't read status of source file while transferring")
   894  		}
   895  		if file.o.size != fi.Size() {
   896  			return 0, fserrors.NoLowLevelRetryError(errors.Errorf("can't copy - source file is being updated (size changed from %d to %d)", file.o.size, fi.Size()))
   897  		}
   898  		if !file.o.modTime.Equal(fi.ModTime()) {
   899  			return 0, fserrors.NoLowLevelRetryError(errors.Errorf("can't copy - source file is being updated (mod time changed from %v to %v)", file.o.modTime, fi.ModTime()))
   900  		}
   901  	}
   902  
   903  	n, err = file.in.Read(p)
   904  	if n > 0 {
   905  		// Hash routines never return an error
   906  		_, _ = file.hash.Write(p[:n])
   907  	}
   908  	return
   909  }
   910  
   911  // Close the object and update the hashes
   912  func (file *localOpenFile) Close() (err error) {
   913  	err = file.in.Close()
   914  	if err == nil {
   915  		if file.hash.Size() == file.o.Size() {
   916  			file.o.fs.objectHashesMu.Lock()
   917  			file.o.hashes = file.hash.Sums()
   918  			file.o.fs.objectHashesMu.Unlock()
   919  		}
   920  	}
   921  	return err
   922  }
   923  
   924  // Returns a ReadCloser() object that contains the contents of a symbolic link
   925  func (o *Object) openTranslatedLink(offset, limit int64) (lrc io.ReadCloser, err error) {
   926  	// Read the link and return the destination  it as the contents of the object
   927  	linkdst, err := os.Readlink(o.path)
   928  	if err != nil {
   929  		return nil, err
   930  	}
   931  	return readers.NewLimitedReadCloser(ioutil.NopCloser(strings.NewReader(linkdst[offset:])), limit), nil
   932  }
   933  
   934  // Open an object for read
   935  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
   936  	var offset, limit int64 = 0, -1
   937  	var hasher *hash.MultiHasher
   938  	for _, option := range options {
   939  		switch x := option.(type) {
   940  		case *fs.SeekOption:
   941  			offset = x.Offset
   942  		case *fs.RangeOption:
   943  			offset, limit = x.Decode(o.size)
   944  		case *fs.HashesOption:
   945  			if x.Hashes.Count() > 0 {
   946  				hasher, err = hash.NewMultiHasherTypes(x.Hashes)
   947  				if err != nil {
   948  					return nil, err
   949  				}
   950  			}
   951  		default:
   952  			if option.Mandatory() {
   953  				fs.Logf(o, "Unsupported mandatory option: %v", option)
   954  			}
   955  		}
   956  	}
   957  
   958  	// Handle a translated link
   959  	if o.translatedLink {
   960  		return o.openTranslatedLink(offset, limit)
   961  	}
   962  
   963  	fd, err := file.Open(o.path)
   964  	if err != nil {
   965  		return
   966  	}
   967  	wrappedFd := readers.NewLimitedReadCloser(newFadviseReadCloser(o, fd, offset, limit), limit)
   968  	if offset != 0 {
   969  		// seek the object
   970  		_, err = fd.Seek(offset, io.SeekStart)
   971  		// don't attempt to make checksums
   972  		return wrappedFd, err
   973  	}
   974  	if hasher == nil {
   975  		// no need to wrap since we don't need checksums
   976  		return wrappedFd, nil
   977  	}
   978  	// Update the hashes as we go along
   979  	in = &localOpenFile{
   980  		o:    o,
   981  		in:   wrappedFd,
   982  		hash: hasher,
   983  		fd:   fd,
   984  	}
   985  	return in, nil
   986  }
   987  
   988  // mkdirAll makes all the directories needed to store the object
   989  func (o *Object) mkdirAll() error {
   990  	dir := filepath.Dir(o.path)
   991  	return os.MkdirAll(dir, 0777)
   992  }
   993  
   994  type nopWriterCloser struct {
   995  	*bytes.Buffer
   996  }
   997  
   998  func (nwc nopWriterCloser) Close() error {
   999  	// noop
  1000  	return nil
  1001  }
  1002  
  1003  // Update the object from in with modTime and size
  1004  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
  1005  	var out io.WriteCloser
  1006  	var hasher *hash.MultiHasher
  1007  
  1008  	for _, option := range options {
  1009  		switch x := option.(type) {
  1010  		case *fs.HashesOption:
  1011  			if x.Hashes.Count() > 0 {
  1012  				hasher, err = hash.NewMultiHasherTypes(x.Hashes)
  1013  				if err != nil {
  1014  					return err
  1015  				}
  1016  			}
  1017  		}
  1018  	}
  1019  
  1020  	err = o.mkdirAll()
  1021  	if err != nil {
  1022  		return err
  1023  	}
  1024  
  1025  	var symlinkData bytes.Buffer
  1026  	// If the object is a regular file, create it.
  1027  	// If it is a translated link, just read in the contents, and
  1028  	// then create a symlink
  1029  	if !o.translatedLink {
  1030  		f, err := file.OpenFile(o.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  1031  		if err != nil {
  1032  			if runtime.GOOS == "windows" && os.IsPermission(err) {
  1033  				// If permission denied on Windows might be trying to update a
  1034  				// hidden file, in which case try opening without CREATE
  1035  				// See: https://stackoverflow.com/questions/13215716/ioerror-errno-13-permission-denied-when-trying-to-open-hidden-file-in-w-mod
  1036  				f, err = file.OpenFile(o.path, os.O_WRONLY|os.O_TRUNC, 0666)
  1037  				if err != nil {
  1038  					return err
  1039  				}
  1040  			} else {
  1041  				return err
  1042  			}
  1043  		}
  1044  		// Pre-allocate the file for performance reasons
  1045  		err = file.PreAllocate(src.Size(), f)
  1046  		if err != nil {
  1047  			fs.Debugf(o, "Failed to pre-allocate: %v", err)
  1048  		}
  1049  		out = f
  1050  	} else {
  1051  		out = nopWriterCloser{&symlinkData}
  1052  	}
  1053  
  1054  	// Calculate the hash of the object we are reading as we go along
  1055  	if hasher != nil {
  1056  		in = io.TeeReader(in, hasher)
  1057  	}
  1058  
  1059  	_, err = io.Copy(out, in)
  1060  	closeErr := out.Close()
  1061  	if err == nil {
  1062  		err = closeErr
  1063  	}
  1064  
  1065  	if o.translatedLink {
  1066  		if err == nil {
  1067  			// Remove any current symlink or file, if one exists
  1068  			if _, err := os.Lstat(o.path); err == nil {
  1069  				if removeErr := os.Remove(o.path); removeErr != nil {
  1070  					fs.Errorf(o, "Failed to remove previous file: %v", removeErr)
  1071  					return removeErr
  1072  				}
  1073  			}
  1074  			// Use the contents for the copied object to create a symlink
  1075  			err = os.Symlink(symlinkData.String(), o.path)
  1076  		}
  1077  
  1078  		// only continue if symlink creation succeeded
  1079  		if err != nil {
  1080  			return err
  1081  		}
  1082  	}
  1083  
  1084  	if err != nil {
  1085  		fs.Logf(o, "Removing partially written file on error: %v", err)
  1086  		if removeErr := os.Remove(o.path); removeErr != nil {
  1087  			fs.Errorf(o, "Failed to remove partially written file: %v", removeErr)
  1088  		}
  1089  		return err
  1090  	}
  1091  
  1092  	// All successful so update the hashes
  1093  	if hasher != nil {
  1094  		o.fs.objectHashesMu.Lock()
  1095  		o.hashes = hasher.Sums()
  1096  		o.fs.objectHashesMu.Unlock()
  1097  	}
  1098  
  1099  	// Set the mtime
  1100  	err = o.SetModTime(ctx, src.ModTime(ctx))
  1101  	if err != nil {
  1102  		return err
  1103  	}
  1104  
  1105  	// ReRead info now that we have finished
  1106  	return o.lstat()
  1107  }
  1108  
  1109  var sparseWarning sync.Once
  1110  
  1111  // OpenWriterAt opens with a handle for random access writes
  1112  //
  1113  // Pass in the remote desired and the size if known.
  1114  //
  1115  // It truncates any existing object
  1116  func (f *Fs) OpenWriterAt(ctx context.Context, remote string, size int64) (fs.WriterAtCloser, error) {
  1117  	// Temporary Object under construction
  1118  	o := f.newObject(remote)
  1119  
  1120  	err := o.mkdirAll()
  1121  	if err != nil {
  1122  		return nil, err
  1123  	}
  1124  
  1125  	if o.translatedLink {
  1126  		return nil, errors.New("can't open a symlink for random writing")
  1127  	}
  1128  
  1129  	out, err := file.OpenFile(o.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  1130  	if err != nil {
  1131  		return nil, err
  1132  	}
  1133  	// Pre-allocate the file for performance reasons
  1134  	err = file.PreAllocate(size, out)
  1135  	if err != nil {
  1136  		fs.Debugf(o, "Failed to pre-allocate: %v", err)
  1137  	}
  1138  	if !f.opt.NoSparse && file.SetSparseImplemented {
  1139  		sparseWarning.Do(func() {
  1140  			fs.Infof(nil, "Writing sparse files: use --local-no-sparse or --multi-thread-streams 0 to disable")
  1141  		})
  1142  		// Set the file to be a sparse file (important on Windows)
  1143  		err = file.SetSparse(out)
  1144  		if err != nil {
  1145  			fs.Debugf(o, "Failed to set sparse: %v", err)
  1146  		}
  1147  	}
  1148  
  1149  	return out, nil
  1150  }
  1151  
  1152  // setMetadata sets the file info from the os.FileInfo passed in
  1153  func (o *Object) setMetadata(info os.FileInfo) {
  1154  	// Don't overwrite the info if we don't need to
  1155  	// this avoids upsetting the race detector
  1156  	if o.size != info.Size() {
  1157  		o.size = info.Size()
  1158  	}
  1159  	if !o.modTime.Equal(info.ModTime()) {
  1160  		o.modTime = info.ModTime()
  1161  	}
  1162  	if o.mode != info.Mode() {
  1163  		o.mode = info.Mode()
  1164  	}
  1165  }
  1166  
  1167  // Stat an Object into info
  1168  func (o *Object) lstat() error {
  1169  	info, err := o.fs.lstat(o.path)
  1170  	if err == nil {
  1171  		o.setMetadata(info)
  1172  	}
  1173  	return err
  1174  }
  1175  
  1176  // Remove an object
  1177  func (o *Object) Remove(ctx context.Context) error {
  1178  	return remove(o.path)
  1179  }
  1180  
  1181  func cleanRootPath(s string, noUNC bool, enc encoder.MultiEncoder) string {
  1182  	if runtime.GOOS == "windows" {
  1183  		if !filepath.IsAbs(s) && !strings.HasPrefix(s, "\\") {
  1184  			s2, err := filepath.Abs(s)
  1185  			if err == nil {
  1186  				s = s2
  1187  			}
  1188  		}
  1189  		s = filepath.ToSlash(s)
  1190  		vol := filepath.VolumeName(s)
  1191  		s = vol + enc.FromStandardPath(s[len(vol):])
  1192  		s = filepath.FromSlash(s)
  1193  
  1194  		if !noUNC {
  1195  			// Convert to UNC
  1196  			s = uncPath(s)
  1197  		}
  1198  		return s
  1199  	}
  1200  	if !filepath.IsAbs(s) {
  1201  		s2, err := filepath.Abs(s)
  1202  		if err == nil {
  1203  			s = s2
  1204  		}
  1205  	}
  1206  	s = enc.FromStandardPath(s)
  1207  	return s
  1208  }
  1209  
  1210  // Pattern to match a windows absolute path: "c:\" and similar
  1211  var isAbsWinDrive = regexp.MustCompile(`^[a-zA-Z]\:\\`)
  1212  
  1213  // uncPath converts an absolute Windows path
  1214  // to a UNC long path.
  1215  func uncPath(l string) string {
  1216  	// If prefix is "\\", we already have a UNC path or server.
  1217  	if strings.HasPrefix(l, `\\`) {
  1218  		// If already long path, just keep it
  1219  		if strings.HasPrefix(l, `\\?\`) {
  1220  			return l
  1221  		}
  1222  
  1223  		// Trim "\\" from path and add UNC prefix.
  1224  		return `\\?\UNC\` + strings.TrimPrefix(l, `\\`)
  1225  	}
  1226  	if isAbsWinDrive.MatchString(l) {
  1227  		return `\\?\` + l
  1228  	}
  1229  	return l
  1230  }
  1231  
  1232  // Check the interfaces are satisfied
  1233  var (
  1234  	_ fs.Fs             = &Fs{}
  1235  	_ fs.Purger         = &Fs{}
  1236  	_ fs.PutStreamer    = &Fs{}
  1237  	_ fs.Mover          = &Fs{}
  1238  	_ fs.DirMover       = &Fs{}
  1239  	_ fs.Commander      = &Fs{}
  1240  	_ fs.OpenWriterAter = &Fs{}
  1241  	_ fs.Object         = &Object{}
  1242  )