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