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 }