Răsfoiți Sursa

lib/model: Handle deleted items on RO for remote removes (fixes #6432) (#6464)

Simon Frei 5 ani în urmă
părinte
comite
32245435e2
4 a modificat fișierele cu 89 adăugiri și 4 ștergeri
  1. 1 3
      lib/db/set.go
  2. 8 0
      lib/db/structs.go
  3. 14 0
      lib/model/folder.go
  4. 66 1
      lib/model/folder_recvonly_test.go

+ 1 - 3
lib/db/set.go

@@ -333,9 +333,7 @@ func (s *Snapshot) NeedSize() Counts {
 	return result
 }
 
-// LocalChangedFiles returns a paginated list of currently needed files in
-// progress, queued, and to be queued on next puller iteration, as well as the
-// total number of files currently needed.
+// LocalChangedFiles returns a paginated list of files that were changed locally.
 func (s *Snapshot) LocalChangedFiles(page, perpage int) []FileInfoTruncated {
 	if s.ReceiveOnlyChangedSize().TotalItems() == 0 {
 		return nil

+ 8 - 0
lib/db/structs.go

@@ -140,6 +140,14 @@ func (f FileInfoTruncated) ConvertToDeletedFileInfo(by protocol.ShortID) protoco
 	return file
 }
 
+// ConvertDeletedToFileInfo converts a deleted truncated file info to a regular file info
+func (f FileInfoTruncated) ConvertDeletedToFileInfo() protocol.FileInfo {
+	if !f.Deleted {
+		panic("ConvertDeletedToFileInfo must only be called on deleted items")
+	}
+	return f.copyToFileInfo()
+}
+
 // copyToFileInfo just copies all members of FileInfoTruncated to protocol.FileInfo
 func (f FileInfoTruncated) copyToFileInfo() protocol.FileInfo {
 	return protocol.FileInfo{

+ 14 - 0
lib/model/folder.go

@@ -556,6 +556,20 @@ func (f *folder) scanSubdirs(subDirs []string) error {
 				batch.append(nf)
 				changes++
 			}
+
+			// Check for deleted, locally changed items that noone else has.
+			if f.localFlags&protocol.FlagLocalReceiveOnly == 0 {
+				return true
+			}
+			if !fi.IsDeleted() || !fi.IsReceiveOnlyChanged() || len(snap.Availability(fi.FileName())) > 0 {
+				return true
+			}
+			nf := fi.(db.FileInfoTruncated).ConvertDeletedToFileInfo()
+			nf.LocalFlags = 0
+			nf.Version = protocol.Vector{}
+			batch.append(nf)
+			changes++
+
 			return true
 		})
 

+ 66 - 1
lib/model/folder_recvonly_test.go

@@ -263,6 +263,69 @@ func TestRecvOnlyUndoChanges(t *testing.T) {
 	}
 }
 
+func TestRecvOnlyDeletedRemoteDrop(t *testing.T) {
+	// Get us a model up and running
+
+	m, f := setupROFolder(t)
+	ffs := f.Filesystem()
+	defer cleanupModel(m)
+
+	// Create some test data
+
+	must(t, ffs.MkdirAll(".stfolder", 0755))
+	oldData := []byte("hello\n")
+	knownFiles := setupKnownFiles(t, ffs, oldData)
+
+	// Send an index update for the known stuff
+
+	m.Index(device1, "ro", knownFiles)
+	f.updateLocalsFromScanning(knownFiles)
+
+	// Scan the folder.
+
+	must(t, m.ScanFolder("ro"))
+
+	// Everything should be in sync.
+
+	size := globalSize(t, m, "ro")
+	if size.Files != 1 || size.Directories != 1 {
+		t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
+	}
+	size = localSize(t, m, "ro")
+	if size.Files != 1 || size.Directories != 1 {
+		t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
+	}
+	size = needSize(t, m, "ro")
+	if size.Files+size.Directories > 0 {
+		t.Fatalf("Need: expected nothing: %+v", size)
+	}
+	size = receiveOnlyChangedSize(t, m, "ro")
+	if size.Files+size.Directories > 0 {
+		t.Fatalf("ROChanged: expected nothing: %+v", size)
+	}
+
+	// Delete our file
+
+	must(t, ffs.Remove(knownFiles[1].Name))
+
+	must(t, m.ScanFolder("ro"))
+
+	size = receiveOnlyChangedSize(t, m, "ro")
+	if size.Deleted != 1 {
+		t.Fatalf("Receive only: expected 1 deleted: %+v", size)
+	}
+
+	// Drop the remote
+
+	f.fset.Drop(device1)
+	must(t, m.ScanFolder("ro"))
+
+	size = receiveOnlyChangedSize(t, m, "ro")
+	if size.Deleted != 0 {
+		t.Fatalf("Receive only: expected no deleted: %+v", size)
+	}
+}
+
 func setupKnownFiles(t *testing.T, ffs fs.Filesystem, data []byte) []protocol.FileInfo {
 	t.Helper()
 
@@ -305,11 +368,13 @@ func setupROFolder(t *testing.T) (*model, *receiveOnlyFolder) {
 	t.Helper()
 
 	w := createTmpWrapper(defaultCfg)
+	cfg := w.RawCopy()
 	fcfg := testFolderConfigFake()
 	fcfg.ID = "ro"
 	fcfg.Label = "ro"
 	fcfg.Type = config.FolderTypeReceiveOnly
-	w.SetFolder(fcfg)
+	cfg.Folders = []config.FolderConfiguration{fcfg}
+	w.Replace(cfg)
 
 	m := newModel(w, myID, "syncthing", "dev", db.NewLowlevel(backend.OpenMemory()), nil)
 	m.ServeBackground()