github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/operations/operations.go (about)

     1  // Package operations does generic operations on filesystems and objects
     2  package operations
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"encoding/base64"
     8  	"encoding/csv"
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"mime"
    15  	"net/http"
    16  	"os"
    17  	"path"
    18  	"path/filepath"
    19  	"runtime"
    20  	"sort"
    21  	"strconv"
    22  	"strings"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/rclone/rclone/fs"
    28  	"github.com/rclone/rclone/fs/accounting"
    29  	"github.com/rclone/rclone/fs/cache"
    30  	"github.com/rclone/rclone/fs/config"
    31  	"github.com/rclone/rclone/fs/filter"
    32  	"github.com/rclone/rclone/fs/fserrors"
    33  	"github.com/rclone/rclone/fs/fshttp"
    34  	"github.com/rclone/rclone/fs/hash"
    35  	"github.com/rclone/rclone/fs/object"
    36  	"github.com/rclone/rclone/fs/walk"
    37  	"github.com/rclone/rclone/lib/atexit"
    38  	"github.com/rclone/rclone/lib/errcount"
    39  	"github.com/rclone/rclone/lib/random"
    40  	"github.com/rclone/rclone/lib/readers"
    41  	"golang.org/x/sync/errgroup"
    42  	"golang.org/x/text/unicode/norm"
    43  )
    44  
    45  // CheckHashes checks the two files to see if they have common
    46  // known hash types and compares them
    47  //
    48  // Returns.
    49  //
    50  // equal - which is equality of the hashes
    51  //
    52  // hash - the HashType. This is HashNone if either of the hashes were
    53  // unset or a compatible hash couldn't be found.
    54  //
    55  // err - may return an error which will already have been logged
    56  //
    57  // If an error is returned it will return equal as false
    58  func CheckHashes(ctx context.Context, src fs.ObjectInfo, dst fs.Object) (equal bool, ht hash.Type, err error) {
    59  	common := src.Fs().Hashes().Overlap(dst.Fs().Hashes())
    60  	// fs.Debugf(nil, "Shared hashes: %v", common)
    61  	if common.Count() == 0 {
    62  		return true, hash.None, nil
    63  	}
    64  	equal, ht, _, _, err = checkHashes(ctx, src, dst, common.GetOne())
    65  	return equal, ht, err
    66  }
    67  
    68  var errNoHash = errors.New("no hash available")
    69  
    70  // checkHashes does the work of CheckHashes but takes a hash.Type and
    71  // returns the effective hash type used.
    72  func checkHashes(ctx context.Context, src fs.ObjectInfo, dst fs.Object, ht hash.Type) (equal bool, htOut hash.Type, srcHash, dstHash string, err error) {
    73  	// Calculate hashes in parallel
    74  	g, ctx := errgroup.WithContext(ctx)
    75  	var srcErr, dstErr error
    76  	g.Go(func() (err error) {
    77  		srcHash, srcErr = src.Hash(ctx, ht)
    78  		if srcErr != nil {
    79  			return srcErr
    80  		}
    81  		if srcHash == "" {
    82  			fs.Debugf(src, "Src hash empty - aborting Dst hash check")
    83  			return errNoHash
    84  		}
    85  		return nil
    86  	})
    87  	g.Go(func() (err error) {
    88  		dstHash, dstErr = dst.Hash(ctx, ht)
    89  		if dstErr != nil {
    90  			return dstErr
    91  		}
    92  		if dstHash == "" {
    93  			fs.Debugf(dst, "Dst hash empty - aborting Src hash check")
    94  			return errNoHash
    95  		}
    96  		return nil
    97  	})
    98  	err = g.Wait()
    99  	if err == errNoHash {
   100  		return true, hash.None, srcHash, dstHash, nil
   101  	}
   102  	if srcErr != nil {
   103  		err = fs.CountError(srcErr)
   104  		fs.Errorf(src, "Failed to calculate src hash: %v", err)
   105  	}
   106  	if dstErr != nil {
   107  		err = fs.CountError(dstErr)
   108  		fs.Errorf(dst, "Failed to calculate dst hash: %v", err)
   109  	}
   110  	if err != nil {
   111  		return false, ht, srcHash, dstHash, err
   112  	}
   113  	if srcHash != dstHash {
   114  		fs.Debugf(src, "%v = %s (%v)", ht, srcHash, src.Fs())
   115  		fs.Debugf(dst, "%v = %s (%v)", ht, dstHash, dst.Fs())
   116  	} else {
   117  		fs.Debugf(src, "%v = %s OK", ht, srcHash)
   118  	}
   119  	return srcHash == dstHash, ht, srcHash, dstHash, nil
   120  }
   121  
   122  // Equal checks to see if the src and dst objects are equal by looking at
   123  // size, mtime and hash
   124  //
   125  // If the src and dst size are different then it is considered to be
   126  // not equal.  If --size-only is in effect then this is the only check
   127  // that is done.  If --ignore-size is in effect then this check is
   128  // skipped and the files are considered the same size.
   129  //
   130  // If the size is the same and the mtime is the same then it is
   131  // considered to be equal.  This check is skipped if using --checksum.
   132  //
   133  // If the size is the same and mtime is different, unreadable or
   134  // --checksum is set and the hash is the same then the file is
   135  // considered to be equal.  In this case the mtime on the dst is
   136  // updated if --checksum is not set.
   137  //
   138  // Otherwise the file is considered to be not equal including if there
   139  // were errors reading info.
   140  func Equal(ctx context.Context, src fs.ObjectInfo, dst fs.Object) bool {
   141  	return equal(ctx, src, dst, defaultEqualOpt(ctx))
   142  }
   143  
   144  // DirsEqual is like Equal but for dirs instead of objects.
   145  // It returns true if two dirs should be considered "equal" for the purposes of syncCopyMove
   146  // (in other words, true == "skip updating modtime/metadata for this dir".)
   147  // Unlike Equal, it does not consider size or checksum, as these do not apply to directories.
   148  func DirsEqual(ctx context.Context, src, dst fs.Directory, opt DirsEqualOpt) (equal bool) {
   149  	if dst == nil {
   150  		return false
   151  	}
   152  	ci := fs.GetConfig(ctx)
   153  	if ci.SizeOnly || ci.Immutable || ci.IgnoreExisting || opt.ModifyWindow == fs.ModTimeNotSupported {
   154  		return true
   155  	}
   156  	if ci.IgnoreTimes {
   157  		return false
   158  	}
   159  	if !(opt.SetDirModtime || opt.SetDirMetadata) {
   160  		return true
   161  	}
   162  	srcModTime, dstModTime := src.ModTime(ctx), dst.ModTime(ctx)
   163  	if srcModTime.IsZero() || dstModTime.IsZero() {
   164  		return false
   165  	}
   166  	dt := dstModTime.Sub(srcModTime)
   167  	if dt < opt.ModifyWindow && dt > -opt.ModifyWindow {
   168  		fs.Debugf(dst, "Directory modification time the same (differ by %s, within tolerance %s)", dt, opt.ModifyWindow)
   169  		return true
   170  	}
   171  	if ci.UpdateOlder && dt >= opt.ModifyWindow {
   172  		fs.Debugf(dst, "Destination directory is newer than source, skipping")
   173  		return true
   174  	}
   175  	return false
   176  }
   177  
   178  // sizeDiffers compare the size of src and dst taking into account the
   179  // various ways of ignoring sizes
   180  func sizeDiffers(ctx context.Context, src, dst fs.ObjectInfo) bool {
   181  	ci := fs.GetConfig(ctx)
   182  	if ci.IgnoreSize || src.Size() < 0 || dst.Size() < 0 {
   183  		return false
   184  	}
   185  	return src.Size() != dst.Size()
   186  }
   187  
   188  var checksumWarning sync.Once
   189  
   190  // options for equal function()
   191  type equalOpt struct {
   192  	sizeOnly          bool // if set only check size
   193  	checkSum          bool // if set check checksum+size instead of modtime+size
   194  	updateModTime     bool // if set update the modtime if hashes identical and checking with modtime+size
   195  	forceModTimeMatch bool // if set assume modtimes match
   196  }
   197  
   198  // default set of options for equal()
   199  func defaultEqualOpt(ctx context.Context) equalOpt {
   200  	ci := fs.GetConfig(ctx)
   201  	return equalOpt{
   202  		sizeOnly:          ci.SizeOnly,
   203  		checkSum:          ci.CheckSum,
   204  		updateModTime:     !ci.NoUpdateModTime,
   205  		forceModTimeMatch: false,
   206  	}
   207  }
   208  
   209  // DirsEqualOpt represents options for DirsEqual function()
   210  type DirsEqualOpt struct {
   211  	ModifyWindow   time.Duration // Max time diff to be considered the same
   212  	SetDirModtime  bool          // whether to consider dir modtime
   213  	SetDirMetadata bool          // whether to consider dir metadata
   214  }
   215  
   216  var modTimeUploadOnce sync.Once
   217  
   218  // emit a log if we are about to upload a file to set its modification time
   219  func logModTimeUpload(dst fs.Object) {
   220  	modTimeUploadOnce.Do(func() {
   221  		fs.Logf(dst.Fs(), "Forced to upload files to set modification times on this backend.")
   222  	})
   223  }
   224  
   225  // EqualFn allows replacing Equal() with a custom function during NeedTransfer()
   226  type (
   227  	EqualFn           func(ctx context.Context, src fs.ObjectInfo, dst fs.Object) bool
   228  	equalFnContextKey struct{}
   229  )
   230  
   231  var equalFnKey = equalFnContextKey{}
   232  
   233  // WithEqualFn stores equalFn in ctx and returns a copy of ctx in which equalFnKey = equalFn
   234  func WithEqualFn(ctx context.Context, equalFn EqualFn) context.Context {
   235  	return context.WithValue(ctx, equalFnKey, equalFn)
   236  }
   237  
   238  func equal(ctx context.Context, src fs.ObjectInfo, dst fs.Object, opt equalOpt) bool {
   239  	ci := fs.GetConfig(ctx)
   240  	logger, _ := GetLogger(ctx)
   241  	if sizeDiffers(ctx, src, dst) {
   242  		fs.Debugf(src, "Sizes differ (src %d vs dst %d)", src.Size(), dst.Size())
   243  		logger(ctx, Differ, src, dst, nil)
   244  		return false
   245  	}
   246  	if opt.sizeOnly {
   247  		fs.Debugf(src, "Sizes identical")
   248  		logger(ctx, Match, src, dst, nil)
   249  		return true
   250  	}
   251  
   252  	// Assert: Size is equal or being ignored
   253  
   254  	// If checking checksum and not modtime
   255  	if opt.checkSum {
   256  		// Check the hash
   257  		same, ht, _ := CheckHashes(ctx, src, dst)
   258  		if !same {
   259  			fs.Debugf(src, "%v differ", ht)
   260  			logger(ctx, Differ, src, dst, nil)
   261  			return false
   262  		}
   263  		if ht == hash.None {
   264  			common := src.Fs().Hashes().Overlap(dst.Fs().Hashes())
   265  			if common.Count() == 0 {
   266  				checksumWarning.Do(func() {
   267  					fs.Logf(dst.Fs(), "--checksum is in use but the source and destination have no hashes in common; falling back to --size-only")
   268  				})
   269  			}
   270  			fs.Debugf(src, "Size of src and dst objects identical")
   271  		} else {
   272  			fs.Debugf(src, "Size and %v of src and dst objects identical", ht)
   273  		}
   274  		logger(ctx, Match, src, dst, nil)
   275  		return true
   276  	}
   277  
   278  	srcModTime := src.ModTime(ctx)
   279  	if !opt.forceModTimeMatch {
   280  		// Sizes the same so check the mtime
   281  		modifyWindow := fs.GetModifyWindow(ctx, src.Fs(), dst.Fs())
   282  		if modifyWindow == fs.ModTimeNotSupported {
   283  			fs.Debugf(src, "Sizes identical")
   284  			logger(ctx, Match, src, dst, nil)
   285  			return true
   286  		}
   287  		dstModTime := dst.ModTime(ctx)
   288  		dt := dstModTime.Sub(srcModTime)
   289  		if dt < modifyWindow && dt > -modifyWindow {
   290  			fs.Debugf(src, "Size and modification time the same (differ by %s, within tolerance %s)", dt, modifyWindow)
   291  			logger(ctx, Match, src, dst, nil)
   292  			return true
   293  		}
   294  
   295  		fs.Debugf(src, "Modification times differ by %s: %v, %v", dt, srcModTime, dstModTime)
   296  	}
   297  
   298  	// Check if the hashes are the same
   299  	same, ht, _ := CheckHashes(ctx, src, dst)
   300  	if !same {
   301  		fs.Debugf(src, "%v differ", ht)
   302  		logger(ctx, Differ, src, dst, nil)
   303  		return false
   304  	}
   305  	if ht == hash.None && !ci.RefreshTimes {
   306  		// if couldn't check hash, return that they differ
   307  		logger(ctx, Differ, src, dst, nil)
   308  		return false
   309  	}
   310  
   311  	// mod time differs but hash is the same to reset mod time if required
   312  	if opt.updateModTime {
   313  		if !SkipDestructive(ctx, src, "update modification time") {
   314  			// Size and hash the same but mtime different
   315  			// Error if objects are treated as immutable
   316  			if ci.Immutable {
   317  				fs.Errorf(dst, "Timestamp mismatch between immutable objects")
   318  				logger(ctx, Differ, src, dst, nil)
   319  				return false
   320  			}
   321  			// Update the mtime of the dst object here
   322  			err := dst.SetModTime(ctx, srcModTime)
   323  			if errors.Is(err, fs.ErrorCantSetModTime) {
   324  				logModTimeUpload(dst)
   325  				fs.Infof(dst, "src and dst identical but can't set mod time without re-uploading")
   326  				logger(ctx, Differ, src, dst, nil)
   327  				return false
   328  			} else if errors.Is(err, fs.ErrorCantSetModTimeWithoutDelete) {
   329  				logModTimeUpload(dst)
   330  				fs.Infof(dst, "src and dst identical but can't set mod time without deleting and re-uploading")
   331  				// Remove the file if BackupDir isn't set.  If BackupDir is set we would rather have the old file
   332  				// put in the BackupDir than deleted which is what will happen if we don't delete it.
   333  				if ci.BackupDir == "" {
   334  					err = dst.Remove(ctx)
   335  					if err != nil {
   336  						fs.Errorf(dst, "failed to delete before re-upload: %v", err)
   337  					}
   338  				}
   339  				logger(ctx, Differ, src, dst, nil)
   340  				return false
   341  			} else if err != nil {
   342  				err = fs.CountError(err)
   343  				fs.Errorf(dst, "Failed to set modification time: %v", err)
   344  			} else {
   345  				fs.Infof(src, "Updated modification time in destination")
   346  			}
   347  		}
   348  	}
   349  	logger(ctx, Match, src, dst, nil)
   350  	return true
   351  }
   352  
   353  // CommonHash returns a single hash.Type and a HashOption with that
   354  // type which is in common between the two fs.Fs.
   355  func CommonHash(ctx context.Context, fa, fb fs.Info) (hash.Type, *fs.HashesOption) {
   356  	ci := fs.GetConfig(ctx)
   357  	// work out which hash to use - limit to 1 hash in common
   358  	var common hash.Set
   359  	hashType := hash.None
   360  	if !ci.IgnoreChecksum {
   361  		common = fb.Hashes().Overlap(fa.Hashes())
   362  		if common.Count() > 0 {
   363  			hashType = common.GetOne()
   364  			common = hash.Set(hashType)
   365  		}
   366  	}
   367  	return hashType, &fs.HashesOption{Hashes: common}
   368  }
   369  
   370  // SameObject returns true if src and dst could be pointing to the
   371  // same object.
   372  func SameObject(src, dst fs.Object) bool {
   373  	srcFs, dstFs := src.Fs(), dst.Fs()
   374  	if !SameConfig(srcFs, dstFs) {
   375  		// If same remote type then check ID of objects if available
   376  		doSrcID, srcIDOK := src.(fs.IDer)
   377  		doDstID, dstIDOK := dst.(fs.IDer)
   378  		if srcIDOK && dstIDOK && SameRemoteType(srcFs, dstFs) {
   379  			srcID, dstID := doSrcID.ID(), doDstID.ID()
   380  			if srcID != "" && srcID == dstID {
   381  				return true
   382  			}
   383  		}
   384  		return false
   385  	}
   386  	srcPath := path.Join(srcFs.Root(), src.Remote())
   387  	dstPath := path.Join(dstFs.Root(), dst.Remote())
   388  	if srcFs.Features().IsLocal && dstFs.Features().IsLocal && runtime.GOOS == "darwin" {
   389  		if norm.NFC.String(srcPath) == norm.NFC.String(dstPath) {
   390  			return true
   391  		}
   392  	}
   393  	if dst.Fs().Features().CaseInsensitive {
   394  		srcPath = strings.ToLower(srcPath)
   395  		dstPath = strings.ToLower(dstPath)
   396  	}
   397  	return srcPath == dstPath
   398  }
   399  
   400  // Move src object to dst or fdst if nil.  If dst is nil then it uses
   401  // remote as the name of the new object.
   402  //
   403  // Note that you must check the destination does not exist before
   404  // calling this and pass it as dst.  If you pass dst=nil and the
   405  // destination does exist then this may create duplicates or return
   406  // errors.
   407  //
   408  // It returns the destination object if possible.  Note that this may
   409  // be nil.
   410  //
   411  // This is accounted as a check.
   412  func Move(ctx context.Context, fdst fs.Fs, dst fs.Object, remote string, src fs.Object) (newDst fs.Object, err error) {
   413  	return move(ctx, fdst, dst, remote, src, false)
   414  }
   415  
   416  // MoveTransfer moves src object to dst or fdst if nil. If dst is nil
   417  // then it uses remote as the name of the new object.
   418  //
   419  // This is identical to Move but is accounted as a transfer.
   420  func MoveTransfer(ctx context.Context, fdst fs.Fs, dst fs.Object, remote string, src fs.Object) (newDst fs.Object, err error) {
   421  	return move(ctx, fdst, dst, remote, src, true)
   422  }
   423  
   424  // move - see Move for help
   425  func move(ctx context.Context, fdst fs.Fs, dst fs.Object, remote string, src fs.Object, isTransfer bool) (newDst fs.Object, err error) {
   426  	ci := fs.GetConfig(ctx)
   427  	var tr *accounting.Transfer
   428  	if isTransfer {
   429  		tr = accounting.Stats(ctx).NewTransfer(src, fdst)
   430  	} else {
   431  		tr = accounting.Stats(ctx).NewCheckingTransfer(src, "moving")
   432  	}
   433  	defer func() {
   434  		if err == nil {
   435  			accounting.Stats(ctx).Renames(1)
   436  		}
   437  		tr.Done(ctx, err)
   438  	}()
   439  	newDst = dst
   440  	if SkipDestructive(ctx, src, "move") {
   441  		in := tr.Account(ctx, nil)
   442  		in.DryRun(src.Size())
   443  		return newDst, nil
   444  	}
   445  	// See if we have Move available
   446  	if doMove := fdst.Features().Move; doMove != nil && (SameConfig(src.Fs(), fdst) || (SameRemoteType(src.Fs(), fdst) && (fdst.Features().ServerSideAcrossConfigs || ci.ServerSideAcrossConfigs))) {
   447  		// Delete destination if it exists and is not the same file as src (could be same file while seemingly different if the remote is case insensitive)
   448  		if dst != nil {
   449  			remote = dst.Remote()
   450  			if !SameObject(src, dst) {
   451  				err = DeleteFile(ctx, dst)
   452  				if err != nil {
   453  					return newDst, err
   454  				}
   455  			} else if needsMoveCaseInsensitive(fdst, fdst, remote, src.Remote(), false) {
   456  				doMove = func(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   457  					return MoveCaseInsensitive(ctx, fdst, fdst, remote, src.Remote(), false, src)
   458  				}
   459  			}
   460  		} else if needsMoveCaseInsensitive(fdst, fdst, remote, src.Remote(), false) {
   461  			doMove = func(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   462  				return MoveCaseInsensitive(ctx, fdst, fdst, remote, src.Remote(), false, src)
   463  			}
   464  		}
   465  		// Move dst <- src
   466  		in := tr.Account(ctx, nil) // account the transfer
   467  		in.ServerSideTransferStart()
   468  		newDst, err = doMove(ctx, src, remote)
   469  		switch err {
   470  		case nil:
   471  			if newDst != nil && src.String() != newDst.String() {
   472  				fs.Infof(src, "Moved (server-side) to: %s", newDst.String())
   473  			} else {
   474  				fs.Infof(src, "Moved (server-side)")
   475  			}
   476  			in.ServerSideMoveEnd(newDst.Size()) // account the bytes for the server-side transfer
   477  			_ = in.Close()
   478  			return newDst, nil
   479  		case fs.ErrorCantMove:
   480  			fs.Debugf(src, "Can't move, switching to copy")
   481  			_ = in.Close()
   482  		default:
   483  			err = fs.CountError(err)
   484  			fs.Errorf(src, "Couldn't move: %v", err)
   485  			_ = in.Close()
   486  			return newDst, err
   487  		}
   488  	}
   489  	// Move not found or didn't work so copy dst <- src
   490  	newDst, err = Copy(ctx, fdst, dst, remote, src)
   491  	if err != nil {
   492  		fs.Errorf(src, "Not deleting source as copy failed: %v", err)
   493  		return newDst, err
   494  	}
   495  	// Delete src if no error on copy
   496  	return newDst, DeleteFile(ctx, src)
   497  }
   498  
   499  // CanServerSideMove returns true if fdst support server-side moves or
   500  // server-side copies
   501  //
   502  // Some remotes simulate rename by server-side copy and delete, so include
   503  // remotes that implements either Mover or Copier.
   504  func CanServerSideMove(fdst fs.Fs) bool {
   505  	canMove := fdst.Features().Move != nil
   506  	canCopy := fdst.Features().Copy != nil
   507  	return canMove || canCopy
   508  }
   509  
   510  // SuffixName adds the current --suffix to the remote, obeying
   511  // --suffix-keep-extension if set
   512  func SuffixName(ctx context.Context, remote string) string {
   513  	ci := fs.GetConfig(ctx)
   514  	if ci.Suffix == "" {
   515  		return remote
   516  	}
   517  	if ci.SuffixKeepExtension {
   518  		var (
   519  			base  = remote
   520  			exts  = ""
   521  			first = true
   522  			ext   = path.Ext(remote)
   523  		)
   524  		for ext != "" {
   525  			// Look second and subsequent extensions in mime types.
   526  			// If they aren't found then don't keep it as an extension.
   527  			if !first && mime.TypeByExtension(ext) == "" {
   528  				break
   529  			}
   530  			base = base[:len(base)-len(ext)]
   531  			exts = ext + exts
   532  			first = false
   533  			ext = path.Ext(base)
   534  		}
   535  		return base + ci.Suffix + exts
   536  	}
   537  	return remote + ci.Suffix
   538  }
   539  
   540  // DeleteFileWithBackupDir deletes a single file respecting --dry-run
   541  // and accumulating stats and errors.
   542  //
   543  // If backupDir is set then it moves the file to there instead of
   544  // deleting
   545  func DeleteFileWithBackupDir(ctx context.Context, dst fs.Object, backupDir fs.Fs) (err error) {
   546  	tr := accounting.Stats(ctx).NewCheckingTransfer(dst, "deleting")
   547  	defer func() {
   548  		tr.Done(ctx, err)
   549  	}()
   550  	err = accounting.Stats(ctx).DeleteFile(ctx, dst.Size())
   551  	if err != nil {
   552  		return err
   553  	}
   554  	action, actioned := "delete", "Deleted"
   555  	if backupDir != nil {
   556  		action, actioned = "move into backup dir", "Moved into backup dir"
   557  	}
   558  	skip := SkipDestructive(ctx, dst, action)
   559  	if skip {
   560  		// do nothing
   561  	} else if backupDir != nil {
   562  		err = MoveBackupDir(ctx, backupDir, dst)
   563  	} else {
   564  		err = dst.Remove(ctx)
   565  	}
   566  	if err != nil {
   567  		fs.Errorf(dst, "Couldn't %s: %v", action, err)
   568  		err = fs.CountError(err)
   569  	} else if !skip {
   570  		fs.Infof(dst, actioned)
   571  	}
   572  	return err
   573  }
   574  
   575  // DeleteFile deletes a single file respecting --dry-run and accumulating stats and errors.
   576  //
   577  // If useBackupDir is set and --backup-dir is in effect then it moves
   578  // the file to there instead of deleting
   579  func DeleteFile(ctx context.Context, dst fs.Object) (err error) {
   580  	return DeleteFileWithBackupDir(ctx, dst, nil)
   581  }
   582  
   583  // DeleteFilesWithBackupDir removes all the files passed in the
   584  // channel
   585  //
   586  // If backupDir is set the files will be placed into that directory
   587  // instead of being deleted.
   588  func DeleteFilesWithBackupDir(ctx context.Context, toBeDeleted fs.ObjectsChan, backupDir fs.Fs) error {
   589  	var wg sync.WaitGroup
   590  	ci := fs.GetConfig(ctx)
   591  	wg.Add(ci.Checkers)
   592  	var errorCount atomic.Int32
   593  	var fatalErrorCount atomic.Int32
   594  
   595  	for i := 0; i < ci.Checkers; i++ {
   596  		go func() {
   597  			defer wg.Done()
   598  			for dst := range toBeDeleted {
   599  				err := DeleteFileWithBackupDir(ctx, dst, backupDir)
   600  				if err != nil {
   601  					errorCount.Add(1)
   602  					logger, _ := GetLogger(ctx)
   603  					logger(ctx, TransferError, nil, dst, err)
   604  					if fserrors.IsFatalError(err) {
   605  						fs.Errorf(dst, "Got fatal error on delete: %s", err)
   606  						fatalErrorCount.Add(1)
   607  						return
   608  					}
   609  				}
   610  			}
   611  		}()
   612  	}
   613  	fs.Debugf(nil, "Waiting for deletions to finish")
   614  	wg.Wait()
   615  	if errorCount.Load() > 0 {
   616  		err := fmt.Errorf("failed to delete %d files", errorCount.Load())
   617  		if fatalErrorCount.Load() > 0 {
   618  			return fserrors.FatalError(err)
   619  		}
   620  		return err
   621  	}
   622  	return nil
   623  }
   624  
   625  // DeleteFiles removes all the files passed in the channel
   626  func DeleteFiles(ctx context.Context, toBeDeleted fs.ObjectsChan) error {
   627  	return DeleteFilesWithBackupDir(ctx, toBeDeleted, nil)
   628  }
   629  
   630  // SameRemoteType returns true if fdst and fsrc are the same type
   631  func SameRemoteType(fdst, fsrc fs.Info) bool {
   632  	return fmt.Sprintf("%T", fdst) == fmt.Sprintf("%T", fsrc)
   633  }
   634  
   635  // SameConfig returns true if fdst and fsrc are using the same config
   636  // file entry
   637  func SameConfig(fdst, fsrc fs.Info) bool {
   638  	return fdst.Name() == fsrc.Name()
   639  }
   640  
   641  // SameConfigArr returns true if any of []fsrcs has same config file entry with fdst
   642  func SameConfigArr(fdst fs.Info, fsrcs []fs.Fs) bool {
   643  	for _, fsrc := range fsrcs {
   644  		if fdst.Name() == fsrc.Name() {
   645  			return true
   646  		}
   647  	}
   648  	return false
   649  }
   650  
   651  // Same returns true if fdst and fsrc point to the same underlying Fs
   652  func Same(fdst, fsrc fs.Info) bool {
   653  	return SameConfig(fdst, fsrc) && strings.Trim(fdst.Root(), "/") == strings.Trim(fsrc.Root(), "/")
   654  }
   655  
   656  // fixRoot returns the Root with a trailing / if not empty.
   657  //
   658  // It returns a case folded version for case insensitive file systems
   659  func fixRoot(f fs.Info) (s string, folded string) {
   660  	s = strings.Trim(filepath.ToSlash(f.Root()), "/")
   661  	if s != "" {
   662  		s += "/"
   663  	}
   664  	folded = s
   665  	if f.Features().CaseInsensitive {
   666  		folded = strings.ToLower(s)
   667  	}
   668  	return s, folded
   669  }
   670  
   671  // OverlappingFilterCheck returns true if fdst and fsrc point to the same
   672  // underlying Fs and they overlap without fdst being excluded by any filter rule.
   673  func OverlappingFilterCheck(ctx context.Context, fdst fs.Fs, fsrc fs.Fs) bool {
   674  	if !SameConfig(fdst, fsrc) {
   675  		return false
   676  	}
   677  	fdstRoot, fdstRootFolded := fixRoot(fdst)
   678  	fsrcRoot, fsrcRootFolded := fixRoot(fsrc)
   679  	if fdstRootFolded == fsrcRootFolded {
   680  		return true
   681  	} else if strings.HasPrefix(fdstRootFolded, fsrcRootFolded) {
   682  		fdstRelative := fdstRoot[len(fsrcRoot):]
   683  		return filterCheck(ctx, fsrc, fdstRelative)
   684  	} else if strings.HasPrefix(fsrcRootFolded, fdstRootFolded) {
   685  		fsrcRelative := fsrcRoot[len(fdstRoot):]
   686  		return filterCheck(ctx, fdst, fsrcRelative)
   687  	}
   688  	return false
   689  }
   690  
   691  // filterCheck checks if dir is included in f
   692  func filterCheck(ctx context.Context, f fs.Fs, dir string) bool {
   693  	fi := filter.GetConfig(ctx)
   694  	includeDirectory := fi.IncludeDirectory(ctx, f)
   695  	include, err := includeDirectory(dir)
   696  	if err != nil {
   697  		fs.Errorf(f, "Failed to discover whether directory is included: %v", err)
   698  		return true
   699  	}
   700  	return include
   701  }
   702  
   703  // SameDir returns true if fdst and fsrc point to the same
   704  // underlying Fs and they are the same directory.
   705  func SameDir(fdst, fsrc fs.Info) bool {
   706  	if !SameConfig(fdst, fsrc) {
   707  		return false
   708  	}
   709  	_, fdstRootFolded := fixRoot(fdst)
   710  	_, fsrcRootFolded := fixRoot(fsrc)
   711  	return fdstRootFolded == fsrcRootFolded
   712  }
   713  
   714  // Retry runs fn up to maxTries times if it returns a retriable error
   715  func Retry(ctx context.Context, o interface{}, maxTries int, fn func() error) (err error) {
   716  	for tries := 1; tries <= maxTries; tries++ {
   717  		// Call the function which might error
   718  		err = fn()
   719  		if err == nil {
   720  			break
   721  		}
   722  		// Retry if err returned a retry error
   723  		if fserrors.ContextError(ctx, &err) {
   724  			break
   725  		}
   726  		if fserrors.IsRetryError(err) || fserrors.ShouldRetry(err) {
   727  			fs.Debugf(o, "Received error: %v - low level retry %d/%d", err, tries, maxTries)
   728  			continue
   729  		}
   730  		break
   731  	}
   732  	return err
   733  }
   734  
   735  // ListFn lists the Fs to the supplied function
   736  //
   737  // Lists in parallel which may get them out of order
   738  func ListFn(ctx context.Context, f fs.Fs, fn func(fs.Object)) error {
   739  	ci := fs.GetConfig(ctx)
   740  	return walk.ListR(ctx, f, "", false, ci.MaxDepth, walk.ListObjects, func(entries fs.DirEntries) error {
   741  		entries.ForObject(fn)
   742  		return nil
   743  	})
   744  }
   745  
   746  // StdoutMutex mutex for synchronized output on stdout
   747  var StdoutMutex sync.Mutex
   748  
   749  // SyncPrintf is a global var holding the Printf function so that it
   750  // can be overridden.
   751  //
   752  // This writes to stdout holding the StdoutMutex. If you are going to
   753  // override it and write to os.Stdout then you should hold the
   754  // StdoutMutex too.
   755  var SyncPrintf = func(format string, a ...interface{}) {
   756  	StdoutMutex.Lock()
   757  	defer StdoutMutex.Unlock()
   758  	fmt.Printf(format, a...)
   759  }
   760  
   761  // SyncFprintf - Synchronized fmt.Fprintf
   762  //
   763  // Ignores errors from Fprintf.
   764  //
   765  // Prints to stdout if w is nil
   766  func SyncFprintf(w io.Writer, format string, a ...interface{}) {
   767  	if w == nil || w == os.Stdout {
   768  		SyncPrintf(format, a...)
   769  	} else {
   770  		StdoutMutex.Lock()
   771  		defer StdoutMutex.Unlock()
   772  		_, _ = fmt.Fprintf(w, format, a...)
   773  	}
   774  }
   775  
   776  // SizeString make string representation of size for output
   777  //
   778  // Optional human-readable format including a binary suffix
   779  func SizeString(size int64, humanReadable bool) string {
   780  	if humanReadable {
   781  		if size < 0 {
   782  			return "-" + fs.SizeSuffix(-size).String()
   783  		}
   784  		return fs.SizeSuffix(size).String()
   785  	}
   786  	return strconv.FormatInt(size, 10)
   787  }
   788  
   789  // SizeStringField make string representation of size for output in fixed width field
   790  //
   791  // Optional human-readable format including a binary suffix
   792  // Argument rawWidth is used to format field with of raw value. When humanReadable
   793  // option the width is hard coded to 9, since SizeSuffix strings have precision 3
   794  // and longest value will be "999.999Ei". This way the width can be optimized
   795  // depending to the humanReadable option. To always use a longer width the return
   796  // value can always be fed into another format string with a specific field with.
   797  func SizeStringField(size int64, humanReadable bool, rawWidth int) string {
   798  	str := SizeString(size, humanReadable)
   799  	if humanReadable {
   800  		return fmt.Sprintf("%9s", str)
   801  	}
   802  	return fmt.Sprintf("%[2]*[1]s", str, rawWidth)
   803  }
   804  
   805  // CountString make string representation of count for output
   806  //
   807  // Optional human-readable format including a decimal suffix
   808  func CountString(count int64, humanReadable bool) string {
   809  	if humanReadable {
   810  		if count < 0 {
   811  			return "-" + fs.CountSuffix(-count).String()
   812  		}
   813  		return fs.CountSuffix(count).String()
   814  	}
   815  	return strconv.FormatInt(count, 10)
   816  }
   817  
   818  // CountStringField make string representation of count for output in fixed width field
   819  //
   820  // Similar to SizeStringField, but human readable with decimal prefix and field width 8
   821  // since there is no 'i' in the decimal prefix symbols (e.g. "999.999E")
   822  func CountStringField(count int64, humanReadable bool, rawWidth int) string {
   823  	str := CountString(count, humanReadable)
   824  	if humanReadable {
   825  		return fmt.Sprintf("%8s", str)
   826  	}
   827  	return fmt.Sprintf("%[2]*[1]s", str, rawWidth)
   828  }
   829  
   830  // List the Fs to the supplied writer
   831  //
   832  // Shows size and path - obeys includes and excludes.
   833  //
   834  // Lists in parallel which may get them out of order
   835  func List(ctx context.Context, f fs.Fs, w io.Writer) error {
   836  	ci := fs.GetConfig(ctx)
   837  	return ListFn(ctx, f, func(o fs.Object) {
   838  		SyncFprintf(w, "%s %s\n", SizeStringField(o.Size(), ci.HumanReadable, 9), o.Remote())
   839  	})
   840  }
   841  
   842  // ListLong lists the Fs to the supplied writer
   843  //
   844  // Shows size, mod time and path - obeys includes and excludes.
   845  //
   846  // Lists in parallel which may get them out of order
   847  func ListLong(ctx context.Context, f fs.Fs, w io.Writer) error {
   848  	ci := fs.GetConfig(ctx)
   849  	return ListFn(ctx, f, func(o fs.Object) {
   850  		tr := accounting.Stats(ctx).NewCheckingTransfer(o, "listing")
   851  		defer func() {
   852  			tr.Done(ctx, nil)
   853  		}()
   854  		modTime := o.ModTime(ctx)
   855  		SyncFprintf(w, "%s %s %s\n", SizeStringField(o.Size(), ci.HumanReadable, 9), modTime.Local().Format("2006-01-02 15:04:05.000000000"), o.Remote())
   856  	})
   857  }
   858  
   859  // HashSum returns the human-readable hash for ht passed in.  This may
   860  // be UNSUPPORTED or ERROR. If it isn't returning a valid hash it will
   861  // return an error.
   862  func HashSum(ctx context.Context, ht hash.Type, base64Encoded bool, downloadFlag bool, o fs.Object) (string, error) {
   863  	var sum string
   864  	var err error
   865  
   866  	// If downloadFlag is true, download and hash the file.
   867  	// If downloadFlag is false, call o.Hash asking the remote for the hash
   868  	if downloadFlag {
   869  		// Setup: Define accounting, open the file with NewReOpen to provide restarts, account for the transfer, and setup a multi-hasher with the appropriate type
   870  		// Execution: io.Copy file to hasher, get hash and encode in hex
   871  
   872  		tr := accounting.Stats(ctx).NewTransfer(o, nil)
   873  		defer func() {
   874  			tr.Done(ctx, err)
   875  		}()
   876  
   877  		// Open with NewReOpen to provide restarts
   878  		var options []fs.OpenOption
   879  		for _, option := range fs.GetConfig(ctx).DownloadHeaders {
   880  			options = append(options, option)
   881  		}
   882  		var in io.ReadCloser
   883  		in, err = Open(ctx, o, options...)
   884  		if err != nil {
   885  			return "ERROR", fmt.Errorf("failed to open file %v: %w", o, err)
   886  		}
   887  
   888  		// Account and buffer the transfer
   889  		in = tr.Account(ctx, in).WithBuffer()
   890  
   891  		// Setup hasher
   892  		hasher, err := hash.NewMultiHasherTypes(hash.NewHashSet(ht))
   893  		if err != nil {
   894  			return "UNSUPPORTED", fmt.Errorf("hash unsupported: %w", err)
   895  		}
   896  
   897  		// Copy to hasher, downloading the file and passing directly to hash
   898  		_, err = io.Copy(hasher, in)
   899  		if err != nil {
   900  			return "ERROR", fmt.Errorf("failed to copy file to hasher: %w", err)
   901  		}
   902  
   903  		// Get hash as hex or base64 encoded string
   904  		sum, err = hasher.SumString(ht, base64Encoded)
   905  		if err != nil {
   906  			return "ERROR", fmt.Errorf("hasher returned an error: %w", err)
   907  		}
   908  	} else {
   909  		tr := accounting.Stats(ctx).NewCheckingTransfer(o, "hashing")
   910  		defer func() {
   911  			tr.Done(ctx, err)
   912  		}()
   913  
   914  		sum, err = o.Hash(ctx, ht)
   915  		if base64Encoded {
   916  			hexBytes, _ := hex.DecodeString(sum)
   917  			sum = base64.URLEncoding.EncodeToString(hexBytes)
   918  		}
   919  		if err == hash.ErrUnsupported {
   920  			return "", fmt.Errorf("hash unsupported: %w", err)
   921  		}
   922  		if err != nil {
   923  			return "", fmt.Errorf("failed to get hash %v from backend: %w", ht, err)
   924  		}
   925  	}
   926  
   927  	return sum, nil
   928  }
   929  
   930  // HashLister does an md5sum equivalent for the hash type passed in
   931  // Updated to handle both standard hex encoding and base64
   932  // Updated to perform multiple hashes concurrently
   933  func HashLister(ctx context.Context, ht hash.Type, outputBase64 bool, downloadFlag bool, f fs.Fs, w io.Writer) error {
   934  	width := hash.Width(ht, outputBase64)
   935  	// Use --checkers concurrency unless downloading in which case use --transfers
   936  	concurrency := fs.GetConfig(ctx).Checkers
   937  	if downloadFlag {
   938  		concurrency = fs.GetConfig(ctx).Transfers
   939  	}
   940  	concurrencyControl := make(chan struct{}, concurrency)
   941  	var wg sync.WaitGroup
   942  	err := ListFn(ctx, f, func(o fs.Object) {
   943  		wg.Add(1)
   944  		concurrencyControl <- struct{}{}
   945  		go func() {
   946  			defer func() {
   947  				<-concurrencyControl
   948  				wg.Done()
   949  			}()
   950  			sum, err := HashSum(ctx, ht, outputBase64, downloadFlag, o)
   951  			if err != nil {
   952  				fs.Errorf(o, "%v", fs.CountError(err))
   953  				return
   954  			}
   955  			SyncFprintf(w, "%*s  %s\n", width, sum, o.Remote())
   956  		}()
   957  	})
   958  	wg.Wait()
   959  	return err
   960  }
   961  
   962  // HashSumStream outputs a line compatible with md5sum to w based on the
   963  // input stream in and the hash type ht passed in. If outputBase64 is
   964  // set then the hash will be base64 instead of hexadecimal.
   965  func HashSumStream(ht hash.Type, outputBase64 bool, in io.ReadCloser, w io.Writer) error {
   966  	hasher, err := hash.NewMultiHasherTypes(hash.NewHashSet(ht))
   967  	if err != nil {
   968  		return fmt.Errorf("hash unsupported: %w", err)
   969  	}
   970  	written, err := io.Copy(hasher, in)
   971  	fs.Debugf(nil, "Creating %s hash of %d bytes read from input stream", ht, written)
   972  	if err != nil {
   973  		return fmt.Errorf("failed to copy input to hasher: %w", err)
   974  	}
   975  	sum, err := hasher.SumString(ht, outputBase64)
   976  	if err != nil {
   977  		return fmt.Errorf("hasher returned an error: %w", err)
   978  	}
   979  	width := hash.Width(ht, outputBase64)
   980  	SyncFprintf(w, "%*s  -\n", width, sum)
   981  	return nil
   982  }
   983  
   984  // Count counts the objects and their sizes in the Fs
   985  //
   986  // Obeys includes and excludes
   987  func Count(ctx context.Context, f fs.Fs) (objects int64, size int64, sizelessObjects int64, err error) {
   988  	err = ListFn(ctx, f, func(o fs.Object) {
   989  		atomic.AddInt64(&objects, 1)
   990  		objectSize := o.Size()
   991  		if objectSize < 0 {
   992  			atomic.AddInt64(&sizelessObjects, 1)
   993  		} else if objectSize > 0 {
   994  			atomic.AddInt64(&size, objectSize)
   995  		}
   996  	})
   997  	return
   998  }
   999  
  1000  // ConfigMaxDepth returns the depth to use for a recursive or non recursive listing.
  1001  func ConfigMaxDepth(ctx context.Context, recursive bool) int {
  1002  	ci := fs.GetConfig(ctx)
  1003  	depth := ci.MaxDepth
  1004  	if !recursive && depth < 0 {
  1005  		depth = 1
  1006  	}
  1007  	return depth
  1008  }
  1009  
  1010  // ListDir lists the directories/buckets/containers in the Fs to the supplied writer
  1011  func ListDir(ctx context.Context, f fs.Fs, w io.Writer) error {
  1012  	ci := fs.GetConfig(ctx)
  1013  	return walk.ListR(ctx, f, "", false, ConfigMaxDepth(ctx, false), walk.ListDirs, func(entries fs.DirEntries) error {
  1014  		entries.ForDir(func(dir fs.Directory) {
  1015  			if dir != nil {
  1016  				SyncFprintf(w, "%s %13s %s %s\n", SizeStringField(dir.Size(), ci.HumanReadable, 12), dir.ModTime(ctx).Local().Format("2006-01-02 15:04:05"), CountStringField(dir.Items(), ci.HumanReadable, 9), dir.Remote())
  1017  			}
  1018  		})
  1019  		return nil
  1020  	})
  1021  }
  1022  
  1023  // Mkdir makes a destination directory or container
  1024  func Mkdir(ctx context.Context, f fs.Fs, dir string) error {
  1025  	if SkipDestructive(ctx, fs.LogDirName(f, dir), "make directory") {
  1026  		return nil
  1027  	}
  1028  	fs.Debugf(fs.LogDirName(f, dir), "Making directory")
  1029  	err := f.Mkdir(ctx, dir)
  1030  	if err != nil {
  1031  		err = fs.CountError(err)
  1032  		return err
  1033  	}
  1034  	return nil
  1035  }
  1036  
  1037  // MkdirMetadata makes a destination directory or container with metadata
  1038  //
  1039  // If the destination Fs doesn't support this it will fall back to
  1040  // Mkdir and in this case newDst will be nil.
  1041  func MkdirMetadata(ctx context.Context, f fs.Fs, dir string, metadata fs.Metadata) (newDst fs.Directory, err error) {
  1042  	do := f.Features().MkdirMetadata
  1043  	if do == nil {
  1044  		return nil, Mkdir(ctx, f, dir)
  1045  	}
  1046  	logName := fs.LogDirName(f, dir)
  1047  	if SkipDestructive(ctx, logName, "make directory") {
  1048  		return nil, nil
  1049  	}
  1050  	fs.Debugf(fs.LogDirName(f, dir), "Making directory with metadata")
  1051  	newDst, err = do(ctx, dir, metadata)
  1052  	if err != nil {
  1053  		err = fs.CountError(err)
  1054  		return nil, err
  1055  	}
  1056  	if mtime, ok := metadata["mtime"]; ok {
  1057  		fs.Infof(logName, "Made directory with metadata (mtime=%s)", mtime)
  1058  	} else {
  1059  		fs.Infof(logName, "Made directory with metadata")
  1060  	}
  1061  	return newDst, err
  1062  }
  1063  
  1064  // MkdirModTime makes a destination directory or container with modtime
  1065  //
  1066  // It will try to make the directory with MkdirMetadata and if that
  1067  // succeeds it will return a non-nil newDst. In all other cases newDst
  1068  // will be nil.
  1069  //
  1070  // If the directory was created with MkDir then it will attempt to use
  1071  // Fs.DirSetModTime to update the directory modtime if available.
  1072  func MkdirModTime(ctx context.Context, f fs.Fs, dir string, modTime time.Time) (newDst fs.Directory, err error) {
  1073  	logName := fs.LogDirName(f, dir)
  1074  	if SkipDestructive(ctx, logName, "make directory") {
  1075  		return nil, nil
  1076  	}
  1077  	metadata := fs.Metadata{
  1078  		"mtime": modTime.Format(time.RFC3339Nano),
  1079  	}
  1080  	newDst, err = MkdirMetadata(ctx, f, dir, metadata)
  1081  	if err != nil {
  1082  		return nil, err
  1083  	}
  1084  	if newDst != nil {
  1085  		// The directory was created and we have logged already
  1086  		return newDst, nil
  1087  	}
  1088  	// The directory was created with Mkdir then we should try to set the time
  1089  	if do := f.Features().DirSetModTime; do != nil {
  1090  		err = do(ctx, dir, modTime)
  1091  	}
  1092  	fs.Infof(logName, "Made directory with modification time %v", modTime)
  1093  	return newDst, err
  1094  }
  1095  
  1096  // TryRmdir removes a container but not if not empty.  It doesn't
  1097  // count errors but may return one.
  1098  func TryRmdir(ctx context.Context, f fs.Fs, dir string) error {
  1099  	accounting.Stats(ctx).DeletedDirs(1)
  1100  	if SkipDestructive(ctx, fs.LogDirName(f, dir), "remove directory") {
  1101  		return nil
  1102  	}
  1103  	fs.Infof(fs.LogDirName(f, dir), "Removing directory")
  1104  	return f.Rmdir(ctx, dir)
  1105  }
  1106  
  1107  // Rmdir removes a container but not if not empty
  1108  func Rmdir(ctx context.Context, f fs.Fs, dir string) error {
  1109  	err := TryRmdir(ctx, f, dir)
  1110  	if err != nil {
  1111  		err = fs.CountError(err)
  1112  		return err
  1113  	}
  1114  	return err
  1115  }
  1116  
  1117  // Purge removes a directory and all of its contents
  1118  func Purge(ctx context.Context, f fs.Fs, dir string) (err error) {
  1119  	doFallbackPurge := true
  1120  	if doPurge := f.Features().Purge; doPurge != nil {
  1121  		doFallbackPurge = false
  1122  		accounting.Stats(ctx).DeletedDirs(1)
  1123  		if SkipDestructive(ctx, fs.LogDirName(f, dir), "purge directory") {
  1124  			return nil
  1125  		}
  1126  		err = doPurge(ctx, dir)
  1127  		if errors.Is(err, fs.ErrorCantPurge) {
  1128  			doFallbackPurge = true
  1129  		}
  1130  	}
  1131  	if doFallbackPurge {
  1132  		// DeleteFiles and Rmdir observe --dry-run
  1133  		err = DeleteFiles(ctx, listToChan(ctx, f, dir))
  1134  		if err != nil {
  1135  			return err
  1136  		}
  1137  		err = Rmdirs(ctx, f, dir, false)
  1138  	}
  1139  	if err != nil {
  1140  		err = fs.CountError(err)
  1141  		return err
  1142  	}
  1143  	return nil
  1144  }
  1145  
  1146  // Delete removes all the contents of a container.  Unlike Purge, it
  1147  // obeys includes and excludes.
  1148  func Delete(ctx context.Context, f fs.Fs) error {
  1149  	ci := fs.GetConfig(ctx)
  1150  	delChan := make(fs.ObjectsChan, ci.Checkers)
  1151  	delErr := make(chan error, 1)
  1152  	go func() {
  1153  		delErr <- DeleteFiles(ctx, delChan)
  1154  	}()
  1155  	err := ListFn(ctx, f, func(o fs.Object) {
  1156  		delChan <- o
  1157  	})
  1158  	close(delChan)
  1159  	delError := <-delErr
  1160  	if err == nil {
  1161  		err = delError
  1162  	}
  1163  	return err
  1164  }
  1165  
  1166  // listToChan will transfer all objects in the listing to the output
  1167  //
  1168  // If an error occurs, the error will be logged, and it will close the
  1169  // channel.
  1170  //
  1171  // If the error was ErrorDirNotFound then it will be ignored
  1172  func listToChan(ctx context.Context, f fs.Fs, dir string) fs.ObjectsChan {
  1173  	ci := fs.GetConfig(ctx)
  1174  	o := make(fs.ObjectsChan, ci.Checkers)
  1175  	go func() {
  1176  		defer close(o)
  1177  		err := walk.ListR(ctx, f, dir, true, ci.MaxDepth, walk.ListObjects, func(entries fs.DirEntries) error {
  1178  			entries.ForObject(func(obj fs.Object) {
  1179  				o <- obj
  1180  			})
  1181  			return nil
  1182  		})
  1183  		if err != nil && err != fs.ErrorDirNotFound {
  1184  			err = fmt.Errorf("failed to list: %w", err)
  1185  			err = fs.CountError(err)
  1186  			fs.Errorf(nil, "%v", err)
  1187  		}
  1188  	}()
  1189  	return o
  1190  }
  1191  
  1192  // CleanUp removes the trash for the Fs
  1193  func CleanUp(ctx context.Context, f fs.Fs) error {
  1194  	doCleanUp := f.Features().CleanUp
  1195  	if doCleanUp == nil {
  1196  		return fmt.Errorf("%v doesn't support cleanup", f)
  1197  	}
  1198  	if SkipDestructive(ctx, f, "clean up old files") {
  1199  		return nil
  1200  	}
  1201  	return doCleanUp(ctx)
  1202  }
  1203  
  1204  // wrap a Reader and a Closer together into a ReadCloser
  1205  type readCloser struct {
  1206  	io.Reader
  1207  	io.Closer
  1208  }
  1209  
  1210  // Cat any files to the io.Writer
  1211  //
  1212  // if offset == 0 it will be ignored
  1213  // if offset > 0 then the file will be seeked to that offset
  1214  // if offset < 0 then the file will be seeked that far from the end
  1215  //
  1216  // if count < 0 then it will be ignored
  1217  // if count >= 0 then only that many characters will be output
  1218  func Cat(ctx context.Context, f fs.Fs, w io.Writer, offset, count int64, sep []byte) error {
  1219  	var mu sync.Mutex
  1220  	ci := fs.GetConfig(ctx)
  1221  	return ListFn(ctx, f, func(o fs.Object) {
  1222  		var err error
  1223  		tr := accounting.Stats(ctx).NewTransfer(o, nil)
  1224  		defer func() {
  1225  			tr.Done(ctx, err)
  1226  		}()
  1227  		opt := fs.RangeOption{Start: offset, End: -1}
  1228  		size := o.Size()
  1229  		if opt.Start < 0 {
  1230  			opt.Start += size
  1231  		}
  1232  		if count >= 0 {
  1233  			opt.End = opt.Start + count - 1
  1234  		}
  1235  		var options []fs.OpenOption
  1236  		if opt.Start > 0 || opt.End >= 0 {
  1237  			options = append(options, &opt)
  1238  		}
  1239  		for _, option := range ci.DownloadHeaders {
  1240  			options = append(options, option)
  1241  		}
  1242  		var in io.ReadCloser
  1243  		in, err = Open(ctx, o, options...)
  1244  		if err != nil {
  1245  			err = fs.CountError(err)
  1246  			fs.Errorf(o, "Failed to open: %v", err)
  1247  			return
  1248  		}
  1249  		if count >= 0 {
  1250  			in = &readCloser{Reader: &io.LimitedReader{R: in, N: count}, Closer: in}
  1251  		}
  1252  		in = tr.Account(ctx, in).WithBuffer() // account and buffer the transfer
  1253  		// take the lock just before we output stuff, so at the last possible moment
  1254  		mu.Lock()
  1255  		defer mu.Unlock()
  1256  		_, err = io.Copy(w, in)
  1257  		if err != nil {
  1258  			err = fs.CountError(err)
  1259  			fs.Errorf(o, "Failed to send to output: %v", err)
  1260  		}
  1261  		if len(sep) >= 0 {
  1262  			_, err = w.Write(sep)
  1263  			if err != nil {
  1264  				err = fs.CountError(err)
  1265  				fs.Errorf(o, "Failed to send separator to output: %v", err)
  1266  			}
  1267  		}
  1268  	})
  1269  }
  1270  
  1271  // Rcat reads data from the Reader until EOF and uploads it to a file on remote
  1272  func Rcat(ctx context.Context, fdst fs.Fs, dstFileName string, in io.ReadCloser, modTime time.Time, meta fs.Metadata) (dst fs.Object, err error) {
  1273  	ci := fs.GetConfig(ctx)
  1274  	tr := accounting.Stats(ctx).NewTransferRemoteSize(dstFileName, -1, nil, fdst)
  1275  	defer func() {
  1276  		tr.Done(ctx, err)
  1277  	}()
  1278  	in = tr.Account(ctx, in).WithBuffer()
  1279  
  1280  	readCounter := readers.NewCountingReader(in)
  1281  	var trackingIn io.Reader
  1282  	var hasher *hash.MultiHasher
  1283  	var options []fs.OpenOption
  1284  	if !ci.IgnoreChecksum {
  1285  		hashes := hash.NewHashSet(fdst.Hashes().GetOne()) // just pick one hash
  1286  		hashOption := &fs.HashesOption{Hashes: hashes}
  1287  		options = append(options, hashOption)
  1288  		hasher, err = hash.NewMultiHasherTypes(hashes)
  1289  		if err != nil {
  1290  			return nil, err
  1291  		}
  1292  		trackingIn = io.TeeReader(readCounter, hasher)
  1293  	} else {
  1294  		trackingIn = readCounter
  1295  	}
  1296  	for _, option := range ci.UploadHeaders {
  1297  		options = append(options, option)
  1298  	}
  1299  	if ci.MetadataSet != nil {
  1300  		options = append(options, fs.MetadataOption(ci.MetadataSet))
  1301  	}
  1302  
  1303  	compare := func(dst fs.Object) error {
  1304  		var sums map[hash.Type]string
  1305  		opt := defaultEqualOpt(ctx)
  1306  		if hasher != nil {
  1307  			// force --checksum on if we have hashes
  1308  			opt.checkSum = true
  1309  			sums = hasher.Sums()
  1310  		}
  1311  		src := object.NewStaticObjectInfo(dstFileName, modTime, int64(readCounter.BytesRead()), false, sums, fdst).WithMetadata(meta)
  1312  		if !equal(ctx, src, dst, opt) {
  1313  			err = fmt.Errorf("corrupted on transfer")
  1314  			err = fs.CountError(err)
  1315  			fs.Errorf(dst, "%v", err)
  1316  			return err
  1317  		}
  1318  		return nil
  1319  	}
  1320  
  1321  	// check if file small enough for direct upload
  1322  	buf := make([]byte, ci.StreamingUploadCutoff)
  1323  	if n, err := io.ReadFull(trackingIn, buf); err == io.EOF || err == io.ErrUnexpectedEOF {
  1324  		fs.Debugf(fdst, "File to upload is small (%d bytes), uploading instead of streaming", n)
  1325  		src := object.NewMemoryObject(dstFileName, modTime, buf[:n]).WithMetadata(meta)
  1326  		return Copy(ctx, fdst, nil, dstFileName, src)
  1327  	}
  1328  
  1329  	// Make a new ReadCloser with the bits we've already read
  1330  	in = &readCloser{
  1331  		Reader: io.MultiReader(bytes.NewReader(buf), trackingIn),
  1332  		Closer: in,
  1333  	}
  1334  
  1335  	fStreamTo := fdst
  1336  	canStream := fdst.Features().PutStream != nil
  1337  	if !canStream {
  1338  		fs.Debugf(fdst, "Target remote doesn't support streaming uploads, creating temporary local FS to spool file")
  1339  		tmpLocalFs, err := fs.TemporaryLocalFs(ctx)
  1340  		if err != nil {
  1341  			return nil, fmt.Errorf("failed to create temporary local FS to spool file: %w", err)
  1342  		}
  1343  		defer func() {
  1344  			err := Purge(ctx, tmpLocalFs, "")
  1345  			if err != nil {
  1346  				fs.Infof(tmpLocalFs, "Failed to cleanup temporary FS: %v", err)
  1347  			}
  1348  		}()
  1349  		fStreamTo = tmpLocalFs
  1350  	}
  1351  
  1352  	if SkipDestructive(ctx, dstFileName, "upload from pipe") {
  1353  		// prevents "broken pipe" errors
  1354  		_, err = io.Copy(io.Discard, in)
  1355  		return nil, err
  1356  	}
  1357  
  1358  	objInfo := object.NewStaticObjectInfo(dstFileName, modTime, -1, false, nil, nil).WithMetadata(meta)
  1359  	if dst, err = fStreamTo.Features().PutStream(ctx, in, objInfo, options...); err != nil {
  1360  		return dst, err
  1361  	}
  1362  	if err = compare(dst); err != nil {
  1363  		return dst, err
  1364  	}
  1365  	if !canStream {
  1366  		// copy dst (which is the local object we have just streamed to) to the remote
  1367  		newCtx := ctx
  1368  		if ci.Metadata && len(meta) != 0 {
  1369  			// If we have metadata and we are setting it then use
  1370  			// the --metadataset mechanism to supply it to Copy
  1371  			var newCi *fs.ConfigInfo
  1372  			newCtx, newCi = fs.AddConfig(ctx)
  1373  			if len(newCi.MetadataSet) == 0 {
  1374  				newCi.MetadataSet = meta
  1375  			} else {
  1376  				var newMeta fs.Metadata
  1377  				newMeta.Merge(meta)
  1378  				newMeta.Merge(newCi.MetadataSet) // --metadata-set takes priority
  1379  				newCi.MetadataSet = newMeta
  1380  			}
  1381  		}
  1382  		return Copy(newCtx, fdst, nil, dstFileName, dst)
  1383  	}
  1384  	return dst, nil
  1385  }
  1386  
  1387  // PublicLink adds a "readable by anyone with link" permission on the given file or folder.
  1388  func PublicLink(ctx context.Context, f fs.Fs, remote string, expire fs.Duration, unlink bool) (string, error) {
  1389  	doPublicLink := f.Features().PublicLink
  1390  	if doPublicLink == nil {
  1391  		return "", fmt.Errorf("%v doesn't support public links", f)
  1392  	}
  1393  	return doPublicLink(ctx, remote, expire, unlink)
  1394  }
  1395  
  1396  // Rmdirs removes any empty directories (or directories only
  1397  // containing empty directories) under f, including f.
  1398  //
  1399  // Rmdirs obeys the filters
  1400  func Rmdirs(ctx context.Context, f fs.Fs, dir string, leaveRoot bool) error {
  1401  	ci := fs.GetConfig(ctx)
  1402  	fi := filter.GetConfig(ctx)
  1403  	dirEmpty := make(map[string]bool)
  1404  	dirEmpty[dir] = !leaveRoot
  1405  	err := walk.Walk(ctx, f, dir, false, ci.MaxDepth, func(dirPath string, entries fs.DirEntries, err error) error {
  1406  		if err != nil {
  1407  			err = fs.CountError(err)
  1408  			fs.Errorf(f, "Failed to list %q: %v", dirPath, err)
  1409  			return nil
  1410  		}
  1411  		for _, entry := range entries {
  1412  			switch x := entry.(type) {
  1413  			case fs.Directory:
  1414  				// add a new directory as empty
  1415  				dir := x.Remote()
  1416  				_, found := dirEmpty[dir]
  1417  				if !found {
  1418  					dirEmpty[dir] = true
  1419  				}
  1420  			case fs.Object:
  1421  				// mark the parents of the file as being non-empty
  1422  				dir := x.Remote()
  1423  				for dir != "" {
  1424  					dir = path.Dir(dir)
  1425  					if dir == "." || dir == "/" {
  1426  						dir = ""
  1427  					}
  1428  					empty, found := dirEmpty[dir]
  1429  					// End if we reach a directory which is non-empty
  1430  					if found && !empty {
  1431  						break
  1432  					}
  1433  					dirEmpty[dir] = false
  1434  				}
  1435  			}
  1436  		}
  1437  		return nil
  1438  	})
  1439  	if err != nil {
  1440  		return fmt.Errorf("failed to rmdirs: %w", err)
  1441  	}
  1442  
  1443  	// Group directories to delete by level
  1444  	var toDelete [][]string
  1445  	for dir, empty := range dirEmpty {
  1446  		if empty {
  1447  			// If a filter matches the directory then that
  1448  			// directory is a candidate for deletion
  1449  			if fi.IncludeRemote(dir + "/") {
  1450  				level := strings.Count(dir, "/") + 1
  1451  				// The root directory "" is at the top level
  1452  				if dir == "" {
  1453  					level = 0
  1454  				}
  1455  				if len(toDelete) < level+1 {
  1456  					toDelete = append(toDelete, make([][]string, level+1-len(toDelete))...)
  1457  				}
  1458  				toDelete[level] = append(toDelete[level], dir)
  1459  			}
  1460  		}
  1461  	}
  1462  
  1463  	errCount := errcount.New()
  1464  	// Delete all directories at the same level in parallel
  1465  	for level := len(toDelete) - 1; level >= 0; level-- {
  1466  		dirs := toDelete[level]
  1467  		if len(dirs) == 0 {
  1468  			continue
  1469  		}
  1470  		fs.Debugf(nil, "removing %d level %d directories", len(dirs), level)
  1471  		sort.Strings(dirs)
  1472  		g, gCtx := errgroup.WithContext(ctx)
  1473  		g.SetLimit(ci.Checkers)
  1474  		for _, dir := range dirs {
  1475  			// End early if error
  1476  			if gCtx.Err() != nil {
  1477  				break
  1478  			}
  1479  			dir := dir
  1480  			g.Go(func() error {
  1481  				err := TryRmdir(gCtx, f, dir)
  1482  				if err != nil {
  1483  					err = fs.CountError(err)
  1484  					fs.Errorf(dir, "Failed to rmdir: %v", err)
  1485  					errCount.Add(err)
  1486  				}
  1487  				return nil // don't return errors, just count them
  1488  			})
  1489  		}
  1490  		err := g.Wait()
  1491  		if err != nil {
  1492  			return err
  1493  		}
  1494  	}
  1495  	return errCount.Err("failed to remove directories")
  1496  }
  1497  
  1498  // GetCompareDest sets up --compare-dest
  1499  func GetCompareDest(ctx context.Context) (CompareDest []fs.Fs, err error) {
  1500  	ci := fs.GetConfig(ctx)
  1501  	CompareDest, err = cache.GetArr(ctx, ci.CompareDest)
  1502  	if err != nil {
  1503  		return nil, fserrors.FatalError(fmt.Errorf("failed to make fs for --compare-dest %q: %w", ci.CompareDest, err))
  1504  	}
  1505  	return CompareDest, nil
  1506  }
  1507  
  1508  // compareDest checks --compare-dest to see if src needs to
  1509  // be copied
  1510  //
  1511  // Returns True if src is in --compare-dest
  1512  func compareDest(ctx context.Context, dst, src fs.Object, CompareDest fs.Fs) (NoNeedTransfer bool, err error) {
  1513  	var remote string
  1514  	if dst == nil {
  1515  		remote = src.Remote()
  1516  	} else {
  1517  		remote = dst.Remote()
  1518  	}
  1519  	CompareDestFile, err := CompareDest.NewObject(ctx, remote)
  1520  	switch err {
  1521  	case fs.ErrorObjectNotFound:
  1522  		return false, nil
  1523  	case nil:
  1524  		break
  1525  	default:
  1526  		return false, err
  1527  	}
  1528  	opt := defaultEqualOpt(ctx)
  1529  	opt.updateModTime = false
  1530  	if equal(ctx, src, CompareDestFile, opt) {
  1531  		fs.Debugf(src, "Destination found in --compare-dest, skipping")
  1532  		return true, nil
  1533  	}
  1534  	return false, nil
  1535  }
  1536  
  1537  // GetCopyDest sets up --copy-dest
  1538  func GetCopyDest(ctx context.Context, fdst fs.Fs) (CopyDest []fs.Fs, err error) {
  1539  	ci := fs.GetConfig(ctx)
  1540  	CopyDest, err = cache.GetArr(ctx, ci.CopyDest)
  1541  	if err != nil {
  1542  		return nil, fserrors.FatalError(fmt.Errorf("failed to make fs for --copy-dest %q: %w", ci.CopyDest, err))
  1543  	}
  1544  	if !SameConfigArr(fdst, CopyDest) {
  1545  		return nil, fserrors.FatalError(errors.New("parameter to --copy-dest has to be on the same remote as destination"))
  1546  	}
  1547  	for _, cf := range CopyDest {
  1548  		if cf.Features().Copy == nil {
  1549  			return nil, fserrors.FatalError(errors.New("can't use --copy-dest on a remote which doesn't support server side copy"))
  1550  		}
  1551  	}
  1552  
  1553  	return CopyDest, nil
  1554  }
  1555  
  1556  // copyDest checks --copy-dest to see if src needs to
  1557  // be copied
  1558  //
  1559  // Returns True if src was copied from --copy-dest
  1560  func copyDest(ctx context.Context, fdst fs.Fs, dst, src fs.Object, CopyDest, backupDir fs.Fs) (NoNeedTransfer bool, err error) {
  1561  	var remote string
  1562  	if dst == nil {
  1563  		remote = src.Remote()
  1564  	} else {
  1565  		remote = dst.Remote()
  1566  	}
  1567  	CopyDestFile, err := CopyDest.NewObject(ctx, remote)
  1568  	switch err {
  1569  	case fs.ErrorObjectNotFound:
  1570  		return false, nil
  1571  	case nil:
  1572  		break
  1573  	default:
  1574  		return false, err
  1575  	}
  1576  	opt := defaultEqualOpt(ctx)
  1577  	opt.updateModTime = false
  1578  	if equal(ctx, src, CopyDestFile, opt) {
  1579  		if dst == nil || !Equal(ctx, src, dst) {
  1580  			if dst != nil && backupDir != nil {
  1581  				err = MoveBackupDir(ctx, backupDir, dst)
  1582  				if err != nil {
  1583  					return false, fmt.Errorf("moving to --backup-dir failed: %w", err)
  1584  				}
  1585  				// If successful zero out the dstObj as it is no longer there
  1586  				dst = nil
  1587  			}
  1588  			_, err := Copy(ctx, fdst, dst, remote, CopyDestFile)
  1589  			if err != nil {
  1590  				fs.Errorf(src, "Destination found in --copy-dest, error copying")
  1591  				return false, nil
  1592  			}
  1593  			fs.Debugf(src, "Destination found in --copy-dest, using server-side copy")
  1594  			return true, nil
  1595  		}
  1596  		fs.Debugf(src, "Unchanged skipping")
  1597  		return true, nil
  1598  	}
  1599  	fs.Debugf(src, "Destination not found in --copy-dest")
  1600  	return false, nil
  1601  }
  1602  
  1603  // CompareOrCopyDest checks --compare-dest and --copy-dest to see if src
  1604  // does not need to be copied
  1605  //
  1606  // Returns True if src does not need to be copied
  1607  func CompareOrCopyDest(ctx context.Context, fdst fs.Fs, dst, src fs.Object, CompareOrCopyDest []fs.Fs, backupDir fs.Fs) (NoNeedTransfer bool, err error) {
  1608  	ci := fs.GetConfig(ctx)
  1609  	if len(ci.CompareDest) > 0 {
  1610  		for _, compareF := range CompareOrCopyDest {
  1611  			NoNeedTransfer, err := compareDest(ctx, dst, src, compareF)
  1612  			if NoNeedTransfer || err != nil {
  1613  				return NoNeedTransfer, err
  1614  			}
  1615  		}
  1616  	} else if len(ci.CopyDest) > 0 {
  1617  		for _, copyF := range CompareOrCopyDest {
  1618  			NoNeedTransfer, err := copyDest(ctx, fdst, dst, src, copyF, backupDir)
  1619  			if NoNeedTransfer || err != nil {
  1620  				return NoNeedTransfer, err
  1621  			}
  1622  		}
  1623  	}
  1624  	return false, nil
  1625  }
  1626  
  1627  // NeedTransfer checks to see if src needs to be copied to dst using
  1628  // the current config.
  1629  //
  1630  // Returns a flag which indicates whether the file needs to be
  1631  // transferred or not.
  1632  func NeedTransfer(ctx context.Context, dst, src fs.Object) bool {
  1633  	ci := fs.GetConfig(ctx)
  1634  	logger, _ := GetLogger(ctx)
  1635  	if dst == nil {
  1636  		fs.Debugf(src, "Need to transfer - File not found at Destination")
  1637  		logger(ctx, MissingOnDst, src, nil, nil)
  1638  		return true
  1639  	}
  1640  	// If we should ignore existing files, don't transfer
  1641  	if ci.IgnoreExisting {
  1642  		fs.Debugf(src, "Destination exists, skipping")
  1643  		logger(ctx, Match, src, dst, nil)
  1644  		return false
  1645  	}
  1646  	// If we should upload unconditionally
  1647  	if ci.IgnoreTimes {
  1648  		fs.Debugf(src, "Transferring unconditionally as --ignore-times is in use")
  1649  		logger(ctx, Differ, src, dst, nil)
  1650  		return true
  1651  	}
  1652  	// If UpdateOlder is in effect, skip if dst is newer than src
  1653  	if ci.UpdateOlder {
  1654  		srcModTime := src.ModTime(ctx)
  1655  		dstModTime := dst.ModTime(ctx)
  1656  		dt := dstModTime.Sub(srcModTime)
  1657  		// If have a mutually agreed precision then use that
  1658  		modifyWindow := fs.GetModifyWindow(ctx, dst.Fs(), src.Fs())
  1659  		if modifyWindow == fs.ModTimeNotSupported {
  1660  			// Otherwise use 1 second as a safe default as
  1661  			// the resolution of the time a file was
  1662  			// uploaded.
  1663  			modifyWindow = time.Second
  1664  		}
  1665  		switch {
  1666  		case dt >= modifyWindow:
  1667  			fs.Debugf(src, "Destination is newer than source, skipping")
  1668  			logger(ctx, Match, src, dst, nil)
  1669  			return false
  1670  		case dt <= -modifyWindow:
  1671  			// force --checksum on for the check and do update modtimes by default
  1672  			opt := defaultEqualOpt(ctx)
  1673  			opt.forceModTimeMatch = true
  1674  			if equal(ctx, src, dst, opt) {
  1675  				fs.Debugf(src, "Unchanged skipping")
  1676  				return false
  1677  			}
  1678  		default:
  1679  			// Do a size only compare unless --checksum is set
  1680  			opt := defaultEqualOpt(ctx)
  1681  			opt.sizeOnly = !ci.CheckSum
  1682  			if equal(ctx, src, dst, opt) {
  1683  				fs.Debugf(src, "Destination mod time is within %v of source and files identical, skipping", modifyWindow)
  1684  				return false
  1685  			}
  1686  			fs.Debugf(src, "Destination mod time is within %v of source but files differ, transferring", modifyWindow)
  1687  		}
  1688  	} else {
  1689  		// Check to see if changed or not
  1690  		equalFn, ok := ctx.Value(equalFnKey).(EqualFn)
  1691  		if ok {
  1692  			return !equalFn(ctx, src, dst)
  1693  		}
  1694  		if Equal(ctx, src, dst) && !SameObject(src, dst) {
  1695  			fs.Debugf(src, "Unchanged skipping")
  1696  			return false
  1697  		}
  1698  	}
  1699  	return true
  1700  }
  1701  
  1702  // RcatSize reads data from the Reader until EOF and uploads it to a file on remote.
  1703  // Pass in size >=0 if known, <0 if not known
  1704  func RcatSize(ctx context.Context, fdst fs.Fs, dstFileName string, in io.ReadCloser, size int64, modTime time.Time, meta fs.Metadata) (dst fs.Object, err error) {
  1705  	var obj fs.Object
  1706  
  1707  	if size >= 0 {
  1708  		var err error
  1709  		// Size known use Put
  1710  		tr := accounting.Stats(ctx).NewTransferRemoteSize(dstFileName, size, nil, fdst)
  1711  		defer func() {
  1712  			tr.Done(ctx, err)
  1713  		}()
  1714  		body := io.NopCloser(in)    // we let the server close the body
  1715  		in := tr.Account(ctx, body) // account the transfer (no buffering)
  1716  
  1717  		if SkipDestructive(ctx, dstFileName, "upload from pipe") {
  1718  			// prevents "broken pipe" errors
  1719  			_, err = io.Copy(io.Discard, in)
  1720  			return nil, err
  1721  		}
  1722  
  1723  		info := object.NewStaticObjectInfo(dstFileName, modTime, size, true, nil, fdst).WithMetadata(meta)
  1724  		obj, err = fdst.Put(ctx, in, info)
  1725  		if err != nil {
  1726  			fs.Errorf(dstFileName, "Post request put error: %v", err)
  1727  
  1728  			return nil, err
  1729  		}
  1730  	} else {
  1731  		// Size unknown use Rcat
  1732  		obj, err = Rcat(ctx, fdst, dstFileName, in, modTime, meta)
  1733  		if err != nil {
  1734  			fs.Errorf(dstFileName, "Post request rcat error: %v", err)
  1735  
  1736  			return nil, err
  1737  		}
  1738  	}
  1739  
  1740  	return obj, nil
  1741  }
  1742  
  1743  // copyURLFunc is called from CopyURLFn
  1744  type copyURLFunc func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error)
  1745  
  1746  // copyURLFn copies the data from the url to the function supplied
  1747  func copyURLFn(ctx context.Context, dstFileName string, url string, autoFilename, dstFileNameFromHeader bool, fn copyURLFunc) (err error) {
  1748  	client := fshttp.NewClient(ctx)
  1749  	resp, err := client.Get(url)
  1750  	if err != nil {
  1751  		return err
  1752  	}
  1753  	defer fs.CheckClose(resp.Body, &err)
  1754  	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
  1755  		return fmt.Errorf("CopyURL failed: %s", resp.Status)
  1756  	}
  1757  	modTime, err := http.ParseTime(resp.Header.Get("Last-Modified"))
  1758  	if err != nil {
  1759  		modTime = time.Now()
  1760  	}
  1761  	if autoFilename {
  1762  		if dstFileNameFromHeader {
  1763  			_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  1764  			headerFilename := path.Base(strings.Replace(params["filename"], "\\", "/", -1))
  1765  			if err != nil || headerFilename == "" {
  1766  				return fmt.Errorf("CopyURL failed: filename not found in the Content-Disposition header")
  1767  			}
  1768  			fs.Debugf(headerFilename, "filename found in Content-Disposition header.")
  1769  			return fn(ctx, headerFilename, resp.Body, resp.ContentLength, modTime)
  1770  		}
  1771  
  1772  		dstFileName = path.Base(resp.Request.URL.Path)
  1773  		if dstFileName == "." || dstFileName == "/" {
  1774  			return fmt.Errorf("CopyURL failed: file name wasn't found in url")
  1775  		}
  1776  		fs.Debugf(dstFileName, "File name found in url")
  1777  	}
  1778  	return fn(ctx, dstFileName, resp.Body, resp.ContentLength, modTime)
  1779  }
  1780  
  1781  // CopyURL copies the data from the url to (fdst, dstFileName)
  1782  func CopyURL(ctx context.Context, fdst fs.Fs, dstFileName string, url string, autoFilename, dstFileNameFromHeader bool, noClobber bool) (dst fs.Object, err error) {
  1783  	err = copyURLFn(ctx, dstFileName, url, autoFilename, dstFileNameFromHeader, func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) {
  1784  		if noClobber {
  1785  			_, err = fdst.NewObject(ctx, dstFileName)
  1786  			if err == nil {
  1787  				return errors.New("CopyURL failed: file already exist")
  1788  			}
  1789  		}
  1790  		dst, err = RcatSize(ctx, fdst, dstFileName, in, size, modTime, nil)
  1791  		return err
  1792  	})
  1793  	return dst, err
  1794  }
  1795  
  1796  // CopyURLToWriter copies the data from the url to the io.Writer supplied
  1797  func CopyURLToWriter(ctx context.Context, url string, out io.Writer) (err error) {
  1798  	return copyURLFn(ctx, "", url, false, false, func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) {
  1799  		_, err = io.Copy(out, in)
  1800  		return err
  1801  	})
  1802  }
  1803  
  1804  // BackupDir returns the correctly configured --backup-dir
  1805  func BackupDir(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, srcFileName string) (backupDir fs.Fs, err error) {
  1806  	ci := fs.GetConfig(ctx)
  1807  	if ci.BackupDir != "" {
  1808  		backupDir, err = cache.Get(ctx, ci.BackupDir)
  1809  		if err != nil {
  1810  			return nil, fserrors.FatalError(fmt.Errorf("failed to make fs for --backup-dir %q: %w", ci.BackupDir, err))
  1811  		}
  1812  		if !SameConfig(fdst, backupDir) {
  1813  			return nil, fserrors.FatalError(errors.New("parameter to --backup-dir has to be on the same remote as destination"))
  1814  		}
  1815  		if srcFileName == "" {
  1816  			if OverlappingFilterCheck(ctx, backupDir, fdst) {
  1817  				return nil, fserrors.FatalError(errors.New("destination and parameter to --backup-dir mustn't overlap"))
  1818  			}
  1819  			if OverlappingFilterCheck(ctx, backupDir, fsrc) {
  1820  				return nil, fserrors.FatalError(errors.New("source and parameter to --backup-dir mustn't overlap"))
  1821  			}
  1822  		} else {
  1823  			if ci.Suffix == "" {
  1824  				if SameDir(fdst, backupDir) {
  1825  					return nil, fserrors.FatalError(errors.New("destination and parameter to --backup-dir mustn't be the same"))
  1826  				}
  1827  				if SameDir(fsrc, backupDir) {
  1828  					return nil, fserrors.FatalError(errors.New("source and parameter to --backup-dir mustn't be the same"))
  1829  				}
  1830  			}
  1831  		}
  1832  	} else if ci.Suffix != "" {
  1833  		// --backup-dir is not set but --suffix is - use the destination as the backupDir
  1834  		backupDir = fdst
  1835  	} else {
  1836  		return nil, fserrors.FatalError(errors.New("internal error: BackupDir called when --backup-dir and --suffix both empty"))
  1837  	}
  1838  	if !CanServerSideMove(backupDir) {
  1839  		return nil, fserrors.FatalError(errors.New("can't use --backup-dir on a remote which doesn't support server-side move or copy"))
  1840  	}
  1841  	return backupDir, nil
  1842  }
  1843  
  1844  // MoveBackupDir moves a file to the backup dir
  1845  func MoveBackupDir(ctx context.Context, backupDir fs.Fs, dst fs.Object) (err error) {
  1846  	remoteWithSuffix := SuffixName(ctx, dst.Remote())
  1847  	overwritten, _ := backupDir.NewObject(ctx, remoteWithSuffix)
  1848  	_, err = Move(ctx, backupDir, overwritten, remoteWithSuffix, dst)
  1849  	return err
  1850  }
  1851  
  1852  // needsMoveCaseInsensitive returns true if moveCaseInsensitive is needed
  1853  func needsMoveCaseInsensitive(fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string, cp bool) bool {
  1854  	dstFilePath := path.Join(fdst.Root(), dstFileName)
  1855  	srcFilePath := path.Join(fsrc.Root(), srcFileName)
  1856  	return !cp && fdst.Name() == fsrc.Name() && fdst.Features().CaseInsensitive && dstFileName != srcFileName && strings.EqualFold(dstFilePath, srcFilePath)
  1857  }
  1858  
  1859  // MoveCaseInsensitive handles changing case of a file on a case insensitive remote.
  1860  // This will move the file to a temporary name then
  1861  // move it back to the intended destination. This is required
  1862  // to avoid issues with certain remotes and avoid file deletion.
  1863  // returns nil, nil if !needsMoveCaseInsensitive.
  1864  // this does not account a transfer -- the caller should do that if desired.
  1865  func MoveCaseInsensitive(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string, cp bool, srcObj fs.Object) (newDst fs.Object, err error) {
  1866  	logger, _ := GetLogger(ctx)
  1867  
  1868  	// Choose operations
  1869  	Op := MoveTransfer
  1870  	if cp {
  1871  		Op = Copy
  1872  	}
  1873  
  1874  	if SkipDestructive(ctx, srcFileName, "rename to "+dstFileName) {
  1875  		// avoid fatalpanic on --dry-run (trying to access non-existent tmpObj)
  1876  		return nil, nil
  1877  	}
  1878  	// Create random name to temporarily move file to
  1879  	tmpObjName := dstFileName + "-rclone-move-" + random.String(8)
  1880  	tmpObjFail, err := fdst.NewObject(ctx, tmpObjName)
  1881  	if err != fs.ErrorObjectNotFound {
  1882  		if err == nil {
  1883  			logger(ctx, TransferError, nil, tmpObjFail, err)
  1884  			return nil, errors.New("found an already existing file with a randomly generated name. Try the operation again")
  1885  		}
  1886  		logger(ctx, TransferError, nil, tmpObjFail, err)
  1887  		return nil, fmt.Errorf("error while attempting to move file to a temporary location: %w", err)
  1888  	}
  1889  	fs.Debugf(srcObj, "moving to %v", tmpObjName)
  1890  	tmpObj, err := Op(ctx, fdst, nil, tmpObjName, srcObj)
  1891  	if err != nil {
  1892  		logger(ctx, TransferError, srcObj, tmpObj, err)
  1893  		return nil, fmt.Errorf("error while moving file to temporary location: %w", err)
  1894  	}
  1895  	fs.Debugf(srcObj, "moving to %v", dstFileName)
  1896  	newDst, err = Op(ctx, fdst, nil, dstFileName, tmpObj)
  1897  	logger(ctx, MissingOnDst, tmpObj, nil, err)
  1898  	return newDst, err
  1899  }
  1900  
  1901  // moveOrCopyFile moves or copies a single file possibly to a new name
  1902  func moveOrCopyFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string, cp bool) (err error) {
  1903  	ci := fs.GetConfig(ctx)
  1904  	logger, usingLogger := GetLogger(ctx)
  1905  	dstFilePath := path.Join(fdst.Root(), dstFileName)
  1906  	srcFilePath := path.Join(fsrc.Root(), srcFileName)
  1907  	if fdst.Name() == fsrc.Name() && dstFilePath == srcFilePath {
  1908  		fs.Debugf(fdst, "don't need to copy/move %s, it is already at target location", dstFileName)
  1909  		if usingLogger {
  1910  			srcObj, _ := fsrc.NewObject(ctx, srcFileName)
  1911  			dstObj, _ := fsrc.NewObject(ctx, dstFileName)
  1912  			logger(ctx, Match, srcObj, dstObj, nil)
  1913  		}
  1914  		return nil
  1915  	}
  1916  
  1917  	// Choose operations
  1918  	Op := MoveTransfer
  1919  	if cp {
  1920  		Op = Copy
  1921  	}
  1922  
  1923  	// Find src object
  1924  	srcObj, err := fsrc.NewObject(ctx, srcFileName)
  1925  	if err != nil {
  1926  		logger(ctx, TransferError, srcObj, nil, err)
  1927  		return err
  1928  	}
  1929  
  1930  	// Find dst object if it exists
  1931  	var dstObj fs.Object
  1932  	if !ci.NoCheckDest {
  1933  		dstObj, err = fdst.NewObject(ctx, dstFileName)
  1934  		if errors.Is(err, fs.ErrorObjectNotFound) {
  1935  			dstObj = nil
  1936  		} else if err != nil {
  1937  			logger(ctx, TransferError, nil, dstObj, err)
  1938  			return err
  1939  		}
  1940  	}
  1941  
  1942  	// Special case for changing case of a file on a case insensitive remote
  1943  	// This will move the file to a temporary name then
  1944  	// move it back to the intended destination. This is required
  1945  	// to avoid issues with certain remotes and avoid file deletion.
  1946  	if needsMoveCaseInsensitive(fdst, fsrc, dstFileName, srcFileName, cp) {
  1947  		tr := accounting.Stats(ctx).NewTransfer(srcObj, fdst)
  1948  		defer func() {
  1949  			tr.Done(ctx, err)
  1950  		}()
  1951  		_, err = MoveCaseInsensitive(ctx, fdst, fsrc, dstFileName, srcFileName, cp, srcObj)
  1952  		return err
  1953  	}
  1954  
  1955  	var backupDir fs.Fs
  1956  	var copyDestDir []fs.Fs
  1957  	if ci.BackupDir != "" || ci.Suffix != "" {
  1958  		backupDir, err = BackupDir(ctx, fdst, fsrc, srcFileName)
  1959  		if err != nil {
  1960  			return fmt.Errorf("creating Fs for --backup-dir failed: %w", err)
  1961  		}
  1962  	}
  1963  	if len(ci.CompareDest) > 0 {
  1964  		copyDestDir, err = GetCompareDest(ctx)
  1965  		if err != nil {
  1966  			return err
  1967  		}
  1968  	} else if len(ci.CopyDest) > 0 {
  1969  		copyDestDir, err = GetCopyDest(ctx, fdst)
  1970  		if err != nil {
  1971  			return err
  1972  		}
  1973  	}
  1974  	needTransfer := NeedTransfer(ctx, dstObj, srcObj)
  1975  	if needTransfer {
  1976  		NoNeedTransfer, err := CompareOrCopyDest(ctx, fdst, dstObj, srcObj, copyDestDir, backupDir)
  1977  		if err != nil {
  1978  			return err
  1979  		}
  1980  		if NoNeedTransfer {
  1981  			needTransfer = false
  1982  		}
  1983  	}
  1984  	if needTransfer {
  1985  		// If destination already exists, then we must move it into --backup-dir if required
  1986  		if dstObj != nil && backupDir != nil {
  1987  			err = MoveBackupDir(ctx, backupDir, dstObj)
  1988  			if err != nil {
  1989  				logger(ctx, TransferError, dstObj, nil, err)
  1990  				return fmt.Errorf("moving to --backup-dir failed: %w", err)
  1991  			}
  1992  			// If successful zero out the dstObj as it is no longer there
  1993  			logger(ctx, MissingOnDst, dstObj, nil, nil)
  1994  			dstObj = nil
  1995  		}
  1996  
  1997  		_, err = Op(ctx, fdst, dstObj, dstFileName, srcObj)
  1998  	} else {
  1999  		if !cp {
  2000  			if ci.IgnoreExisting {
  2001  				fs.Debugf(srcObj, "Not removing source file as destination file exists and --ignore-existing is set")
  2002  				logger(ctx, Match, srcObj, dstObj, nil)
  2003  			} else if !SameObject(srcObj, dstObj) {
  2004  				err = DeleteFile(ctx, srcObj)
  2005  				logger(ctx, Differ, srcObj, dstObj, nil)
  2006  			}
  2007  		}
  2008  	}
  2009  	return err
  2010  }
  2011  
  2012  // MoveFile moves a single file possibly to a new name
  2013  //
  2014  // This is treated as a transfer.
  2015  func MoveFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string) (err error) {
  2016  	return moveOrCopyFile(ctx, fdst, fsrc, dstFileName, srcFileName, false)
  2017  }
  2018  
  2019  // SetTier changes tier of object in remote
  2020  func SetTier(ctx context.Context, fsrc fs.Fs, tier string) error {
  2021  	return ListFn(ctx, fsrc, func(o fs.Object) {
  2022  		objImpl, ok := o.(fs.SetTierer)
  2023  		if !ok {
  2024  			fs.Errorf(fsrc, "Remote object does not implement SetTier")
  2025  			return
  2026  		}
  2027  		err := objImpl.SetTier(tier)
  2028  		if err != nil {
  2029  			fs.Errorf(fsrc, "Failed to do SetTier, %v", err)
  2030  		}
  2031  	})
  2032  }
  2033  
  2034  // SetTierFile changes tier of a single file in remote
  2035  func SetTierFile(ctx context.Context, o fs.Object, tier string) error {
  2036  	do, ok := o.(fs.SetTierer)
  2037  	if !ok {
  2038  		return errors.New("remote object does not implement SetTier")
  2039  	}
  2040  	err := do.SetTier(tier)
  2041  	if err != nil {
  2042  		fs.Errorf(o, "Failed to do SetTier, %v", err)
  2043  		return err
  2044  	}
  2045  	return nil
  2046  }
  2047  
  2048  // TouchDir touches every file in directory with time t
  2049  func TouchDir(ctx context.Context, f fs.Fs, remote string, t time.Time, recursive bool) error {
  2050  	return walk.ListR(ctx, f, remote, false, ConfigMaxDepth(ctx, recursive), walk.ListObjects, func(entries fs.DirEntries) error {
  2051  		entries.ForObject(func(o fs.Object) {
  2052  			if !SkipDestructive(ctx, o, "touch") {
  2053  				fs.Debugf(f, "Touching %q", o.Remote())
  2054  				err := o.SetModTime(ctx, t)
  2055  				if err != nil {
  2056  					err = fmt.Errorf("failed to touch: %w", err)
  2057  					err = fs.CountError(err)
  2058  					fs.Errorf(o, "%v", err)
  2059  				}
  2060  			}
  2061  		})
  2062  		return nil
  2063  	})
  2064  }
  2065  
  2066  // ListFormat defines files information print format
  2067  type ListFormat struct {
  2068  	separator string
  2069  	dirSlash  bool
  2070  	absolute  bool
  2071  	output    []func(entry *ListJSONItem) string
  2072  	csv       *csv.Writer
  2073  	buf       bytes.Buffer
  2074  }
  2075  
  2076  // SetSeparator changes separator in struct
  2077  func (l *ListFormat) SetSeparator(separator string) {
  2078  	l.separator = separator
  2079  }
  2080  
  2081  // SetDirSlash defines if slash should be printed
  2082  func (l *ListFormat) SetDirSlash(dirSlash bool) {
  2083  	l.dirSlash = dirSlash
  2084  }
  2085  
  2086  // SetAbsolute prints a leading slash in front of path names
  2087  func (l *ListFormat) SetAbsolute(absolute bool) {
  2088  	l.absolute = absolute
  2089  }
  2090  
  2091  // SetCSV defines if the output should be csv
  2092  //
  2093  // Note that you should call SetSeparator before this if you want a
  2094  // custom separator
  2095  func (l *ListFormat) SetCSV(useCSV bool) {
  2096  	if useCSV {
  2097  		l.csv = csv.NewWriter(&l.buf)
  2098  		if l.separator != "" {
  2099  			l.csv.Comma = []rune(l.separator)[0]
  2100  		}
  2101  	} else {
  2102  		l.csv = nil
  2103  	}
  2104  }
  2105  
  2106  // SetOutput sets functions used to create files information
  2107  func (l *ListFormat) SetOutput(output []func(entry *ListJSONItem) string) {
  2108  	l.output = output
  2109  }
  2110  
  2111  // AddModTime adds file's Mod Time to output
  2112  func (l *ListFormat) AddModTime(timeFormat string) {
  2113  	switch timeFormat {
  2114  	case "":
  2115  		timeFormat = "2006-01-02 15:04:05"
  2116  	case "Layout":
  2117  		timeFormat = time.Layout
  2118  	case "ANSIC":
  2119  		timeFormat = time.ANSIC
  2120  	case "UnixDate":
  2121  		timeFormat = time.UnixDate
  2122  	case "RubyDate":
  2123  		timeFormat = time.RubyDate
  2124  	case "RFC822":
  2125  		timeFormat = time.RFC822
  2126  	case "RFC822Z":
  2127  		timeFormat = time.RFC822Z
  2128  	case "RFC850":
  2129  		timeFormat = time.RFC850
  2130  	case "RFC1123":
  2131  		timeFormat = time.RFC1123
  2132  	case "RFC1123Z":
  2133  		timeFormat = time.RFC1123Z
  2134  	case "RFC3339":
  2135  		timeFormat = time.RFC3339
  2136  	case "RFC3339Nano":
  2137  		timeFormat = time.RFC3339Nano
  2138  	case "Kitchen":
  2139  		timeFormat = time.Kitchen
  2140  	case "Stamp":
  2141  		timeFormat = time.Stamp
  2142  	case "StampMilli":
  2143  		timeFormat = time.StampMilli
  2144  	case "StampMicro":
  2145  		timeFormat = time.StampMicro
  2146  	case "StampNano":
  2147  		timeFormat = time.StampNano
  2148  	case "DateTime":
  2149  		// timeFormat = time.DateTime // missing in go1.19
  2150  		timeFormat = "2006-01-02 15:04:05"
  2151  	case "DateOnly":
  2152  		// timeFormat = time.DateOnly // missing in go1.19
  2153  		timeFormat = "2006-01-02"
  2154  	case "TimeOnly":
  2155  		// timeFormat = time.TimeOnly // missing in go1.19
  2156  		timeFormat = "15:04:05"
  2157  	}
  2158  	l.AppendOutput(func(entry *ListJSONItem) string {
  2159  		return entry.ModTime.When.Local().Format(timeFormat)
  2160  	})
  2161  }
  2162  
  2163  // AddSize adds file's size to output
  2164  func (l *ListFormat) AddSize() {
  2165  	l.AppendOutput(func(entry *ListJSONItem) string {
  2166  		return strconv.FormatInt(entry.Size, 10)
  2167  	})
  2168  }
  2169  
  2170  // normalisePath makes sure the path has the correct slashes for the current mode
  2171  func (l *ListFormat) normalisePath(entry *ListJSONItem, remote string) string {
  2172  	if l.absolute && !strings.HasPrefix(remote, "/") {
  2173  		remote = "/" + remote
  2174  	}
  2175  	if entry.IsDir && l.dirSlash {
  2176  		remote += "/"
  2177  	}
  2178  	return remote
  2179  }
  2180  
  2181  // AddPath adds path to file to output
  2182  func (l *ListFormat) AddPath() {
  2183  	l.AppendOutput(func(entry *ListJSONItem) string {
  2184  		return l.normalisePath(entry, entry.Path)
  2185  	})
  2186  }
  2187  
  2188  // AddEncrypted adds the encrypted path to file to output
  2189  func (l *ListFormat) AddEncrypted() {
  2190  	l.AppendOutput(func(entry *ListJSONItem) string {
  2191  		return l.normalisePath(entry, entry.Encrypted)
  2192  	})
  2193  }
  2194  
  2195  // AddHash adds the hash of the type given to the output
  2196  func (l *ListFormat) AddHash(ht hash.Type) {
  2197  	hashName := ht.String()
  2198  	l.AppendOutput(func(entry *ListJSONItem) string {
  2199  		if entry.IsDir {
  2200  			return ""
  2201  		}
  2202  		return entry.Hashes[hashName]
  2203  	})
  2204  }
  2205  
  2206  // AddID adds file's ID to the output if known
  2207  func (l *ListFormat) AddID() {
  2208  	l.AppendOutput(func(entry *ListJSONItem) string {
  2209  		return entry.ID
  2210  	})
  2211  }
  2212  
  2213  // AddOrigID adds file's Original ID to the output if known
  2214  func (l *ListFormat) AddOrigID() {
  2215  	l.AppendOutput(func(entry *ListJSONItem) string {
  2216  		return entry.OrigID
  2217  	})
  2218  }
  2219  
  2220  // AddTier adds file's Tier to the output if known
  2221  func (l *ListFormat) AddTier() {
  2222  	l.AppendOutput(func(entry *ListJSONItem) string {
  2223  		return entry.Tier
  2224  	})
  2225  }
  2226  
  2227  // AddMimeType adds file's MimeType to the output if known
  2228  func (l *ListFormat) AddMimeType() {
  2229  	l.AppendOutput(func(entry *ListJSONItem) string {
  2230  		return entry.MimeType
  2231  	})
  2232  }
  2233  
  2234  // AddMetadata adds file's Metadata to the output if known
  2235  func (l *ListFormat) AddMetadata() {
  2236  	l.AppendOutput(func(entry *ListJSONItem) string {
  2237  		metadata := entry.Metadata
  2238  		if metadata == nil {
  2239  			metadata = make(fs.Metadata)
  2240  		}
  2241  		out, err := json.Marshal(metadata)
  2242  		if err != nil {
  2243  			return fmt.Sprintf("Failed to read metadata: %v", err.Error())
  2244  		}
  2245  		return string(out)
  2246  	})
  2247  }
  2248  
  2249  // AppendOutput adds string generated by specific function to printed output
  2250  func (l *ListFormat) AppendOutput(functionToAppend func(item *ListJSONItem) string) {
  2251  	l.output = append(l.output, functionToAppend)
  2252  }
  2253  
  2254  // Format prints information about the DirEntry in the format defined
  2255  func (l *ListFormat) Format(entry *ListJSONItem) (result string) {
  2256  	var out []string
  2257  	for _, fun := range l.output {
  2258  		out = append(out, fun(entry))
  2259  	}
  2260  	if l.csv != nil {
  2261  		l.buf.Reset()
  2262  		_ = l.csv.Write(out) // can't fail writing to bytes.Buffer
  2263  		l.csv.Flush()
  2264  		result = strings.TrimRight(l.buf.String(), "\n")
  2265  	} else {
  2266  		result = strings.Join(out, l.separator)
  2267  	}
  2268  	return result
  2269  }
  2270  
  2271  // FormatForLSFPrecision Returns a time format for the given precision
  2272  func FormatForLSFPrecision(precision time.Duration) string {
  2273  	switch {
  2274  	case precision <= time.Nanosecond:
  2275  		return "2006-01-02 15:04:05.000000000"
  2276  	case precision <= 10*time.Nanosecond:
  2277  		return "2006-01-02 15:04:05.00000000"
  2278  	case precision <= 100*time.Nanosecond:
  2279  		return "2006-01-02 15:04:05.0000000"
  2280  	case precision <= time.Microsecond:
  2281  		return "2006-01-02 15:04:05.000000"
  2282  	case precision <= 10*time.Microsecond:
  2283  		return "2006-01-02 15:04:05.00000"
  2284  	case precision <= 100*time.Microsecond:
  2285  		return "2006-01-02 15:04:05.0000"
  2286  	case precision <= time.Millisecond:
  2287  		return "2006-01-02 15:04:05.000"
  2288  	case precision <= 10*time.Millisecond:
  2289  		return "2006-01-02 15:04:05.00"
  2290  	case precision <= 100*time.Millisecond:
  2291  		return "2006-01-02 15:04:05.0"
  2292  	}
  2293  	return "2006-01-02 15:04:05"
  2294  }
  2295  
  2296  // DirMove renames srcRemote to dstRemote
  2297  //
  2298  // It does this by loading the directory tree into memory (using ListR
  2299  // if available) and doing renames in parallel.
  2300  func DirMove(ctx context.Context, f fs.Fs, srcRemote, dstRemote string) (err error) {
  2301  	ci := fs.GetConfig(ctx)
  2302  
  2303  	if SkipDestructive(ctx, srcRemote, "dirMove") {
  2304  		accounting.Stats(ctx).Renames(1)
  2305  		return nil
  2306  	}
  2307  
  2308  	// Use DirMove if possible
  2309  	if doDirMove := f.Features().DirMove; doDirMove != nil {
  2310  		err = doDirMove(ctx, f, srcRemote, dstRemote)
  2311  		if err == nil {
  2312  			accounting.Stats(ctx).Renames(1)
  2313  		}
  2314  		if err != fs.ErrorCantDirMove && err != fs.ErrorDirExists {
  2315  			return err
  2316  		}
  2317  		fs.Infof(f, "Can't DirMove - falling back to file moves: %v", err)
  2318  	}
  2319  
  2320  	// Load the directory tree into memory
  2321  	tree, err := walk.NewDirTree(ctx, f, srcRemote, true, -1)
  2322  	if err != nil {
  2323  		return fmt.Errorf("RenameDir tree walk: %w", err)
  2324  	}
  2325  
  2326  	// Get the directories in sorted order
  2327  	dirs := tree.Dirs()
  2328  
  2329  	// Make the destination directories - must be done in order not in parallel
  2330  	for _, dir := range dirs {
  2331  		dstPath := dstRemote + dir[len(srcRemote):]
  2332  		err := f.Mkdir(ctx, dstPath)
  2333  		if err != nil {
  2334  			return fmt.Errorf("RenameDir mkdir: %w", err)
  2335  		}
  2336  	}
  2337  
  2338  	// Rename the files in parallel
  2339  	type rename struct {
  2340  		o       fs.Object
  2341  		newPath string
  2342  	}
  2343  	renames := make(chan rename, ci.Checkers)
  2344  	g, gCtx := errgroup.WithContext(context.Background())
  2345  	for i := 0; i < ci.Checkers; i++ {
  2346  		g.Go(func() error {
  2347  			for job := range renames {
  2348  				dstOverwritten, _ := f.NewObject(gCtx, job.newPath)
  2349  				_, err := Move(gCtx, f, dstOverwritten, job.newPath, job.o)
  2350  				if err != nil {
  2351  					return err
  2352  				}
  2353  				select {
  2354  				case <-gCtx.Done():
  2355  					return gCtx.Err()
  2356  				default:
  2357  				}
  2358  
  2359  			}
  2360  			return nil
  2361  		})
  2362  	}
  2363  	for dir, entries := range tree {
  2364  		dstPath := dstRemote + dir[len(srcRemote):]
  2365  		for _, entry := range entries {
  2366  			if o, ok := entry.(fs.Object); ok {
  2367  				renames <- rename{o, path.Join(dstPath, path.Base(o.Remote()))}
  2368  			}
  2369  		}
  2370  	}
  2371  	close(renames)
  2372  	err = g.Wait()
  2373  	if err != nil {
  2374  		return fmt.Errorf("RenameDir renames: %w", err)
  2375  	}
  2376  
  2377  	// Remove the source directories in reverse order
  2378  	for i := len(dirs) - 1; i >= 0; i-- {
  2379  		err := f.Rmdir(ctx, dirs[i])
  2380  		if err != nil {
  2381  			return fmt.Errorf("RenameDir rmdir: %w", err)
  2382  		}
  2383  	}
  2384  
  2385  	return nil
  2386  }
  2387  
  2388  // DirMoveCaseInsensitive does DirMove in two steps (to temp name, then real name)
  2389  // which is necessary for some case-insensitive backends
  2390  func DirMoveCaseInsensitive(ctx context.Context, f fs.Fs, srcRemote, dstRemote string) (err error) {
  2391  	tmpDstRemote := dstRemote + "-rclone-move-" + random.String(8)
  2392  	err = DirMove(ctx, f, srcRemote, tmpDstRemote)
  2393  	if err != nil {
  2394  		return err
  2395  	}
  2396  	return DirMove(ctx, f, tmpDstRemote, dstRemote)
  2397  }
  2398  
  2399  // FsInfo provides information about a remote
  2400  type FsInfo struct {
  2401  	// Name of the remote (as passed into NewFs)
  2402  	Name string
  2403  
  2404  	// Root of the remote (as passed into NewFs)
  2405  	Root string
  2406  
  2407  	// String returns a description of the FS
  2408  	String string
  2409  
  2410  	// Precision of the ModTimes in this Fs in Nanoseconds
  2411  	Precision time.Duration
  2412  
  2413  	// Returns the supported hash types of the filesystem
  2414  	Hashes []string
  2415  
  2416  	// Features returns the optional features of this Fs
  2417  	Features map[string]bool
  2418  
  2419  	// MetadataInfo returns info about the metadata for this backend
  2420  	MetadataInfo *fs.MetadataInfo
  2421  }
  2422  
  2423  // GetFsInfo gets the information (FsInfo) about a given Fs
  2424  func GetFsInfo(f fs.Fs) *FsInfo {
  2425  	features := f.Features()
  2426  	info := &FsInfo{
  2427  		Name:         f.Name(),
  2428  		Root:         f.Root(),
  2429  		String:       f.String(),
  2430  		Precision:    f.Precision(),
  2431  		Hashes:       make([]string, 0, 4),
  2432  		Features:     features.Enabled(),
  2433  		MetadataInfo: nil,
  2434  	}
  2435  	for _, hashType := range f.Hashes().Array() {
  2436  		info.Hashes = append(info.Hashes, hashType.String())
  2437  	}
  2438  	fsInfo, _, _, _, err := fs.ParseRemote(fs.ConfigString(f))
  2439  	if err == nil && fsInfo != nil && fsInfo.MetadataInfo != nil {
  2440  		info.MetadataInfo = fsInfo.MetadataInfo
  2441  	}
  2442  	return info
  2443  }
  2444  
  2445  var (
  2446  	interactiveMu sync.Mutex // protects the following variables
  2447  	skipped       = map[string]bool{}
  2448  )
  2449  
  2450  // skipDestructiveChoose asks the user which action to take
  2451  //
  2452  // Call with interactiveMu held
  2453  func skipDestructiveChoose(ctx context.Context, subject interface{}, action string) (skip bool) {
  2454  	// Lock the StdoutMutex - must not call fs.Log anything
  2455  	// otherwise it will deadlock with --interactive --progress
  2456  	StdoutMutex.Lock()
  2457  
  2458  	fmt.Printf("\nrclone: %s \"%v\"?\n", action, subject)
  2459  	i := config.CommandDefault([]string{
  2460  		"yYes, this is OK",
  2461  		"nNo, skip this",
  2462  		fmt.Sprintf("sSkip all %s operations with no more questions", action),
  2463  		fmt.Sprintf("!Do all %s operations with no more questions", action),
  2464  		"qExit rclone now.",
  2465  	}, 0)
  2466  
  2467  	StdoutMutex.Unlock()
  2468  
  2469  	switch i {
  2470  	case 'y':
  2471  		skip = false
  2472  	case 'n':
  2473  		skip = true
  2474  	case 's':
  2475  		skip = true
  2476  		skipped[action] = true
  2477  		fs.Logf(nil, "Skipping all %s operations from now on without asking", action)
  2478  	case '!':
  2479  		skip = false
  2480  		skipped[action] = false
  2481  		fs.Logf(nil, "Doing all %s operations from now on without asking", action)
  2482  	case 'q':
  2483  		fs.Logf(nil, "Quitting rclone now")
  2484  		atexit.Run()
  2485  		os.Exit(0)
  2486  	default:
  2487  		skip = true
  2488  		fs.Errorf(nil, "Bad choice %c", i)
  2489  	}
  2490  	return skip
  2491  }
  2492  
  2493  // SkipDestructive should be called whenever rclone is about to do an destructive operation.
  2494  //
  2495  // It will check the --dry-run flag and it will ask the user if the --interactive flag is set.
  2496  //
  2497  // subject should be the object or directory in use
  2498  //
  2499  // action should be a descriptive word or short phrase
  2500  //
  2501  // Together they should make sense in this sentence: "Rclone is about
  2502  // to action subject".
  2503  func SkipDestructive(ctx context.Context, subject interface{}, action string) (skip bool) {
  2504  	var flag string
  2505  	ci := fs.GetConfig(ctx)
  2506  	switch {
  2507  	case ci.DryRun:
  2508  		flag = "--dry-run"
  2509  		skip = true
  2510  	case ci.Interactive:
  2511  		flag = "--interactive"
  2512  		interactiveMu.Lock()
  2513  		defer interactiveMu.Unlock()
  2514  		var found bool
  2515  		skip, found = skipped[action]
  2516  		if !found {
  2517  			skip = skipDestructiveChoose(ctx, subject, action)
  2518  		}
  2519  	default:
  2520  		return false
  2521  	}
  2522  	if skip {
  2523  		size := int64(-1)
  2524  		if do, ok := subject.(interface{ Size() int64 }); ok {
  2525  			size = do.Size()
  2526  		}
  2527  		if size >= 0 {
  2528  			fs.Logf(subject, "Skipped %s as %s is set (size %v)", fs.LogValue("skipped", action), flag, fs.LogValue("size", fs.SizeSuffix(size)))
  2529  		} else {
  2530  			fs.Logf(subject, "Skipped %s as %s is set", fs.LogValue("skipped", action), flag)
  2531  		}
  2532  	}
  2533  	return skip
  2534  }
  2535  
  2536  // Return the best way of describing the directory for the logs
  2537  func dirName(f fs.Fs, dst fs.Directory, dir string) any {
  2538  	if dst != nil {
  2539  		if dst.Remote() != "" {
  2540  			return dst
  2541  		}
  2542  		// Root is described as the Fs
  2543  		return f
  2544  	}
  2545  	if dir != "" {
  2546  		return dir
  2547  	}
  2548  	// Root is described as the Fs
  2549  	return f
  2550  }
  2551  
  2552  // CopyDirMetadata copies the src directory to dst or f if nil.  If dst is nil then it uses
  2553  // dir as the name of the new directory.
  2554  //
  2555  // It returns the destination directory if possible.  Note that this may
  2556  // be nil.
  2557  func CopyDirMetadata(ctx context.Context, f fs.Fs, dst fs.Directory, dir string, src fs.Directory) (newDst fs.Directory, err error) {
  2558  	ci := fs.GetConfig(ctx)
  2559  	logName := dirName(f, dst, dir)
  2560  	if SkipDestructive(ctx, logName, "update directory metadata") {
  2561  		return nil, nil
  2562  	}
  2563  
  2564  	// Options for the directory metadata
  2565  	options := []fs.OpenOption{}
  2566  	if ci.MetadataSet != nil {
  2567  		options = append(options, fs.MetadataOption(ci.MetadataSet))
  2568  	}
  2569  
  2570  	// Read metadata from src and add options and use metadata mapper
  2571  	metadata, err := fs.GetMetadataOptions(ctx, f, src, options)
  2572  	if err != nil {
  2573  		return nil, err
  2574  	}
  2575  
  2576  	// Fall back to ModTime if metadata not available
  2577  	if metadata == nil {
  2578  		metadata = fs.Metadata{}
  2579  	}
  2580  	if metadata["mtime"] == "" {
  2581  		metadata["mtime"] = src.ModTime(ctx).Format(time.RFC3339Nano)
  2582  	}
  2583  
  2584  	// Now set the metadata
  2585  	if dst == nil {
  2586  		do := f.Features().MkdirMetadata
  2587  		if do == nil {
  2588  			return nil, fmt.Errorf("internal error: expecting %v to have MkdirMetadata method: %w", f, fs.ErrorNotImplemented)
  2589  		}
  2590  		newDst, err = do(ctx, dir, metadata)
  2591  	} else {
  2592  		do, ok := dst.(fs.SetMetadataer)
  2593  		if !ok {
  2594  			return nil, fmt.Errorf("internal error: expecting directory %s (%T) from %v to have SetMetadata method: %w", logName, dst, f, fs.ErrorNotImplemented)
  2595  		}
  2596  		err = do.SetMetadata(ctx, metadata)
  2597  		newDst = dst
  2598  	}
  2599  	if err != nil {
  2600  		return nil, err
  2601  	}
  2602  	fs.Infof(logName, "Updated directory metadata")
  2603  	return newDst, nil
  2604  }
  2605  
  2606  // SetDirModTime sets the modtime on dst or dir
  2607  //
  2608  // If dst is nil then it uses dir as the name of the directory.
  2609  //
  2610  // It returns the destination directory if possible. Note that this
  2611  // may be nil.
  2612  //
  2613  // It does not create the directory.
  2614  func SetDirModTime(ctx context.Context, f fs.Fs, dst fs.Directory, dir string, modTime time.Time) (newDst fs.Directory, err error) {
  2615  	logName := dirName(f, dst, dir)
  2616  	ci := fs.GetConfig(ctx)
  2617  	if ci.NoUpdateDirModTime {
  2618  		fs.Debugf(logName, "Skipping set directory modification time as --no-update-dir-modtime is set")
  2619  		return nil, nil
  2620  	}
  2621  	if SkipDestructive(ctx, logName, "set directory modification time") {
  2622  		return nil, nil
  2623  	}
  2624  	if dst != nil {
  2625  		dir = dst.Remote()
  2626  	}
  2627  
  2628  	// Try to set the ModTime with the Directory.SetModTime method first as this is the most efficient
  2629  	if dst != nil {
  2630  		if do, ok := dst.(fs.SetModTimer); ok {
  2631  			err := do.SetModTime(ctx, modTime)
  2632  			if errors.Is(err, fs.ErrorNotImplemented) {
  2633  				// Fall through and run the code below if not implemented
  2634  				// This can happen for fs.DirWrapper instances
  2635  			} else if err != nil {
  2636  				return dst, err
  2637  			} else {
  2638  				fs.Infof(logName, "Set directory modification time (using SetModTime)")
  2639  				return dst, nil
  2640  			}
  2641  		}
  2642  	}
  2643  
  2644  	// Next try to set the ModTime with the Fs.DirSetModTime method as this works for non-metadata backends
  2645  	if do := f.Features().DirSetModTime; do != nil {
  2646  		err := do(ctx, dir, modTime)
  2647  		if err != nil {
  2648  			return dst, err
  2649  		}
  2650  		fs.Infof(logName, "Set directory modification time (using DirSetModTime)")
  2651  		return dst, nil
  2652  	}
  2653  
  2654  	// Something should have worked so return an error
  2655  	return nil, fmt.Errorf("no method to set directory modtime found for %v (%T): %w", f, dst, fs.ErrorNotImplemented)
  2656  }