diff --git a/fs/sync/sync.go b/fs/sync/sync.go index 6ec0c074f..075e18188 100644 --- a/fs/sync/sync.go +++ b/fs/sync/sync.go @@ -392,13 +392,26 @@ func (s *syncCopyMove) pairChecker(in *pipe, out *pipe, fraction int, wg *sync.W } } // 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 s.ci.FixCase && !s.ci.Immutable && pair.Dst != nil && src.Remote() != pair.Dst.Remote() { + // NeedTransfer's equality check may have deleted pair.Dst as a precursor + // to re-uploading it (the way modtime updates are done on backends like + // Dropbox that can't set modtime in place). If so, there is nothing to + // rename - nil out pair.Dst so the upload below recreates the file at + // src.Remote() (the correctly-cased name). + if needTransfer { + if _, statErr := s.fdst.NewObject(s.ctx, pair.Dst.Remote()); errors.Is(statErr, fs.ErrorObjectNotFound) { + fs.Debugf(pair.Dst, "Skipping fix-case rename: destination removed for re-upload, will recreate at %s", src.Remote()) + pair.Dst = nil + } + } + if pair.Dst != nil { + 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 {