lib/model: Fix checking children when trying to delete a dir (fixes #6646) (#6647)

This commit is contained in:
Simon Frei 2020-05-25 08:46:24 +02:00 committed by GitHub
parent cf75329067
commit c3b5eba205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 34 deletions

View File

@ -61,17 +61,18 @@ type copyBlocksState struct {
const retainBits = fs.ModeSetgid | fs.ModeSetuid | fs.ModeSticky
var (
activity = newDeviceActivity()
errNoDevice = errors.New("peers who had this file went away, or the file has changed while syncing. will retry later")
errDirPrefix = "directory has been deleted on a remote device but "
errDirHasToBeScanned = errors.New(errDirPrefix + "contains unexpected files, scheduling scan")
errDirHasIgnored = errors.New(errDirPrefix + "contains ignored files (see ignore documentation for (?d) prefix)")
errDirNotEmpty = errors.New(errDirPrefix + "is not empty; the contents are probably ignored on that remote device, but not locally")
errNotAvailable = errors.New("no connected device has the required version of this file")
errModified = errors.New("file modified but not rescanned; will try again later")
errUnexpectedDirOnFileDel = errors.New("encountered directory when trying to remove file/symlink")
errIncompatibleSymlink = errors.New("incompatible symlink entry; rescan with newer Syncthing on source")
contextRemovingOldItem = "removing item to be replaced"
activity = newDeviceActivity()
errNoDevice = errors.New("peers who had this file went away, or the file has changed while syncing. will retry later")
errDirPrefix = "directory has been deleted on a remote device but "
errDirHasToBeScanned = errors.New(errDirPrefix + "contains changed files, scheduling scan")
errDirHasIgnored = errors.New(errDirPrefix + "contains ignored files (see ignore documentation for (?d) prefix)")
errDirHasReceiveOnlyChanged = errors.New(errDirPrefix + "contains locally changed files")
errDirNotEmpty = errors.New(errDirPrefix + "is not empty; the contents are probably ignored on that remote device, but not locally")
errNotAvailable = errors.New("no connected device has the required version of this file")
errModified = errors.New("file modified but not rescanned; will try again later")
errUnexpectedDirOnFileDel = errors.New("encountered directory when trying to remove file/symlink")
errIncompatibleSymlink = errors.New("incompatible symlink entry; rescan with newer Syncthing on source")
contextRemovingOldItem = "removing item to be replaced"
)
const (
@ -1819,31 +1820,52 @@ func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanC
toBeDeleted := make([]string, 0, len(files))
hasIgnored := false
hasKnown := false
hasToBeScanned := false
var hasIgnored, hasKnown, hasToBeScanned, hasReceiveOnlyChanged bool
for _, dirFile := range files {
fullDirFile := filepath.Join(dir, dirFile)
if fs.IsTemporary(dirFile) || f.ignores.Match(fullDirFile).IsDeletable() {
switch {
case fs.IsTemporary(dirFile) || f.ignores.Match(fullDirFile).IsDeletable():
toBeDeleted = append(toBeDeleted, fullDirFile)
} else if f.ignores != nil && f.ignores.Match(fullDirFile).IsIgnored() {
continue
case f.ignores != nil && f.ignores.Match(fullDirFile).IsIgnored():
hasIgnored = true
} else if cf, ok := snap.Get(protocol.LocalDeviceID, fullDirFile); !ok || cf.IsDeleted() || cf.IsInvalid() {
// Something appeared in the dir that we either are not aware of
// at all, that we think should be deleted or that is invalid,
// but not currently ignored -> schedule scan. The scanChan
// might be nil, in which case we trust the scanning to be
// handled later as a result of our error return.
if scanChan != nil {
scanChan <- fullDirFile
}
hasToBeScanned = true
} else {
// Dir contains file that is valid according to db and
// not ignored -> something weird is going on
hasKnown = true
continue
}
cf, ok := snap.Get(protocol.LocalDeviceID, fullDirFile)
switch {
case !ok || cf.IsDeleted():
// Something appeared in the dir that we either are not
// aware of at all or that we think should be deleted
// -> schedule scan.
scanChan <- fullDirFile
hasToBeScanned = true
continue
case ok && f.Type == config.FolderTypeReceiveOnly && cf.IsReceiveOnlyChanged():
hasReceiveOnlyChanged = true
continue
}
info, err := f.fs.Lstat(fullDirFile)
var diskFile protocol.FileInfo
if err == nil {
diskFile, err = scanner.CreateFileInfo(info, fullDirFile, f.fs)
}
if err != nil {
// Lets just assume the file has changed.
scanChan <- fullDirFile
hasToBeScanned = true
continue
}
if !cf.IsEquivalentOptional(diskFile, f.ModTimeWindow(), f.IgnorePerms, true, protocol.LocalAllFlags) {
// File on disk changed compared to what we have in db
// -> schedule scan.
scanChan <- fullDirFile
hasToBeScanned = true
continue
}
// Dir contains file that is valid according to db and
// not ignored -> something weird is going on
hasKnown = true
}
if hasToBeScanned {
@ -1852,6 +1874,9 @@ func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanC
if hasIgnored {
return errDirHasIgnored
}
if hasReceiveOnlyChanged {
return errDirHasReceiveOnlyChanged
}
if hasKnown {
return errDirNotEmpty
}

View File

@ -818,9 +818,14 @@ func TestCopyOwner(t *testing.T) {
// owner/group.
dbUpdateChan := make(chan dbUpdateJob, 1)
scanChan := make(chan string)
defer close(dbUpdateChan)
f.handleDir(dir, f.fset.Snapshot(), dbUpdateChan, nil)
<-dbUpdateChan // empty the channel for later
f.handleDir(dir, f.fset.Snapshot(), dbUpdateChan, scanChan)
select {
case <-dbUpdateChan: // empty the channel for later
case toScan := <-scanChan:
t.Fatal("Unexpected receive on scanChan:", toScan)
}
info, err := f.fs.Lstat("foo/bar")
if err != nil {
@ -874,8 +879,12 @@ func TestCopyOwner(t *testing.T) {
SymlinkTarget: "over the rainbow",
}
f.handleSymlink(symlink, snap, dbUpdateChan, nil)
<-dbUpdateChan
f.handleSymlink(symlink, snap, dbUpdateChan, scanChan)
select {
case <-dbUpdateChan:
case toScan := <-scanChan:
t.Fatal("Unexpected receive on scanChan:", toScan)
}
info, err = f.fs.Lstat("foo/bar/sym")
if err != nil {