lib/model: Refactor file deletions when pulling (#5699)

This commit is contained in:
Simon Frei 2019-05-17 18:29:54 +02:00 committed by GitHub
parent 8015f3f937
commit 441ea109a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -333,17 +333,21 @@ func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyC
// Perform directory deletions at the end, as we may have // Perform directory deletions at the end, as we may have
// files to delete inside them before we get to that point. // files to delete inside them before we get to that point.
dirDeletions = append(dirDeletions, file) dirDeletions = append(dirDeletions, file)
} else if file.IsSymlink() {
f.deleteFile(file, dbUpdateChan, scanChan)
} else { } else {
fileDeletions[file.Name] = file
df, ok := f.fset.Get(protocol.LocalDeviceID, file.Name) df, ok := f.fset.Get(protocol.LocalDeviceID, file.Name)
// Local file can be already deleted, but with a lower version // Local file can be already deleted, but with a lower version
// number, hence the deletion coming in again as part of // number, hence the deletion coming in again as part of
// WithNeed, furthermore, the file can simply be of the wrong // WithNeed, furthermore, the file can simply be of the wrong
// type if we haven't yet managed to pull it. // type if we haven't yet managed to pull it.
if ok && !df.IsDeleted() && !df.IsSymlink() && !df.IsDirectory() && !df.IsInvalid() { if ok && !df.IsDeleted() && !df.IsSymlink() && !df.IsDirectory() && !df.IsInvalid() {
fileDeletions[file.Name] = file
// Put files into buckets per first hash // Put files into buckets per first hash
key := string(df.Blocks[0].Hash) key := string(df.Blocks[0].Hash)
buckets[key] = append(buckets[key], df) buckets[key] = append(buckets[key], df)
} else {
f.deleteFileWithCurrent(file, df, ok, dbUpdateChan, scanChan)
} }
} }
changed++ changed++
@ -354,7 +358,6 @@ func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyC
// We are supposed to copy the entire file, and then fetch nothing. We // We are supposed to copy the entire file, and then fetch nothing. We
// are only updating metadata, so we don't actually *need* to make the // are only updating metadata, so we don't actually *need* to make the
// copy. // copy.
f.resetPullError(file.Name)
f.shortcutFile(file, curFile, dbUpdateChan) f.shortcutFile(file, curFile, dbUpdateChan)
} else { } else {
// Queue files for processing after directories and symlinks. // Queue files for processing after directories and symlinks.
@ -371,7 +374,6 @@ func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyC
case file.IsDirectory() && !file.IsSymlink(): case file.IsDirectory() && !file.IsSymlink():
changed++ changed++
l.Debugln(f, "Handling directory", file.Name) l.Debugln(f, "Handling directory", file.Name)
f.resetPullError(file.Name)
if f.checkParent(file.Name, scanChan) { if f.checkParent(file.Name, scanChan) {
f.handleDir(file, dbUpdateChan, scanChan) f.handleDir(file, dbUpdateChan, scanChan)
} }
@ -379,7 +381,6 @@ func (f *sendReceiveFolder) processNeeded(dbUpdateChan chan<- dbUpdateJob, copyC
case file.IsSymlink(): case file.IsSymlink():
changed++ changed++
l.Debugln(f, "Handling symlink", file.Name) l.Debugln(f, "Handling symlink", file.Name)
f.resetPullError(file.Name)
if f.checkParent(file.Name, scanChan) { if f.checkParent(file.Name, scanChan) {
f.handleSymlink(file, dbUpdateChan, scanChan) f.handleSymlink(file, dbUpdateChan, scanChan)
} }
@ -504,13 +505,8 @@ func (f *sendReceiveFolder) processDeletions(fileDeletions map[string]protocol.F
default: default:
} }
l.Debugln(f, "Deleting file", file.Name)
f.resetPullError(file.Name) f.resetPullError(file.Name)
if update, err := f.deleteFile(file, scanChan); err != nil { f.deleteFile(file, dbUpdateChan, scanChan)
f.newPullError(file.Name, errors.Wrap(err, "delete file"))
} else {
dbUpdateChan <- update
}
} }
// Process in reverse order to delete depth first // Process in reverse order to delete depth first
@ -534,6 +530,8 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, dbUpdateChan chan<
// care not declare another err. // care not declare another err.
var err error var err error
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ events.Default.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
@ -692,6 +690,8 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan c
// care not declare another err. // care not declare another err.
var err error var err error
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ events.Default.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
@ -804,11 +804,20 @@ func (f *sendReceiveFolder) deleteDir(file protocol.FileInfo, dbUpdateChan chan<
} }
// deleteFile attempts to delete the given file // deleteFile attempts to delete the given file
func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- string) (dbUpdateJob, error) { func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) {
cur, hasCur := f.fset.Get(protocol.LocalDeviceID, file.Name)
f.deleteFileWithCurrent(file, cur, hasCur, dbUpdateChan, scanChan)
}
func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, hasCur bool, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) {
// Used in the defer closure below, updated by the function body. Take // Used in the defer closure below, updated by the function body. Take
// care not declare another err. // care not declare another err.
var err error var err error
l.Debugln(f, "Deleting file", file.Name)
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ events.Default.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
@ -817,6 +826,9 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- s
}) })
defer func() { defer func() {
if err != nil {
f.newPullError(file.Name, errors.Wrap(err, "delete file"))
}
events.Default.Log(events.ItemFinished, map[string]interface{}{ events.Default.Log(events.ItemFinished, map[string]interface{}{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,
@ -826,20 +838,21 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- s
}) })
}() }()
cur, ok := f.fset.Get(protocol.LocalDeviceID, file.Name) if !hasCur {
if !ok {
// We should never try to pull a deletion for a file we don't have in the DB. // We should never try to pull a deletion for a file we don't have in the DB.
l.Debugln(f, "not deleting file we don't have", file.Name) l.Debugln(f, "not deleting file we don't have", file.Name)
return dbUpdateJob{file, dbUpdateDeleteFile}, nil dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteFile}
return
} }
if err = f.checkToBeDeleted(cur, scanChan); err != nil { if err = f.checkToBeDeleted(cur, scanChan); err != nil {
return dbUpdateJob{}, err return
} }
// We are asked to delete a file, but what we have on disk and in db // We are asked to delete a file, but what we have on disk and in db
// is a directory. Something is wrong here, should probably not happen. // is a directory. Something is wrong here, should probably not happen.
if cur.IsDirectory() { if cur.IsDirectory() {
return dbUpdateJob{}, errUnexpectedDirOnFileDel err = errUnexpectedDirOnFileDel
return
} }
if f.inConflict(cur.Version, file.Version) { if f.inConflict(cur.Version, file.Version) {
@ -847,7 +860,8 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- s
// always lose. Merge the version vector of the file we have // always lose. Merge the version vector of the file we have
// locally and commit it to db to resolve the conflict. // locally and commit it to db to resolve the conflict.
cur.Version = cur.Version.Merge(file.Version) cur.Version = cur.Version.Merge(file.Version)
return dbUpdateJob{cur, dbUpdateHandleFile}, nil dbUpdateChan <- dbUpdateJob{cur, dbUpdateHandleFile}
return
} }
if f.versioner != nil && !cur.IsSymlink() { if f.versioner != nil && !cur.IsSymlink() {
@ -858,7 +872,8 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- s
if err == nil || fs.IsNotExist(err) { if err == nil || fs.IsNotExist(err) {
// It was removed or it doesn't exist to start with // It was removed or it doesn't exist to start with
return dbUpdateJob{file, dbUpdateDeleteFile}, nil dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteFile}
return
} }
if _, serr := f.fs.Lstat(file.Name); serr != nil && !fs.IsPermission(serr) { if _, serr := f.fs.Lstat(file.Name); serr != nil && !fs.IsPermission(serr) {
@ -867,10 +882,8 @@ func (f *sendReceiveFolder) deleteFile(file protocol.FileInfo, scanChan chan<- s
// does not exist" (possibly expressed as some parent being a file and // does not exist" (possibly expressed as some parent being a file and
// not a directory etc) and that the delete is handled. // not a directory etc) and that the delete is handled.
err = nil err = nil
return dbUpdateJob{file, dbUpdateDeleteFile}, nil dbUpdateChan <- dbUpdateJob{file, dbUpdateDeleteFile}
} }
return dbUpdateJob{}, err
} }
// renameFile attempts to rename an existing file to a destination // renameFile attempts to rename an existing file to a destination
@ -1162,6 +1175,8 @@ func populateOffsets(blocks []protocol.BlockInfo) {
func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob) { func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob) {
l.Debugln(f, "taking shortcut on", file.Name) l.Debugln(f, "taking shortcut on", file.Name)
f.resetPullError(file.Name)
events.Default.Log(events.ItemStarted, map[string]string{ events.Default.Log(events.ItemStarted, map[string]string{
"folder": f.folderID, "folder": f.folderID,
"item": file.Name, "item": file.Name,