lib/model: Properly handle deleting multiple files when doing scans with subs (fixes #2851)

This commit is contained in:
Jakob Borg 2016-03-18 12:16:33 +00:00 committed by Audrius Butkevicius
parent 2df001fe5c
commit aba2cc4db2
3 changed files with 61 additions and 64 deletions

View File

@ -259,11 +259,11 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, l
return maxLocalVer return maxLocalVer
} }
func (db *Instance) withHave(folder, device []byte, truncate bool, fn Iterator) { func (db *Instance) withHave(folder, device, prefix []byte, truncate bool, fn Iterator) {
t := db.newReadOnlyTransaction() t := db.newReadOnlyTransaction()
defer t.close() defer t.close()
dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, nil)[:keyPrefixLen+keyFolderLen+keyDeviceLen]), nil) dbi := t.NewIterator(util.BytesPrefix(db.deviceKey(folder, device, prefix)[:keyPrefixLen+keyFolderLen+keyDeviceLen+len(prefix)]), nil)
defer dbi.Release() defer dbi.Release()
for dbi.Next() { for dbi.Next() {

View File

@ -171,14 +171,18 @@ func (s *FileSet) WithNeedTruncated(device protocol.DeviceID, fn Iterator) {
func (s *FileSet) WithHave(device protocol.DeviceID, fn Iterator) { func (s *FileSet) WithHave(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHave(%v)", s.folder, device) l.Debugf("%s WithHave(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], false, nativeFileIterator(fn)) s.db.withHave([]byte(s.folder), device[:], nil, false, nativeFileIterator(fn))
} }
func (s *FileSet) WithHaveTruncated(device protocol.DeviceID, fn Iterator) { func (s *FileSet) WithHaveTruncated(device protocol.DeviceID, fn Iterator) {
l.Debugf("%s WithHaveTruncated(%v)", s.folder, device) l.Debugf("%s WithHaveTruncated(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], true, nativeFileIterator(fn)) s.db.withHave([]byte(s.folder), device[:], nil, true, nativeFileIterator(fn))
} }
func (s *FileSet) WithPrefixedHaveTruncated(device protocol.DeviceID, prefix string, fn Iterator) {
l.Debugf("%s WithPrefixedHaveTruncated(%v)", s.folder, device)
s.db.withHave([]byte(s.folder), device[:], []byte(prefix), true, nativeFileIterator(fn))
}
func (s *FileSet) WithGlobal(fn Iterator) { func (s *FileSet) WithGlobal(fn Iterator) {
l.Debugf("%s WithGlobal()", s.folder) l.Debugf("%s WithGlobal()", s.folder)
s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn)) s.db.withGlobal([]byte(s.folder), nil, false, nativeFileIterator(fn))

View File

@ -1392,76 +1392,69 @@ func (m *Model) internalScanFolderSubs(folder string, subs []string) error {
m.updateLocals(folder, batch) m.updateLocals(folder, batch)
} }
if len(subs) == 0 {
// If we have no specific subdirectories to traverse, set it to one
// empty prefix so we traverse the entire folder contents once.
subs = []string{""}
}
// Do a scan of the database for each prefix, to check for deleted files.
batch = batch[:0] batch = batch[:0]
// TODO: We should limit the Have scanning to start at sub for _, sub := range subs {
seenPrefix := false var iterError error
var iterError error
fs.WithHaveTruncated(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
f := fi.(db.FileInfoTruncated)
hasPrefix := len(subs) == 0
for _, sub := range subs {
if strings.HasPrefix(f.Name, sub) {
hasPrefix = true
break
}
}
// Return true so that we keep iterating, until we get to the part
// of the tree we are interested in. Then return false so we stop
// iterating when we've passed the end of the subtree.
if !hasPrefix {
return !seenPrefix
}
seenPrefix = true fs.WithPrefixedHaveTruncated(protocol.LocalDeviceID, sub, func(fi db.FileIntf) bool {
if !f.IsDeleted() { f := fi.(db.FileInfoTruncated)
if f.IsInvalid() { if !f.IsDeleted() {
return true if f.IsInvalid() {
} return true
if len(batch) == batchSizeFiles {
if err := m.CheckFolderHealth(folder); err != nil {
iterError = err
return false
} }
m.updateLocals(folder, batch)
batch = batch[:0]
}
if ignores.Match(f.Name) || symlinkInvalid(folder, f) { if len(batch) == batchSizeFiles {
// File has been ignored or an unsupported symlink. Set invalid bit. if err := m.CheckFolderHealth(folder); err != nil {
l.Debugln("setting invalid bit on ignored", f) iterError = err
nf := protocol.FileInfo{ return false
Name: f.Name, }
Flags: f.Flags | protocol.FlagInvalid, m.updateLocals(folder, batch)
Modified: f.Modified, batch = batch[:0]
Version: f.Version, // The file is still the same, so don't bump version
} }
batch = append(batch, nf)
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
// File has been deleted.
// We don't specifically verify that the error is if ignores.Match(f.Name) || symlinkInvalid(folder, f) {
// os.IsNotExist because there is a corner case when a // File has been ignored or an unsupported symlink. Set invalid bit.
// directory is suddenly transformed into a file. When that l.Debugln("setting invalid bit on ignored", f)
// happens, files that were in the directory (that is now a nf := protocol.FileInfo{
// file) are deleted but will return a confusing error ("not a Name: f.Name,
// directory") when we try to Lstat() them. Flags: f.Flags | protocol.FlagInvalid,
Modified: f.Modified,
Version: f.Version, // The file is still the same, so don't bump version
}
batch = append(batch, nf)
} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
// File has been deleted.
nf := protocol.FileInfo{ // We don't specifically verify that the error is
Name: f.Name, // os.IsNotExist because there is a corner case when a
Flags: f.Flags | protocol.FlagDeleted, // directory is suddenly transformed into a file. When that
Modified: f.Modified, // happens, files that were in the directory (that is now a
Version: f.Version.Update(m.shortID), // file) are deleted but will return a confusing error ("not a
// directory") when we try to Lstat() them.
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagDeleted,
Modified: f.Modified,
Version: f.Version.Update(m.shortID),
}
batch = append(batch, nf)
} }
batch = append(batch, nf)
} }
return true
})
if iterError != nil {
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
return iterError
} }
return true
})
if iterError != nil {
l.Infof("Stopping folder %s mid-scan due to folder error: %s", folder, iterError)
return iterError
} }
if err := m.CheckFolderHealth(folder); err != nil { if err := m.CheckFolderHealth(folder); err != nil {