diff --git a/internal/config/config.go b/internal/config/config.go index 8d90d9200..e847b5faf 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -57,6 +57,7 @@ type FolderConfiguration struct { RescanIntervalS int `xml:"rescanIntervalS,attr" default:"60"` IgnorePerms bool `xml:"ignorePerms,attr"` Versioning VersioningConfiguration `xml:"versioning"` + LenientMtimes bool `xml:"lenientMtimes"` Invalid string `xml:"-"` // Set at runtime when there is an error, not saved diff --git a/internal/model/model.go b/internal/model/model.go index 4e0323396..682f89170 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -167,11 +167,12 @@ func (m *Model) StartFolderRW(folder string) { panic("cannot start already running folder " + folder) } p := &Puller{ - folder: folder, - dir: cfg.Path, - scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second, - model: m, - ignorePerms: cfg.IgnorePerms, + folder: folder, + dir: cfg.Path, + scanIntv: time.Duration(cfg.RescanIntervalS) * time.Second, + model: m, + ignorePerms: cfg.IgnorePerms, + lenientMtimes: cfg.LenientMtimes, } m.folderRunners[folder] = p m.fmut.Unlock() @@ -184,6 +185,10 @@ func (m *Model) StartFolderRW(folder string) { p.versioner = factory(folder, cfg.Path, cfg.Versioning.Params) } + if cfg.LenientMtimes { + l.Infof("Folder %q is running with LenientMtimes workaround. Syncing may not work properly.", folder) + } + go p.Serve() } diff --git a/internal/model/puller.go b/internal/model/puller.go index dbf9f5b09..c8b53ea4a 100644 --- a/internal/model/puller.go +++ b/internal/model/puller.go @@ -62,13 +62,14 @@ var ( ) type Puller struct { - folder string - dir string - scanIntv time.Duration - model *Model - stop chan struct{} - versioner versioner.Versioner - ignorePerms bool + folder string + dir string + scanIntv time.Duration + model *Model + stop chan struct{} + versioner versioner.Versioner + ignorePerms bool + lenientMtimes bool } // Serve will run scans and pulls. It will return when Stop()ed or on a @@ -528,8 +529,16 @@ func (p *Puller) shortcutFile(file protocol.FileInfo) { t := time.Unix(file.Modified, 0) err := os.Chtimes(realName, t, t) if err != nil { - l.Infof("Puller (folder %q, file %q): shortcut: %v", p.folder, file.Name, err) - return + if p.lenientMtimes { + // We accept the failure with a warning here and allow the sync to + // continue. We'll sync the new mtime back to the other devices later. + // If they have the same problem & setting, we might never get in + // sync. + l.Infof("Puller (folder %q, file %q): shortcut: %v (continuing anyway as requested)", p.folder, file.Name, err) + } else { + l.Infof("Puller (folder %q, file %q): shortcut: %v", p.folder, file.Name, err) + return + } } p.model.updateLocal(p.folder, file) @@ -666,9 +675,17 @@ func (p *Puller) finisherRoutine(in <-chan *sharedPullerState) { t := time.Unix(state.file.Modified, 0) err = os.Chtimes(state.tempName, t, t) if err != nil { - os.Remove(state.tempName) - l.Warnln("puller: final:", err) - continue + if p.lenientMtimes { + // We accept the failure with a warning here and allow the sync to + // continue. We'll sync the new mtime back to the other devices later. + // If they have the same problem & setting, we might never get in + // sync. + l.Infof("Puller (folder %q, file %q): final: %v (continuing anyway as requested)", p.folder, state.file.Name, err) + } else { + os.Remove(state.tempName) + l.Warnln("puller: final:", err) + continue + } } // If we should use versioning, let the versioner archive the old