combine: fix operations.DirMove across upstreams - fixes #7661

Before this change, operations.DirMove would fail when moving a directory, if
the src and dest were on different upstreams of a combine remote.

The issue only affected operations.DirMove, and not sync.MoveDir, because they
checked for server-side-move support in different ways.

MoveDir checks by just trying it and seeing what error comes back. This works
fine for combine because combine returns fs.ErrorCantDirMove which MoveDir
understands what to do with.

DirMove, however, only checked whether the function pointer is nil. This is an
unreliable way to check for combine, because combine does advertise support for
DirMove, despite not always being able to do it.

This change fixes the issue by checking the returned error in a manner similar
to sync.MoveDir and falling back to individual file moves (copy + delete)
depending on which error was returned.
This commit is contained in:
nielash 2024-03-06 13:26:50 -05:00 committed by Nick Craig-Wood
parent 252562d00a
commit 431524445e
2 changed files with 31 additions and 1 deletions

View file

@ -2310,7 +2310,10 @@ func DirMove(ctx context.Context, f fs.Fs, srcRemote, dstRemote string) (err err
if err == nil { if err == nil {
accounting.Stats(ctx).Renames(1) accounting.Stats(ctx).Renames(1)
} }
return err if err != fs.ErrorCantDirMove && err != fs.ErrorDirExists {
return err
}
fs.Infof(f, "Can't DirMove - falling back to file moves: %v", err)
} }
// Load the directory tree into memory // Load the directory tree into memory

View file

@ -1405,6 +1405,33 @@ func TestDirMove(t *testing.T) {
fs.GetModifyWindow(ctx, r.Fremote), fs.GetModifyWindow(ctx, r.Fremote),
) )
// Try with a DirMove method that exists but returns fs.ErrorCantDirMove (ex. combine moving across upstreams)
// Should fall back to manual move (copy + delete)
features.DirMove = func(ctx context.Context, src fs.Fs, srcRemote string, dstRemote string) error {
return fs.ErrorCantDirMove
}
assert.NoError(t, operations.DirMove(ctx, r.Fremote, "A3", "A4"))
for i := range files {
files[i].Path = strings.ReplaceAll(files[i].Path, "A3/", "A4/")
}
fstest.CheckListingWithPrecision(
t,
r.Fremote,
files,
[]string{
"A4",
"A4/B1",
"A4/B2",
"A4/B1/C1",
"A4/B1/C2",
"A4/B1/C3",
},
fs.GetModifyWindow(ctx, r.Fremote),
)
} }
func TestGetFsInfo(t *testing.T) { func TestGetFsInfo(t *testing.T) {