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