diff --git a/lib/model/folder.go b/lib/model/folder.go index e9dedf1a9..bb92703e1 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -45,6 +45,7 @@ type folder struct { shortID protocol.ShortID fset *db.FileSet ignores *ignore.Matcher + mtimefs fs.Filesystem modTimeWindow time.Duration ctx context.Context // used internally, only accessible on serve lifetime done chan struct{} // used externally, accessible regardless of serve @@ -100,6 +101,7 @@ func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg conf shortID: model.shortID, fset: fset, ignores: ignores, + mtimefs: fset.MtimeFS(), modTimeWindow: cfg.ModTimeWindow(), done: make(chan struct{}), @@ -457,7 +459,6 @@ func (f *folder) scanSubdirs(subDirs []string) error { // to be cancelled. scanCtx, scanCancel := context.WithCancel(f.ctx) defer scanCancel() - mtimefs := f.fset.MtimeFS() scanConfig := scanner.Config{ Folder: f.ID, @@ -465,7 +466,7 @@ func (f *folder) scanSubdirs(subDirs []string) error { Matcher: f.ignores, TempLifetime: time.Duration(f.model.cfg.Options().KeepTemporariesH) * time.Hour, CurrentFiler: cFiler{snap}, - Filesystem: mtimefs, + Filesystem: f.mtimefs, IgnorePerms: f.IgnorePerms, AutoNormalize: f.AutoNormalize, Hashers: f.model.numHashers(f.ID), @@ -530,8 +531,8 @@ func (f *folder) scanSubdirs(subDirs []string) error { // We don't track it, but check if anything still exists // within and delete it otherwise. if fi.IsDirectory() && protocol.IsEncryptedParent(fi.Name) { - if names, err := mtimefs.DirNames(fi.Name); err == nil && len(names) == 0 { - mtimefs.Remove(fi.Name) + if names, err := f.mtimefs.DirNames(fi.Name); err == nil && len(names) == 0 { + f.mtimefs.Remove(fi.Name) } changes-- return @@ -570,7 +571,7 @@ func (f *folder) scanSubdirs(subDirs []string) error { switch f.Type { case config.FolderTypeReceiveOnly, config.FolderTypeReceiveEncrypted: default: - if nf, ok := f.findRename(snap, mtimefs, res.File, alreadyUsed); ok { + if nf, ok := f.findRename(snap, res.File, alreadyUsed); ok { batchAppend(nf, snap) changes++ } @@ -658,7 +659,7 @@ func (f *folder) scanSubdirs(subDirs []string) error { // it's still here. Simply stat:ing it wont do as there are // tons of corner cases (e.g. parent dir->symlink, missing // permissions) - if !osutil.IsDeleted(mtimefs, file.Name) { + if !osutil.IsDeleted(f.mtimefs, file.Name) { if ignoredParent != "" { // Don't ignore parents of this not ignored item toIgnore = toIgnore[:0] @@ -727,7 +728,7 @@ func (f *folder) scanSubdirs(subDirs []string) error { return nil } -func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file protocol.FileInfo, alreadyUsed map[string]struct{}) (protocol.FileInfo, bool) { +func (f *folder) findRename(snap *db.Snapshot, file protocol.FileInfo, alreadyUsed map[string]struct{}) (protocol.FileInfo, bool) { if len(file.Blocks) == 0 || file.Size == 0 { return protocol.FileInfo{}, false } @@ -763,7 +764,7 @@ func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file proto return true } - if !osutil.IsDeleted(mtimefs, fi.Name) { + if !osutil.IsDeleted(f.mtimefs, fi.Name) { return true } diff --git a/lib/model/folder_recvenc.go b/lib/model/folder_recvenc.go index d26ad4d55..1ddd3aa7f 100644 --- a/lib/model/folder_recvenc.go +++ b/lib/model/folder_recvenc.go @@ -27,8 +27,8 @@ type receiveEncryptedFolder struct { *sendReceiveFolder } -func newReceiveEncryptedFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger, ioLimiter *byteSemaphore) service { - return &receiveEncryptedFolder{newSendReceiveFolder(model, fset, ignores, cfg, ver, fs, evLogger, ioLimiter).(*sendReceiveFolder)} +func newReceiveEncryptedFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service { + return &receiveEncryptedFolder{newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder)} } func (f *receiveEncryptedFolder) Revert() { @@ -65,7 +65,7 @@ func (f *receiveEncryptedFolder) revert() { return true } - if err := f.inWritableDir(f.fs.Remove, fit.Name); err != nil && !fs.IsNotExist(err) { + if err := f.inWritableDir(f.mtimefs.Remove, fit.Name); err != nil && !fs.IsNotExist(err) { f.newScanError(fit.Name, fmt.Errorf("deleting unexpected item: %w", err)) } diff --git a/lib/model/folder_recvonly.go b/lib/model/folder_recvonly.go index 85ced3431..e63e145ec 100644 --- a/lib/model/folder_recvonly.go +++ b/lib/model/folder_recvonly.go @@ -13,7 +13,6 @@ import ( "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/versioner" @@ -57,8 +56,8 @@ type receiveOnlyFolder struct { *sendReceiveFolder } -func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem, evLogger events.Logger, ioLimiter *byteSemaphore) service { - sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, fs, evLogger, ioLimiter).(*sendReceiveFolder) +func newReceiveOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service { + sr := newSendReceiveFolder(model, fset, ignores, cfg, ver, evLogger, ioLimiter).(*sendReceiveFolder) sr.localFlags = protocol.FlagLocalReceiveOnly // gets propagated to the scanner, and set on locally changed files return &receiveOnlyFolder{sr} } diff --git a/lib/model/folder_sendonly.go b/lib/model/folder_sendonly.go index 4b41f106d..479f7f879 100644 --- a/lib/model/folder_sendonly.go +++ b/lib/model/folder_sendonly.go @@ -10,7 +10,6 @@ import ( "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/events" - "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/versioner" @@ -24,7 +23,7 @@ type sendOnlyFolder struct { folder } -func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, _ fs.Filesystem, evLogger events.Logger, ioLimiter *byteSemaphore) service { +func newSendOnlyFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, _ versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service { f := &sendOnlyFolder{ folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, nil), } diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index f869c7f1b..c53119ab0 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -121,8 +121,6 @@ type dbUpdateJob struct { type sendReceiveFolder struct { folder - fs fs.Filesystem - queue *jobQueue blockPullReorderer blockPullReorderer writeLimiter *byteSemaphore @@ -130,10 +128,9 @@ type sendReceiveFolder struct { tempPullErrors map[string]string // pull errors that might be just transient } -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 { +func newSendReceiveFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg config.FolderConfiguration, ver versioner.Versioner, evLogger events.Logger, ioLimiter *byteSemaphore) service { f := &sendReceiveFolder{ folder: newFolder(model, fset, ignores, cfg, evLogger, ioLimiter, ver), - fs: fs, queue: newJobQueue(), blockPullReorderer: newBlockPullReorderer(cfg.BlockPullOrder, model.id, cfg.DeviceIDs()), writeLimiter: newByteSemaphore(cfg.MaxConcurrentWrites), @@ -578,7 +575,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, l.Debugf("need dir\n\t%v\n\t%v", file, curFile) } - info, err := f.fs.Lstat(file.Name) + info, err := f.mtimefs.Lstat(file.Name) switch { // There is already something under that name, we need to handle that. // Unless it already is a directory, as we only track permissions, @@ -617,7 +614,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, // we can pass it to InWritableDir. We use a regular Mkdir and // not MkdirAll because the parent should already exist. mkdir := func(path string) error { - err = f.fs.Mkdir(path, mode) + err = f.mtimefs.Mkdir(path, mode) if err != nil || f.IgnorePerms || file.NoPermissions { return err } @@ -628,14 +625,14 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, } // Stat the directory so we can check its permissions. - info, err := f.fs.Lstat(path) + info, err := f.mtimefs.Lstat(path) if err != nil { return err } // Mask for the bits we want to preserve and add them in to the // directories permissions. - return f.fs.Chmod(path, mode|(info.Mode()&retainBits)) + return f.mtimefs.Chmod(path, mode|(info.Mode()&retainBits)) } if err = f.inWritableDir(mkdir, file.Name); err == nil { @@ -655,7 +652,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, // don't handle modification times on directories, because that sucks...) // It's OK to change mode bits on stuff within non-writable directories. if !f.IgnorePerms && !file.NoPermissions { - if err := f.fs.Chmod(file.Name, mode|(info.Mode()&retainBits)); err != nil { + if err := f.mtimefs.Chmod(file.Name, mode|(info.Mode()&retainBits)); err != nil { f.newPullError(file.Name, err) return } @@ -668,7 +665,7 @@ func (f *sendReceiveFolder) handleDir(file protocol.FileInfo, snap *db.Snapshot, func (f *sendReceiveFolder) checkParent(file string, scanChan chan<- string) bool { parent := filepath.Dir(file) - if err := osutil.TraversesSymlink(f.fs, parent); err != nil { + if err := osutil.TraversesSymlink(f.mtimefs, parent); err != nil { f.newPullError(file, errors.Wrap(err, "checking parent dirs")) return false } @@ -688,12 +685,12 @@ func (f *sendReceiveFolder) checkParent(file string, scanChan chan<- string) boo // And if this is an encrypted folder: // Encrypted files have made-up filenames with two synthetic parent // directories which don't have any meaning. Create those if necessary. - if _, err := f.fs.Lstat(parent); !fs.IsNotExist(err) { + if _, err := f.mtimefs.Lstat(parent); !fs.IsNotExist(err) { l.Debugf("%v parent not missing %v", f, file) return true } l.Debugf("%v creating parent directory of %v", f, file) - if err := f.fs.MkdirAll(parent, 0755); err != nil { + if err := f.mtimefs.MkdirAll(parent, 0755); err != nil { f.newPullError(file, errors.Wrap(err, "creating parent dir")) return false } @@ -746,7 +743,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps // We declare a function that acts on only the path name, so // we can pass it to InWritableDir. createLink := func(path string) error { - if err := f.fs.CreateSymlink(file.SymlinkTarget, path); err != nil { + if err := f.mtimefs.CreateSymlink(file.SymlinkTarget, path); err != nil { return err } return f.maybeCopyOwner(path) @@ -761,7 +758,7 @@ func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, snap *db.Snaps func (f *sendReceiveFolder) handleSymlinkCheckExisting(file protocol.FileInfo, snap *db.Snapshot, scanChan chan<- string) error { // If there is already something under that name, we need to handle that. - info, err := f.fs.Lstat(file.Name) + info, err := f.mtimefs.Lstat(file.Name) if err != nil { if fs.IsNotExist(err) { return nil @@ -892,7 +889,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h if f.versioner != nil && !cur.IsSymlink() { err = f.inWritableDir(f.versioner.Archive, file.Name) } else { - err = f.inWritableDir(f.fs.Remove, file.Name) + err = f.inWritableDir(f.mtimefs.Remove, file.Name) } if err == nil || fs.IsNotExist(err) { @@ -901,7 +898,7 @@ func (f *sendReceiveFolder) deleteFileWithCurrent(file, cur protocol.FileInfo, h return } - if _, serr := f.fs.Lstat(file.Name); serr != nil && !fs.IsPermission(serr) { + if _, serr := f.mtimefs.Lstat(file.Name); serr != nil && !fs.IsPermission(serr) { // We get an error just looking at the file, and it's not a permission // problem. Lets assume the error is in fact some variant of "file // does not exist" (possibly expressed as some parent being a file and @@ -956,7 +953,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn } // Check that the target corresponds to what we have in the DB curTarget, ok := snap.Get(protocol.LocalDeviceID, target.Name) - switch stat, serr := f.fs.Lstat(target.Name); { + switch stat, serr := f.mtimefs.Lstat(target.Name); { case serr != nil: var caseErr *fs.ErrCaseConflict switch { @@ -983,7 +980,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn err = errModified default: var fi protocol.FileInfo - if fi, err = scanner.CreateFileInfo(stat, target.Name, f.fs); err == nil { + if fi, err = scanner.CreateFileInfo(stat, target.Name, f.mtimefs); err == nil { if !fi.IsEquivalentOptional(curTarget, f.modTimeWindow, f.IgnorePerms, true, protocol.LocalAllFlags) { // Target changed scanChan <- target.Name @@ -1000,13 +997,13 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn if f.versioner != nil { err = f.CheckAvailableSpace(uint64(source.Size)) if err == nil { - err = osutil.Copy(f.CopyRangeMethod, f.fs, f.fs, source.Name, tempName) + err = osutil.Copy(f.CopyRangeMethod, f.mtimefs, f.mtimefs, source.Name, tempName) if err == nil { err = f.inWritableDir(f.versioner.Archive, source.Name) } } } else { - err = osutil.RenameOrCopy(f.CopyRangeMethod, f.fs, f.fs, source.Name, tempName) + err = osutil.RenameOrCopy(f.CopyRangeMethod, f.mtimefs, f.mtimefs, source.Name, tempName) } if err != nil { return err @@ -1081,12 +1078,12 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot // Check for an old temporary file which might have some blocks we could // reuse. - tempBlocks, err := scanner.HashFile(f.ctx, f.fs, tempName, file.BlockSize(), nil, false) + tempBlocks, err := scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) if err != nil { var caseErr *fs.ErrCaseConflict if errors.As(err, &caseErr) { - if rerr := f.fs.Rename(caseErr.Real, tempName); rerr == nil { - tempBlocks, err = scanner.HashFile(f.ctx, f.fs, tempName, file.BlockSize(), nil, false) + if rerr := f.mtimefs.Rename(caseErr.Real, tempName); rerr == nil { + tempBlocks, err = scanner.HashFile(f.ctx, f.mtimefs, tempName, file.BlockSize(), nil, false) } } } @@ -1116,7 +1113,7 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot // Otherwise, discard the file ourselves in order for the // sharedpuller not to panic when it fails to exclusively create a // file which already exists - f.inWritableDir(f.fs.Remove, tempName) + f.inWritableDir(f.mtimefs.Remove, tempName) } } else { // Copy the blocks, as we don't want to shuffle them on the FileInfo @@ -1133,7 +1130,7 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, snap *db.Snapshot "action": "update", }) - s := newSharedPullerState(file, f.fs, f.folderID, tempName, blocks, reused, f.IgnorePerms || file.NoPermissions, hasCurFile, curFile, !f.DisableSparseFiles, !f.DisableFsync) + s := newSharedPullerState(file, f.mtimefs, f.folderID, tempName, blocks, reused, f.IgnorePerms || file.NoPermissions, hasCurFile, curFile, !f.DisableSparseFiles, !f.DisableFsync) l.Debugf("%v need file %s; copy %d, reused %v", f, file.Name, len(blocks), len(reused)) @@ -1208,13 +1205,13 @@ func (f *sendReceiveFolder) shortcutFile(file, curFile protocol.FileInfo, dbUpda f.queue.Done(file.Name) if !f.IgnorePerms && !file.NoPermissions { - if err = f.fs.Chmod(file.Name, fs.FileMode(file.Permissions&0777)); err != nil { + if err = f.mtimefs.Chmod(file.Name, fs.FileMode(file.Permissions&0777)); err != nil { f.newPullError(file.Name, err) return } } - f.fs.Chtimes(file.Name, file.ModTime(), file.ModTime()) // never fails + f.mtimefs.Chtimes(file.Name, file.ModTime(), file.ModTime()) // never fails dbUpdateChan <- dbUpdateJob{file, dbUpdateShortcutFile} } @@ -1403,7 +1400,7 @@ func (f *sendReceiveFolder) initWeakHashFinder(state copyBlocksState) (*weakhash return nil, nil } - file, err := f.fs.Open(state.file.Name) + file, err := f.mtimefs.Open(state.file.Name) if err != nil { l.Debugln("weak hasher", err) return nil, nil @@ -1551,7 +1548,7 @@ func (f *sendReceiveFolder) pullBlock(state pullBlockState, out chan<- *sharedPu func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCurFile bool, tempName string, snap *db.Snapshot, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) error { // Set the correct permission bits on the new file if !f.IgnorePerms && !file.NoPermissions { - if err := f.fs.Chmod(tempName, fs.FileMode(file.Permissions&0777)); err != nil { + if err := f.mtimefs.Chmod(tempName, fs.FileMode(file.Permissions&0777)); err != nil { return err } } @@ -1561,7 +1558,7 @@ func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCu return err } - if stat, err := f.fs.Lstat(file.Name); err == nil { + if stat, err := f.mtimefs.Lstat(file.Name); err == nil { // There is an old file or directory already in place. We need to // handle that. @@ -1590,12 +1587,12 @@ func (f *sendReceiveFolder) performFinish(file, curFile protocol.FileInfo, hasCu // Replace the original content with the new one. If it didn't work, // leave the temp file in place for reuse. - if err := osutil.RenameOrCopy(f.CopyRangeMethod, f.fs, f.fs, tempName, file.Name); err != nil { + if err := osutil.RenameOrCopy(f.CopyRangeMethod, f.mtimefs, f.mtimefs, tempName, file.Name); err != nil { return err } // Set the correct timestamp on the new file - f.fs.Chtimes(file.Name, file.ModTime(), file.ModTime()) // never fails + f.mtimefs.Chtimes(file.Name, file.ModTime(), file.ModTime()) // never fails // Record the updated file in the index dbUpdateChan <- dbUpdateJob{file, dbUpdateHandleFile} @@ -1671,7 +1668,7 @@ func (f *sendReceiveFolder) dbUpdaterRoutine(dbUpdateChan <-chan dbUpdateJob) { for dir := range changedDirs { delete(changedDirs, dir) if !f.FolderConfiguration.DisableFsync { - fd, err := f.fs.Open(dir) + fd, err := f.mtimefs.Open(dir) if err != nil { l.Debugf("fsync %q failed: %v", dir, err) continue @@ -1786,21 +1783,21 @@ func removeAvailability(availabilities []Availability, availability Availability func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan chan<- string) error { if isConflict(name) { l.Infoln("Conflict for", name, "which is already a conflict copy; not copying again.") - if err := f.fs.Remove(name); err != nil && !fs.IsNotExist(err) { + if err := f.mtimefs.Remove(name); err != nil && !fs.IsNotExist(err) { return errors.Wrap(err, contextRemovingOldItem) } return nil } if f.MaxConflicts == 0 { - if err := f.fs.Remove(name); err != nil && !fs.IsNotExist(err) { + if err := f.mtimefs.Remove(name); err != nil && !fs.IsNotExist(err) { return errors.Wrap(err, contextRemovingOldItem) } return nil } newName := conflictName(name, lastModBy) - err := f.fs.Rename(name, newName) + err := f.mtimefs.Rename(name, newName) if fs.IsNotExist(err) { // We were supposed to move a file away but it does not exist. Either // the user has already moved it away, or the conflict was between a @@ -1809,11 +1806,11 @@ func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan cha err = nil } if f.MaxConflicts > -1 { - matches := existingConflicts(name, f.fs) + matches := existingConflicts(name, f.mtimefs) if len(matches) > f.MaxConflicts { sort.Sort(sort.Reverse(sort.StringSlice(matches))) for _, match := range matches[f.MaxConflicts:] { - if gerr := f.fs.Remove(match); gerr != nil { + if gerr := f.mtimefs.Remove(match); gerr != nil { l.Debugln(f, "removing extra conflict", gerr) } } @@ -1871,17 +1868,17 @@ func (f *sendReceiveFolder) deleteItemOnDisk(item protocol.FileInfo, snap *db.Sn return f.inWritableDir(f.versioner.Archive, item.Name) } - return f.inWritableDir(f.fs.Remove, item.Name) + return f.inWritableDir(f.mtimefs.Remove, item.Name) } // deleteDirOnDisk attempts to delete a directory. It checks for files/dirs inside // the directory and removes them if possible or returns an error if it fails func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanChan chan<- string) error { - if err := osutil.TraversesSymlink(f.fs, filepath.Dir(dir)); err != nil { + if err := osutil.TraversesSymlink(f.mtimefs, filepath.Dir(dir)); err != nil { return err } - files, _ := f.fs.DirNames(dir) + files, _ := f.mtimefs.DirNames(dir) toBeDeleted := make([]string, 0, len(files)) @@ -1910,10 +1907,10 @@ func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanC hasReceiveOnlyChanged = true continue } - info, err := f.fs.Lstat(fullDirFile) + info, err := f.mtimefs.Lstat(fullDirFile) var diskFile protocol.FileInfo if err == nil { - diskFile, err = scanner.CreateFileInfo(info, fullDirFile, f.fs) + diskFile, err = scanner.CreateFileInfo(info, fullDirFile, f.mtimefs) } if err != nil { // Lets just assume the file has changed. @@ -1950,15 +1947,15 @@ func (f *sendReceiveFolder) deleteDirOnDisk(dir string, snap *db.Snapshot, scanC } for _, del := range toBeDeleted { - f.fs.RemoveAll(del) + f.mtimefs.RemoveAll(del) } - err := f.inWritableDir(f.fs.Remove, dir) + err := f.inWritableDir(f.mtimefs.Remove, dir) if err == nil || fs.IsNotExist(err) { // It was removed or it doesn't exist to start with return nil } - if _, serr := f.fs.Lstat(dir); serr != nil && !fs.IsPermission(serr) { + if _, serr := f.mtimefs.Lstat(dir); serr != nil && !fs.IsPermission(serr) { // We get an error just looking at the directory, and it's not a // permission problem. Lets assume the error is in fact some variant // of "file does not exist" (possibly expressed as some parent being a @@ -1988,7 +1985,7 @@ func (f *sendReceiveFolder) scanIfItemChanged(name string, stat fs.FileInfo, ite // to the database. If there's a mismatch here, there might be local // changes that we don't know about yet and we should scan before // touching the item. - statItem, err := scanner.CreateFileInfo(stat, item.Name, f.fs) + statItem, err := scanner.CreateFileInfo(stat, item.Name, f.mtimefs) if err != nil { return errors.Wrap(err, "comparing item on disk to db") } @@ -2004,12 +2001,12 @@ func (f *sendReceiveFolder) scanIfItemChanged(name string, stat fs.FileInfo, ite // in the DB before the caller proceeds with actually deleting it. // I.e. non-nil error status means "Do not delete!" or "is already deleted". func (f *sendReceiveFolder) checkToBeDeleted(file, cur protocol.FileInfo, hasCur bool, scanChan chan<- string) error { - if err := osutil.TraversesSymlink(f.fs, filepath.Dir(file.Name)); err != nil { + if err := osutil.TraversesSymlink(f.mtimefs, filepath.Dir(file.Name)); err != nil { l.Debugln(f, "not deleting item behind symlink on disk, but update db", file.Name) return fs.ErrNotExist } - stat, err := f.fs.Lstat(file.Name) + stat, err := f.mtimefs.Lstat(file.Name) deleted := fs.IsNotExist(err) || fs.IsErrCaseConflict(err) if !deleted && err != nil { return err @@ -2036,18 +2033,18 @@ func (f *sendReceiveFolder) maybeCopyOwner(path string) error { return nil } - info, err := f.fs.Lstat(filepath.Dir(path)) + info, err := f.mtimefs.Lstat(filepath.Dir(path)) if err != nil { return errors.Wrap(err, "copy owner from parent") } - if err := f.fs.Lchown(path, info.Owner(), info.Group()); err != nil { + if err := f.mtimefs.Lchown(path, info.Owner(), info.Group()); err != nil { return errors.Wrap(err, "copy owner from parent") } return nil } func (f *sendReceiveFolder) inWritableDir(fn func(string) error, path string) error { - return inWritableDir(fn, f.fs, path, f.IgnorePerms) + return inWritableDir(fn, f.mtimefs, path, f.IgnorePerms) } func (f *sendReceiveFolder) limitedWriteAt(fd io.WriterAt, data []byte, offset int64) error { diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index ca3d08dd6..d8934ed07 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -807,12 +807,13 @@ func TestCopyOwner(t *testing.T) { f.folder.FolderConfiguration = config.NewFolderConfiguration(m.id, f.ID, f.Label, fs.FilesystemTypeFake, "/TestCopyOwner") f.folder.FolderConfiguration.CopyOwnershipFromParent = true - f.fs = f.Filesystem() + f.fset = newFileSet(t, f.ID, f.Filesystem(), m.db) + f.mtimefs = f.fset.MtimeFS() // Create a parent dir with a certain owner/group. - f.fs.Mkdir("foo", 0755) - f.fs.Lchown("foo", expOwner, expGroup) + f.mtimefs.Mkdir("foo", 0755) + f.mtimefs.Lchown("foo", expOwner, expGroup) dir := protocol.FileInfo{ Name: "foo/bar", @@ -833,7 +834,7 @@ func TestCopyOwner(t *testing.T) { t.Fatal("Unexpected receive on scanChan:", toScan) } - info, err := f.fs.Lstat("foo/bar") + info, err := f.mtimefs.Lstat("foo/bar") if err != nil { t.Fatal("Unexpected error (dir):", err) } @@ -869,7 +870,7 @@ func TestCopyOwner(t *testing.T) { f.handleFile(file, snap, copierChan) <-dbUpdateChan - info, err = f.fs.Lstat("foo/bar/baz") + info, err = f.mtimefs.Lstat("foo/bar/baz") if err != nil { t.Fatal("Unexpected error (file):", err) } @@ -892,7 +893,7 @@ func TestCopyOwner(t *testing.T) { t.Fatal("Unexpected receive on scanChan:", toScan) } - info, err = f.fs.Lstat("foo/bar/sym") + info, err = f.mtimefs.Lstat("foo/bar/sym") if err != nil { t.Fatal("Unexpected error (file):", err) } @@ -1215,7 +1216,7 @@ func TestPullTempFileCaseConflict(t *testing.T) { file := protocol.FileInfo{Name: "foo"} confl := "Foo" tempNameConfl := fs.TempName(confl) - if fd, err := f.fs.Create(tempNameConfl); err != nil { + if fd, err := f.mtimefs.Create(tempNameConfl); err != nil { t.Fatal(err) } else { if _, err := fd.Write([]byte("data")); err != nil { @@ -1239,7 +1240,7 @@ func TestPullCaseOnlyRename(t *testing.T) { // tempNameConfl := fs.TempName(confl) name := "foo" - if fd, err := f.fs.Create(name); err != nil { + if fd, err := f.mtimefs.Create(name); err != nil { t.Fatal(err) } else { if _, err := fd.Write([]byte("data")); err != nil { @@ -1280,7 +1281,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) { defer cleanupSRFolder(f, m) name := "foo" - if fd, err := f.fs.Create(name); err != nil { + if fd, err := f.mtimefs.Create(name); err != nil { t.Fatal(err) } else { if _, err := fd.Write([]byte("data")); err != nil { @@ -1308,7 +1309,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) { } else if !file.IsUnsupported() { t.Error("symlink entry isn't marked as unsupported") } - if _, err := f.fs.Lstat(name); err == nil { + if _, err := f.mtimefs.Lstat(name); err == nil { t.Error("old file still exists on disk") } else if !fs.IsNotExist(err) { t.Error(err) @@ -1324,7 +1325,7 @@ func TestPullDeleteCaseConflict(t *testing.T) { dbUpdateChan := make(chan dbUpdateJob, 1) scanChan := make(chan string) - if fd, err := f.fs.Create(name); err != nil { + if fd, err := f.mtimefs.Create(name); err != nil { t.Fatal(err) } else { if _, err := fd.Write([]byte("data")); err != nil { diff --git a/lib/model/model.go b/lib/model/model.go index 8b7a04728..87535df3f 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -164,7 +164,7 @@ type model struct { started chan struct{} } -type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, fs.Filesystem, events.Logger, *byteSemaphore) service +type folderFactory func(*model, *db.FileSet, *ignore.Matcher, config.FolderConfiguration, versioner.Versioner, events.Logger, *byteSemaphore) service var ( folderFactories = make(map[config.FolderType]folderFactory) @@ -384,8 +384,6 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio } } - ffs := fset.MtimeFS() - if cfg.Type == config.FolderTypeReceiveEncrypted { if encryptionToken, err := readEncryptionToken(cfg); err == nil { m.folderEncryptionPasswordTokens[folder] = encryptionToken @@ -395,6 +393,7 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio } // These are our metadata files, and they should always be hidden. + ffs := cfg.Filesystem() _ = ffs.Hide(config.DefaultMarkerName) _ = ffs.Hide(".stversions") _ = ffs.Hide(".stignore") @@ -409,7 +408,7 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio } m.folderVersioners[folder] = ver - p := folderFactory(m, fset, ignores, cfg, ver, ffs, m.evLogger, m.folderIOLimiter) + p := folderFactory(m, fset, ignores, cfg, ver, m.evLogger, m.folderIOLimiter) m.folderRunners[folder] = p