Skip to content

Commit aba2cc4

Browse files
calmhAudriusButkevicius
authored andcommitted
lib/model: Properly handle deleting multiple files when doing scans with subs (fixes syncthing#2851)
1 parent 2df001f commit aba2cc4

File tree

3 files changed

+63
-66
lines changed

3 files changed

+63
-66
lines changed

lib/db/leveldb_dbinstance.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,11 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, l
259259
return maxLocalVer
260260
}
261261

262-
func (db *Instance) withHave(folder, device []byte, truncate bool, fn Iterator) {
262+
func (db *Instance) withHave(folder, device, prefix []byte, truncate bool, fn Iterator) {
263263
t := db.newReadOnlyTransaction()
264264
defer t.close()
265265

266-
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, nil)[:keyPrefixLen+keyFolderLen+keyDeviceLen]), nil)
266+
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, prefix)[:keyPrefixLen+keyFolderLen+keyDeviceLen+len(prefix)]), nil)
267267
defer dbi.Release()
268268

269269
for dbi.Next() {

lib/db/set.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,18 @@ func (s *FileSet) WithNeedTruncated(device protocol.DeviceID, fn Iterator) {
171171

172172
func (s *FileSet) WithHave(device protocol.DeviceID, fn Iterator) {
173173
l.Debugf("%s WithHave(%v)", s.folder, device)
174-
s.db.withHave([]byte(s.folder), device[:], false, nativeFileIterator(fn))
174+
s.db.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn))
175175
}
176176

177177
func (s *FileSet) WithHaveTruncated(device protocol.DeviceID, fn Iterator) {
178178
l.Debugf("%s WithHaveTruncated(%v)", s.folder, device)
179-
s.db.withHave([]byte(s.folder), device[:], true, nativeFileIterator(fn))
179+
s.db.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn))
180180
}
181181

182+
func (s *FileSet) WithPrefixedHaveTruncated(device protocol.DeviceID, prefix string, fn Iterator) {
183+
l.Debugf("%s WithPrefixedHaveTruncated(%v)", s.folder, device)
184+
s.db.withHave([]byte(s.folder), device[:], []byte(prefix), true, nativeFileIterator(fn))
185+
}
182186
func (s *FileSet) WithGlobal(fn Iterator) {
183187
l.Debugf("%s WithGlobal()", s.folder)
184188
s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn))

lib/model/model.go

Lines changed: 55 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,76 +1392,69 @@ func (m *Model) internalScanFolderSubs(folder string, subs []string) error {
13921392
m.updateLocals(folder, batch)
13931393
}
13941394

1395-
batch = batch[:0]
1396-
// TODO: We should limit the Have scanning to start at sub
1397-
seenPrefix := false
1398-
var iterError error
1399-
fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
1400-
f := fi.(db.FileInfoTruncated)
1401-
hasPrefix := len(subs) == 0
1402-
for _, sub := range subs {
1403-
if strings.HasPrefix(f.Name, sub) {
1404-
hasPrefix = true
1405-
break
1406-
}
1407-
}
1408-
// Return true so that we keep iterating, until we get to the part
1409-
// of the tree we are interested in. Then return false so we stop
1410-
// iterating when we've passed the end of the subtree.
1411-
if !hasPrefix {
1412-
return !seenPrefix
1413-
}
1395+
if len(subs) == 0 {
1396+
// If we have no specific subdirectories to traverse, set it to one
1397+
// empty prefix so we traverse the entire folder contents once.
1398+
subs = []string{""}
1399+
}
14141400

1415-
seenPrefix = true
1416-
if !f.IsDeleted() {
1417-
if f.IsInvalid() {
1418-
return true
1419-
}
1401+
// Do a scan of the database for each prefix, to check for deleted files.
1402+
batch = batch[:0]
1403+
for _, sub := range subs {
1404+
var iterError error
14201405

1421-
if len(batch) == batchSizeFiles {
1422-
if err := m.CheckFolderHealth(folder); err != nil {
1423-
iterError = err
1424-
return false
1406+
fs.WithPrefixedHaveTruncated(protocol.LocalDeviceID, sub, func(fi db.FileIntf) bool {
1407+
f := fi.(db.FileInfoTruncated)
1408+
if !f.IsDeleted() {
1409+
if f.IsInvalid() {
1410+
return true
14251411
}
1426-
m.updateLocals(folder, batch)
1427-
batch = batch[:0]
1428-
}
14291412

1430-
if ignores.Match(f.Name) || symlinkInvalid(folder, f) {
1431-
// File has been ignored or an unsupported symlink. Set invalid bit.
1432-
l.Debugln("setting invalid bit on ignored", f)
1433-
nf := protocol.FileInfo{
1434-
Name: f.Name,
1435-
Flags: f.Flags | protocol.FlagInvalid,
1436-
Modified: f.Modified,
1437-
Version: f.Version, // The file is still the same, so don't bump version
1413+
if len(batch) == batchSizeFiles {
1414+
if err := m.CheckFolderHealth(folder); err != nil {
1415+
iterError = err
1416+
return false
1417+
}
1418+
m.updateLocals(folder, batch)
1419+
batch = batch[:0]
14381420
}
1439-
batch = append(batch, nf)
1440-
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
1441-
// File has been deleted.
1442-
1443-
// We don't specifically verify that the error is
1444-
// os.IsNotExist because there is a corner case when a
1445-
// directory is suddenly transformed into a file. When that
1446-
// happens, files that were in the directory (that is now a
1447-
// file) are deleted but will return a confusing error ("not a
1448-
// directory") when we try to Lstat() them.
1449-
1450-
nf := protocol.FileInfo{
1451-
Name: f.Name,
1452-
Flags: f.Flags | protocol.FlagDeleted,
1453-
Modified: f.Modified,
1454-
Version: f.Version.Update(m.shortID),
1421+
1422+
if ignores.Match(f.Name) || symlinkInvalid(folder, f) {
1423+
// File has been ignored or an unsupported symlink. Set invalid bit.
1424+
l.Debugln("setting invalid bit on ignored", f)
1425+
nf := protocol.FileInfo{
1426+
Name: f.Name,
1427+
Flags: f.Flags | protocol.FlagInvalid,
1428+
Modified: f.Modified,
1429+
Version: f.Version, // The file is still the same, so don't bump version
1430+
}
1431+
batch = append(batch, nf)
1432+
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
1433+
// File has been deleted.
1434+
1435+
// We don't specifically verify that the error is
1436+
// os.IsNotExist because there is a corner case when a
1437+
// directory is suddenly transformed into a file. When that
1438+
// happens, files that were in the directory (that is now a
1439+
// file) are deleted but will return a confusing error ("not a
1440+
// directory") when we try to Lstat() them.
1441+
1442+
nf := protocol.FileInfo{
1443+
Name: f.Name,
1444+
Flags: f.Flags | protocol.FlagDeleted,
1445+
Modified: f.Modified,
1446+
Version: f.Version.Update(m.shortID),
1447+
}
1448+
batch = append(batch, nf)
14551449
}
1456-
batch = append(batch, nf)
14571450
}
1458-
}
1459-
return true
1460-
})
1451+
return true
1452+
})
14611453

1462-
if iterError != nil {
1463-
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
1464-
return iterError
1454+
if iterError != nil {
1455+
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
1456+
return iterError
1457+
}
14651458
}
14661459

14671460
if err := m.CheckFolderHealth(folder); err != nil {

0 commit comments

Comments
 (0)