|
@@ -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.newPullError(file.Name, errors.Wrap(err, "delete file"))
|
|
|
|
- } else {
|
|
|
|
- dbUpdateChan <- update
|
|
|
|
- }
|
|
|
|
|
|
+ f.deleteFile(file, dbUpdateChan, scanChan)
|
|
}
|
|
}
|
|
|
|
|
|
// 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 !ok {
|
|
|
|
|
|
+ if !hasCur {
|
|
// 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,
|