github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/operations/operations.go (about) 1 // Package operations does generic operations on filesystems and objects 2 package operations 3 4 import ( 5 "bytes" 6 "context" 7 "encoding/base64" 8 "encoding/csv" 9 "encoding/hex" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "net/http" 14 "path" 15 "path/filepath" 16 "sort" 17 "strconv" 18 "strings" 19 "sync" 20 "sync/atomic" 21 "time" 22 23 "github.com/pkg/errors" 24 "github.com/rclone/rclone/fs" 25 "github.com/rclone/rclone/fs/accounting" 26 "github.com/rclone/rclone/fs/cache" 27 "github.com/rclone/rclone/fs/fserrors" 28 "github.com/rclone/rclone/fs/fshttp" 29 "github.com/rclone/rclone/fs/hash" 30 "github.com/rclone/rclone/fs/march" 31 "github.com/rclone/rclone/fs/object" 32 "github.com/rclone/rclone/fs/walk" 33 "github.com/rclone/rclone/lib/random" 34 "github.com/rclone/rclone/lib/readers" 35 "golang.org/x/sync/errgroup" 36 ) 37 38 // CheckHashes checks the two files to see if they have common 39 // known hash types and compares them 40 // 41 // Returns 42 // 43 // equal - which is equality of the hashes 44 // 45 // hash - the HashType. This is HashNone if either of the hashes were 46 // unset or a compatible hash couldn't be found. 47 // 48 // err - may return an error which will already have been logged 49 // 50 // If an error is returned it will return equal as false 51 func CheckHashes(ctx context.Context, src fs.ObjectInfo, dst fs.Object) (equal bool, ht hash.Type, err error) { 52 common := src.Fs().Hashes().Overlap(dst.Fs().Hashes()) 53 // fs.Debugf(nil, "Shared hashes: %v", common) 54 if common.Count() == 0 { 55 return true, hash.None, nil 56 } 57 equal, ht, _, _, err = checkHashes(ctx, src, dst, common.GetOne()) 58 return equal, ht, err 59 } 60 61 // checkHashes does the work of CheckHashes but takes a hash.Type and 62 // returns the effective hash type used. 63 func checkHashes(ctx context.Context, src fs.ObjectInfo, dst fs.Object, ht hash.Type) (equal bool, htOut hash.Type, srcHash, dstHash string, err error) { 64 // Calculate hashes in parallel 65 g, ctx := errgroup.WithContext(ctx) 66 g.Go(func() (err error) { 67 srcHash, err = src.Hash(ctx, ht) 68 if err != nil { 69 err = fs.CountError(err) 70 fs.Errorf(src, "Failed to calculate src hash: %v", err) 71 } 72 return err 73 }) 74 g.Go(func() (err error) { 75 dstHash, err = dst.Hash(ctx, ht) 76 if err != nil { 77 err = fs.CountError(err) 78 fs.Errorf(dst, "Failed to calculate dst hash: %v", err) 79 } 80 return err 81 }) 82 err = g.Wait() 83 if err != nil { 84 return false, ht, srcHash, dstHash, err 85 } 86 if srcHash == "" { 87 return true, hash.None, srcHash, dstHash, nil 88 } 89 if dstHash == "" { 90 return true, hash.None, srcHash, dstHash, nil 91 } 92 if srcHash != dstHash { 93 fs.Debugf(src, "%v = %s (%v)", ht, srcHash, src.Fs()) 94 fs.Debugf(dst, "%v = %s (%v)", ht, dstHash, dst.Fs()) 95 } else { 96 fs.Debugf(src, "%v = %s OK", ht, srcHash) 97 } 98 return srcHash == dstHash, ht, srcHash, dstHash, nil 99 } 100 101 // Equal checks to see if the src and dst objects are equal by looking at 102 // size, mtime and hash 103 // 104 // If the src and dst size are different then it is considered to be 105 // not equal. If --size-only is in effect then this is the only check 106 // that is done. If --ignore-size is in effect then this check is 107 // skipped and the files are considered the same size. 108 // 109 // If the size is the same and the mtime is the same then it is 110 // considered to be equal. This check is skipped if using --checksum. 111 // 112 // If the size is the same and mtime is different, unreadable or 113 // --checksum is set and the hash is the same then the file is 114 // considered to be equal. In this case the mtime on the dst is 115 // updated if --checksum is not set. 116 // 117 // Otherwise the file is considered to be not equal including if there 118 // were errors reading info. 119 func Equal(ctx context.Context, src fs.ObjectInfo, dst fs.Object) bool { 120 return equal(ctx, src, dst, defaultEqualOpt()) 121 } 122 123 // sizeDiffers compare the size of src and dst taking into account the 124 // various ways of ignoring sizes 125 func sizeDiffers(src, dst fs.ObjectInfo) bool { 126 if fs.Config.IgnoreSize || src.Size() < 0 || dst.Size() < 0 { 127 return false 128 } 129 return src.Size() != dst.Size() 130 } 131 132 var checksumWarning sync.Once 133 134 // options for equal function() 135 type equalOpt struct { 136 sizeOnly bool // if set only check size 137 checkSum bool // if set check checksum+size instead of modtime+size 138 updateModTime bool // if set update the modtime if hashes identical and checking with modtime+size 139 forceModTimeMatch bool // if set assume modtimes match 140 } 141 142 // default set of options for equal() 143 func defaultEqualOpt() equalOpt { 144 return equalOpt{ 145 sizeOnly: fs.Config.SizeOnly, 146 checkSum: fs.Config.CheckSum, 147 updateModTime: !fs.Config.NoUpdateModTime, 148 forceModTimeMatch: false, 149 } 150 } 151 152 func equal(ctx context.Context, src fs.ObjectInfo, dst fs.Object, opt equalOpt) bool { 153 if sizeDiffers(src, dst) { 154 fs.Debugf(src, "Sizes differ (src %d vs dst %d)", src.Size(), dst.Size()) 155 return false 156 } 157 if opt.sizeOnly { 158 fs.Debugf(src, "Sizes identical") 159 return true 160 } 161 162 // Assert: Size is equal or being ignored 163 164 // If checking checksum and not modtime 165 if opt.checkSum { 166 // Check the hash 167 same, ht, _ := CheckHashes(ctx, src, dst) 168 if !same { 169 fs.Debugf(src, "%v differ", ht) 170 return false 171 } 172 if ht == hash.None { 173 checksumWarning.Do(func() { 174 fs.Logf(dst.Fs(), "--checksum is in use but the source and destination have no hashes in common; falling back to --size-only") 175 }) 176 fs.Debugf(src, "Size of src and dst objects identical") 177 } else { 178 fs.Debugf(src, "Size and %v of src and dst objects identical", ht) 179 } 180 return true 181 } 182 183 srcModTime := src.ModTime(ctx) 184 if !opt.forceModTimeMatch { 185 // Sizes the same so check the mtime 186 modifyWindow := fs.GetModifyWindow(src.Fs(), dst.Fs()) 187 if modifyWindow == fs.ModTimeNotSupported { 188 fs.Debugf(src, "Sizes identical") 189 return true 190 } 191 dstModTime := dst.ModTime(ctx) 192 dt := dstModTime.Sub(srcModTime) 193 if dt < modifyWindow && dt > -modifyWindow { 194 fs.Debugf(src, "Size and modification time the same (differ by %s, within tolerance %s)", dt, modifyWindow) 195 return true 196 } 197 198 fs.Debugf(src, "Modification times differ by %s: %v, %v", dt, srcModTime, dstModTime) 199 } 200 201 // Check if the hashes are the same 202 same, ht, _ := CheckHashes(ctx, src, dst) 203 if !same { 204 fs.Debugf(src, "%v differ", ht) 205 return false 206 } 207 if ht == hash.None { 208 // if couldn't check hash, return that they differ 209 return false 210 } 211 212 // mod time differs but hash is the same to reset mod time if required 213 if opt.updateModTime { 214 if fs.Config.DryRun { 215 fs.Logf(src, "Not updating modification time as --dry-run") 216 } else { 217 // Size and hash the same but mtime different 218 // Error if objects are treated as immutable 219 if fs.Config.Immutable { 220 fs.Errorf(dst, "StartedAt mismatch between immutable objects") 221 return false 222 } 223 // Update the mtime of the dst object here 224 err := dst.SetModTime(ctx, srcModTime) 225 if err == fs.ErrorCantSetModTime { 226 fs.Debugf(dst, "src and dst identical but can't set mod time without re-uploading") 227 return false 228 } else if err == fs.ErrorCantSetModTimeWithoutDelete { 229 fs.Debugf(dst, "src and dst identical but can't set mod time without deleting and re-uploading") 230 // Remove the file if BackupDir isn't set. If BackupDir is set we would rather have the old file 231 // put in the BackupDir than deleted which is what will happen if we don't delete it. 232 if fs.Config.BackupDir == "" { 233 err = dst.Remove(ctx) 234 if err != nil { 235 fs.Errorf(dst, "failed to delete before re-upload: %v", err) 236 } 237 } 238 return false 239 } else if err != nil { 240 err = fs.CountError(err) 241 fs.Errorf(dst, "Failed to set modification time: %v", err) 242 } else { 243 fs.Infof(src, "Updated modification time in destination") 244 } 245 } 246 } 247 return true 248 } 249 250 // Used to remove a failed copy 251 // 252 // Returns whether the file was successfully removed or not 253 func removeFailedCopy(ctx context.Context, dst fs.Object) bool { 254 if dst == nil { 255 return false 256 } 257 fs.Infof(dst, "Removing failed copy") 258 removeErr := dst.Remove(ctx) 259 if removeErr != nil { 260 fs.Infof(dst, "Failed to remove failed copy: %s", removeErr) 261 return false 262 } 263 return true 264 } 265 266 // OverrideRemote is a wrapper to override the Remote for an 267 // ObjectInfo 268 type OverrideRemote struct { 269 fs.ObjectInfo 270 remote string 271 } 272 273 // NewOverrideRemote returns an OverrideRemoteObject which will 274 // return the remote specified 275 func NewOverrideRemote(oi fs.ObjectInfo, remote string) *OverrideRemote { 276 return &OverrideRemote{ 277 ObjectInfo: oi, 278 remote: remote, 279 } 280 } 281 282 // Remote returns the overridden remote name 283 func (o *OverrideRemote) Remote() string { 284 return o.remote 285 } 286 287 // MimeType returns the mime type of the underlying object or "" if it 288 // can't be worked out 289 func (o *OverrideRemote) MimeType(ctx context.Context) string { 290 if do, ok := o.ObjectInfo.(fs.MimeTyper); ok { 291 return do.MimeType(ctx) 292 } 293 return "" 294 } 295 296 // ID returns the ID of the Object if known, or "" if not 297 func (o *OverrideRemote) ID() string { 298 if do, ok := o.ObjectInfo.(fs.IDer); ok { 299 return do.ID() 300 } 301 return "" 302 } 303 304 // UnWrap returns the Object that this Object is wrapping or nil if it 305 // isn't wrapping anything 306 func (o *OverrideRemote) UnWrap() fs.Object { 307 if o, ok := o.ObjectInfo.(fs.Object); ok { 308 return o 309 } 310 return nil 311 } 312 313 // GetTier returns storage tier or class of the Object 314 func (o *OverrideRemote) GetTier() string { 315 if do, ok := o.ObjectInfo.(fs.GetTierer); ok { 316 return do.GetTier() 317 } 318 return "" 319 } 320 321 // Check all optional interfaces satisfied 322 var _ fs.FullObjectInfo = (*OverrideRemote)(nil) 323 324 // CommonHash returns a single hash.Type and a HashOption with that 325 // type which is in common between the two fs.Fs. 326 func CommonHash(fa, fb fs.Info) (hash.Type, *fs.HashesOption) { 327 // work out which hash to use - limit to 1 hash in common 328 var common hash.Set 329 hashType := hash.None 330 if !fs.Config.IgnoreChecksum { 331 common = fb.Hashes().Overlap(fa.Hashes()) 332 if common.Count() > 0 { 333 hashType = common.GetOne() 334 common = hash.Set(hashType) 335 } 336 } 337 return hashType, &fs.HashesOption{Hashes: common} 338 } 339 340 // Copy src object to dst or f if nil. If dst is nil then it uses 341 // remote as the name of the new object. 342 // 343 // It returns the destination object if possible. Note that this may 344 // be nil. 345 func Copy(ctx context.Context, f fs.Fs, dst fs.Object, remote string, src fs.Object) (newDst fs.Object, err error) { 346 tr := accounting.Stats(ctx).NewTransfer(src) 347 defer func() { 348 tr.Done(err) 349 }() 350 newDst = dst 351 if fs.Config.DryRun { 352 fs.Logf(src, "Not copying as --dry-run") 353 return newDst, nil 354 } 355 maxTries := fs.Config.LowLevelRetries 356 tries := 0 357 doUpdate := dst != nil 358 hashType, hashOption := CommonHash(f, src.Fs()) 359 360 var actionTaken string 361 for { 362 // Try server side copy first - if has optional interface and 363 // is same underlying remote 364 actionTaken = "Copied (server side copy)" 365 if fs.Config.MaxTransfer >= 0 && (accounting.Stats(ctx).GetBytes() >= int64(fs.Config.MaxTransfer) || 366 (fs.Config.CutoffMode == fs.CutoffModeCautious && accounting.Stats(ctx).GetBytesWithPending()+src.Size() >= int64(fs.Config.MaxTransfer))) { 367 return nil, accounting.ErrorMaxTransferLimitReachedFatal 368 } 369 if doCopy := f.Features().Copy; doCopy != nil && (SameConfig(src.Fs(), f) || (SameRemoteType(src.Fs(), f) && f.Features().ServerSideAcrossConfigs)) { 370 in := tr.Account(nil) // account the transfer 371 in.ServerSideCopyStart() 372 newDst, err = doCopy(ctx, src, remote) 373 if err == nil { 374 dst = newDst 375 in.ServerSideCopyEnd(dst.Size()) // account the bytes for the server side transfer 376 err = in.Close() 377 } else { 378 _ = in.Close() 379 } 380 if err == fs.ErrorCantCopy { 381 tr.Reset() // skip incomplete accounting - will be overwritten by the manual copy below 382 } 383 } else { 384 err = fs.ErrorCantCopy 385 } 386 // If can't server side copy, do it manually 387 if err == fs.ErrorCantCopy { 388 if doMultiThreadCopy(f, src) { 389 // Number of streams proportional to size 390 streams := src.Size() / int64(fs.Config.MultiThreadCutoff) 391 // With maximum 392 if streams > int64(fs.Config.MultiThreadStreams) { 393 streams = int64(fs.Config.MultiThreadStreams) 394 } 395 if streams < 2 { 396 streams = 2 397 } 398 dst, err = multiThreadCopy(ctx, f, remote, src, int(streams), tr) 399 if doUpdate { 400 actionTaken = "Multi-thread Copied (replaced existing)" 401 } else { 402 actionTaken = "Multi-thread Copied (new)" 403 } 404 } else { 405 var in0 io.ReadCloser 406 options := []fs.OpenOption{hashOption} 407 for _, option := range fs.Config.DownloadHeaders { 408 options = append(options, option) 409 } 410 in0, err = NewReOpen(ctx, src, fs.Config.LowLevelRetries, options...) 411 if err != nil { 412 err = errors.Wrap(err, "failed to open source object") 413 } else { 414 if src.Size() == -1 { 415 // -1 indicates unknown size. Use Rcat to handle both remotes supporting and not supporting PutStream. 416 if doUpdate { 417 actionTaken = "Copied (Rcat, replaced existing)" 418 } else { 419 actionTaken = "Copied (Rcat, new)" 420 } 421 // NB Rcat closes in0 422 dst, err = Rcat(ctx, f, remote, in0, src.ModTime(ctx)) 423 newDst = dst 424 } else { 425 in := tr.Account(in0).WithBuffer() // account and buffer the transfer 426 var wrappedSrc fs.ObjectInfo = src 427 // We try to pass the original object if possible 428 if src.Remote() != remote { 429 wrappedSrc = NewOverrideRemote(src, remote) 430 } 431 options := []fs.OpenOption{hashOption} 432 for _, option := range fs.Config.UploadHeaders { 433 options = append(options, option) 434 } 435 if doUpdate { 436 actionTaken = "Copied (replaced existing)" 437 err = dst.Update(ctx, in, wrappedSrc, options...) 438 } else { 439 actionTaken = "Copied (new)" 440 dst, err = f.Put(ctx, in, wrappedSrc, options...) 441 } 442 closeErr := in.Close() 443 if err == nil { 444 newDst = dst 445 err = closeErr 446 } 447 } 448 } 449 } 450 } 451 tries++ 452 if tries >= maxTries { 453 break 454 } 455 // Retry if err returned a retry error 456 if fserrors.IsRetryError(err) || fserrors.ShouldRetry(err) { 457 fs.Debugf(src, "Received error: %v - low level retry %d/%d", err, tries, maxTries) 458 tr.Reset() // skip incomplete accounting - will be overwritten by retry 459 continue 460 } 461 // otherwise finish 462 break 463 } 464 if err != nil { 465 err = fs.CountError(err) 466 fs.Errorf(src, "Failed to copy: %v", err) 467 return newDst, err 468 } 469 470 // Verify sizes are the same after transfer 471 if sizeDiffers(src, dst) { 472 err = errors.Errorf("corrupted on transfer: sizes differ %d vs %d", src.Size(), dst.Size()) 473 fs.Errorf(dst, "%v", err) 474 err = fs.CountError(err) 475 removeFailedCopy(ctx, dst) 476 return newDst, err 477 } 478 479 // Verify hashes are the same after transfer - ignoring blank hashes 480 if hashType != hash.None { 481 // checkHashes has logged and counted errors 482 equal, _, srcSum, dstSum, _ := checkHashes(ctx, src, dst, hashType) 483 if !equal { 484 err = errors.Errorf("corrupted on transfer: %v hash differ %q vs %q", hashType, srcSum, dstSum) 485 fs.Errorf(dst, "%v", err) 486 err = fs.CountError(err) 487 removeFailedCopy(ctx, dst) 488 return newDst, err 489 } 490 } 491 492 fs.Infof(src, actionTaken) 493 return newDst, err 494 } 495 496 // SameObject returns true if src and dst could be pointing to the 497 // same object. 498 func SameObject(src, dst fs.Object) bool { 499 if !SameConfig(src.Fs(), dst.Fs()) { 500 return false 501 } 502 srcPath := path.Join(src.Fs().Root(), src.Remote()) 503 dstPath := path.Join(dst.Fs().Root(), dst.Remote()) 504 if dst.Fs().Features().CaseInsensitive { 505 srcPath = strings.ToLower(srcPath) 506 dstPath = strings.ToLower(dstPath) 507 } 508 return srcPath == dstPath 509 } 510 511 // Move src object to dst or fdst if nil. If dst is nil then it uses 512 // remote as the name of the new object. 513 // 514 // Note that you must check the destination does not exist before 515 // calling this and pass it as dst. If you pass dst=nil and the 516 // destination does exist then this may create duplicates or return 517 // errors. 518 // 519 // It returns the destination object if possible. Note that this may 520 // be nil. 521 func Move(ctx context.Context, fdst fs.Fs, dst fs.Object, remote string, src fs.Object) (newDst fs.Object, err error) { 522 tr := accounting.Stats(ctx).NewCheckingTransfer(src) 523 defer func() { 524 if err == nil { 525 accounting.Stats(ctx).Renames(1) 526 } 527 tr.Done(err) 528 }() 529 newDst = dst 530 if fs.Config.DryRun { 531 fs.Logf(src, "Not moving as --dry-run") 532 return newDst, nil 533 } 534 // See if we have Move available 535 if doMove := fdst.Features().Move; doMove != nil && (SameConfig(src.Fs(), fdst) || (SameRemoteType(src.Fs(), fdst) && fdst.Features().ServerSideAcrossConfigs)) { 536 // Delete destination if it exists and is not the same file as src (could be same file while seemingly different if the remote is case insensitive) 537 if dst != nil && !SameObject(src, dst) { 538 err = DeleteFile(ctx, dst) 539 if err != nil { 540 return newDst, err 541 } 542 } 543 // Move dst <- src 544 newDst, err = doMove(ctx, src, remote) 545 switch err { 546 case nil: 547 fs.Infof(src, "Moved (server side)") 548 return newDst, nil 549 case fs.ErrorCantMove: 550 fs.Debugf(src, "Can't move, switching to copy") 551 default: 552 err = fs.CountError(err) 553 fs.Errorf(src, "Couldn't move: %v", err) 554 return newDst, err 555 } 556 } 557 // Move not found or didn't work so copy dst <- src 558 newDst, err = Copy(ctx, fdst, dst, remote, src) 559 if err != nil { 560 fs.Errorf(src, "Not deleting source as copy failed: %v", err) 561 return newDst, err 562 } 563 // Delete src if no error on copy 564 return newDst, DeleteFile(ctx, src) 565 } 566 567 // CanServerSideMove returns true if fdst support server side moves or 568 // server side copies 569 // 570 // Some remotes simulate rename by server-side copy and delete, so include 571 // remotes that implements either Mover or Copier. 572 func CanServerSideMove(fdst fs.Fs) bool { 573 canMove := fdst.Features().Move != nil 574 canCopy := fdst.Features().Copy != nil 575 return canMove || canCopy 576 } 577 578 // SuffixName adds the current --suffix to the remote, obeying 579 // --suffix-keep-extension if set 580 func SuffixName(remote string) string { 581 if fs.Config.Suffix == "" { 582 return remote 583 } 584 if fs.Config.SuffixKeepExtension { 585 ext := path.Ext(remote) 586 base := remote[:len(remote)-len(ext)] 587 return base + fs.Config.Suffix + ext 588 } 589 return remote + fs.Config.Suffix 590 } 591 592 // DeleteFileWithBackupDir deletes a single file respecting --dry-run 593 // and accumulating stats and errors. 594 // 595 // If backupDir is set then it moves the file to there instead of 596 // deleting 597 func DeleteFileWithBackupDir(ctx context.Context, dst fs.Object, backupDir fs.Fs) (err error) { 598 tr := accounting.Stats(ctx).NewCheckingTransfer(dst) 599 defer func() { 600 tr.Done(err) 601 }() 602 numDeletes := accounting.Stats(ctx).Deletes(1) 603 if fs.Config.MaxDelete != -1 && numDeletes > fs.Config.MaxDelete { 604 return fserrors.FatalError(errors.New("--max-delete threshold reached")) 605 } 606 action, actioned, actioning := "delete", "Deleted", "deleting" 607 if backupDir != nil { 608 action, actioned, actioning = "move into backup dir", "Moved into backup dir", "moving into backup dir" 609 } 610 if fs.Config.DryRun { 611 fs.Logf(dst, "Not %s as --dry-run", actioning) 612 } else if backupDir != nil { 613 err = MoveBackupDir(ctx, backupDir, dst) 614 } else { 615 err = dst.Remove(ctx) 616 } 617 if err != nil { 618 fs.Errorf(dst, "Couldn't %s: %v", action, err) 619 err = fs.CountError(err) 620 } else if !fs.Config.DryRun { 621 fs.Infof(dst, actioned) 622 } 623 return err 624 } 625 626 // DeleteFile deletes a single file respecting --dry-run and accumulating stats and errors. 627 // 628 // If useBackupDir is set and --backup-dir is in effect then it moves 629 // the file to there instead of deleting 630 func DeleteFile(ctx context.Context, dst fs.Object) (err error) { 631 return DeleteFileWithBackupDir(ctx, dst, nil) 632 } 633 634 // DeleteFilesWithBackupDir removes all the files passed in the 635 // channel 636 // 637 // If backupDir is set the files will be placed into that directory 638 // instead of being deleted. 639 func DeleteFilesWithBackupDir(ctx context.Context, toBeDeleted fs.ObjectsChan, backupDir fs.Fs) error { 640 var wg sync.WaitGroup 641 wg.Add(fs.Config.Transfers) 642 var errorCount int32 643 var fatalErrorCount int32 644 645 for i := 0; i < fs.Config.Transfers; i++ { 646 go func() { 647 defer wg.Done() 648 for dst := range toBeDeleted { 649 err := DeleteFileWithBackupDir(ctx, dst, backupDir) 650 if err != nil { 651 atomic.AddInt32(&errorCount, 1) 652 if fserrors.IsFatalError(err) { 653 fs.Errorf(nil, "Got fatal error on delete: %s", err) 654 atomic.AddInt32(&fatalErrorCount, 1) 655 return 656 } 657 } 658 } 659 }() 660 } 661 fs.Debugf(nil, "Waiting for deletions to finish") 662 wg.Wait() 663 if errorCount > 0 { 664 err := errors.Errorf("failed to delete %d files", errorCount) 665 if fatalErrorCount > 0 { 666 return fserrors.FatalError(err) 667 } 668 return err 669 } 670 return nil 671 } 672 673 // DeleteFiles removes all the files passed in the channel 674 func DeleteFiles(ctx context.Context, toBeDeleted fs.ObjectsChan) error { 675 return DeleteFilesWithBackupDir(ctx, toBeDeleted, nil) 676 } 677 678 // SameRemoteType returns true if fdst and fsrc are the same type 679 func SameRemoteType(fdst, fsrc fs.Info) bool { 680 return fmt.Sprintf("%T", fdst) == fmt.Sprintf("%T", fsrc) 681 } 682 683 // SameConfig returns true if fdst and fsrc are using the same config 684 // file entry 685 func SameConfig(fdst, fsrc fs.Info) bool { 686 return fdst.Name() == fsrc.Name() 687 } 688 689 // Same returns true if fdst and fsrc point to the same underlying Fs 690 func Same(fdst, fsrc fs.Info) bool { 691 return SameConfig(fdst, fsrc) && strings.Trim(fdst.Root(), "/") == strings.Trim(fsrc.Root(), "/") 692 } 693 694 // fixRoot returns the Root with a trailing / if not empty. It is 695 // aware of case insensitive filesystems. 696 func fixRoot(f fs.Info) string { 697 s := strings.Trim(filepath.ToSlash(f.Root()), "/") 698 if s != "" { 699 s += "/" 700 } 701 if f.Features().CaseInsensitive { 702 s = strings.ToLower(s) 703 } 704 return s 705 } 706 707 // Overlapping returns true if fdst and fsrc point to the same 708 // underlying Fs and they overlap. 709 func Overlapping(fdst, fsrc fs.Info) bool { 710 if !SameConfig(fdst, fsrc) { 711 return false 712 } 713 fdstRoot := fixRoot(fdst) 714 fsrcRoot := fixRoot(fsrc) 715 return strings.HasPrefix(fdstRoot, fsrcRoot) || strings.HasPrefix(fsrcRoot, fdstRoot) 716 } 717 718 // SameDir returns true if fdst and fsrc point to the same 719 // underlying Fs and they are the same directory. 720 func SameDir(fdst, fsrc fs.Info) bool { 721 if !SameConfig(fdst, fsrc) { 722 return false 723 } 724 fdstRoot := fixRoot(fdst) 725 fsrcRoot := fixRoot(fsrc) 726 return fdstRoot == fsrcRoot 727 } 728 729 // checkIdentical checks to see if dst and src are identical 730 // 731 // it returns true if differences were found 732 // it also returns whether it couldn't be hashed 733 func checkIdentical(ctx context.Context, dst, src fs.Object) (differ bool, noHash bool) { 734 same, ht, err := CheckHashes(ctx, src, dst) 735 if err != nil { 736 // CheckHashes will log and count errors 737 return true, false 738 } 739 if ht == hash.None { 740 return false, true 741 } 742 if !same { 743 err = errors.Errorf("%v differ", ht) 744 fs.Errorf(src, "%v", err) 745 _ = fs.CountError(err) 746 return true, false 747 } 748 return false, false 749 } 750 751 // checkFn is the type of the checking function used in CheckFn() 752 type checkFn func(ctx context.Context, a, b fs.Object) (differ bool, noHash bool) 753 754 // checkMarch is used to march over two Fses in the same way as 755 // sync/copy 756 type checkMarch struct { 757 fdst, fsrc fs.Fs 758 check checkFn 759 oneway bool 760 differences int32 761 noHashes int32 762 srcFilesMissing int32 763 dstFilesMissing int32 764 matches int32 765 } 766 767 // DstOnly have an object which is in the destination only 768 func (c *checkMarch) DstOnly(dst fs.DirEntry) (recurse bool) { 769 switch dst.(type) { 770 case fs.Object: 771 if c.oneway { 772 return false 773 } 774 err := errors.Errorf("File not in %v", c.fsrc) 775 fs.Errorf(dst, "%v", err) 776 _ = fs.CountError(err) 777 atomic.AddInt32(&c.differences, 1) 778 atomic.AddInt32(&c.srcFilesMissing, 1) 779 case fs.Directory: 780 // Do the same thing to the entire contents of the directory 781 if c.oneway { 782 return false 783 } 784 return true 785 default: 786 panic("Bad object in DirEntries") 787 } 788 return false 789 } 790 791 // SrcOnly have an object which is in the source only 792 func (c *checkMarch) SrcOnly(src fs.DirEntry) (recurse bool) { 793 switch src.(type) { 794 case fs.Object: 795 err := errors.Errorf("File not in %v", c.fdst) 796 fs.Errorf(src, "%v", err) 797 _ = fs.CountError(err) 798 atomic.AddInt32(&c.differences, 1) 799 atomic.AddInt32(&c.dstFilesMissing, 1) 800 case fs.Directory: 801 // Do the same thing to the entire contents of the directory 802 return true 803 default: 804 panic("Bad object in DirEntries") 805 } 806 return false 807 } 808 809 // check to see if two objects are identical using the check function 810 func (c *checkMarch) checkIdentical(ctx context.Context, dst, src fs.Object) (differ bool, noHash bool) { 811 var err error 812 tr := accounting.Stats(ctx).NewCheckingTransfer(src) 813 defer func() { 814 tr.Done(err) 815 }() 816 if sizeDiffers(src, dst) { 817 err = errors.Errorf("Sizes differ") 818 fs.Errorf(src, "%v", err) 819 return true, false 820 } 821 if fs.Config.SizeOnly { 822 return false, false 823 } 824 return c.check(ctx, dst, src) 825 } 826 827 // Match is called when src and dst are present, so sync src to dst 828 func (c *checkMarch) Match(ctx context.Context, dst, src fs.DirEntry) (recurse bool) { 829 switch srcX := src.(type) { 830 case fs.Object: 831 dstX, ok := dst.(fs.Object) 832 if ok { 833 differ, noHash := c.checkIdentical(ctx, dstX, srcX) 834 if differ { 835 atomic.AddInt32(&c.differences, 1) 836 } else { 837 atomic.AddInt32(&c.matches, 1) 838 if noHash { 839 atomic.AddInt32(&c.noHashes, 1) 840 fs.Debugf(dstX, "OK - could not check hash") 841 } else { 842 fs.Debugf(dstX, "OK") 843 } 844 } 845 } else { 846 err := errors.Errorf("is file on %v but directory on %v", c.fsrc, c.fdst) 847 fs.Errorf(src, "%v", err) 848 _ = fs.CountError(err) 849 atomic.AddInt32(&c.differences, 1) 850 atomic.AddInt32(&c.dstFilesMissing, 1) 851 } 852 case fs.Directory: 853 // Do the same thing to the entire contents of the directory 854 _, ok := dst.(fs.Directory) 855 if ok { 856 return true 857 } 858 err := errors.Errorf("is file on %v but directory on %v", c.fdst, c.fsrc) 859 fs.Errorf(dst, "%v", err) 860 _ = fs.CountError(err) 861 atomic.AddInt32(&c.differences, 1) 862 atomic.AddInt32(&c.srcFilesMissing, 1) 863 864 default: 865 panic("Bad object in DirEntries") 866 } 867 return false 868 } 869 870 // CheckFn checks the files in fsrc and fdst according to Size and 871 // hash using checkFunction on each file to check the hashes. 872 // 873 // checkFunction sees if dst and src are identical 874 // 875 // it returns true if differences were found 876 // it also returns whether it couldn't be hashed 877 func CheckFn(ctx context.Context, fdst, fsrc fs.Fs, check checkFn, oneway bool) error { 878 c := &checkMarch{ 879 fdst: fdst, 880 fsrc: fsrc, 881 check: check, 882 oneway: oneway, 883 } 884 885 // set up a march over fdst and fsrc 886 m := &march.March{ 887 Ctx: ctx, 888 Fdst: fdst, 889 Fsrc: fsrc, 890 Dir: "", 891 Callback: c, 892 } 893 fs.Debugf(fdst, "Waiting for checks to finish") 894 err := m.Run() 895 896 if c.dstFilesMissing > 0 { 897 fs.Logf(fdst, "%d files missing", c.dstFilesMissing) 898 } 899 if c.srcFilesMissing > 0 { 900 fs.Logf(fsrc, "%d files missing", c.srcFilesMissing) 901 } 902 903 fs.Logf(fdst, "%d differences found", c.differences) 904 if errs := accounting.Stats(ctx).GetErrors(); errs > 0 { 905 fs.Logf(fdst, "%d errors while checking", errs) 906 } 907 if c.noHashes > 0 { 908 fs.Logf(fdst, "%d hashes could not be checked", c.noHashes) 909 } 910 if c.matches > 0 { 911 fs.Logf(fdst, "%d matching files", c.matches) 912 } 913 if c.differences > 0 { 914 return errors.Errorf("%d differences found", c.differences) 915 } 916 return err 917 } 918 919 // Check the files in fsrc and fdst according to Size and hash 920 func Check(ctx context.Context, fdst, fsrc fs.Fs, oneway bool) error { 921 return CheckFn(ctx, fdst, fsrc, checkIdentical, oneway) 922 } 923 924 // CheckEqualReaders checks to see if in1 and in2 have the same 925 // content when read. 926 // 927 // it returns true if differences were found 928 func CheckEqualReaders(in1, in2 io.Reader) (differ bool, err error) { 929 const bufSize = 64 * 1024 930 buf1 := make([]byte, bufSize) 931 buf2 := make([]byte, bufSize) 932 for { 933 n1, err1 := readers.ReadFill(in1, buf1) 934 n2, err2 := readers.ReadFill(in2, buf2) 935 // check errors 936 if err1 != nil && err1 != io.EOF { 937 return true, err1 938 } else if err2 != nil && err2 != io.EOF { 939 return true, err2 940 } 941 // err1 && err2 are nil or io.EOF here 942 // process the data 943 if n1 != n2 || !bytes.Equal(buf1[:n1], buf2[:n2]) { 944 return true, nil 945 } 946 // if both streams finished the we have finished 947 if err1 == io.EOF && err2 == io.EOF { 948 break 949 } 950 } 951 return false, nil 952 } 953 954 // CheckIdentical checks to see if dst and src are identical by 955 // reading all their bytes if necessary. 956 // 957 // it returns true if differences were found 958 func CheckIdentical(ctx context.Context, dst, src fs.Object) (differ bool, err error) { 959 in1, err := dst.Open(ctx) 960 if err != nil { 961 return true, errors.Wrapf(err, "failed to open %q", dst) 962 } 963 tr1 := accounting.Stats(ctx).NewTransfer(dst) 964 defer func() { 965 tr1.Done(err) 966 }() 967 in1 = tr1.Account(in1).WithBuffer() // account and buffer the transfer 968 969 in2, err := src.Open(ctx) 970 if err != nil { 971 return true, errors.Wrapf(err, "failed to open %q", src) 972 } 973 tr2 := accounting.Stats(ctx).NewTransfer(dst) 974 defer func() { 975 tr2.Done(err) 976 }() 977 in2 = tr2.Account(in2).WithBuffer() // account and buffer the transfer 978 979 // To assign err variable before defer. 980 differ, err = CheckEqualReaders(in1, in2) 981 return 982 } 983 984 // CheckDownload checks the files in fsrc and fdst according to Size 985 // and the actual contents of the files. 986 func CheckDownload(ctx context.Context, fdst, fsrc fs.Fs, oneway bool) error { 987 check := func(ctx context.Context, a, b fs.Object) (differ bool, noHash bool) { 988 differ, err := CheckIdentical(ctx, a, b) 989 if err != nil { 990 err = fs.CountError(err) 991 fs.Errorf(a, "Failed to download: %v", err) 992 return true, true 993 } 994 return differ, false 995 } 996 return CheckFn(ctx, fdst, fsrc, check, oneway) 997 } 998 999 // ListFn lists the Fs to the supplied function 1000 // 1001 // Lists in parallel which may get them out of order 1002 func ListFn(ctx context.Context, f fs.Fs, fn func(fs.Object)) error { 1003 return walk.ListR(ctx, f, "", false, fs.Config.MaxDepth, walk.ListObjects, func(entries fs.DirEntries) error { 1004 entries.ForObject(fn) 1005 return nil 1006 }) 1007 } 1008 1009 // mutex for synchronized output 1010 var outMutex sync.Mutex 1011 1012 // Synchronized fmt.Fprintf 1013 // 1014 // Ignores errors from Fprintf 1015 func syncFprintf(w io.Writer, format string, a ...interface{}) { 1016 outMutex.Lock() 1017 defer outMutex.Unlock() 1018 _, _ = fmt.Fprintf(w, format, a...) 1019 } 1020 1021 // List the Fs to the supplied writer 1022 // 1023 // Shows size and path - obeys includes and excludes 1024 // 1025 // Lists in parallel which may get them out of order 1026 func List(ctx context.Context, f fs.Fs, w io.Writer) error { 1027 return ListFn(ctx, f, func(o fs.Object) { 1028 syncFprintf(w, "%9d %s\n", o.Size(), o.Remote()) 1029 }) 1030 } 1031 1032 // ListLong lists the Fs to the supplied writer 1033 // 1034 // Shows size, mod time and path - obeys includes and excludes 1035 // 1036 // Lists in parallel which may get them out of order 1037 func ListLong(ctx context.Context, f fs.Fs, w io.Writer) error { 1038 return ListFn(ctx, f, func(o fs.Object) { 1039 tr := accounting.Stats(ctx).NewCheckingTransfer(o) 1040 defer func() { 1041 tr.Done(nil) 1042 }() 1043 modTime := o.ModTime(ctx) 1044 syncFprintf(w, "%9d %s %s\n", o.Size(), modTime.Local().Format("2006-01-02 15:04:05.000000000"), o.Remote()) 1045 }) 1046 } 1047 1048 // Md5sum list the Fs to the supplied writer 1049 // 1050 // Produces the same output as the md5sum command - obeys includes and 1051 // excludes 1052 // 1053 // Lists in parallel which may get them out of order 1054 func Md5sum(ctx context.Context, f fs.Fs, w io.Writer) error { 1055 return HashLister(ctx, hash.MD5, f, w) 1056 } 1057 1058 // Sha1sum list the Fs to the supplied writer 1059 // 1060 // Obeys includes and excludes 1061 // 1062 // Lists in parallel which may get them out of order 1063 func Sha1sum(ctx context.Context, f fs.Fs, w io.Writer) error { 1064 return HashLister(ctx, hash.SHA1, f, w) 1065 } 1066 1067 // hashSum returns the human readable hash for ht passed in. This may 1068 // be UNSUPPORTED or ERROR. If it isn't returning a valid hash it will 1069 // return an error. 1070 func hashSum(ctx context.Context, ht hash.Type, o fs.Object) (string, error) { 1071 var err error 1072 tr := accounting.Stats(ctx).NewCheckingTransfer(o) 1073 defer func() { 1074 tr.Done(err) 1075 }() 1076 sum, err := o.Hash(ctx, ht) 1077 if err == hash.ErrUnsupported { 1078 sum = "UNSUPPORTED" 1079 } else if err != nil { 1080 fs.Debugf(o, "Failed to read %v: %v", ht, err) 1081 sum = "ERROR" 1082 } 1083 return sum, err 1084 } 1085 1086 // HashLister does an md5sum equivalent for the hash type passed in 1087 func HashLister(ctx context.Context, ht hash.Type, f fs.Fs, w io.Writer) error { 1088 return ListFn(ctx, f, func(o fs.Object) { 1089 sum, _ := hashSum(ctx, ht, o) 1090 syncFprintf(w, "%*s %s\n", hash.Width(ht), sum, o.Remote()) 1091 }) 1092 } 1093 1094 // HashListerBase64 does an md5sum equivalent for the hash type passed in with base64 encoded 1095 func HashListerBase64(ctx context.Context, ht hash.Type, f fs.Fs, w io.Writer) error { 1096 return ListFn(ctx, f, func(o fs.Object) { 1097 sum, err := hashSum(ctx, ht, o) 1098 if err == nil { 1099 hexBytes, _ := hex.DecodeString(sum) 1100 sum = base64.URLEncoding.EncodeToString(hexBytes) 1101 } 1102 width := base64.URLEncoding.EncodedLen(hash.Width(ht) / 2) 1103 syncFprintf(w, "%*s %s\n", width, sum, o.Remote()) 1104 }) 1105 } 1106 1107 // Count counts the objects and their sizes in the Fs 1108 // 1109 // Obeys includes and excludes 1110 func Count(ctx context.Context, f fs.Fs) (objects int64, size int64, err error) { 1111 err = ListFn(ctx, f, func(o fs.Object) { 1112 atomic.AddInt64(&objects, 1) 1113 objectSize := o.Size() 1114 if objectSize > 0 { 1115 atomic.AddInt64(&size, objectSize) 1116 } 1117 }) 1118 return 1119 } 1120 1121 // ConfigMaxDepth returns the depth to use for a recursive or non recursive listing. 1122 func ConfigMaxDepth(recursive bool) int { 1123 depth := fs.Config.MaxDepth 1124 if !recursive && depth < 0 { 1125 depth = 1 1126 } 1127 return depth 1128 } 1129 1130 // ListDir lists the directories/buckets/containers in the Fs to the supplied writer 1131 func ListDir(ctx context.Context, f fs.Fs, w io.Writer) error { 1132 return walk.ListR(ctx, f, "", false, ConfigMaxDepth(false), walk.ListDirs, func(entries fs.DirEntries) error { 1133 entries.ForDir(func(dir fs.Directory) { 1134 if dir != nil { 1135 syncFprintf(w, "%12d %13s %9d %s\n", dir.Size(), dir.ModTime(ctx).Local().Format("2006-01-02 15:04:05"), dir.Items(), dir.Remote()) 1136 } 1137 }) 1138 return nil 1139 }) 1140 } 1141 1142 // Mkdir makes a destination directory or container 1143 func Mkdir(ctx context.Context, f fs.Fs, dir string) error { 1144 if fs.Config.DryRun { 1145 fs.Logf(fs.LogDirName(f, dir), "Not making directory as dry run is set") 1146 return nil 1147 } 1148 fs.Debugf(fs.LogDirName(f, dir), "Making directory") 1149 err := f.Mkdir(ctx, dir) 1150 if err != nil { 1151 err = fs.CountError(err) 1152 return err 1153 } 1154 return nil 1155 } 1156 1157 // TryRmdir removes a container but not if not empty. It doesn't 1158 // count errors but may return one. 1159 func TryRmdir(ctx context.Context, f fs.Fs, dir string) error { 1160 if fs.Config.DryRun { 1161 fs.Logf(fs.LogDirName(f, dir), "Not deleting as dry run is set") 1162 return nil 1163 } 1164 fs.Debugf(fs.LogDirName(f, dir), "Removing directory") 1165 return f.Rmdir(ctx, dir) 1166 } 1167 1168 // Rmdir removes a container but not if not empty 1169 func Rmdir(ctx context.Context, f fs.Fs, dir string) error { 1170 err := TryRmdir(ctx, f, dir) 1171 if err != nil { 1172 err = fs.CountError(err) 1173 return err 1174 } 1175 return err 1176 } 1177 1178 // Purge removes a directory and all of its contents 1179 func Purge(ctx context.Context, f fs.Fs, dir string) error { 1180 doFallbackPurge := true 1181 var err error 1182 if dir == "" { 1183 // FIXME change the Purge interface so it takes a dir - see #1891 1184 if doPurge := f.Features().Purge; doPurge != nil { 1185 doFallbackPurge = false 1186 if fs.Config.DryRun { 1187 fs.Logf(f, "Not purging as --dry-run set") 1188 } else { 1189 err = doPurge(ctx) 1190 if err == fs.ErrorCantPurge { 1191 doFallbackPurge = true 1192 } 1193 } 1194 } 1195 } 1196 if doFallbackPurge { 1197 // DeleteFiles and Rmdir observe --dry-run 1198 err = DeleteFiles(ctx, listToChan(ctx, f, dir)) 1199 if err != nil { 1200 return err 1201 } 1202 err = Rmdirs(ctx, f, dir, false) 1203 } 1204 if err != nil { 1205 err = fs.CountError(err) 1206 return err 1207 } 1208 return nil 1209 } 1210 1211 // Delete removes all the contents of a container. Unlike Purge, it 1212 // obeys includes and excludes. 1213 func Delete(ctx context.Context, f fs.Fs) error { 1214 delChan := make(fs.ObjectsChan, fs.Config.Transfers) 1215 delErr := make(chan error, 1) 1216 go func() { 1217 delErr <- DeleteFiles(ctx, delChan) 1218 }() 1219 err := ListFn(ctx, f, func(o fs.Object) { 1220 delChan <- o 1221 }) 1222 close(delChan) 1223 delError := <-delErr 1224 if err == nil { 1225 err = delError 1226 } 1227 return err 1228 } 1229 1230 // listToChan will transfer all objects in the listing to the output 1231 // 1232 // If an error occurs, the error will be logged, and it will close the 1233 // channel. 1234 // 1235 // If the error was ErrorDirNotFound then it will be ignored 1236 func listToChan(ctx context.Context, f fs.Fs, dir string) fs.ObjectsChan { 1237 o := make(fs.ObjectsChan, fs.Config.Checkers) 1238 go func() { 1239 defer close(o) 1240 err := walk.ListR(ctx, f, dir, true, fs.Config.MaxDepth, walk.ListObjects, func(entries fs.DirEntries) error { 1241 entries.ForObject(func(obj fs.Object) { 1242 o <- obj 1243 }) 1244 return nil 1245 }) 1246 if err != nil && err != fs.ErrorDirNotFound { 1247 err = errors.Wrap(err, "failed to list") 1248 err = fs.CountError(err) 1249 fs.Errorf(nil, "%v", err) 1250 } 1251 }() 1252 return o 1253 } 1254 1255 // CleanUp removes the trash for the Fs 1256 func CleanUp(ctx context.Context, f fs.Fs) error { 1257 doCleanUp := f.Features().CleanUp 1258 if doCleanUp == nil { 1259 return errors.Errorf("%v doesn't support cleanup", f) 1260 } 1261 if fs.Config.DryRun { 1262 fs.Logf(f, "Not running cleanup as --dry-run set") 1263 return nil 1264 } 1265 return doCleanUp(ctx) 1266 } 1267 1268 // wrap a Reader and a Closer together into a ReadCloser 1269 type readCloser struct { 1270 io.Reader 1271 io.Closer 1272 } 1273 1274 // Cat any files to the io.Writer 1275 // 1276 // if offset == 0 it will be ignored 1277 // if offset > 0 then the file will be seeked to that offset 1278 // if offset < 0 then the file will be seeked that far from the end 1279 // 1280 // if count < 0 then it will be ignored 1281 // if count >= 0 then only that many characters will be output 1282 func Cat(ctx context.Context, f fs.Fs, w io.Writer, offset, count int64) error { 1283 var mu sync.Mutex 1284 return ListFn(ctx, f, func(o fs.Object) { 1285 var err error 1286 tr := accounting.Stats(ctx).NewTransfer(o) 1287 defer func() { 1288 tr.Done(err) 1289 }() 1290 opt := fs.RangeOption{Start: offset, End: -1} 1291 size := o.Size() 1292 if opt.Start < 0 { 1293 opt.Start += size 1294 } 1295 if count >= 0 { 1296 opt.End = opt.Start + count - 1 1297 } 1298 var options []fs.OpenOption 1299 if opt.Start > 0 || opt.End >= 0 { 1300 options = append(options, &opt) 1301 } 1302 for _, option := range fs.Config.DownloadHeaders { 1303 options = append(options, option) 1304 } 1305 in, err := o.Open(ctx, options...) 1306 if err != nil { 1307 err = fs.CountError(err) 1308 fs.Errorf(o, "Failed to open: %v", err) 1309 return 1310 } 1311 if count >= 0 { 1312 in = &readCloser{Reader: &io.LimitedReader{R: in, N: count}, Closer: in} 1313 } 1314 in = tr.Account(in).WithBuffer() // account and buffer the transfer 1315 // take the lock just before we output stuff, so at the last possible moment 1316 mu.Lock() 1317 defer mu.Unlock() 1318 _, err = io.Copy(w, in) 1319 if err != nil { 1320 err = fs.CountError(err) 1321 fs.Errorf(o, "Failed to send to output: %v", err) 1322 } 1323 }) 1324 } 1325 1326 // Rcat reads data from the Reader until EOF and uploads it to a file on remote 1327 func Rcat(ctx context.Context, fdst fs.Fs, dstFileName string, in io.ReadCloser, modTime time.Time) (dst fs.Object, err error) { 1328 tr := accounting.Stats(ctx).NewTransferRemoteSize(dstFileName, -1) 1329 defer func() { 1330 tr.Done(err) 1331 }() 1332 in = tr.Account(in).WithBuffer() 1333 1334 readCounter := readers.NewCountingReader(in) 1335 var trackingIn io.Reader 1336 var hasher *hash.MultiHasher 1337 var options []fs.OpenOption 1338 if !fs.Config.IgnoreChecksum { 1339 hashes := hash.NewHashSet(fdst.Hashes().GetOne()) // just pick one hash 1340 hashOption := &fs.HashesOption{Hashes: hashes} 1341 options = append(options, hashOption) 1342 hasher, err = hash.NewMultiHasherTypes(hashes) 1343 if err != nil { 1344 return nil, err 1345 } 1346 trackingIn = io.TeeReader(readCounter, hasher) 1347 } else { 1348 trackingIn = readCounter 1349 } 1350 for _, option := range fs.Config.UploadHeaders { 1351 options = append(options, option) 1352 } 1353 1354 compare := func(dst fs.Object) error { 1355 var sums map[hash.Type]string 1356 if hasher != nil { 1357 sums = hasher.Sums() 1358 } 1359 src := object.NewStaticObjectInfo(dstFileName, modTime, int64(readCounter.BytesRead()), false, sums, fdst) 1360 if !Equal(ctx, src, dst) { 1361 err = errors.Errorf("corrupted on transfer") 1362 err = fs.CountError(err) 1363 fs.Errorf(dst, "%v", err) 1364 return err 1365 } 1366 return nil 1367 } 1368 1369 // check if file small enough for direct upload 1370 buf := make([]byte, fs.Config.StreamingUploadCutoff) 1371 if n, err := io.ReadFull(trackingIn, buf); err == io.EOF || err == io.ErrUnexpectedEOF { 1372 fs.Debugf(fdst, "File to upload is small (%d bytes), uploading instead of streaming", n) 1373 src := object.NewMemoryObject(dstFileName, modTime, buf[:n]) 1374 return Copy(ctx, fdst, nil, dstFileName, src) 1375 } 1376 1377 // Make a new ReadCloser with the bits we've already read 1378 in = &readCloser{ 1379 Reader: io.MultiReader(bytes.NewReader(buf), trackingIn), 1380 Closer: in, 1381 } 1382 1383 fStreamTo := fdst 1384 canStream := fdst.Features().PutStream != nil 1385 if !canStream { 1386 fs.Debugf(fdst, "Target remote doesn't support streaming uploads, creating temporary local FS to spool file") 1387 tmpLocalFs, err := fs.TemporaryLocalFs() 1388 if err != nil { 1389 return nil, errors.Wrap(err, "Failed to create temporary local FS to spool file") 1390 } 1391 defer func() { 1392 err := Purge(ctx, tmpLocalFs, "") 1393 if err != nil { 1394 fs.Infof(tmpLocalFs, "Failed to cleanup temporary FS: %v", err) 1395 } 1396 }() 1397 fStreamTo = tmpLocalFs 1398 } 1399 1400 if fs.Config.DryRun { 1401 fs.Logf("stdin", "Not uploading as --dry-run") 1402 // prevents "broken pipe" errors 1403 _, err = io.Copy(ioutil.Discard, in) 1404 return nil, err 1405 } 1406 1407 objInfo := object.NewStaticObjectInfo(dstFileName, modTime, -1, false, nil, nil) 1408 if dst, err = fStreamTo.Features().PutStream(ctx, in, objInfo, options...); err != nil { 1409 return dst, err 1410 } 1411 if err = compare(dst); err != nil { 1412 return dst, err 1413 } 1414 if !canStream { 1415 // copy dst (which is the local object we have just streamed to) to the remote 1416 return Copy(ctx, fdst, nil, dstFileName, dst) 1417 } 1418 return dst, nil 1419 } 1420 1421 // PublicLink adds a "readable by anyone with link" permission on the given file or folder. 1422 func PublicLink(ctx context.Context, f fs.Fs, remote string) (string, error) { 1423 doPublicLink := f.Features().PublicLink 1424 if doPublicLink == nil { 1425 return "", errors.Errorf("%v doesn't support public links", f) 1426 } 1427 return doPublicLink(ctx, remote) 1428 } 1429 1430 // Rmdirs removes any empty directories (or directories only 1431 // containing empty directories) under f, including f. 1432 func Rmdirs(ctx context.Context, f fs.Fs, dir string, leaveRoot bool) error { 1433 dirEmpty := make(map[string]bool) 1434 dirEmpty[dir] = !leaveRoot 1435 err := walk.Walk(ctx, f, dir, true, fs.Config.MaxDepth, func(dirPath string, entries fs.DirEntries, err error) error { 1436 if err != nil { 1437 err = fs.CountError(err) 1438 fs.Errorf(f, "Failed to list %q: %v", dirPath, err) 1439 return nil 1440 } 1441 for _, entry := range entries { 1442 switch x := entry.(type) { 1443 case fs.Directory: 1444 // add a new directory as empty 1445 dir := x.Remote() 1446 _, found := dirEmpty[dir] 1447 if !found { 1448 dirEmpty[dir] = true 1449 } 1450 case fs.Object: 1451 // mark the parents of the file as being non-empty 1452 dir := x.Remote() 1453 for dir != "" { 1454 dir = path.Dir(dir) 1455 if dir == "." || dir == "/" { 1456 dir = "" 1457 } 1458 empty, found := dirEmpty[dir] 1459 // End if we reach a directory which is non-empty 1460 if found && !empty { 1461 break 1462 } 1463 dirEmpty[dir] = false 1464 } 1465 } 1466 } 1467 return nil 1468 }) 1469 if err != nil { 1470 return errors.Wrap(err, "failed to rmdirs") 1471 } 1472 // Now delete the empty directories, starting from the longest path 1473 var toDelete []string 1474 for dir, empty := range dirEmpty { 1475 if empty { 1476 toDelete = append(toDelete, dir) 1477 } 1478 } 1479 sort.Strings(toDelete) 1480 for i := len(toDelete) - 1; i >= 0; i-- { 1481 dir := toDelete[i] 1482 err := TryRmdir(ctx, f, dir) 1483 if err != nil { 1484 err = fs.CountError(err) 1485 fs.Errorf(dir, "Failed to rmdir: %v", err) 1486 return err 1487 } 1488 } 1489 return nil 1490 } 1491 1492 // GetCompareDest sets up --compare-dest 1493 func GetCompareDest() (CompareDest fs.Fs, err error) { 1494 CompareDest, err = cache.Get(fs.Config.CompareDest) 1495 if err != nil { 1496 return nil, fserrors.FatalError(errors.Errorf("Failed to make fs for --compare-dest %q: %v", fs.Config.CompareDest, err)) 1497 } 1498 return CompareDest, nil 1499 } 1500 1501 // compareDest checks --compare-dest to see if src needs to 1502 // be copied 1503 // 1504 // Returns True if src is in --compare-dest 1505 func compareDest(ctx context.Context, dst, src fs.Object, CompareDest fs.Fs) (NoNeedTransfer bool, err error) { 1506 var remote string 1507 if dst == nil { 1508 remote = src.Remote() 1509 } else { 1510 remote = dst.Remote() 1511 } 1512 CompareDestFile, err := CompareDest.NewObject(ctx, remote) 1513 switch err { 1514 case fs.ErrorObjectNotFound: 1515 return false, nil 1516 case nil: 1517 break 1518 default: 1519 return false, err 1520 } 1521 if Equal(ctx, src, CompareDestFile) { 1522 fs.Debugf(src, "Destination found in --compare-dest, skipping") 1523 return true, nil 1524 } 1525 return false, nil 1526 } 1527 1528 // GetCopyDest sets up --copy-dest 1529 func GetCopyDest(fdst fs.Fs) (CopyDest fs.Fs, err error) { 1530 CopyDest, err = cache.Get(fs.Config.CopyDest) 1531 if err != nil { 1532 return nil, fserrors.FatalError(errors.Errorf("Failed to make fs for --copy-dest %q: %v", fs.Config.CopyDest, err)) 1533 } 1534 if !SameConfig(fdst, CopyDest) { 1535 return nil, fserrors.FatalError(errors.New("parameter to --copy-dest has to be on the same remote as destination")) 1536 } 1537 if CopyDest.Features().Copy == nil { 1538 return nil, fserrors.FatalError(errors.New("can't use --copy-dest on a remote which doesn't support server side copy")) 1539 } 1540 return CopyDest, nil 1541 } 1542 1543 // copyDest checks --copy-dest to see if src needs to 1544 // be copied 1545 // 1546 // Returns True if src was copied from --copy-dest 1547 func copyDest(ctx context.Context, fdst fs.Fs, dst, src fs.Object, CopyDest, backupDir fs.Fs) (NoNeedTransfer bool, err error) { 1548 var remote string 1549 if dst == nil { 1550 remote = src.Remote() 1551 } else { 1552 remote = dst.Remote() 1553 } 1554 CopyDestFile, err := CopyDest.NewObject(ctx, remote) 1555 switch err { 1556 case fs.ErrorObjectNotFound: 1557 return false, nil 1558 case nil: 1559 break 1560 default: 1561 return false, err 1562 } 1563 opt := defaultEqualOpt() 1564 opt.updateModTime = false 1565 if equal(ctx, src, CopyDestFile, opt) { 1566 if dst == nil || !Equal(ctx, src, dst) { 1567 if dst != nil && backupDir != nil { 1568 err = MoveBackupDir(ctx, backupDir, dst) 1569 if err != nil { 1570 return false, errors.Wrap(err, "moving to --backup-dir failed") 1571 } 1572 // If successful zero out the dstObj as it is no longer there 1573 dst = nil 1574 } 1575 _, err := Copy(ctx, fdst, dst, remote, CopyDestFile) 1576 if err != nil { 1577 fs.Errorf(src, "Destination found in --copy-dest, error copying") 1578 return false, nil 1579 } 1580 fs.Debugf(src, "Destination found in --copy-dest, using server side copy") 1581 return true, nil 1582 } 1583 fs.Debugf(src, "Unchanged skipping") 1584 return true, nil 1585 } 1586 fs.Debugf(src, "Destination not found in --copy-dest") 1587 return false, nil 1588 } 1589 1590 // CompareOrCopyDest checks --compare-dest and --copy-dest to see if src 1591 // does not need to be copied 1592 // 1593 // Returns True if src does not need to be copied 1594 func CompareOrCopyDest(ctx context.Context, fdst fs.Fs, dst, src fs.Object, CompareOrCopyDest, backupDir fs.Fs) (NoNeedTransfer bool, err error) { 1595 if fs.Config.CompareDest != "" { 1596 return compareDest(ctx, dst, src, CompareOrCopyDest) 1597 } else if fs.Config.CopyDest != "" { 1598 return copyDest(ctx, fdst, dst, src, CompareOrCopyDest, backupDir) 1599 } 1600 return false, nil 1601 } 1602 1603 // NeedTransfer checks to see if src needs to be copied to dst using 1604 // the current config. 1605 // 1606 // Returns a flag which indicates whether the file needs to be 1607 // transferred or not. 1608 func NeedTransfer(ctx context.Context, dst, src fs.Object) bool { 1609 if dst == nil { 1610 fs.Debugf(src, "Need to transfer - File not found at Destination") 1611 return true 1612 } 1613 // If we should ignore existing files, don't transfer 1614 if fs.Config.IgnoreExisting { 1615 fs.Debugf(src, "Destination exists, skipping") 1616 return false 1617 } 1618 // If we should upload unconditionally 1619 if fs.Config.IgnoreTimes { 1620 fs.Debugf(src, "Transferring unconditionally as --ignore-times is in use") 1621 return true 1622 } 1623 // If UpdateOlder is in effect, skip if dst is newer than src 1624 if fs.Config.UpdateOlder { 1625 srcModTime := src.ModTime(ctx) 1626 dstModTime := dst.ModTime(ctx) 1627 dt := dstModTime.Sub(srcModTime) 1628 // If have a mutually agreed precision then use that 1629 modifyWindow := fs.GetModifyWindow(dst.Fs(), src.Fs()) 1630 if modifyWindow == fs.ModTimeNotSupported { 1631 // Otherwise use 1 second as a safe default as 1632 // the resolution of the time a file was 1633 // uploaded. 1634 modifyWindow = time.Second 1635 } 1636 switch { 1637 case dt >= modifyWindow: 1638 fs.Debugf(src, "Destination is newer than source, skipping") 1639 return false 1640 case dt <= -modifyWindow: 1641 // force --checksum on for the check and do update modtimes by default 1642 opt := defaultEqualOpt() 1643 opt.forceModTimeMatch = true 1644 if equal(ctx, src, dst, opt) { 1645 fs.Debugf(src, "Unchanged skipping") 1646 return false 1647 } 1648 default: 1649 // Do a size only compare unless --checksum is set 1650 opt := defaultEqualOpt() 1651 opt.sizeOnly = !fs.Config.CheckSum 1652 if equal(ctx, src, dst, opt) { 1653 fs.Debugf(src, "Destination mod time is within %v of source and files identical, skipping", modifyWindow) 1654 return false 1655 } 1656 fs.Debugf(src, "Destination mod time is within %v of source but files differ, transferring", modifyWindow) 1657 } 1658 } else { 1659 // Check to see if changed or not 1660 if Equal(ctx, src, dst) { 1661 fs.Debugf(src, "Unchanged skipping") 1662 return false 1663 } 1664 } 1665 return true 1666 } 1667 1668 // RcatSize reads data from the Reader until EOF and uploads it to a file on remote. 1669 // Pass in size >=0 if known, <0 if not known 1670 func RcatSize(ctx context.Context, fdst fs.Fs, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (dst fs.Object, err error) { 1671 var obj fs.Object 1672 1673 if size >= 0 { 1674 var err error 1675 // Size known use Put 1676 tr := accounting.Stats(ctx).NewTransferRemoteSize(dstFileName, size) 1677 defer func() { 1678 tr.Done(err) 1679 }() 1680 body := ioutil.NopCloser(in) // we let the server close the body 1681 in := tr.Account(body) // account the transfer (no buffering) 1682 1683 if fs.Config.DryRun { 1684 fs.Logf("stdin", "Not uploading as --dry-run") 1685 // prevents "broken pipe" errors 1686 _, err = io.Copy(ioutil.Discard, in) 1687 return nil, err 1688 } 1689 1690 info := object.NewStaticObjectInfo(dstFileName, modTime, size, true, nil, fdst) 1691 obj, err = fdst.Put(ctx, in, info) 1692 if err != nil { 1693 fs.Errorf(dstFileName, "Post request put error: %v", err) 1694 1695 return nil, err 1696 } 1697 } else { 1698 // Size unknown use Rcat 1699 obj, err = Rcat(ctx, fdst, dstFileName, in, modTime) 1700 if err != nil { 1701 fs.Errorf(dstFileName, "Post request rcat error: %v", err) 1702 1703 return nil, err 1704 } 1705 } 1706 1707 return obj, nil 1708 } 1709 1710 // copyURLFunc is called from CopyURLFn 1711 type copyURLFunc func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) 1712 1713 // copyURLFn copies the data from the url to the function supplied 1714 func copyURLFn(ctx context.Context, dstFileName string, url string, dstFileNameFromURL bool, fn copyURLFunc) (err error) { 1715 client := fshttp.NewClient(fs.Config) 1716 resp, err := client.Get(url) 1717 if err != nil { 1718 return err 1719 } 1720 defer fs.CheckClose(resp.Body, &err) 1721 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 1722 return errors.Errorf("CopyURL failed: %s", resp.Status) 1723 } 1724 modTime, err := http.ParseTime(resp.Header.Get("Last-Modified")) 1725 if err != nil { 1726 modTime = time.Now() 1727 } 1728 if dstFileNameFromURL { 1729 dstFileName = path.Base(resp.Request.URL.Path) 1730 if dstFileName == "." || dstFileName == "/" { 1731 return errors.Errorf("CopyURL failed: file name wasn't found in url") 1732 } 1733 } 1734 return fn(ctx, dstFileName, resp.Body, resp.ContentLength, modTime) 1735 } 1736 1737 // CopyURL copies the data from the url to (fdst, dstFileName) 1738 func CopyURL(ctx context.Context, fdst fs.Fs, dstFileName string, url string, dstFileNameFromURL bool, noClobber bool) (dst fs.Object, err error) { 1739 1740 err = copyURLFn(ctx, dstFileName, url, dstFileNameFromURL, func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) { 1741 if noClobber { 1742 _, err = fdst.NewObject(ctx, dstFileName) 1743 if err == nil { 1744 return errors.New("CopyURL failed: file already exist") 1745 } 1746 } 1747 dst, err = RcatSize(ctx, fdst, dstFileName, in, size, modTime) 1748 return err 1749 }) 1750 return dst, err 1751 } 1752 1753 // CopyURLToWriter copies the data from the url to the io.Writer supplied 1754 func CopyURLToWriter(ctx context.Context, url string, out io.Writer) (err error) { 1755 return copyURLFn(ctx, "", url, false, func(ctx context.Context, dstFileName string, in io.ReadCloser, size int64, modTime time.Time) (err error) { 1756 _, err = io.Copy(out, in) 1757 return err 1758 }) 1759 } 1760 1761 // BackupDir returns the correctly configured --backup-dir 1762 func BackupDir(fdst fs.Fs, fsrc fs.Fs, srcFileName string) (backupDir fs.Fs, err error) { 1763 if fs.Config.BackupDir != "" { 1764 backupDir, err = cache.Get(fs.Config.BackupDir) 1765 if err != nil { 1766 return nil, fserrors.FatalError(errors.Errorf("Failed to make fs for --backup-dir %q: %v", fs.Config.BackupDir, err)) 1767 } 1768 if !SameConfig(fdst, backupDir) { 1769 return nil, fserrors.FatalError(errors.New("parameter to --backup-dir has to be on the same remote as destination")) 1770 } 1771 if srcFileName == "" { 1772 if Overlapping(fdst, backupDir) { 1773 return nil, fserrors.FatalError(errors.New("destination and parameter to --backup-dir mustn't overlap")) 1774 } 1775 if Overlapping(fsrc, backupDir) { 1776 return nil, fserrors.FatalError(errors.New("source and parameter to --backup-dir mustn't overlap")) 1777 } 1778 } else { 1779 if fs.Config.Suffix == "" { 1780 if SameDir(fdst, backupDir) { 1781 return nil, fserrors.FatalError(errors.New("destination and parameter to --backup-dir mustn't be the same")) 1782 } 1783 if SameDir(fsrc, backupDir) { 1784 return nil, fserrors.FatalError(errors.New("source and parameter to --backup-dir mustn't be the same")) 1785 } 1786 } 1787 } 1788 } else { 1789 if srcFileName == "" { 1790 return nil, fserrors.FatalError(errors.New("--suffix must be used with a file or with --backup-dir")) 1791 } 1792 // --backup-dir is not set but --suffix is - use the destination as the backupDir 1793 backupDir = fdst 1794 } 1795 if !CanServerSideMove(backupDir) { 1796 return nil, fserrors.FatalError(errors.New("can't use --backup-dir on a remote which doesn't support server side move or copy")) 1797 } 1798 return backupDir, nil 1799 } 1800 1801 // MoveBackupDir moves a file to the backup dir 1802 func MoveBackupDir(ctx context.Context, backupDir fs.Fs, dst fs.Object) (err error) { 1803 remoteWithSuffix := SuffixName(dst.Remote()) 1804 overwritten, _ := backupDir.NewObject(ctx, remoteWithSuffix) 1805 _, err = Move(ctx, backupDir, overwritten, remoteWithSuffix, dst) 1806 return err 1807 } 1808 1809 // moveOrCopyFile moves or copies a single file possibly to a new name 1810 func moveOrCopyFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string, cp bool) (err error) { 1811 dstFilePath := path.Join(fdst.Root(), dstFileName) 1812 srcFilePath := path.Join(fsrc.Root(), srcFileName) 1813 if fdst.Name() == fsrc.Name() && dstFilePath == srcFilePath { 1814 fs.Debugf(fdst, "don't need to copy/move %s, it is already at target location", dstFileName) 1815 return nil 1816 } 1817 1818 // Choose operations 1819 Op := Move 1820 if cp { 1821 Op = Copy 1822 } 1823 1824 // Find src object 1825 srcObj, err := fsrc.NewObject(ctx, srcFileName) 1826 if err != nil { 1827 return err 1828 } 1829 1830 // Find dst object if it exists 1831 var dstObj fs.Object 1832 if !fs.Config.NoCheckDest { 1833 dstObj, err = fdst.NewObject(ctx, dstFileName) 1834 if err == fs.ErrorObjectNotFound { 1835 dstObj = nil 1836 } else if err != nil { 1837 return err 1838 } 1839 } 1840 1841 // Special case for changing case of a file on a case insensitive remote 1842 // This will move the file to a temporary name then 1843 // move it back to the intended destination. This is required 1844 // to avoid issues with certain remotes and avoid file deletion. 1845 if !cp && fdst.Name() == fsrc.Name() && fdst.Features().CaseInsensitive && dstFileName != srcFileName && strings.ToLower(dstFilePath) == strings.ToLower(srcFilePath) { 1846 // Create random name to temporarily move file to 1847 tmpObjName := dstFileName + "-rclone-move-" + random.String(8) 1848 _, err := fdst.NewObject(ctx, tmpObjName) 1849 if err != fs.ErrorObjectNotFound { 1850 if err == nil { 1851 return errors.New("found an already existing file with a randomly generated name. Try the operation again") 1852 } 1853 return errors.Wrap(err, "error while attempting to move file to a temporary location") 1854 } 1855 tr := accounting.Stats(ctx).NewTransfer(srcObj) 1856 defer func() { 1857 tr.Done(err) 1858 }() 1859 tmpObj, err := Op(ctx, fdst, nil, tmpObjName, srcObj) 1860 if err != nil { 1861 return errors.Wrap(err, "error while moving file to temporary location") 1862 } 1863 _, err = Op(ctx, fdst, nil, dstFileName, tmpObj) 1864 return err 1865 } 1866 1867 var backupDir, copyDestDir fs.Fs 1868 if fs.Config.BackupDir != "" || fs.Config.Suffix != "" { 1869 backupDir, err = BackupDir(fdst, fsrc, srcFileName) 1870 if err != nil { 1871 return errors.Wrap(err, "creating Fs for --backup-dir failed") 1872 } 1873 } 1874 if fs.Config.CompareDest != "" { 1875 copyDestDir, err = GetCompareDest() 1876 if err != nil { 1877 return err 1878 } 1879 } else if fs.Config.CopyDest != "" { 1880 copyDestDir, err = GetCopyDest(fdst) 1881 if err != nil { 1882 return err 1883 } 1884 } 1885 NoNeedTransfer, err := CompareOrCopyDest(ctx, fdst, dstObj, srcObj, copyDestDir, backupDir) 1886 if err != nil { 1887 return err 1888 } 1889 if !NoNeedTransfer && NeedTransfer(ctx, dstObj, srcObj) { 1890 // If destination already exists, then we must move it into --backup-dir if required 1891 if dstObj != nil && backupDir != nil { 1892 err = MoveBackupDir(ctx, backupDir, dstObj) 1893 if err != nil { 1894 return errors.Wrap(err, "moving to --backup-dir failed") 1895 } 1896 // If successful zero out the dstObj as it is no longer there 1897 dstObj = nil 1898 } 1899 1900 _, err = Op(ctx, fdst, dstObj, dstFileName, srcObj) 1901 } else { 1902 tr := accounting.Stats(ctx).NewCheckingTransfer(srcObj) 1903 if !cp { 1904 err = DeleteFile(ctx, srcObj) 1905 } 1906 tr.Done(err) 1907 } 1908 return err 1909 } 1910 1911 // MoveFile moves a single file possibly to a new name 1912 func MoveFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string) (err error) { 1913 return moveOrCopyFile(ctx, fdst, fsrc, dstFileName, srcFileName, false) 1914 } 1915 1916 // CopyFile moves a single file possibly to a new name 1917 func CopyFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName string) (err error) { 1918 return moveOrCopyFile(ctx, fdst, fsrc, dstFileName, srcFileName, true) 1919 } 1920 1921 // SetTier changes tier of object in remote 1922 func SetTier(ctx context.Context, fsrc fs.Fs, tier string) error { 1923 return ListFn(ctx, fsrc, func(o fs.Object) { 1924 objImpl, ok := o.(fs.SetTierer) 1925 if !ok { 1926 fs.Errorf(fsrc, "Remote object does not implement SetTier") 1927 return 1928 } 1929 err := objImpl.SetTier(tier) 1930 if err != nil { 1931 fs.Errorf(fsrc, "Failed to do SetTier, %v", err) 1932 } 1933 }) 1934 } 1935 1936 // ListFormat defines files information print format 1937 type ListFormat struct { 1938 separator string 1939 dirSlash bool 1940 absolute bool 1941 output []func(entry *ListJSONItem) string 1942 csv *csv.Writer 1943 buf bytes.Buffer 1944 } 1945 1946 // SetSeparator changes separator in struct 1947 func (l *ListFormat) SetSeparator(separator string) { 1948 l.separator = separator 1949 } 1950 1951 // SetDirSlash defines if slash should be printed 1952 func (l *ListFormat) SetDirSlash(dirSlash bool) { 1953 l.dirSlash = dirSlash 1954 } 1955 1956 // SetAbsolute prints a leading slash in front of path names 1957 func (l *ListFormat) SetAbsolute(absolute bool) { 1958 l.absolute = absolute 1959 } 1960 1961 // SetCSV defines if the output should be csv 1962 // 1963 // Note that you should call SetSeparator before this if you want a 1964 // custom separator 1965 func (l *ListFormat) SetCSV(useCSV bool) { 1966 if useCSV { 1967 l.csv = csv.NewWriter(&l.buf) 1968 if l.separator != "" { 1969 l.csv.Comma = []rune(l.separator)[0] 1970 } 1971 } else { 1972 l.csv = nil 1973 } 1974 } 1975 1976 // SetOutput sets functions used to create files information 1977 func (l *ListFormat) SetOutput(output []func(entry *ListJSONItem) string) { 1978 l.output = output 1979 } 1980 1981 // AddModTime adds file's Mod Time to output 1982 func (l *ListFormat) AddModTime() { 1983 l.AppendOutput(func(entry *ListJSONItem) string { 1984 return entry.ModTime.When.Local().Format("2006-01-02 15:04:05") 1985 }) 1986 } 1987 1988 // AddSize adds file's size to output 1989 func (l *ListFormat) AddSize() { 1990 l.AppendOutput(func(entry *ListJSONItem) string { 1991 return strconv.FormatInt(entry.Size, 10) 1992 }) 1993 } 1994 1995 // normalisePath makes sure the path has the correct slashes for the current mode 1996 func (l *ListFormat) normalisePath(entry *ListJSONItem, remote string) string { 1997 if l.absolute && !strings.HasPrefix(remote, "/") { 1998 remote = "/" + remote 1999 } 2000 if entry.IsDir && l.dirSlash { 2001 remote += "/" 2002 } 2003 return remote 2004 } 2005 2006 // AddPath adds path to file to output 2007 func (l *ListFormat) AddPath() { 2008 l.AppendOutput(func(entry *ListJSONItem) string { 2009 return l.normalisePath(entry, entry.Path) 2010 }) 2011 } 2012 2013 // AddEncrypted adds the encrypted path to file to output 2014 func (l *ListFormat) AddEncrypted() { 2015 l.AppendOutput(func(entry *ListJSONItem) string { 2016 return l.normalisePath(entry, entry.Encrypted) 2017 }) 2018 } 2019 2020 // AddHash adds the hash of the type given to the output 2021 func (l *ListFormat) AddHash(ht hash.Type) { 2022 hashName := ht.String() 2023 l.AppendOutput(func(entry *ListJSONItem) string { 2024 if entry.IsDir { 2025 return "" 2026 } 2027 return entry.Hashes[hashName] 2028 }) 2029 } 2030 2031 // AddID adds file's ID to the output if known 2032 func (l *ListFormat) AddID() { 2033 l.AppendOutput(func(entry *ListJSONItem) string { 2034 return entry.ID 2035 }) 2036 } 2037 2038 // AddOrigID adds file's Original ID to the output if known 2039 func (l *ListFormat) AddOrigID() { 2040 l.AppendOutput(func(entry *ListJSONItem) string { 2041 return entry.OrigID 2042 }) 2043 } 2044 2045 // AddTier adds file's Tier to the output if known 2046 func (l *ListFormat) AddTier() { 2047 l.AppendOutput(func(entry *ListJSONItem) string { 2048 return entry.Tier 2049 }) 2050 } 2051 2052 // AddMimeType adds file's MimeType to the output if known 2053 func (l *ListFormat) AddMimeType() { 2054 l.AppendOutput(func(entry *ListJSONItem) string { 2055 return entry.MimeType 2056 }) 2057 } 2058 2059 // AppendOutput adds string generated by specific function to printed output 2060 func (l *ListFormat) AppendOutput(functionToAppend func(item *ListJSONItem) string) { 2061 l.output = append(l.output, functionToAppend) 2062 } 2063 2064 // Format prints information about the DirEntry in the format defined 2065 func (l *ListFormat) Format(entry *ListJSONItem) (result string) { 2066 var out []string 2067 for _, fun := range l.output { 2068 out = append(out, fun(entry)) 2069 } 2070 if l.csv != nil { 2071 l.buf.Reset() 2072 _ = l.csv.Write(out) // can't fail writing to bytes.Buffer 2073 l.csv.Flush() 2074 result = strings.TrimRight(l.buf.String(), "\n") 2075 } else { 2076 result = strings.Join(out, l.separator) 2077 } 2078 return result 2079 } 2080 2081 // DirMove renames srcRemote to dstRemote 2082 // 2083 // It does this by loading the directory tree into memory (using ListR 2084 // if available) and doing renames in parallel. 2085 func DirMove(ctx context.Context, f fs.Fs, srcRemote, dstRemote string) (err error) { 2086 // Use DirMove if possible 2087 if doDirMove := f.Features().DirMove; doDirMove != nil { 2088 err = doDirMove(ctx, f, srcRemote, dstRemote) 2089 if err == nil { 2090 accounting.Stats(ctx).Renames(1) 2091 } 2092 return err 2093 } 2094 2095 // Load the directory tree into memory 2096 tree, err := walk.NewDirTree(ctx, f, srcRemote, true, -1) 2097 if err != nil { 2098 return errors.Wrap(err, "RenameDir tree walk") 2099 } 2100 2101 // Get the directories in sorted order 2102 dirs := tree.Dirs() 2103 2104 // Make the destination directories - must be done in order not in parallel 2105 for _, dir := range dirs { 2106 dstPath := dstRemote + dir[len(srcRemote):] 2107 err := f.Mkdir(ctx, dstPath) 2108 if err != nil { 2109 return errors.Wrap(err, "RenameDir mkdir") 2110 } 2111 } 2112 2113 // Rename the files in parallel 2114 type rename struct { 2115 o fs.Object 2116 newPath string 2117 } 2118 renames := make(chan rename, fs.Config.Transfers) 2119 g, gCtx := errgroup.WithContext(context.Background()) 2120 for i := 0; i < fs.Config.Transfers; i++ { 2121 g.Go(func() error { 2122 for job := range renames { 2123 dstOverwritten, _ := f.NewObject(gCtx, job.newPath) 2124 _, err := Move(gCtx, f, dstOverwritten, job.newPath, job.o) 2125 if err != nil { 2126 return err 2127 } 2128 select { 2129 case <-gCtx.Done(): 2130 return gCtx.Err() 2131 default: 2132 } 2133 2134 } 2135 return nil 2136 }) 2137 } 2138 for dir, entries := range tree { 2139 dstPath := dstRemote + dir[len(srcRemote):] 2140 for _, entry := range entries { 2141 if o, ok := entry.(fs.Object); ok { 2142 renames <- rename{o, path.Join(dstPath, path.Base(o.Remote()))} 2143 } 2144 } 2145 } 2146 close(renames) 2147 err = g.Wait() 2148 if err != nil { 2149 return errors.Wrap(err, "RenameDir renames") 2150 } 2151 2152 // Remove the source directories in reverse order 2153 for i := len(dirs) - 1; i >= 0; i-- { 2154 err := f.Rmdir(ctx, dirs[i]) 2155 if err != nil { 2156 return errors.Wrap(err, "RenameDir rmdir") 2157 } 2158 } 2159 2160 return nil 2161 } 2162 2163 // FsInfo provides information about a remote 2164 type FsInfo struct { 2165 // Name of the remote (as passed into NewFs) 2166 Name string 2167 2168 // Root of the remote (as passed into NewFs) 2169 Root string 2170 2171 // String returns a description of the FS 2172 String string 2173 2174 // Precision of the ModTimes in this Fs in Nanoseconds 2175 Precision time.Duration 2176 2177 // Returns the supported hash types of the filesystem 2178 Hashes []string 2179 2180 // Features returns the optional features of this Fs 2181 Features map[string]bool 2182 } 2183 2184 // GetFsInfo gets the information (FsInfo) about a given Fs 2185 func GetFsInfo(f fs.Fs) *FsInfo { 2186 info := &FsInfo{ 2187 Name: f.Name(), 2188 Root: f.Root(), 2189 String: f.String(), 2190 Precision: f.Precision(), 2191 Hashes: make([]string, 0, 4), 2192 Features: f.Features().Enabled(), 2193 } 2194 for _, hashType := range f.Hashes().Array() { 2195 info.Hashes = append(info.Hashes, hashType.String()) 2196 } 2197 return info 2198 }