With this change we emulate a case sensitive filesystem on top of insensitive filesystems. This means we correctly pick up case-only renames and throw a case conflict error when there would be multiple files differing only in case. This safety check has a small performance hit (about 20% more filesystem operations when scanning for changes). The new advanced folder option `caseSensitiveFS` can be used to disable the safety checks, retaining the previous behavior on systems known to be fully case sensitive. Co-authored-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
committed by
Jakob Borg
co-authored by
Jakob Borg
parent
21dd9d6b43
commit
932d8c69de
@@ -129,9 +129,9 @@ type sendReceiveFolder struct {
|
||||
blockPullReorderer blockPullReorderer
|
||||
writeLimiter *byteSemaphore
|
||||
|
||||
pullErrors map[string]string // errors for most recent/current iteration
|
||||
oldPullErrors map[string]string // errors from previous iterations for log filtering only
|
||||
pullErrorsMut sync.Mutex
|
||||
pullErrors map[string]string // actual exposed pull errors
|
||||
tempPullErrors map[string]string // pull errors that might be just transient
|
||||
pullErrorsMut sync.Mutex
|
||||
}
|
||||
|
||||
func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger, ioLimiter *byteSemaphore) service {
|
||||
@@ -192,6 +192,10 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
|
||||
changed := 0
|
||||
|
||||
f.pullErrorsMut.Lock()
|
||||
f.pullErrors = nil
|
||||
f.pullErrorsMut.Unlock()
|
||||
|
||||
for tries := 0; tries < maxPullerIterations; tries++ {
|
||||
select {
|
||||
case <-f.ctx.Done():
|
||||
@@ -216,8 +220,14 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
}
|
||||
|
||||
f.pullErrorsMut.Lock()
|
||||
f.pullErrors = f.tempPullErrors
|
||||
f.tempPullErrors = nil
|
||||
for path, err := range f.pullErrors {
|
||||
l.Infof("Puller (folder %s, item %q): %v", f.Description(), path, err)
|
||||
}
|
||||
pullErrNum := len(f.pullErrors)
|
||||
f.pullErrorsMut.Unlock()
|
||||
|
||||
if pullErrNum > 0 {
|
||||
l.Infof("%v: Failed to sync %v items", f.Description(), pullErrNum)
|
||||
f.evLogger.Log(events.FolderErrors, map[string]interface{}{
|
||||
@@ -235,8 +245,7 @@ func (f *sendReceiveFolder) pull() bool {
|
||||
// flagged as needed in the folder.
|
||||
func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) int {
|
||||
f.pullErrorsMut.Lock()
|
||||
f.oldPullErrors = f.pullErrors
|
||||
f.pullErrors = make(map[string]string)
|
||||
f.tempPullErrors = make(map[string]string)
|
||||
f.pullErrorsMut.Unlock()
|
||||
|
||||
snap := f.fset.Snapshot()
|
||||
@@ -306,10 +315,6 @@ func (f *sendReceiveFolder) pullerIteration(scanChan chan<- string) int {
|
||||
close(dbUpdateChan)
|
||||
updateWg.Wait()
|
||||
|
||||
f.pullErrorsMut.Lock()
|
||||
f.oldPullErrors = nil
|
||||
f.pullErrorsMut.Unlock()
|
||||
|
||||
f.queue.Reset()
|
||||
|
||||
return changed
|
||||
@@ -739,7 +744,11 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps
|
||||
}
|
||||
|
||||
// There is already something under that name, we need to handle that.
|
||||
if info, err := f.fs.Lstat(file.Name); err == nil {
|
||||
switch info, err := f.fs.Lstat(file.Name); {
|
||||
case err != nil && !fs.IsNotExist(err):
|
||||
f.newPullError(file.Name, errors.Wrap(err, "checking for existing symlink"))
|
||||
return
|
||||
case err == nil:
|
||||
// Check that it is what we have in the database.
|
||||
curFile, hasCurFile := f.model.CurrentFolderFile(f.folderID, file.Name)
|
||||
if err := f.scanIfItemChanged(file.Name, info, curFile, hasCurFile, scanChan); err != nil {
|
||||
@@ -1783,7 +1792,7 @@ func (f *sendReceiveFolder) newPullError(path string, err error) {
|
||||
// We might get more than one error report for a file (i.e. error on
|
||||
// Write() followed by Close()); we keep the first error as that is
|
||||
// probably closer to the root cause.
|
||||
if _, ok := f.pullErrors[path]; ok {
|
||||
if _, ok := f.tempPullErrors[path]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1791,15 +1800,9 @@ func (f *sendReceiveFolder) newPullError(path string, err error) {
|
||||
// Use "syncing" as opposed to "pulling" as the latter might be used
|
||||
// for errors occurring specificly in the puller routine.
|
||||
errStr := fmt.Sprintln("syncing:", err)
|
||||
f.pullErrors[path] = errStr
|
||||
f.tempPullErrors[path] = errStr
|
||||
|
||||
if oldErr, ok := f.oldPullErrors[path]; ok && oldErr == errStr {
|
||||
l.Debugf("Repeat error on puller (folder %s, item %q): %v", f.Description(), path, err)
|
||||
delete(f.oldPullErrors, path) // Potential repeats are now caught by f.pullErrors itself
|
||||
return
|
||||
}
|
||||
|
||||
l.Infof("Puller (folder %s, item %q): %v", f.Description(), path, err)
|
||||
l.Debugf("%v new error for %v: %v", f, path, err)
|
||||
}
|
||||
|
||||
func (f *sendReceiveFolder) Errors() []FileError {
|
||||
|
||||
Reference in New Issue
Block a user