Parcourir la source

lib/model: Fix case-only renames on pull (#6978)

Simon Frei il y a 5 ans
Parent
commit
56d48d341f
2 fichiers modifiés avec 58 ajouts et 9 suppressions
  1. 19 9
      lib/model/folder_sendrecv.go
  2. 39 0
      lib/model/folder_sendrecv_test.go

+ 19 - 9
lib/model/folder_sendrecv.go

@@ -945,16 +945,26 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn
 	// Check that the target corresponds to what we have in the DB
 	// Check that the target corresponds to what we have in the DB
 	curTarget, ok := snap.Get(protocol.LocalDeviceID, target.Name)
 	curTarget, ok := snap.Get(protocol.LocalDeviceID, target.Name)
 	switch stat, serr := f.fs.Lstat(target.Name); {
 	switch stat, serr := f.fs.Lstat(target.Name); {
-	case serr != nil && fs.IsNotExist(serr):
-		if !ok || curTarget.IsDeleted() {
-			break
-		}
-		scanChan <- target.Name
-		err = errModified
 	case serr != nil:
 	case serr != nil:
-		// We can't check whether the file changed as compared to the db,
-		// do not delete.
-		err = serr
+		var caseErr *fs.ErrCaseConflict
+		switch {
+		case errors.As(serr, &caseErr):
+			if caseErr.Real != source.Name {
+				err = serr
+				break
+			}
+			fallthrough // This is a case only rename
+		case fs.IsNotExist(serr):
+			if !ok || curTarget.IsDeleted() {
+				break
+			}
+			scanChan <- target.Name
+			err = errModified
+		default:
+			// We can't check whether the file changed as compared to the db,
+			// do not delete.
+			err = serr
+		}
 	case !ok:
 	case !ok:
 		// Target appeared from nowhere
 		// Target appeared from nowhere
 		scanChan <- target.Name
 		scanChan <- target.Name

+ 39 - 0
lib/model/folder_sendrecv_test.go

@@ -1242,6 +1242,45 @@ func TestPullTempFileCaseConflict(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestPullCaseOnlyRename(t *testing.T) {
+	m, f := setupSendReceiveFolder()
+	defer cleanupSRFolder(f, m)
+
+	// tempNameConfl := fs.TempName(confl)
+
+	name := "foo"
+	if fd, err := f.fs.Create(name); err != nil {
+		t.Fatal(err)
+	} else {
+		if _, err := fd.Write([]byte("data")); err != nil {
+			t.Fatal(err)
+		}
+		fd.Close()
+	}
+
+	must(t, f.scanSubdirs(nil))
+
+	cur, ok := m.CurrentFolderFile(f.ID, name)
+	if !ok {
+		t.Fatal("file missing")
+	}
+
+	deleted := cur
+	deleted.SetDeleted(myID.Short())
+
+	confl := cur
+	confl.Name = "Foo"
+	confl.Version = confl.Version.Update(device1.Short())
+
+	dbUpdateChan := make(chan dbUpdateJob, 2)
+	scanChan := make(chan string, 2)
+	snap := f.fset.Snapshot()
+	defer snap.Release()
+	if err := f.renameFile(cur, deleted, confl, snap, dbUpdateChan, scanChan); err != nil {
+		t.Error(err)
+	}
+}
+
 func cleanupSharedPullerState(s *sharedPullerState) {
 func cleanupSharedPullerState(s *sharedPullerState) {
 	s.mut.Lock()
 	s.mut.Lock()
 	defer s.mut.Unlock()
 	defer s.mut.Unlock()