From e3cd9219b87cacb946189c0c317e596fcc0fa37f Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Wed, 9 Sep 2020 11:47:14 +0200 Subject: [PATCH] lib/model: Don't fail over case-conflict on tempfile (fixes #6973) (#6975) --- lib/fs/casefs.go | 4 ++-- lib/model/folder_sendrecv.go | 8 ++++++++ lib/model/folder_sendrecv_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/fs/casefs.go b/lib/fs/casefs.go index 6d09d90cc..bdf5db883 100644 --- a/lib/fs/casefs.go +++ b/lib/fs/casefs.go @@ -25,11 +25,11 @@ const ( ) type ErrCaseConflict struct { - given, real string + Given, Real string } func (e *ErrCaseConflict) Error() string { - return fmt.Sprintf(`given name "%v" differs from name in filesystem "%v"`, e.given, e.real) + return fmt.Sprintf(`given name "%v" differs from name in filesystem "%v"`, e.Given, e.Real) } func IsErrCaseConflict(err error) bool { diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index af1611a21..1f1a7899c 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -1060,6 +1060,14 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot // Check for an old temporary file which might have some blocks we could // reuse. tempBlocks, err := scanner.HashFile(f.ctx, f.fs, tempName, file.BlockSize(), nil, false) + if err != nil { + var caseErr *fs.ErrCaseConflict + if errors.As(err, &caseErr) { + if rerr := f.fs.Rename(caseErr.Real, tempName); rerr == nil { + tempBlocks, err = scanner.HashFile(f.ctx, f.fs, tempName, file.BlockSize(), nil, false) + } + } + } if err == nil { // Check for any reusable blocks in the temp file tempCopyBlocks, _ := blockDiff(tempBlocks, file.Blocks) diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index edff7c37d..ed2e5c77c 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -1216,6 +1216,32 @@ func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) { } } +func TestPullTempFileCaseConflict(t *testing.T) { + m, f := setupSendReceiveFolder() + defer cleanupSRFolder(f, m) + + copyChan := make(chan copyBlocksState, 1) + + file := protocol.FileInfo{Name: "foo"} + confl := "Foo" + tempNameConfl := fs.TempName(confl) + if fd, err := f.fs.Create(tempNameConfl); err != nil { + t.Fatal(err) + } else { + if _, err := fd.Write([]byte("data")); err != nil { + t.Fatal(err) + } + fd.Close() + } + + f.handleFile(file, f.fset.Snapshot(), copyChan) + + cs := <-copyChan + if _, err := cs.tempFile(); err != nil { + t.Error(err) + } +} + func cleanupSharedPullerState(s *sharedPullerState) { s.mut.Lock() defer s.mut.Unlock()