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

This commit is contained in:
Simon Frei 2020-04-02 16:14:25 +02:00 committed by GitHub
parent 2658369051
commit 32245435e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 4 deletions

View File

@ -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

View File

@ -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{

View File

@ -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
})

View File

@ -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()