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