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