From c63f1865f36058e126d40496eb9e2a7e48e3a9ce Mon Sep 17 00:00:00 2001 From: Georg Welzel Date: Sat, 13 Jul 2024 13:35:47 -0700 Subject: [PATCH] operations: copy: generate stable partial suffix --- docs/content/docs.md | 5 +++-- fs/operations/copy.go | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/content/docs.md b/docs/content/docs.md index 0554a0695..06cde0a24 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -1347,11 +1347,12 @@ flag set) such as: - local - ftp - sftp +- pcloud Without `--inplace` (the default) rclone will first upload to a temporary file with an extension like this, where `XXXXXX` represents a -random string and `.partial` is [--partial-suffix](#partial-suffix) value -(`.partial` by default). +hash of the source file's fingerprint and `.partial` is +[--partial-suffix](#partial-suffix) value (`.partial` by default). original-file-name.XXXXXX.partial diff --git a/fs/operations/copy.go b/fs/operations/copy.go index 43afc5f01..4e2f09762 100644 --- a/fs/operations/copy.go +++ b/fs/operations/copy.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "hash/crc32" "io" "path" "strings" @@ -20,7 +21,6 @@ import ( "github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/lib/pacer" - "github.com/rclone/rclone/lib/random" ) // State of the copy @@ -87,7 +87,7 @@ func TruncateString(s string, n int) string { } // Check to see if we should be using a partial name and return the name for the copy and the inplace flag -func (c *copy) checkPartial() (remoteForCopy string, inplace bool, err error) { +func (c *copy) checkPartial(ctx context.Context) (remoteForCopy string, inplace bool, err error) { remoteForCopy = c.remote if c.ci.Inplace || c.dstFeatures.Move == nil || !c.dstFeatures.PartialUploads || strings.HasSuffix(c.remote, ".rclonelink") { return remoteForCopy, true, nil @@ -97,7 +97,11 @@ func (c *copy) checkPartial() (remoteForCopy string, inplace bool, err error) { } // Avoid making the leaf name longer if it's already lengthy to avoid // trouble with file name length limits. - suffix := "." + random.String(8) + c.ci.PartialSuffix + + // generate a stable random suffix by hashing the fingerprint + hash := crc32.ChecksumIEEE([]byte(fs.Fingerprint(ctx, c.src, true))) + + suffix := fmt.Sprintf(".%x%s", hash, c.ci.PartialSuffix) base := path.Base(remoteForCopy) if len(base) > 100 { remoteForCopy = TruncateString(remoteForCopy, len(remoteForCopy)-len(suffix)) + suffix @@ -400,7 +404,7 @@ func Copy(ctx context.Context, f fs.Fs, dst fs.Object, remote string, src fs.Obj // Are we using partials? // // If so set the flag and update the name we use for the copy - c.remoteForCopy, c.inplace, err = c.checkPartial() + c.remoteForCopy, c.inplace, err = c.checkPartial(ctx) if err != nil { return nil, err }