From 3c7ad8d961afc11ecc9b81fa5a02a671b3a75c19 Mon Sep 17 00:00:00 2001 From: nielash Date: Tue, 24 Sep 2024 22:52:04 -0400 Subject: [PATCH] box: fix server-side copying a file over existing dst - fixes #3511 Before this change, server-side copying a src file over a dst that already exists gave `Error "item_name_in_use" (409): Item with the same name already exists`. This change fixes the error by copying to a temporary name first, then moving it to the real name. There might be a more graceful way to overwrite a file during a copy, but I didn't see one in the API docs. https://developer.box.com/reference/post-files-id-copy/ In the meantime, this workaround is better than a critical error. This should (hopefully) fix 8 bisync integration tests. --- backend/box/box.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/backend/box/box.go b/backend/box/box.go index 4f38955eb..7183f35e5 100644 --- a/backend/box/box.go +++ b/backend/box/box.go @@ -43,6 +43,7 @@ import ( "github.com/rclone/rclone/lib/jwtutil" "github.com/rclone/rclone/lib/oauthutil" "github.com/rclone/rclone/lib/pacer" + "github.com/rclone/rclone/lib/random" "github.com/rclone/rclone/lib/rest" "github.com/youmark/pkcs8" "golang.org/x/oauth2" @@ -256,7 +257,6 @@ func getQueryParams(boxConfig *api.ConfigJSON) map[string]string { } func getDecryptedPrivateKey(boxConfig *api.ConfigJSON) (key *rsa.PrivateKey, err error) { - block, rest := pem.Decode([]byte(boxConfig.BoxAppSettings.AppAuth.PrivateKey)) if len(rest) > 0 { return nil, fmt.Errorf("box: extra data included in private key: %w", err) @@ -619,7 +619,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, return shouldRetry(ctx, resp, err) }) if err != nil { - //fmt.Printf("...Error %v\n", err) + // fmt.Printf("...Error %v\n", err) return "", err } // fmt.Printf("...Id %q\n", *info.Id) @@ -966,6 +966,26 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, return nil, err } + // check if dest already exists + item, err := f.preUploadCheck(ctx, leaf, directoryID, src.Size()) + if err != nil { + return nil, err + } + if item != nil { // dest already exists, need to copy to temp name and then move + tempSuffix := "-rclone-copy-" + random.String(8) + fs.Debugf(remote, "dst already exists, copying to temp name %v", remote+tempSuffix) + tempObj, err := f.Copy(ctx, src, remote+tempSuffix) + if err != nil { + return nil, err + } + fs.Debugf(remote+tempSuffix, "moving to real name %v", remote) + err = f.deleteObject(ctx, item.ID) + if err != nil { + return nil, err + } + return f.Move(ctx, tempObj, remote) + } + // Copy the object opts := rest.Opts{ Method: "POST",