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

     1  // Package sync is the implementation of sync/copy/move
     2  package sync
     3  
     4  import (
     5  	"context"
     6  	"fmt"
     7  	"path"
     8  	"sort"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/rclone/rclone/fs"
    15  	"github.com/rclone/rclone/fs/accounting"
    16  	"github.com/rclone/rclone/fs/filter"
    17  	"github.com/rclone/rclone/fs/fserrors"
    18  	"github.com/rclone/rclone/fs/hash"
    19  	"github.com/rclone/rclone/fs/march"
    20  	"github.com/rclone/rclone/fs/operations"
    21  )
    22  
    23  type syncCopyMove struct {
    24  	// parameters
    25  	fdst               fs.Fs
    26  	fsrc               fs.Fs
    27  	deleteMode         fs.DeleteMode // how we are doing deletions
    28  	DoMove             bool
    29  	copyEmptySrcDirs   bool
    30  	deleteEmptySrcDirs bool
    31  	dir                string
    32  	// internal state
    33  	ctx                    context.Context        // internal context for controlling go-routines
    34  	cancel                 func()                 // cancel the context
    35  	noTraverse             bool                   // if set don't traverse the dst
    36  	noCheckDest            bool                   // if set transfer all objects regardless without checking dst
    37  	noUnicodeNormalization bool                   // don't normalize unicode characters in filenames
    38  	deletersWg             sync.WaitGroup         // for delete before go routine
    39  	deleteFilesCh          chan fs.Object         // channel to receive deletes if delete before
    40  	trackRenames           bool                   // set if we should do server side renames
    41  	trackRenamesStrategy   trackRenamesStrategy   // stratgies used for tracking renames
    42  	dstFilesMu             sync.Mutex             // protect dstFiles
    43  	dstFiles               map[string]fs.Object   // dst files, always filled
    44  	srcFiles               map[string]fs.Object   // src files, only used if deleteBefore
    45  	srcFilesChan           chan fs.Object         // passes src objects
    46  	srcFilesResult         chan error             // error result of src listing
    47  	dstFilesResult         chan error             // error result of dst listing
    48  	dstEmptyDirsMu         sync.Mutex             // protect dstEmptyDirs
    49  	dstEmptyDirs           map[string]fs.DirEntry // potentially empty directories
    50  	srcEmptyDirsMu         sync.Mutex             // protect srcEmptyDirs
    51  	srcEmptyDirs           map[string]fs.DirEntry // potentially empty directories
    52  	checkerWg              sync.WaitGroup         // wait for checkers
    53  	toBeChecked            *pipe                  // checkers channel
    54  	transfersWg            sync.WaitGroup         // wait for transfers
    55  	toBeUploaded           *pipe                  // copiers channel
    56  	errorMu                sync.Mutex             // Mutex covering the errors variables
    57  	err                    error                  // normal error from copy process
    58  	noRetryErr             error                  // error with NoRetry set
    59  	fatalErr               error                  // fatal error
    60  	commonHash             hash.Type              // common hash type between src and dst
    61  	modifyWindow           time.Duration          // modify window between fsrc, fdst
    62  	renameMapMu            sync.Mutex             // mutex to protect the below
    63  	renameMap              map[string][]fs.Object // dst files by hash - only used by trackRenames
    64  	renamerWg              sync.WaitGroup         // wait for renamers
    65  	toBeRenamed            *pipe                  // renamers channel
    66  	trackRenamesWg         sync.WaitGroup         // wg for background track renames
    67  	trackRenamesCh         chan fs.Object         // objects are pumped in here
    68  	renameCheck            []fs.Object            // accumulate files to check for rename here
    69  	compareCopyDest        fs.Fs                  // place to check for files to server side copy
    70  	backupDir              fs.Fs                  // place to store overwrites/deletes
    71  	checkFirst             bool                   // if set run all the checkers before starting transfers
    72  }
    73  
    74  type trackRenamesStrategy byte
    75  
    76  const (
    77  	trackRenamesStrategyHash trackRenamesStrategy = 1 << iota
    78  	trackRenamesStrategyModtime
    79  )
    80  
    81  func (strategy trackRenamesStrategy) hash() bool {
    82  	return (strategy & trackRenamesStrategyHash) != 0
    83  }
    84  
    85  func (strategy trackRenamesStrategy) modTime() bool {
    86  	return (strategy & trackRenamesStrategyModtime) != 0
    87  }
    88  
    89  func newSyncCopyMove(ctx context.Context, fdst, fsrc fs.Fs, deleteMode fs.DeleteMode, DoMove bool, deleteEmptySrcDirs bool, copyEmptySrcDirs bool) (*syncCopyMove, error) {
    90  	if (deleteMode != fs.DeleteModeOff || DoMove) && operations.Overlapping(fdst, fsrc) {
    91  		return nil, fserrors.FatalError(fs.ErrorOverlapping)
    92  	}
    93  	s := &syncCopyMove{
    94  		fdst:                   fdst,
    95  		fsrc:                   fsrc,
    96  		deleteMode:             deleteMode,
    97  		DoMove:                 DoMove,
    98  		copyEmptySrcDirs:       copyEmptySrcDirs,
    99  		deleteEmptySrcDirs:     deleteEmptySrcDirs,
   100  		dir:                    "",
   101  		srcFilesChan:           make(chan fs.Object, fs.Config.Checkers+fs.Config.Transfers),
   102  		srcFilesResult:         make(chan error, 1),
   103  		dstFilesResult:         make(chan error, 1),
   104  		dstEmptyDirs:           make(map[string]fs.DirEntry),
   105  		srcEmptyDirs:           make(map[string]fs.DirEntry),
   106  		noTraverse:             fs.Config.NoTraverse,
   107  		noCheckDest:            fs.Config.NoCheckDest,
   108  		noUnicodeNormalization: fs.Config.NoUnicodeNormalization,
   109  		deleteFilesCh:          make(chan fs.Object, fs.Config.Checkers),
   110  		trackRenames:           fs.Config.TrackRenames,
   111  		commonHash:             fsrc.Hashes().Overlap(fdst.Hashes()).GetOne(),
   112  		modifyWindow:           fs.GetModifyWindow(fsrc, fdst),
   113  		trackRenamesCh:         make(chan fs.Object, fs.Config.Checkers),
   114  		checkFirst:             fs.Config.CheckFirst,
   115  	}
   116  	backlog := fs.Config.MaxBacklog
   117  	if s.checkFirst {
   118  		fs.Infof(s.fdst, "Running all checks before starting transfers")
   119  		backlog = -1
   120  	}
   121  	var err error
   122  	s.toBeChecked, err = newPipe(fs.Config.OrderBy, accounting.Stats(ctx).SetCheckQueue, backlog)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	s.toBeUploaded, err = newPipe(fs.Config.OrderBy, accounting.Stats(ctx).SetTransferQueue, backlog)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	s.toBeRenamed, err = newPipe(fs.Config.OrderBy, accounting.Stats(ctx).SetRenameQueue, backlog)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	// If a max session duration has been defined add a deadline to the context
   135  	if fs.Config.MaxDuration > 0 {
   136  		endTime := time.Now().Add(fs.Config.MaxDuration)
   137  		fs.Infof(s.fdst, "Transfer session deadline: %s", endTime.Format("2006/01/02 15:04:05"))
   138  		s.ctx, s.cancel = context.WithDeadline(ctx, endTime)
   139  	} else {
   140  		s.ctx, s.cancel = context.WithCancel(ctx)
   141  	}
   142  	if s.noTraverse && s.deleteMode != fs.DeleteModeOff {
   143  		fs.Errorf(nil, "Ignoring --no-traverse with sync")
   144  		s.noTraverse = false
   145  	}
   146  	s.trackRenamesStrategy, err = parseTrackRenamesStrategy(fs.Config.TrackRenamesStrategy)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	if s.noCheckDest {
   151  		if s.deleteMode != fs.DeleteModeOff {
   152  			return nil, errors.New("can't use --no-check-dest with sync: use copy instead")
   153  		}
   154  		if fs.Config.Immutable {
   155  			return nil, errors.New("can't use --no-check-dest with --immutable")
   156  		}
   157  		if s.backupDir != nil {
   158  			return nil, errors.New("can't use --no-check-dest with --backup-dir")
   159  		}
   160  	}
   161  	if s.trackRenames {
   162  		// Don't track renames for remotes without server-side move support.
   163  		if !operations.CanServerSideMove(fdst) {
   164  			fs.Errorf(fdst, "Ignoring --track-renames as the destination does not support server-side move or copy")
   165  			s.trackRenames = false
   166  		}
   167  		if s.trackRenamesStrategy.hash() && s.commonHash == hash.None {
   168  			fs.Errorf(fdst, "Ignoring --track-renames as the source and destination do not have a common hash")
   169  			s.trackRenames = false
   170  		}
   171  
   172  		if s.trackRenamesStrategy.modTime() && s.modifyWindow == fs.ModTimeNotSupported {
   173  			fs.Errorf(fdst, "Ignoring --track-renames as either the source or destination do not support modtime")
   174  			s.trackRenames = false
   175  		}
   176  
   177  		if s.deleteMode == fs.DeleteModeOff {
   178  			fs.Errorf(fdst, "Ignoring --track-renames as it doesn't work with copy or move, only sync")
   179  			s.trackRenames = false
   180  		}
   181  	}
   182  	if s.trackRenames {
   183  		// track renames needs delete after
   184  		if s.deleteMode != fs.DeleteModeOff {
   185  			s.deleteMode = fs.DeleteModeAfter
   186  		}
   187  		if s.noTraverse {
   188  			fs.Errorf(nil, "Ignoring --no-traverse with --track-renames")
   189  			s.noTraverse = false
   190  		}
   191  	}
   192  	// Make Fs for --backup-dir if required
   193  	if fs.Config.BackupDir != "" || fs.Config.Suffix != "" {
   194  		var err error
   195  		s.backupDir, err = operations.BackupDir(fdst, fsrc, "")
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  	}
   200  	if fs.Config.CompareDest != "" {
   201  		var err error
   202  		s.compareCopyDest, err = operations.GetCompareDest()
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  	} else if fs.Config.CopyDest != "" {
   207  		var err error
   208  		s.compareCopyDest, err = operations.GetCopyDest(fdst)
   209  		if err != nil {
   210  			return nil, err
   211  		}
   212  	}
   213  	return s, nil
   214  }
   215  
   216  // Check to see if the context has been cancelled
   217  func (s *syncCopyMove) aborting() bool {
   218  	return s.ctx.Err() != nil
   219  }
   220  
   221  // This reads the map and pumps it into the channel passed in, closing
   222  // the channel at the end
   223  func (s *syncCopyMove) pumpMapToChan(files map[string]fs.Object, out chan<- fs.Object) {
   224  outer:
   225  	for _, o := range files {
   226  		if s.aborting() {
   227  			break outer
   228  		}
   229  		select {
   230  		case out <- o:
   231  		case <-s.ctx.Done():
   232  			break outer
   233  		}
   234  	}
   235  	close(out)
   236  	s.srcFilesResult <- nil
   237  }
   238  
   239  // This checks the types of errors returned while copying files
   240  func (s *syncCopyMove) processError(err error) {
   241  	if err == nil {
   242  		return
   243  	}
   244  	if err == context.DeadlineExceeded {
   245  		err = fserrors.NoRetryError(err)
   246  	}
   247  	s.errorMu.Lock()
   248  	defer s.errorMu.Unlock()
   249  	switch {
   250  	case fserrors.IsFatalError(err):
   251  		if !s.aborting() {
   252  			fs.Errorf(nil, "Cancelling sync due to fatal error: %v", err)
   253  			s.cancel()
   254  		}
   255  		s.fatalErr = err
   256  	case fserrors.IsNoRetryError(err):
   257  		s.noRetryErr = err
   258  	default:
   259  		s.err = err
   260  	}
   261  }
   262  
   263  // Returns the current error (if any) in the order of precedence
   264  //   fatalErr
   265  //   normal error
   266  //   noRetryErr
   267  func (s *syncCopyMove) currentError() error {
   268  	s.errorMu.Lock()
   269  	defer s.errorMu.Unlock()
   270  	if s.fatalErr != nil {
   271  		return s.fatalErr
   272  	}
   273  	if s.err != nil {
   274  		return s.err
   275  	}
   276  	return s.noRetryErr
   277  }
   278  
   279  // pairChecker reads Objects~s on in send to out if they need transferring.
   280  //
   281  // FIXME potentially doing lots of hashes at once
   282  func (s *syncCopyMove) pairChecker(in *pipe, out *pipe, fraction int, wg *sync.WaitGroup) {
   283  	defer wg.Done()
   284  	for {
   285  		pair, ok := in.GetMax(s.ctx, fraction)
   286  		if !ok {
   287  			return
   288  		}
   289  		src := pair.Src
   290  		var err error
   291  		tr := accounting.Stats(s.ctx).NewCheckingTransfer(src)
   292  		// Check to see if can store this
   293  		if src.Storable() {
   294  			NoNeedTransfer, err := operations.CompareOrCopyDest(s.ctx, s.fdst, pair.Dst, pair.Src, s.compareCopyDest, s.backupDir)
   295  			if err != nil {
   296  				s.processError(err)
   297  			}
   298  			if !NoNeedTransfer && operations.NeedTransfer(s.ctx, pair.Dst, pair.Src) {
   299  				// If files are treated as immutable, fail if destination exists and does not match
   300  				if fs.Config.Immutable && pair.Dst != nil {
   301  					fs.Errorf(pair.Dst, "Source and destination exist but do not match: immutable file modified")
   302  					s.processError(fs.ErrorImmutableModified)
   303  				} else {
   304  					// If destination already exists, then we must move it into --backup-dir if required
   305  					if pair.Dst != nil && s.backupDir != nil {
   306  						err := operations.MoveBackupDir(s.ctx, s.backupDir, pair.Dst)
   307  						if err != nil {
   308  							s.processError(err)
   309  						} else {
   310  							// If successful zero out the dst as it is no longer there and copy the file
   311  							pair.Dst = nil
   312  							ok = out.Put(s.ctx, pair)
   313  							if !ok {
   314  								return
   315  							}
   316  						}
   317  					} else {
   318  						ok = out.Put(s.ctx, pair)
   319  						if !ok {
   320  							return
   321  						}
   322  					}
   323  				}
   324  			} else {
   325  				// If moving need to delete the files we don't need to copy
   326  				if s.DoMove {
   327  					// Delete src if no error on copy
   328  					s.processError(operations.DeleteFile(s.ctx, src))
   329  				}
   330  			}
   331  		}
   332  		tr.Done(err)
   333  	}
   334  }
   335  
   336  // pairRenamer reads Objects~s on in and attempts to rename them,
   337  // otherwise it sends them out if they need transferring.
   338  func (s *syncCopyMove) pairRenamer(in *pipe, out *pipe, fraction int, wg *sync.WaitGroup) {
   339  	defer wg.Done()
   340  	for {
   341  		pair, ok := in.GetMax(s.ctx, fraction)
   342  		if !ok {
   343  			return
   344  		}
   345  		src := pair.Src
   346  		if !s.tryRename(src) {
   347  			// pass on if not renamed
   348  			ok = out.Put(s.ctx, pair)
   349  			if !ok {
   350  				return
   351  			}
   352  		}
   353  	}
   354  }
   355  
   356  // pairCopyOrMove reads Objects on in and moves or copies them.
   357  func (s *syncCopyMove) pairCopyOrMove(ctx context.Context, in *pipe, fdst fs.Fs, fraction int, wg *sync.WaitGroup) {
   358  	defer wg.Done()
   359  	var err error
   360  	for {
   361  		pair, ok := in.GetMax(s.ctx, fraction)
   362  		if !ok {
   363  			return
   364  		}
   365  		src := pair.Src
   366  		if s.DoMove {
   367  			_, err = operations.Move(ctx, fdst, pair.Dst, src.Remote(), src)
   368  		} else {
   369  			_, err = operations.Copy(ctx, fdst, pair.Dst, src.Remote(), src)
   370  		}
   371  		s.processError(err)
   372  	}
   373  }
   374  
   375  // This starts the background checkers.
   376  func (s *syncCopyMove) startCheckers() {
   377  	s.checkerWg.Add(fs.Config.Checkers)
   378  	for i := 0; i < fs.Config.Checkers; i++ {
   379  		fraction := (100 * i) / fs.Config.Checkers
   380  		go s.pairChecker(s.toBeChecked, s.toBeUploaded, fraction, &s.checkerWg)
   381  	}
   382  }
   383  
   384  // This stops the background checkers
   385  func (s *syncCopyMove) stopCheckers() {
   386  	s.toBeChecked.Close()
   387  	fs.Debugf(s.fdst, "Waiting for checks to finish")
   388  	s.checkerWg.Wait()
   389  }
   390  
   391  // This starts the background transfers
   392  func (s *syncCopyMove) startTransfers() {
   393  	s.transfersWg.Add(fs.Config.Transfers)
   394  	for i := 0; i < fs.Config.Transfers; i++ {
   395  		fraction := (100 * i) / fs.Config.Transfers
   396  		go s.pairCopyOrMove(s.ctx, s.toBeUploaded, s.fdst, fraction, &s.transfersWg)
   397  	}
   398  }
   399  
   400  // This stops the background transfers
   401  func (s *syncCopyMove) stopTransfers() {
   402  	s.toBeUploaded.Close()
   403  	fs.Debugf(s.fdst, "Waiting for transfers to finish")
   404  	s.transfersWg.Wait()
   405  }
   406  
   407  // This starts the background renamers.
   408  func (s *syncCopyMove) startRenamers() {
   409  	if !s.trackRenames {
   410  		return
   411  	}
   412  	s.renamerWg.Add(fs.Config.Checkers)
   413  	for i := 0; i < fs.Config.Checkers; i++ {
   414  		fraction := (100 * i) / fs.Config.Checkers
   415  		go s.pairRenamer(s.toBeRenamed, s.toBeUploaded, fraction, &s.renamerWg)
   416  	}
   417  }
   418  
   419  // This stops the background renamers
   420  func (s *syncCopyMove) stopRenamers() {
   421  	if !s.trackRenames {
   422  		return
   423  	}
   424  	s.toBeRenamed.Close()
   425  	fs.Debugf(s.fdst, "Waiting for renames to finish")
   426  	s.renamerWg.Wait()
   427  }
   428  
   429  // This starts the collection of possible renames
   430  func (s *syncCopyMove) startTrackRenames() {
   431  	if !s.trackRenames {
   432  		return
   433  	}
   434  	s.trackRenamesWg.Add(1)
   435  	go func() {
   436  		defer s.trackRenamesWg.Done()
   437  		for o := range s.trackRenamesCh {
   438  			s.renameCheck = append(s.renameCheck, o)
   439  		}
   440  	}()
   441  }
   442  
   443  // This stops the background rename collection
   444  func (s *syncCopyMove) stopTrackRenames() {
   445  	if !s.trackRenames {
   446  		return
   447  	}
   448  	close(s.trackRenamesCh)
   449  	s.trackRenamesWg.Wait()
   450  }
   451  
   452  // This starts the background deletion of files for --delete-during
   453  func (s *syncCopyMove) startDeleters() {
   454  	if s.deleteMode != fs.DeleteModeDuring && s.deleteMode != fs.DeleteModeOnly {
   455  		return
   456  	}
   457  	s.deletersWg.Add(1)
   458  	go func() {
   459  		defer s.deletersWg.Done()
   460  		err := operations.DeleteFilesWithBackupDir(s.ctx, s.deleteFilesCh, s.backupDir)
   461  		s.processError(err)
   462  	}()
   463  }
   464  
   465  // This stops the background deleters
   466  func (s *syncCopyMove) stopDeleters() {
   467  	if s.deleteMode != fs.DeleteModeDuring && s.deleteMode != fs.DeleteModeOnly {
   468  		return
   469  	}
   470  	close(s.deleteFilesCh)
   471  	s.deletersWg.Wait()
   472  }
   473  
   474  // This deletes the files in the dstFiles map.  If checkSrcMap is set
   475  // then it checks to see if they exist first in srcFiles the source
   476  // file map, otherwise it unconditionally deletes them.  If
   477  // checkSrcMap is clear then it assumes that the any source files that
   478  // have been found have been removed from dstFiles already.
   479  func (s *syncCopyMove) deleteFiles(checkSrcMap bool) error {
   480  	if accounting.Stats(s.ctx).Errored() && !fs.Config.IgnoreErrors {
   481  		fs.Errorf(s.fdst, "%v", fs.ErrorNotDeleting)
   482  		return fs.ErrorNotDeleting
   483  	}
   484  
   485  	// Delete the spare files
   486  	toDelete := make(fs.ObjectsChan, fs.Config.Transfers)
   487  	go func() {
   488  	outer:
   489  		for remote, o := range s.dstFiles {
   490  			if checkSrcMap {
   491  				_, exists := s.srcFiles[remote]
   492  				if exists {
   493  					continue
   494  				}
   495  			}
   496  			if s.aborting() {
   497  				break
   498  			}
   499  			select {
   500  			case <-s.ctx.Done():
   501  				break outer
   502  			case toDelete <- o:
   503  			}
   504  		}
   505  		close(toDelete)
   506  	}()
   507  	return operations.DeleteFilesWithBackupDir(s.ctx, toDelete, s.backupDir)
   508  }
   509  
   510  // This deletes the empty directories in the slice passed in.  It
   511  // ignores any errors deleting directories
   512  func deleteEmptyDirectories(ctx context.Context, f fs.Fs, entriesMap map[string]fs.DirEntry) error {
   513  	if len(entriesMap) == 0 {
   514  		return nil
   515  	}
   516  	if accounting.Stats(ctx).Errored() && !fs.Config.IgnoreErrors {
   517  		fs.Errorf(f, "%v", fs.ErrorNotDeletingDirs)
   518  		return fs.ErrorNotDeletingDirs
   519  	}
   520  
   521  	var entries fs.DirEntries
   522  	for _, entry := range entriesMap {
   523  		entries = append(entries, entry)
   524  	}
   525  	// Now delete the empty directories starting from the longest path
   526  	sort.Sort(entries)
   527  	var errorCount int
   528  	var okCount int
   529  	for i := len(entries) - 1; i >= 0; i-- {
   530  		entry := entries[i]
   531  		dir, ok := entry.(fs.Directory)
   532  		if ok {
   533  			// TryRmdir only deletes empty directories
   534  			err := operations.TryRmdir(ctx, f, dir.Remote())
   535  			if err != nil {
   536  				fs.Debugf(fs.LogDirName(f, dir.Remote()), "Failed to Rmdir: %v", err)
   537  				errorCount++
   538  			} else {
   539  				okCount++
   540  			}
   541  		} else {
   542  			fs.Errorf(f, "Not a directory: %v", entry)
   543  		}
   544  	}
   545  	if errorCount > 0 {
   546  		fs.Debugf(f, "failed to delete %d directories", errorCount)
   547  	}
   548  	if okCount > 0 {
   549  		fs.Debugf(f, "deleted %d directories", okCount)
   550  	}
   551  	return nil
   552  }
   553  
   554  // This copies the empty directories in the slice passed in and logs
   555  // any errors copying the directories
   556  func copyEmptyDirectories(ctx context.Context, f fs.Fs, entries map[string]fs.DirEntry) error {
   557  	if len(entries) == 0 {
   558  		return nil
   559  	}
   560  
   561  	var okCount int
   562  	for _, entry := range entries {
   563  		dir, ok := entry.(fs.Directory)
   564  		if ok {
   565  			err := operations.Mkdir(ctx, f, dir.Remote())
   566  			if err != nil {
   567  				fs.Errorf(fs.LogDirName(f, dir.Remote()), "Failed to Mkdir: %v", err)
   568  			} else {
   569  				okCount++
   570  			}
   571  		} else {
   572  			fs.Errorf(f, "Not a directory: %v", entry)
   573  		}
   574  	}
   575  
   576  	if accounting.Stats(ctx).Errored() {
   577  		fs.Debugf(f, "failed to copy %d directories", accounting.Stats(ctx).GetErrors())
   578  	}
   579  
   580  	if okCount > 0 {
   581  		fs.Debugf(f, "copied %d directories", okCount)
   582  	}
   583  	return nil
   584  }
   585  
   586  func (s *syncCopyMove) srcParentDirCheck(entry fs.DirEntry) {
   587  	// If we are moving files then we don't want to remove directories with files in them
   588  	// from the srcEmptyDirs as we are about to move them making the directory empty.
   589  	if s.DoMove {
   590  		return
   591  	}
   592  	parentDir := path.Dir(entry.Remote())
   593  	if parentDir == "." {
   594  		parentDir = ""
   595  	}
   596  	if _, ok := s.srcEmptyDirs[parentDir]; ok {
   597  		delete(s.srcEmptyDirs, parentDir)
   598  	}
   599  }
   600  
   601  // parseTrackRenamesStrategy turns a config string into a trackRenamesStrategy
   602  func parseTrackRenamesStrategy(strategies string) (strategy trackRenamesStrategy, err error) {
   603  	if len(strategies) == 0 {
   604  		return strategy, nil
   605  	}
   606  	for _, s := range strings.Split(strategies, ",") {
   607  		switch s {
   608  		case "hash":
   609  			strategy |= trackRenamesStrategyHash
   610  		case "modtime":
   611  			strategy |= trackRenamesStrategyModtime
   612  		case "size":
   613  			// ignore
   614  		default:
   615  			return strategy, errors.Errorf("unknown track renames strategy %q", s)
   616  		}
   617  	}
   618  	return strategy, nil
   619  }
   620  
   621  // renameID makes a string with the size and the other identifiers of the requested rename strategies
   622  //
   623  // it may return an empty string in which case no hash could be made
   624  func (s *syncCopyMove) renameID(obj fs.Object, renamesStrategy trackRenamesStrategy, precision time.Duration) string {
   625  	var builder strings.Builder
   626  
   627  	fmt.Fprintf(&builder, "%d", obj.Size())
   628  
   629  	if renamesStrategy.hash() {
   630  		var err error
   631  		hash, err := obj.Hash(s.ctx, s.commonHash)
   632  
   633  		if err != nil {
   634  			fs.Debugf(obj, "Hash failed: %v", err)
   635  			return ""
   636  		}
   637  		if hash == "" {
   638  			return ""
   639  		}
   640  
   641  		fmt.Fprintf(&builder, ",%s", hash)
   642  	}
   643  
   644  	// for renamesStrategy.modTime() we don't add to the hash but we check the times in
   645  	// popRenameMap
   646  
   647  	return builder.String()
   648  }
   649  
   650  // pushRenameMap adds the object with hash to the rename map
   651  func (s *syncCopyMove) pushRenameMap(hash string, obj fs.Object) {
   652  	s.renameMapMu.Lock()
   653  	s.renameMap[hash] = append(s.renameMap[hash], obj)
   654  	s.renameMapMu.Unlock()
   655  }
   656  
   657  // popRenameMap finds the object with hash and pop the first match from
   658  // renameMap or returns nil if not found.
   659  func (s *syncCopyMove) popRenameMap(hash string, src fs.Object) (dst fs.Object) {
   660  	s.renameMapMu.Lock()
   661  	dsts, ok := s.renameMap[hash]
   662  	if ok && len(dsts) > 0 {
   663  		// Element to remove
   664  		i := 0
   665  
   666  		// If using track renames strategy modtime then we need to check the modtimes here
   667  		if s.trackRenamesStrategy.modTime() {
   668  			i = -1
   669  			srcModTime := src.ModTime(s.ctx)
   670  			for j, dst := range dsts {
   671  				dstModTime := dst.ModTime(s.ctx)
   672  				dt := dstModTime.Sub(srcModTime)
   673  				if dt < s.modifyWindow && dt > -s.modifyWindow {
   674  					i = j
   675  					break
   676  				}
   677  			}
   678  			// If nothing matched then return nil
   679  			if i < 0 {
   680  				return nil
   681  			}
   682  		}
   683  
   684  		// Remove the entry and return it
   685  		dst = dsts[i]
   686  		dsts = append(dsts[:i], dsts[i+1:]...)
   687  		if len(dsts) > 0 {
   688  			s.renameMap[hash] = dsts
   689  		} else {
   690  			delete(s.renameMap, hash)
   691  		}
   692  	}
   693  	s.renameMapMu.Unlock()
   694  	return dst
   695  }
   696  
   697  // makeRenameMap builds a map of the destination files by hash that
   698  // match sizes in the slice of objects in s.renameCheck
   699  func (s *syncCopyMove) makeRenameMap() {
   700  	fs.Infof(s.fdst, "Making map for --track-renames")
   701  
   702  	// first make a map of possible sizes we need to check
   703  	possibleSizes := map[int64]struct{}{}
   704  	for _, obj := range s.renameCheck {
   705  		possibleSizes[obj.Size()] = struct{}{}
   706  	}
   707  
   708  	// pump all the dstFiles into in
   709  	in := make(chan fs.Object, fs.Config.Checkers)
   710  	go s.pumpMapToChan(s.dstFiles, in)
   711  
   712  	// now make a map of size,hash for all dstFiles
   713  	s.renameMap = make(map[string][]fs.Object)
   714  	var wg sync.WaitGroup
   715  	wg.Add(fs.Config.Transfers)
   716  	for i := 0; i < fs.Config.Transfers; i++ {
   717  		go func() {
   718  			defer wg.Done()
   719  			for obj := range in {
   720  				// only create hash for dst fs.Object if its size could match
   721  				if _, found := possibleSizes[obj.Size()]; found {
   722  					tr := accounting.Stats(s.ctx).NewCheckingTransfer(obj)
   723  					hash := s.renameID(obj, s.trackRenamesStrategy, s.modifyWindow)
   724  
   725  					if hash != "" {
   726  						s.pushRenameMap(hash, obj)
   727  					}
   728  
   729  					tr.Done(nil)
   730  				}
   731  			}
   732  		}()
   733  	}
   734  	wg.Wait()
   735  	fs.Infof(s.fdst, "Finished making map for --track-renames")
   736  }
   737  
   738  // tryRename renames an src object when doing track renames if
   739  // possible, it returns true if the object was renamed.
   740  func (s *syncCopyMove) tryRename(src fs.Object) bool {
   741  	// Calculate the hash of the src object
   742  	hash := s.renameID(src, s.trackRenamesStrategy, fs.GetModifyWindow(s.fsrc, s.fdst))
   743  
   744  	if hash == "" {
   745  		return false
   746  	}
   747  
   748  	// Get a match on fdst
   749  	dst := s.popRenameMap(hash, src)
   750  	if dst == nil {
   751  		return false
   752  	}
   753  
   754  	// Find dst object we are about to overwrite if it exists
   755  	dstOverwritten, _ := s.fdst.NewObject(s.ctx, src.Remote())
   756  
   757  	// Rename dst to have name src.Remote()
   758  	_, err := operations.Move(s.ctx, s.fdst, dstOverwritten, src.Remote(), dst)
   759  	if err != nil {
   760  		fs.Debugf(src, "Failed to rename to %q: %v", dst.Remote(), err)
   761  		return false
   762  	}
   763  
   764  	// remove file from dstFiles if present
   765  	s.dstFilesMu.Lock()
   766  	delete(s.dstFiles, dst.Remote())
   767  	s.dstFilesMu.Unlock()
   768  
   769  	fs.Infof(src, "Renamed from %q", dst.Remote())
   770  	return true
   771  }
   772  
   773  // Syncs fsrc into fdst
   774  //
   775  // If Delete is true then it deletes any files in fdst that aren't in fsrc
   776  //
   777  // If DoMove is true then files will be moved instead of copied
   778  //
   779  // dir is the start directory, "" for root
   780  func (s *syncCopyMove) run() error {
   781  	if operations.Same(s.fdst, s.fsrc) {
   782  		fs.Errorf(s.fdst, "Nothing to do as source and destination are the same")
   783  		return nil
   784  	}
   785  
   786  	// Start background checking and transferring pipeline
   787  	s.startCheckers()
   788  	s.startRenamers()
   789  	if !s.checkFirst {
   790  		s.startTransfers()
   791  	}
   792  	s.startDeleters()
   793  	s.dstFiles = make(map[string]fs.Object)
   794  
   795  	s.startTrackRenames()
   796  
   797  	// set up a march over fdst and fsrc
   798  	m := &march.March{
   799  		Ctx:                    s.ctx,
   800  		Fdst:                   s.fdst,
   801  		Fsrc:                   s.fsrc,
   802  		Dir:                    s.dir,
   803  		NoTraverse:             s.noTraverse,
   804  		Callback:               s,
   805  		DstIncludeAll:          filter.Active.Opt.DeleteExcluded,
   806  		NoCheckDest:            s.noCheckDest,
   807  		NoUnicodeNormalization: s.noUnicodeNormalization,
   808  	}
   809  	s.processError(m.Run())
   810  
   811  	s.stopTrackRenames()
   812  	if s.trackRenames {
   813  		// Build the map of the remaining dstFiles by hash
   814  		s.makeRenameMap()
   815  		// Attempt renames for all the files which don't have a matching dst
   816  		for _, src := range s.renameCheck {
   817  			ok := s.toBeRenamed.Put(s.ctx, fs.ObjectPair{Src: src, Dst: nil})
   818  			if !ok {
   819  				break
   820  			}
   821  		}
   822  	}
   823  
   824  	// Stop background checking and transferring pipeline
   825  	s.stopCheckers()
   826  	if s.checkFirst {
   827  		fs.Infof(s.fdst, "Checks finished, now starting transfers")
   828  		s.startTransfers()
   829  	}
   830  	s.stopRenamers()
   831  	s.stopTransfers()
   832  	s.stopDeleters()
   833  
   834  	if s.copyEmptySrcDirs {
   835  		s.processError(copyEmptyDirectories(s.ctx, s.fdst, s.srcEmptyDirs))
   836  	}
   837  
   838  	// Delete files after
   839  	if s.deleteMode == fs.DeleteModeAfter {
   840  		if s.currentError() != nil && !fs.Config.IgnoreErrors {
   841  			fs.Errorf(s.fdst, "%v", fs.ErrorNotDeleting)
   842  		} else {
   843  			s.processError(s.deleteFiles(false))
   844  		}
   845  	}
   846  
   847  	// Prune empty directories
   848  	if s.deleteMode != fs.DeleteModeOff {
   849  		if s.currentError() != nil && !fs.Config.IgnoreErrors {
   850  			fs.Errorf(s.fdst, "%v", fs.ErrorNotDeletingDirs)
   851  		} else {
   852  			s.processError(deleteEmptyDirectories(s.ctx, s.fdst, s.dstEmptyDirs))
   853  		}
   854  	}
   855  
   856  	// Delete empty fsrc subdirectories
   857  	// if DoMove and --delete-empty-src-dirs flag is set
   858  	if s.DoMove && s.deleteEmptySrcDirs {
   859  		//delete empty subdirectories that were part of the move
   860  		s.processError(deleteEmptyDirectories(s.ctx, s.fsrc, s.srcEmptyDirs))
   861  	}
   862  
   863  	// Read the error out of the context if there is one
   864  	s.processError(s.ctx.Err())
   865  
   866  	if s.deleteMode != fs.DeleteModeOnly && accounting.Stats(s.ctx).GetTransfers() == 0 {
   867  		fs.Infof(nil, "There was nothing to transfer")
   868  	}
   869  
   870  	// cancel the context to free resources
   871  	s.cancel()
   872  	return s.currentError()
   873  }
   874  
   875  // DstOnly have an object which is in the destination only
   876  func (s *syncCopyMove) DstOnly(dst fs.DirEntry) (recurse bool) {
   877  	if s.deleteMode == fs.DeleteModeOff {
   878  		return false
   879  	}
   880  	switch x := dst.(type) {
   881  	case fs.Object:
   882  		switch s.deleteMode {
   883  		case fs.DeleteModeAfter:
   884  			// record object as needs deleting
   885  			s.dstFilesMu.Lock()
   886  			s.dstFiles[x.Remote()] = x
   887  			s.dstFilesMu.Unlock()
   888  		case fs.DeleteModeDuring, fs.DeleteModeOnly:
   889  			select {
   890  			case <-s.ctx.Done():
   891  				return
   892  			case s.deleteFilesCh <- x:
   893  			}
   894  		default:
   895  			panic(fmt.Sprintf("unexpected delete mode %d", s.deleteMode))
   896  		}
   897  	case fs.Directory:
   898  		// Do the same thing to the entire contents of the directory
   899  		// Record directory as it is potentially empty and needs deleting
   900  		if s.fdst.Features().CanHaveEmptyDirectories {
   901  			s.dstEmptyDirsMu.Lock()
   902  			s.dstEmptyDirs[dst.Remote()] = dst
   903  			s.dstEmptyDirsMu.Unlock()
   904  		}
   905  		return true
   906  	default:
   907  		panic("Bad object in DirEntries")
   908  
   909  	}
   910  	return false
   911  }
   912  
   913  // SrcOnly have an object which is in the source only
   914  func (s *syncCopyMove) SrcOnly(src fs.DirEntry) (recurse bool) {
   915  	if s.deleteMode == fs.DeleteModeOnly {
   916  		return false
   917  	}
   918  	switch x := src.(type) {
   919  	case fs.Object:
   920  		// If it's a copy operation,
   921  		// remove parent directory from srcEmptyDirs
   922  		// since it's not really empty
   923  		s.srcEmptyDirsMu.Lock()
   924  		s.srcParentDirCheck(src)
   925  		s.srcEmptyDirsMu.Unlock()
   926  
   927  		if s.trackRenames {
   928  			// Save object to check for a rename later
   929  			select {
   930  			case <-s.ctx.Done():
   931  				return
   932  			case s.trackRenamesCh <- x:
   933  			}
   934  		} else {
   935  			// Check CompareDest && CopyDest
   936  			NoNeedTransfer, err := operations.CompareOrCopyDest(s.ctx, s.fdst, nil, x, s.compareCopyDest, s.backupDir)
   937  			if err != nil {
   938  				s.processError(err)
   939  			}
   940  			if !NoNeedTransfer {
   941  				// No need to check since doesn't exist
   942  				ok := s.toBeUploaded.Put(s.ctx, fs.ObjectPair{Src: x, Dst: nil})
   943  				if !ok {
   944  					return
   945  				}
   946  			}
   947  		}
   948  	case fs.Directory:
   949  		// Do the same thing to the entire contents of the directory
   950  		// Record the directory for deletion
   951  		s.srcEmptyDirsMu.Lock()
   952  		s.srcParentDirCheck(src)
   953  		s.srcEmptyDirs[src.Remote()] = src
   954  		s.srcEmptyDirsMu.Unlock()
   955  		return true
   956  	default:
   957  		panic("Bad object in DirEntries")
   958  	}
   959  	return false
   960  }
   961  
   962  // Match is called when src and dst are present, so sync src to dst
   963  func (s *syncCopyMove) Match(ctx context.Context, dst, src fs.DirEntry) (recurse bool) {
   964  	switch srcX := src.(type) {
   965  	case fs.Object:
   966  		s.srcEmptyDirsMu.Lock()
   967  		s.srcParentDirCheck(src)
   968  		s.srcEmptyDirsMu.Unlock()
   969  
   970  		if s.deleteMode == fs.DeleteModeOnly {
   971  			return false
   972  		}
   973  		dstX, ok := dst.(fs.Object)
   974  		if ok {
   975  			ok = s.toBeChecked.Put(s.ctx, fs.ObjectPair{Src: srcX, Dst: dstX})
   976  			if !ok {
   977  				return false
   978  			}
   979  		} else {
   980  			// FIXME src is file, dst is directory
   981  			err := errors.New("can't overwrite directory with file")
   982  			fs.Errorf(dst, "%v", err)
   983  			s.processError(err)
   984  		}
   985  	case fs.Directory:
   986  		// Do the same thing to the entire contents of the directory
   987  		_, ok := dst.(fs.Directory)
   988  		if ok {
   989  			// Only record matched (src & dst) empty dirs when performing move
   990  			if s.DoMove {
   991  				// Record the src directory for deletion
   992  				s.srcEmptyDirsMu.Lock()
   993  				s.srcParentDirCheck(src)
   994  				s.srcEmptyDirs[src.Remote()] = src
   995  				s.srcEmptyDirsMu.Unlock()
   996  			}
   997  
   998  			return true
   999  		}
  1000  		// FIXME src is dir, dst is file
  1001  		err := errors.New("can't overwrite file with directory")
  1002  		fs.Errorf(dst, "%v", err)
  1003  		s.processError(err)
  1004  	default:
  1005  		panic("Bad object in DirEntries")
  1006  	}
  1007  	return false
  1008  }
  1009  
  1010  // Syncs fsrc into fdst
  1011  //
  1012  // If Delete is true then it deletes any files in fdst that aren't in fsrc
  1013  //
  1014  // If DoMove is true then files will be moved instead of copied
  1015  //
  1016  // dir is the start directory, "" for root
  1017  func runSyncCopyMove(ctx context.Context, fdst, fsrc fs.Fs, deleteMode fs.DeleteMode, DoMove bool, deleteEmptySrcDirs bool, copyEmptySrcDirs bool) error {
  1018  	if deleteMode != fs.DeleteModeOff && DoMove {
  1019  		return fserrors.FatalError(errors.New("can't delete and move at the same time"))
  1020  	}
  1021  	// Run an extra pass to delete only
  1022  	if deleteMode == fs.DeleteModeBefore {
  1023  		if fs.Config.TrackRenames {
  1024  			return fserrors.FatalError(errors.New("can't use --delete-before with --track-renames"))
  1025  		}
  1026  		// only delete stuff during in this pass
  1027  		do, err := newSyncCopyMove(ctx, fdst, fsrc, fs.DeleteModeOnly, false, deleteEmptySrcDirs, copyEmptySrcDirs)
  1028  		if err != nil {
  1029  			return err
  1030  		}
  1031  		err = do.run()
  1032  		if err != nil {
  1033  			return err
  1034  		}
  1035  		// Next pass does a copy only
  1036  		deleteMode = fs.DeleteModeOff
  1037  	}
  1038  	do, err := newSyncCopyMove(ctx, fdst, fsrc, deleteMode, DoMove, deleteEmptySrcDirs, copyEmptySrcDirs)
  1039  	if err != nil {
  1040  		return err
  1041  	}
  1042  	return do.run()
  1043  }
  1044  
  1045  // Sync fsrc into fdst
  1046  func Sync(ctx context.Context, fdst, fsrc fs.Fs, copyEmptySrcDirs bool) error {
  1047  	return runSyncCopyMove(ctx, fdst, fsrc, fs.Config.DeleteMode, false, false, copyEmptySrcDirs)
  1048  }
  1049  
  1050  // CopyDir copies fsrc into fdst
  1051  func CopyDir(ctx context.Context, fdst, fsrc fs.Fs, copyEmptySrcDirs bool) error {
  1052  	return runSyncCopyMove(ctx, fdst, fsrc, fs.DeleteModeOff, false, false, copyEmptySrcDirs)
  1053  }
  1054  
  1055  // moveDir moves fsrc into fdst
  1056  func moveDir(ctx context.Context, fdst, fsrc fs.Fs, deleteEmptySrcDirs bool, copyEmptySrcDirs bool) error {
  1057  	return runSyncCopyMove(ctx, fdst, fsrc, fs.DeleteModeOff, true, deleteEmptySrcDirs, copyEmptySrcDirs)
  1058  }
  1059  
  1060  // MoveDir moves fsrc into fdst
  1061  func MoveDir(ctx context.Context, fdst, fsrc fs.Fs, deleteEmptySrcDirs bool, copyEmptySrcDirs bool) error {
  1062  	if operations.Same(fdst, fsrc) {
  1063  		fs.Errorf(fdst, "Nothing to do as source and destination are the same")
  1064  		return nil
  1065  	}
  1066  
  1067  	// First attempt to use DirMover if exists, same Fs and no filters are active
  1068  	if fdstDirMove := fdst.Features().DirMove; fdstDirMove != nil && operations.SameConfig(fsrc, fdst) && filter.Active.InActive() {
  1069  		if fs.Config.DryRun {
  1070  			fs.Logf(fdst, "Not doing server side directory move as --dry-run")
  1071  			return nil
  1072  		}
  1073  		fs.Debugf(fdst, "Using server side directory move")
  1074  		err := fdstDirMove(ctx, fsrc, "", "")
  1075  		switch err {
  1076  		case fs.ErrorCantDirMove, fs.ErrorDirExists:
  1077  			fs.Infof(fdst, "Server side directory move failed - fallback to file moves: %v", err)
  1078  		case nil:
  1079  			fs.Infof(fdst, "Server side directory move succeeded")
  1080  			return nil
  1081  		default:
  1082  			err = fs.CountError(err)
  1083  			fs.Errorf(fdst, "Server side directory move failed: %v", err)
  1084  			return err
  1085  		}
  1086  	}
  1087  
  1088  	// Otherwise move the files one by one
  1089  	return moveDir(ctx, fdst, fsrc, deleteEmptySrcDirs, copyEmptySrcDirs)
  1090  }