Forráskód Böngészése

lib/model: Consider case conflicts when checking to be deleted items (#6986)

Simon Frei 5 éve
szülő
commit
8210466b03
2 módosított fájl, 49 hozzáadás és 24 törlés
  1. 13 12
      lib/model/folder_sendrecv.go
  2. 36 12
      lib/model/folder_sendrecv_test.go

+ 13 - 12
lib/model/folder_sendrecv.go

@@ -813,9 +813,10 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, snap *db.Snapshot,
 
 	cur, hasCur := snap.Get(protocol.LocalDeviceID, file.Name)
 
-	if err = f.checkToBeDeleted(file, cur, hasCur, dbUpdateDeleteDir, dbUpdateChan, scanChan); err != nil {
-		if fs.IsNotExist(err) {
+	if err = f.checkToBeDeleted(file, cur, hasCur, scanChan); err != nil {
+		if fs.IsNotExist(err) || fs.IsErrCaseConflict(err) {
 			err = nil
+			dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteDir}
 		}
 		return
 	}
@@ -860,9 +861,10 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h
 		})
 	}()
 
-	if err = f.checkToBeDeleted(file, cur, hasCur, dbUpdateDeleteFile, dbUpdateChan, scanChan); err != nil {
-		if fs.IsNotExist(err) {
+	if err = f.checkToBeDeleted(file, cur, hasCur, scanChan); err != nil {
+		if fs.IsNotExist(err) || fs.IsErrCaseConflict(err) {
 			err = nil
+			dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteFile}
 		}
 		return
 	}
@@ -945,7 +947,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn
 	l.Debugln(f, "taking rename shortcut", source.Name, "->", target.Name)
 
 	// Check that source is compatible with what we have in the DB
-	if err = f.checkToBeDeleted(source, cur, true, dbUpdateDeleteFile, dbUpdateChan, scanChan); err != nil {
+	if err = f.checkToBeDeleted(source, cur, true, scanChan); err != nil {
 		return err
 	}
 	// Check that the target corresponds to what we have in the DB
@@ -1979,26 +1981,25 @@ func (f *sendReceiveFolder) scanIfItemChanged(name string, stat fs.FileInfo, ite
 
 // checkToBeDeleted makes sure the file on disk is compatible with what there is
 // in the DB before the caller proceeds with actually deleting it.
-// I.e. non-nil error status means "Do not delete!".
-func (f *sendReceiveFolder) checkToBeDeleted(file, cur protocol.FileInfo, hasCur bool, updateType dbUpdateType, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error {
+// I.e. non-nil error status means "Do not delete!" or "is already deleted".
+func (f *sendReceiveFolder) checkToBeDeleted(file, cur protocol.FileInfo, hasCur bool, scanChan chan<- string) error {
 	if err := osutil.TraversesSymlink(f.fs, filepath.Dir(file.Name)); err != nil {
 		l.Debugln(f, "not deleting item behind symlink on disk, but update db", file.Name)
-		dbUpdateChan <- dbUpdateJob{file, updateType}
 		return fs.ErrNotExist
 	}
 
 	stat, err := f.fs.Lstat(file.Name)
-	if !fs.IsNotExist(err) && err != nil {
+	deleted := fs.IsNotExist(err) || fs.IsErrCaseConflict(err)
+	if !deleted && err != nil {
 		return err
 	}
-	if fs.IsNotExist(err) {
+	if deleted {
 		if hasCur && !cur.Deleted && !cur.IsUnsupported() {
 			scanChan <- file.Name
 			return errModified
 		}
 		l.Debugln(f, "not deleting item we don't have, but update db", file.Name)
-		dbUpdateChan <- dbUpdateJob{file, updateType}
-		return fs.ErrNotExist
+		return err
 	}
 
 	return f.scanIfItemChanged(file.Name, stat, cur, hasCur, scanChan)

+ 36 - 12
lib/model/folder_sendrecv_test.go

@@ -781,18 +781,8 @@ func TestDeleteIgnorePerms(t *testing.T) {
 	fi, err := scanner.CreateFileInfo(stat, name, ffs)
 	must(t, err)
 	ffs.Chmod(name, 0600)
-	scanChan := make(chan string)
-	dbUpdateChan := make(chan dbUpdateJob)
-	finished := make(chan struct{})
-	go func() {
-		err = f.checkToBeDeleted(fi, fi, true, dbUpdateDeleteFile, dbUpdateChan, scanChan)
-		close(finished)
-	}()
-	select {
-	case <-scanChan:
-		<-finished
-	case <-finished:
-	}
+	scanChan := make(chan string, 1)
+	err = f.checkToBeDeleted(fi, fi, true, scanChan)
 	must(t, err)
 }
 
@@ -1325,6 +1315,40 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) {
 	}
 }
 
+func TestPullDeleteCaseConflict(t *testing.T) {
+	m, f := setupSendReceiveFolder()
+	defer cleanupSRFolder(f, m)
+
+	name := "foo"
+	fi := protocol.FileInfo{Name: "Foo"}
+	dbUpdateChan := make(chan dbUpdateJob, 1)
+	scanChan := make(chan string)
+
+	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()
+	}
+	f.deleteFileWithCurrent(fi, protocol.FileInfo{}, false, dbUpdateChan, scanChan)
+	select {
+	case <-dbUpdateChan:
+	default:
+		t.Error("Missing db update for file")
+	}
+
+	snap := f.fset.Snapshot()
+	defer snap.Release()
+	f.deleteDir(fi, snap, dbUpdateChan, scanChan)
+	select {
+	case <-dbUpdateChan:
+	default:
+		t.Error("Missing db update for dir")
+	}
+}
+
 func cleanupSharedPullerState(s *sharedPullerState) {
 	s.mut.Lock()
 	defer s.mut.Unlock()