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 }