sync: fix --fix-case rename failing on backends that can't update modtime

When --fix-case was used (e.g. by bisync) on backends that can't set
modification times in place - such as Dropbox - files whose content
matched but whose modtimes differed would fail to rename with a
"from_lookup/not_found" error and abort the operation.

This happened because operations.NeedTransfer was called before the
fix-case rename. NeedTransfer's equality check would delete the
destination as a precursor to re-uploading it (the standard way to
update a modtime on these backends), so by the time the rename ran the
file no longer existed on the remote.

Fix by running the fix-case rename first, so that any subsequent
delete/re-upload happens at the correctly-cased destination path.

See: #8881
This commit is contained in:
Nick Craig-Wood
2026-05-06 17:01:07 +01:00
parent 40642fee01
commit de67f29b3f

View File

@@ -380,6 +380,20 @@ func (s *syncCopyMove) pairChecker(in *pipe, out *pipe, fraction int, wg *sync.W
tr := accounting.Stats(s.ctx).NewCheckingTransfer(src, "checking")
// Check to see if can store this
if src.Storable() {
// Fix case for case insensitive filesystems before checking
// whether a transfer is needed, since NeedTransfer may delete
// the destination (when content matches but modtime can't be
// set without re-upload), which would cause the rename below
// to fail with a "not found" error.
if s.ci.FixCase && !s.ci.Immutable && src.Remote() != pair.Dst.Remote() {
if newDst, err := operations.Move(s.ctx, s.fdst, nil, src.Remote(), pair.Dst); err != nil {
fs.Errorf(pair.Dst, "Error while attempting to rename to %s: %v", src.Remote(), err)
s.processError(err)
} else {
fs.Infof(pair.Dst, "Fixed case by renaming to: %s", src.Remote())
pair.Dst = newDst
}
}
needTransfer := operations.NeedTransfer(s.ctx, pair.Dst, pair.Src)
if needTransfer {
NoNeedTransfer, err := operations.CompareOrCopyDest(s.ctx, s.fdst, pair.Dst, pair.Src, s.compareCopyDest, s.backupDir)
@@ -391,16 +405,6 @@ func (s *syncCopyMove) pairChecker(in *pipe, out *pipe, fraction int, wg *sync.W
needTransfer = false
}
}
// Fix case for case insensitive filesystems
if s.ci.FixCase && !s.ci.Immutable && src.Remote() != pair.Dst.Remote() {
if newDst, err := operations.Move(s.ctx, s.fdst, nil, src.Remote(), pair.Dst); err != nil {
fs.Errorf(pair.Dst, "Error while attempting to rename to %s: %v", src.Remote(), err)
s.processError(err)
} else {
fs.Infof(pair.Dst, "Fixed case by renaming to: %s", src.Remote())
pair.Dst = newDst
}
}
if needTransfer {
// If files are treated as immutable, fail if destination exists and does not match
if s.ci.Immutable && pair.Dst != nil {