diff --git a/lib/config/config_test.go b/lib/config/config_test.go index a029dce0e..628099f51 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -457,7 +457,7 @@ func TestIssue1262(t *testing.T) { t.Fatal(err) } - actual := cfg.Folders()["test"].Filesystem().URI() + actual := cfg.Folders()["test"].Filesystem(nil).URI() expected := `e:\` if actual != expected { @@ -494,7 +494,7 @@ func TestFolderPath(t *testing.T) { Path: "~/tmp", } - realPath := folder.Filesystem().URI() + realPath := folder.Filesystem(nil).URI() if !filepath.IsAbs(realPath) { t.Error(realPath, "should be absolute") } diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go index 214343db6..9a733b9e1 100644 --- a/lib/config/folderconfiguration.go +++ b/lib/config/folderconfiguration.go @@ -16,6 +16,7 @@ import ( "github.com/shirou/gopsutil/v3/disk" + "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/util" @@ -42,24 +43,29 @@ func (f FolderConfiguration) Copy() FolderConfiguration { return c } -func (f FolderConfiguration) Filesystem() fs.Filesystem { +// Filesystem creates a filesystem for the path and options of this folder. +// The fset parameter may be nil, in which case no mtime handling on top of +// the fileystem is provided. +func (f FolderConfiguration) Filesystem(fset *db.FileSet) fs.Filesystem { // This is intentionally not a pointer method, because things like - // cfg.Folders["default"].Filesystem() should be valid. - var opts []fs.Option + // cfg.Folders["default"].Filesystem(nil) should be valid. + opts := make([]fs.Option, 0, 3) if f.FilesystemType == fs.FilesystemTypeBasic && f.JunctionsAsDirs { opts = append(opts, new(fs.OptionJunctionsAsDirs)) } - filesystem := fs.NewFilesystem(f.FilesystemType, f.Path, opts...) if !f.CaseSensitiveFS { - filesystem = fs.NewCaseFilesystem(filesystem) + opts = append(opts, new(fs.OptionDetectCaseConflicts)) } - return filesystem + if fset != nil { + opts = append(opts, fset.MtimeOption()) + } + return fs.NewFilesystem(f.FilesystemType, f.Path, opts...) } func (f FolderConfiguration) ModTimeWindow() time.Duration { dur := time.Duration(f.RawModTimeWindowS) * time.Second if f.RawModTimeWindowS < 1 && runtime.GOOS == "android" { - if usage, err := disk.Usage(f.Filesystem().URI()); err != nil { + if usage, err := disk.Usage(f.Filesystem(nil).URI()); err != nil { dur = 2 * time.Second l.Debugf(`Detecting FS at "%v" on android: Setting mtime window to 2s: err == "%v"`, f.Path, err) } else if strings.HasPrefix(strings.ToLower(usage.Fstype), "ext2") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext3") || strings.HasPrefix(strings.ToLower(usage.Fstype), "ext4") { @@ -89,7 +95,7 @@ func (f *FolderConfiguration) CreateMarker() error { // begin with. permBits = 0700 } - fs := f.Filesystem() + fs := f.Filesystem(nil) err := fs.Mkdir(DefaultMarkerName, permBits) if err != nil { return err @@ -106,7 +112,7 @@ func (f *FolderConfiguration) CreateMarker() error { // CheckPath returns nil if the folder root exists and contains the marker file func (f *FolderConfiguration) CheckPath() error { - fi, err := f.Filesystem().Stat(".") + fi, err := f.Filesystem(nil).Stat(".") if err != nil { if !fs.IsNotExist(err) { return err @@ -124,7 +130,7 @@ func (f *FolderConfiguration) CheckPath() error { return ErrPathNotDirectory } - _, err = f.Filesystem().Stat(f.MarkerName) + _, err = f.Filesystem(nil).Stat(f.MarkerName) if err != nil { if !fs.IsNotExist(err) { return err @@ -145,7 +151,7 @@ func (f *FolderConfiguration) CreateRoot() (err error) { permBits = 0700 } - filesystem := f.Filesystem() + filesystem := f.Filesystem(nil) if _, err = filesystem.Stat("."); fs.IsNotExist(err) { err = filesystem.MkdirAll(".", permBits) @@ -256,7 +262,7 @@ func (f *FolderConfiguration) CheckAvailableSpace(req uint64) error { if val <= 0 { return nil } - fs := f.Filesystem() + fs := f.Filesystem(nil) usage, err := fs.Usage(".") if err != nil { return nil diff --git a/lib/config/migrations.go b/lib/config/migrations.go index f018d936f..29ee0addc 100644 --- a/lib/config/migrations.go +++ b/lib/config/migrations.go @@ -199,7 +199,7 @@ func migrateToConfigV23(cfg *Configuration) { // marker name in later versions. for i := range cfg.Folders { - fs := cfg.Folders[i].Filesystem() + fs := cfg.Folders[i].Filesystem(nil) // Invalid config posted, or tests. if fs == nil { continue @@ -235,18 +235,18 @@ func migrateToConfigV21(cfg *Configuration) { switch folder.Versioning.Type { case "simple", "trashcan": // Clean out symlinks in the known place - cleanSymlinks(folder.Filesystem(), ".stversions") + cleanSymlinks(folder.Filesystem(nil), ".stversions") case "staggered": versionDir := folder.Versioning.Params["versionsPath"] if versionDir == "" { // default place - cleanSymlinks(folder.Filesystem(), ".stversions") + cleanSymlinks(folder.Filesystem(nil), ".stversions") } else if filepath.IsAbs(versionDir) { // absolute cleanSymlinks(fs.NewFilesystem(fs.FilesystemTypeBasic, versionDir), ".") } else { // relative to folder - cleanSymlinks(folder.Filesystem(), versionDir) + cleanSymlinks(folder.Filesystem(nil), versionDir) } } } diff --git a/lib/db/set.go b/lib/db/set.go index 836a51114..178800fd1 100644 --- a/lib/db/set.go +++ b/lib/db/set.go @@ -403,8 +403,8 @@ func (s *FileSet) SetIndexID(device protocol.DeviceID, id protocol.IndexID) { } } -func (s *FileSet) MtimeFS(filesystem fs.Filesystem) fs.Filesystem { - opStr := fmt.Sprintf("%s MtimeFS()", s.folder) +func (s *FileSet) MtimeOption() fs.Option { + opStr := fmt.Sprintf("%s MtimeOption()", s.folder) l.Debugf(opStr) prefix, err := s.db.keyer.GenerateMtimesKey(nil, []byte(s.folder)) if backend.IsClosed(err) { @@ -413,7 +413,7 @@ func (s *FileSet) MtimeFS(filesystem fs.Filesystem) fs.Filesystem { fatalError(err, opStr, s.db) } kv := NewNamespacedKV(s.db, string(prefix)) - return fs.NewMtimeFS(filesystem, kv) + return fs.NewMtimeOption(kv) } func (s *FileSet) ListDevices() []protocol.DeviceID { diff --git a/lib/fs/basicfs.go b/lib/fs/basicfs.go index 28392e703..9ace634a8 100644 --- a/lib/fs/basicfs.go +++ b/lib/fs/basicfs.go @@ -27,12 +27,13 @@ var ( type OptionJunctionsAsDirs struct{} -func (o *OptionJunctionsAsDirs) apply(fs Filesystem) { +func (o *OptionJunctionsAsDirs) apply(fs Filesystem) Filesystem { if basic, ok := fs.(*BasicFilesystem); !ok { l.Warnln("WithJunctionsAsDirs must only be used with FilesystemTypeBasic") } else { basic.junctionsAsDirs = true } + return fs } func (o *OptionJunctionsAsDirs) String() string { diff --git a/lib/fs/casefs.go b/lib/fs/casefs.go index ffbd371e8..71bef00d8 100644 --- a/lib/fs/casefs.go +++ b/lib/fs/casefs.go @@ -123,21 +123,27 @@ func (r *caseFilesystemRegistry) cleaner() { var globalCaseFilesystemRegistry = caseFilesystemRegistry{fss: make(map[fskey]*caseFilesystem)} -// caseFilesystem is a BasicFilesystem with additional checks to make a -// potentially case insensitive underlying FS behave like it's case-sensitive. -type caseFilesystem struct { - Filesystem - realCaser -} - -// NewCaseFilesystem ensures that the given, potentially case-insensitive filesystem +// OptionDetectCaseConflicts ensures that the potentially case-insensitive filesystem // behaves like a case-sensitive filesystem. Meaning that it takes into account // the real casing of a path and returns ErrCaseConflict if the given path differs // from the real path. It is safe to use with any filesystem, i.e. also a // case-sensitive one. However it will add some overhead and thus shouldn't be // used if the filesystem is known to already behave case-sensitively. -func NewCaseFilesystem(fs Filesystem) Filesystem { - return wrapFilesystem(fs, globalCaseFilesystemRegistry.get) +type OptionDetectCaseConflicts struct{} + +func (o *OptionDetectCaseConflicts) apply(fs Filesystem) Filesystem { + return globalCaseFilesystemRegistry.get(fs) +} + +func (o *OptionDetectCaseConflicts) String() string { + return "detectCaseConflicts" +} + +// caseFilesystem is a BasicFilesystem with additional checks to make a +// potentially case insensitive underlying FS behave like it's case-sensitive. +type caseFilesystem struct { + Filesystem + realCaser } func (f *caseFilesystem) Chmod(name string, mode FileMode) error { diff --git a/lib/fs/casefs_test.go b/lib/fs/casefs_test.go index 9c5915db8..b819197f0 100644 --- a/lib/fs/casefs_test.go +++ b/lib/fs/casefs_test.go @@ -34,8 +34,12 @@ func TestRealCase(t *testing.T) { }) } +func newCaseFilesystem(fsys Filesystem) *caseFilesystem { + return globalCaseFilesystemRegistry.get(fsys).(*caseFilesystem) +} + func testRealCase(t *testing.T, fsys Filesystem) { - testFs := NewCaseFilesystem(fsys).(*caseFilesystem) + testFs := newCaseFilesystem(fsys) comps := []string{"Foo", "bar", "BAZ", "bAs"} path := filepath.Join(comps...) testFs.MkdirAll(filepath.Join(comps[:len(comps)-1]...), 0777) @@ -86,7 +90,7 @@ func TestRealCaseSensitive(t *testing.T) { } func testRealCaseSensitive(t *testing.T, fsys Filesystem) { - testFs := NewCaseFilesystem(fsys).(*caseFilesystem) + testFs := newCaseFilesystem(fsys) names := make([]string, 2) names[0] = "foo" @@ -139,7 +143,7 @@ func testCaseFSStat(t *testing.T, fsys Filesystem) { sensitive = false } - testFs := NewCaseFilesystem(fsys) + testFs := newCaseFilesystem(fsys) _, err = testFs.Stat("FOO") if sensitive { if IsNotExist(err) { diff --git a/lib/fs/filesystem.go b/lib/fs/filesystem.go index d2af30518..d834f9950 100644 --- a/lib/fs/filesystem.go +++ b/lib/fs/filesystem.go @@ -202,10 +202,29 @@ var IsPathSeparator = os.IsPathSeparator // representation of those must be part of the returned string. type Option interface { String() string - apply(Filesystem) + apply(Filesystem) Filesystem } func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem { + var caseOpt Option + var mtimeOpt Option + i := 0 + for _, opt := range opts { + if caseOpt != nil && mtimeOpt != nil { + break + } + switch opt.(type) { + case *OptionDetectCaseConflicts: + caseOpt = opt + case *optionMtime: + mtimeOpt = opt + default: + opts[i] = opt + i++ + } + } + opts = opts[:i] + var fs Filesystem switch fsType { case FilesystemTypeBasic: @@ -221,6 +240,17 @@ func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem } } + // Case handling is the innermost, as any filesystem calls by wrappers should be case-resolved + if caseOpt != nil { + fs = caseOpt.apply(fs) + } + + // mtime handling should happen inside walking, as filesystem calls while + // walking should be mtime-resolved too + if mtimeOpt != nil { + fs = mtimeOpt.apply(fs) + } + if l.ShouldDebug("walkfs") { return NewWalkFilesystem(&logFilesystem{fs}) } @@ -289,21 +319,6 @@ func Canonicalize(file string) (string, error) { return file, nil } -// wrapFilesystem should always be used when wrapping a Filesystem. -// It ensures proper wrapping order, which right now means: -// `logFilesystem` needs to be the outermost wrapper for caller lookup. -func wrapFilesystem(fs Filesystem, wrapFn func(Filesystem) Filesystem) Filesystem { - logFs, ok := fs.(*logFilesystem) - if ok { - fs = logFs.Filesystem - } - fs = wrapFn(fs) - if ok { - fs = &logFilesystem{fs} - } - return fs -} - // unwrapFilesystem removes "wrapping" filesystems to expose the filesystem of the requested wrapperType, if it exists. func unwrapFilesystem(fs Filesystem, wrapperType filesystemWrapperType) (Filesystem, bool) { var ok bool diff --git a/lib/fs/mtimefs.go b/lib/fs/mtimefs.go index 5113fc0b7..832b872c9 100644 --- a/lib/fs/mtimefs.go +++ b/lib/fs/mtimefs.go @@ -33,20 +33,34 @@ func WithCaseInsensitivity(v bool) MtimeFSOption { } } -// NewMtimeFS returns a filesystem with nanosecond mtime precision, regardless -// of what shenanigans the underlying filesystem gets up to. -func NewMtimeFS(fs Filesystem, db database, options ...MtimeFSOption) Filesystem { - return wrapFilesystem(fs, func(underlying Filesystem) Filesystem { - f := &mtimeFS{ - Filesystem: underlying, - chtimes: underlying.Chtimes, // for mocking it out in the tests - db: db, - } - for _, opt := range options { - opt(f) - } - return f - }) +type optionMtime struct { + db database + options []MtimeFSOption +} + +// NewMtimeOption makes any filesystem provide nanosecond mtime precision, +// regardless of what shenanigans the underlying filesystem gets up to. +func NewMtimeOption(db database, options ...MtimeFSOption) Option { + return &optionMtime{ + db: db, + options: options, + } +} + +func (o *optionMtime) apply(fs Filesystem) Filesystem { + f := &mtimeFS{ + Filesystem: fs, + chtimes: fs.Chtimes, // for mocking it out in the tests + db: o.db, + } + for _, opt := range o.options { + opt(f) + } + return f +} + +func (_ *optionMtime) String() string { + return "mtime" } func (f *mtimeFS) Chtimes(name string, atime, mtime time.Time) error { @@ -104,25 +118,6 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) { return info, nil } -func (f *mtimeFS) Walk(root string, walkFn WalkFunc) error { - return f.Filesystem.Walk(root, func(path string, info FileInfo, err error) error { - if info != nil { - mtimeMapping, loadErr := f.load(path) - if loadErr != nil && err == nil { - // The iterator gets to deal with the error - err = loadErr - } - if mtimeMapping.Real == info.ModTime() { - info = mtimeFileInfo{ - FileInfo: info, - mtime: mtimeMapping.Virtual, - } - } - } - return walkFn(path, info, err) - }) -} - func (f *mtimeFS) Create(name string) (File, error) { fd, err := f.Filesystem.Create(name) if err != nil { diff --git a/lib/fs/mtimefs_test.go b/lib/fs/mtimefs_test.go index bf14f514e..6c294bd24 100644 --- a/lib/fs/mtimefs_test.go +++ b/lib/fs/mtimefs_test.go @@ -26,7 +26,7 @@ func TestMtimeFS(t *testing.T) { // a random time with nanosecond precision testTime := time.Unix(1234567890, 123456789) - mtimefs := newMtimeFS(newBasicFilesystem("."), make(mapStore)) + mtimefs := newMtimeFS(".", make(mapStore)) // Do one Chtimes call that will go through to the normal filesystem mtimefs.chtimes = os.Chtimes @@ -88,8 +88,8 @@ func TestMtimeFSWalk(t *testing.T) { } defer func() { _ = os.RemoveAll(dir) }() - underlying := NewFilesystem(FilesystemTypeBasic, dir) - mtimefs := newMtimeFS(underlying, make(mapStore)) + mtimefs, walkFs := newMtimeFSWithWalk(dir, make(mapStore)) + underlying := mtimefs.Filesystem mtimefs.chtimes = failChtimes if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil { @@ -120,7 +120,7 @@ func TestMtimeFSWalk(t *testing.T) { } found := false - _ = mtimefs.Walk("", func(path string, info FileInfo, err error) error { + _ = walkFs.Walk("", func(path string, info FileInfo, err error) error { if path == "file" { found = true if !info.ModTime().Equal(newTime) { @@ -142,8 +142,8 @@ func TestMtimeFSOpen(t *testing.T) { } defer func() { _ = os.RemoveAll(dir) }() - underlying := NewFilesystem(FilesystemTypeBasic, dir) - mtimefs := newMtimeFS(underlying, make(mapStore)) + mtimefs := newMtimeFS(dir, make(mapStore)) + underlying := mtimefs.Filesystem mtimefs.chtimes = failChtimes if err := os.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil { @@ -222,12 +222,12 @@ func TestMtimeFSInsensitive(t *testing.T) { // The test should fail with a case sensitive mtimefs t.Run("with case sensitive mtimefs", func(t *testing.T) { - theTest(t, newMtimeFS(newBasicFilesystem("."), make(mapStore)), false) + theTest(t, newMtimeFS(".", make(mapStore)), false) }) // And succeed with a case insensitive one. t.Run("with case insensitive mtimefs", func(t *testing.T) { - theTest(t, newMtimeFS(newBasicFilesystem("."), make(mapStore), WithCaseInsensitivity(true)), true) + theTest(t, newMtimeFS(".", make(mapStore), WithCaseInsensitivity(true)), true) }) } @@ -261,6 +261,12 @@ func evilChtimes(name string, mtime, atime time.Time) error { return os.Chtimes(name, mtime.Add(300*time.Hour).Truncate(time.Hour), atime.Add(300*time.Hour).Truncate(time.Hour)) } -func newMtimeFS(fs Filesystem, db database, options ...MtimeFSOption) *mtimeFS { - return NewMtimeFS(fs, db, options...).(*mtimeFS) +func newMtimeFS(path string, db database, options ...MtimeFSOption) *mtimeFS { + mtimefs, _ := newMtimeFSWithWalk(path, db, options...) + return mtimefs +} + +func newMtimeFSWithWalk(path string, db database, options ...MtimeFSOption) (*mtimeFS, *walkFilesystem) { + wfs := NewFilesystem(FilesystemTypeBasic, path, NewMtimeOption(db, options...)).(*walkFilesystem) + return wfs.Filesystem.(*mtimeFS), wfs } diff --git a/lib/model/folder.go b/lib/model/folder.go index a318a64ee..016800126 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -108,7 +108,7 @@ func newFolder(model *model, fset *db.FileSet, ignores *ignore.Matcher, cfg conf shortID: model.shortID, fset: fset, ignores: ignores, - mtimefs: fset.MtimeFS(cfg.Filesystem()), + mtimefs: cfg.Filesystem(fset), modTimeWindow: cfg.ModTimeWindow(), done: make(chan struct{}), @@ -1001,7 +1001,7 @@ func (f *folder) monitorWatch(ctx context.Context) { for { select { case <-failTimer.C: - eventChan, errChan, err = f.Filesystem().Watch(".", f.ignores, ctx, f.IgnorePerms) + eventChan, errChan, err = f.mtimefs.Watch(".", f.ignores, ctx, f.IgnorePerms) // We do this once per minute initially increased to // max one hour in case of repeat failures. f.scanOnWatchErr() diff --git a/lib/model/folder_recvonly_test.go b/lib/model/folder_recvonly_test.go index 740a31202..0e3dbb3f7 100644 --- a/lib/model/folder_recvonly_test.go +++ b/lib/model/folder_recvonly_test.go @@ -28,7 +28,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) @@ -110,7 +110,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) @@ -200,7 +200,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) @@ -270,7 +270,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) @@ -335,7 +335,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) @@ -425,7 +425,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) { m, f, wcfgCancel := setupROFolder(t) defer wcfgCancel() - ffs := f.Filesystem() + ffs := f.Filesystem(nil) defer cleanupModel(m) addFakeConn(m, device1, f.ID) diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index af57b7b0f..e8f0af524 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -1264,7 +1264,7 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch // Hope that it's usually in the same folder, so start with that one. folders := []string{f.folderID} for folder, cfg := range f.model.cfg.Folders() { - folderFilesystems[folder] = cfg.Filesystem() + folderFilesystems[folder] = cfg.Filesystem(nil) if folder != f.folderID { folders = append(folders, folder) } diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index 4999e99b5..0e87bccaf 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -109,7 +109,7 @@ func setupSendReceiveFolder(t testing.TB, files ...protocol.FileInfo) (*testMode func cleanupSRFolder(f *sendReceiveFolder, m *testModel, wrapperCancel context.CancelFunc) { wrapperCancel() os.Remove(m.cfg.ConfigPath()) - os.RemoveAll(f.Filesystem().URI()) + os.RemoveAll(f.Filesystem(nil).URI()) } // Layout of the files: (indexes from the above array) @@ -172,7 +172,7 @@ func TestHandleFileWithTemp(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t, existingFile) defer cleanupSRFolder(f, m, wcfgCancel) - if _, err := prepareTmpFile(f.Filesystem()); err != nil { + if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil { t.Fatal(err) } @@ -230,7 +230,7 @@ func TestCopierFinder(t *testing.T) { defer cleanupSRFolder(f, m, wcfgCancel) - if _, err := prepareTmpFile(f.Filesystem()); err != nil { + if _, err := prepareTmpFile(f.Filesystem(nil)); err != nil { t.Fatal(err) } @@ -290,7 +290,7 @@ func TestCopierFinder(t *testing.T) { } // Verify that the fetched blocks have actually been written to the temp file - blks, err := scanner.HashFile(context.TODO(), f.Filesystem(), tempFile, protocol.MinBlockSize, nil, false) + blks, err := scanner.HashFile(context.TODO(), f.Filesystem(nil), tempFile, protocol.MinBlockSize, nil, false) if err != nil { t.Log(err) } @@ -308,7 +308,7 @@ func TestWeakHash(t *testing.T) { // Setup the model/pull environment model, fo, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(fo, model, wcfgCancel) - ffs := fo.Filesystem() + ffs := fo.Filesystem(nil) tempFile := fs.TempName("weakhash") var shift int64 = 10 @@ -673,7 +673,7 @@ func TestDeregisterOnFailInPull(t *testing.T) { func TestIssue3164(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) tmpDir := ffs.URI() ignDir := filepath.Join("issue3164", "oktodelete") @@ -764,7 +764,7 @@ func TestDiffEmpty(t *testing.T) { func TestDeleteIgnorePerms(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) f.IgnorePerms = true name := "deleteIgnorePerms" @@ -806,7 +806,7 @@ func TestCopyOwner(t *testing.T) { f.folder.FolderConfiguration.CopyOwnershipFromParent = true f.fset = newFileSet(t, f.ID, m.db) - f.mtimefs = f.fset.MtimeFS(f.Filesystem()) + f.mtimefs = f.Filesystem(f.fset) // Create a parent dir with a certain owner/group. @@ -905,7 +905,7 @@ func TestCopyOwner(t *testing.T) { func TestSRConflictReplaceFileByDir(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) name := "foo" @@ -937,7 +937,7 @@ func TestSRConflictReplaceFileByDir(t *testing.T) { func TestSRConflictReplaceFileByLink(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) name := "foo" @@ -970,7 +970,7 @@ func TestSRConflictReplaceFileByLink(t *testing.T) { func TestDeleteBehindSymlink(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) destDir := createTmpDir() defer os.RemoveAll(destDir) @@ -1063,7 +1063,7 @@ func TestPullCtxCancel(t *testing.T) { func TestPullDeleteUnscannedDir(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) dir := "foobar" must(t, ffs.MkdirAll(dir, 0777)) @@ -1092,7 +1092,7 @@ func TestPullDeleteUnscannedDir(t *testing.T) { func TestPullCaseOnlyPerformFinish(t *testing.T) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) name := "foo" contents := []byte("contents") @@ -1153,7 +1153,7 @@ func TestPullCaseOnlySymlink(t *testing.T) { func testPullCaseOnlyDirOrSymlink(t *testing.T, dir bool) { m, f, wcfgCancel := setupSendReceiveFolder(t) defer cleanupSRFolder(f, m, wcfgCancel) - ffs := f.Filesystem() + ffs := f.Filesystem(nil) name := "foo" if dir { diff --git a/lib/model/model.go b/lib/model/model.go index af6bb93f9..67eb793a1 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -332,7 +332,7 @@ func (m *model) StartDeadlockDetector(timeout time.Duration) { // Need to hold lock on m.fmut when calling this. func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration, fset *db.FileSet, cacheIgnoredFiles bool) { - ignores := ignore.New(cfg.Filesystem(), ignore.WithCache(cacheIgnoredFiles)) + ignores := ignore.New(cfg.Filesystem(nil), ignore.WithCache(cacheIgnoredFiles)) if cfg.Type != config.FolderTypeReceiveEncrypted { if err := ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) { l.Warnln("Loading ignores:", err) @@ -396,7 +396,7 @@ func (m *model) addAndStartFolderLockedWithIgnores(cfg config.FolderConfiguratio } // These are our metadata files, and they should always be hidden. - ffs := cfg.Filesystem() + ffs := cfg.Filesystem(nil) _ = ffs.Hide(config.DefaultMarkerName) _ = ffs.Hide(".stversions") _ = ffs.Hide(".stignore") @@ -428,7 +428,7 @@ func (m *model) warnAboutOverwritingProtectedFiles(cfg config.FolderConfiguratio } // This is a bit of a hack. - ffs := cfg.Filesystem() + ffs := cfg.Filesystem(nil) if ffs.Type() != fs.FilesystemTypeBasic { return } @@ -478,7 +478,7 @@ func (m *model) removeFolder(cfg config.FolderConfiguration) { if isPathUnique { // Remove (if empty and removable) or move away (if non-empty or // otherwise not removable) Syncthing-specific marker files. - fs := cfg.Filesystem() + fs := cfg.Filesystem(nil) if err := fs.Remove(config.DefaultMarkerName); err != nil { moved := config.DefaultMarkerName + time.Now().Format(".removed-20060102-150405") _ = fs.Rename(config.DefaultMarkerName, moved) @@ -1839,7 +1839,7 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, blockNo // Grab the FS after limiting, as it causes I/O and we want to minimize // the race time between the symlink check and the read. - folderFs := folderCfg.Filesystem() + folderFs := folderCfg.Filesystem(nil) if err := osutil.TraversesSymlink(folderFs, filepath.Dir(name)); err != nil { l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size) @@ -2005,7 +2005,7 @@ func (m *model) GetMtimeMapping(folder string, file string) (fs.MtimeMapping, er if !ok { return fs.MtimeMapping{}, ErrFolderMissing } - return fs.GetMtimeMapping(ffs.MtimeFS(fcfg.Filesystem()), file) + return fs.GetMtimeMapping(fcfg.Filesystem(ffs), file) } // Connection returns the current connection for device, and a boolean whether a connection was found. @@ -2039,7 +2039,7 @@ func (m *model) LoadIgnores(folder string) ([]string, []string, error) { } if !ignoresOk { - ignores = ignore.New(cfg.Filesystem()) + ignores = ignore.New(cfg.Filesystem(nil)) } err := ignores.Load(".stignore") @@ -2094,7 +2094,7 @@ func (m *model) setIgnores(cfg config.FolderConfiguration, content []string) err return err } - if err := ignore.WriteIgnores(cfg.Filesystem(), ".stignore", content); err != nil { + if err := ignore.WriteIgnores(cfg.Filesystem(nil), ".stignore", content); err != nil { l.Warnln("Saving .stignore:", err) return err } @@ -3207,7 +3207,7 @@ type storedEncryptionToken struct { } func readEncryptionToken(cfg config.FolderConfiguration) ([]byte, error) { - fd, err := cfg.Filesystem().Open(encryptionTokenPath(cfg)) + fd, err := cfg.Filesystem(nil).Open(encryptionTokenPath(cfg)) if err != nil { return nil, err } @@ -3221,7 +3221,7 @@ func readEncryptionToken(cfg config.FolderConfiguration) ([]byte, error) { func writeEncryptionToken(token []byte, cfg config.FolderConfiguration) error { tokenName := encryptionTokenPath(cfg) - fd, err := cfg.Filesystem().OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0666) + fd, err := cfg.Filesystem(nil).OpenFile(tokenName, fs.OptReadWrite|fs.OptCreate, 0666) if err != nil { return err } diff --git a/lib/model/model_test.go b/lib/model/model_test.go index eac0b69fe..e6f8ee49e 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -222,7 +222,7 @@ func BenchmarkIndex_100(b *testing.B) { func benchmarkIndex(b *testing.B, nfiles int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := genFiles(nfiles) must(b, m.Index(device1, fcfg.ID, files)) @@ -249,7 +249,7 @@ func BenchmarkIndexUpdate_10000_1(b *testing.B) { func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := genFiles(nfiles) ufiles := genFiles(nufiles) @@ -1517,7 +1517,7 @@ func TestIgnores(t *testing.T) { // Invalid path, treated like no patterns at all. fcfg := config.FolderConfiguration{ID: "fresh", Path: "XXX"} - ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles)) + ignores := ignore.New(fcfg.Filesystem(nil), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles)) m.fmut.Lock() m.folderCfgs[fcfg.ID] = fcfg m.folderIgnores[fcfg.ID] = ignores @@ -1709,7 +1709,7 @@ func TestRWScanRecovery(t *testing.T) { func TestGlobalDirectoryTree(t *testing.T) { m, _, fcfg, wCancel := setupModelWithConnection(t) defer wCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) b := func(isfile bool, path ...string) protocol.FileInfo { typ := protocol.FileInfoTypeDirectory @@ -2012,7 +2012,7 @@ func BenchmarkTree_100_10(b *testing.B) { func benchmarkTree(b *testing.B, n1, n2 int) { m, _, fcfg, wcfgCancel := setupModelWithConnection(b) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) m.ScanFolder(fcfg.ID) files := genDeepFiles(n1, n2) @@ -2477,7 +2477,7 @@ func TestIssue2571(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - testFs := fcfg.Filesystem() + testFs := fcfg.Filesystem(nil) defer os.RemoveAll(testFs.URI()) for _, dir := range []string{"toLink", "linkTarget"} { @@ -2516,7 +2516,7 @@ func TestIssue4573(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - testFs := fcfg.Filesystem() + testFs := fcfg.Filesystem(nil) defer os.RemoveAll(testFs.URI()) must(t, testFs.MkdirAll("inaccessible", 0755)) @@ -2546,7 +2546,7 @@ func TestIssue4573(t *testing.T) { func TestInternalScan(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - testFs := fcfg.Filesystem() + testFs := fcfg.Filesystem(nil) defer os.RemoveAll(testFs.URI()) testCases := map[string]func(protocol.FileInfo) bool{ @@ -2644,7 +2644,7 @@ func TestCustomMarkerName(t *testing.T) { func TestRemoveDirWithContent(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) tfs.MkdirAll("dirwith", 0755) @@ -2706,7 +2706,7 @@ func TestIssue4475(t *testing.T) { m, conn, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() defer cleanupModel(m) - testFs := fcfg.Filesystem() + testFs := fcfg.Filesystem(nil) // Scenario: Dir is deleted locally and before syncing/index exchange // happens, a file is create in that dir on the remote. @@ -2769,7 +2769,7 @@ func TestVersionRestore(t *testing.T) { fcfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", fs.FilesystemTypeBasic, dir) fcfg.Versioning.Type = "simple" fcfg.FSWatcherEnabled = false - filesystem := fcfg.Filesystem() + filesystem := fcfg.Filesystem(nil) rawConfig := config.Configuration{ Folders: []config.FolderConfiguration{fcfg}, @@ -3005,7 +3005,7 @@ func TestIssue4094(t *testing.T) { t.Fatalf("failed setting ignores: %v", err) } - if _, err := fcfg.Filesystem().Lstat(".stignore"); err != nil { + if _, err := fcfg.Filesystem(nil).Lstat(".stignore"); err != nil { t.Fatalf("failed stating .stignore: %v", err) } } @@ -3037,7 +3037,7 @@ func TestIssue4903(t *testing.T) { t.Fatalf("expected path missing error, got: %v", err) } - if _, err := fcfg.Filesystem().Lstat("."); !fs.IsNotExist(err) { + if _, err := fcfg.Filesystem(nil).Lstat("."); !fs.IsNotExist(err) { t.Fatalf("Expected missing path error, got: %v", err) } } @@ -3067,7 +3067,7 @@ func TestParentOfUnignored(t *testing.T) { m, cancel := newState(t, defaultCfg) defer cleanupModel(m) defer cancel() - defer defaultFolderConfig.Filesystem().Remove(".stignore") + defer defaultFolderConfig.Filesystem(nil).Remove(".stignore") m.SetIgnores("default", []string{"!quux", "*"}) @@ -3184,7 +3184,7 @@ func TestConnCloseOnRestart(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() m := setupModel(t, w) - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) br := &testutils.BlockingRW{} nw := &testutils.NoopRW{} @@ -3220,7 +3220,7 @@ func TestConnCloseOnRestart(t *testing.T) { func TestModTimeWindow(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) fcfg.RawModTimeWindowS = 2 setFolder(t, w, fcfg) m := setupModel(t, w) @@ -3277,7 +3277,7 @@ func TestModTimeWindow(t *testing.T) { func TestDevicePause(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) sub := m.evLogger.Subscribe(events.DevicePaused) defer sub.Unsubscribe() @@ -3304,7 +3304,7 @@ func TestDevicePause(t *testing.T) { func TestDeviceWasSeen(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) m.deviceWasSeen(device1) @@ -3399,7 +3399,7 @@ func TestRenameSequenceOrder(t *testing.T) { numFiles := 20 - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) for i := 0; i < numFiles; i++ { v := fmt.Sprintf("%d", i) writeFile(t, ffs, v, []byte(v)) @@ -3468,7 +3468,7 @@ func TestRenameSameFile(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) writeFile(t, ffs, "file", []byte("file")) m.ScanFolders() @@ -3519,7 +3519,7 @@ func TestRenameEmptyFile(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) writeFile(t, ffs, "file", []byte("data")) writeFile(t, ffs, "empty", nil) @@ -3596,7 +3596,7 @@ func TestBlockListMap(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) writeFile(t, ffs, "one", []byte("content")) writeFile(t, ffs, "two", []byte("content")) writeFile(t, ffs, "three", []byte("content")) @@ -3664,7 +3664,7 @@ func TestScanRenameCaseOnly(t *testing.T) { m := setupModel(t, wcfg) defer cleanupModel(m) - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) name := "foo" writeFile(t, ffs, name, []byte("contents")) @@ -3782,7 +3782,7 @@ func TestAddFolderCompletion(t *testing.T) { func TestScanDeletedROChangedOnSR(t *testing.T) { m, _, fcfg, wCancel := setupModelWithConnection(t) - ffs := fcfg.Filesystem() + ffs := fcfg.Filesystem(nil) defer wCancel() defer cleanupModelAndRemoveDir(m, ffs.URI()) fcfg.Type = config.FolderTypeReceiveOnly @@ -3886,7 +3886,7 @@ func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSec func TestIssue6961(t *testing.T) { wcfg, fcfg, wcfgCancel := tmpDefaultWrapper() defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) waiter, err := wcfg.Modify(func(cfg *config.Configuration) { cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2")) fcfg.Type = config.FolderTypeReceiveOnly @@ -3956,7 +3956,7 @@ func TestIssue6961(t *testing.T) { func TestCompletionEmptyGlobal(t *testing.T) { m, _, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID.Short()), Sequence: 1}} m.fmut.Lock() m.folderFiles[fcfg.ID].Update(protocol.LocalDeviceID, files) @@ -4157,7 +4157,7 @@ func TestCCFolderNotRunning(t *testing.T) { // Create the folder, but don't start it. w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) m := newModel(t, w, myID, "syncthing", "dev", nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) @@ -4264,7 +4264,7 @@ func TestDeletedNotLocallyChangedReceiveEncrypted(t *testing.T) { func deletedNotLocallyChanged(t *testing.T, ft config.FolderType) { w, fcfg, wCancel := tmpDefaultWrapper() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) fcfg.Type = ft setFolder(t, w, fcfg) defer wCancel() diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 76868748a..2721901a4 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -32,7 +32,7 @@ func TestRequestSimple(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) // We listen for incoming index updates and trigger when we see one for @@ -79,7 +79,7 @@ func TestSymlinkTraversalRead(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) // We listen for incoming index updates and trigger when we see one for // the expected test file. @@ -122,7 +122,7 @@ func TestSymlinkTraversalWrite(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) // We listen for incoming index updates and trigger when we see one for // the expected names. @@ -181,7 +181,7 @@ func TestRequestCreateTmpSymlink(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) // We listen for incoming index updates and trigger when we see one for // the expected test file. @@ -224,7 +224,7 @@ func TestRequestVersioningSymlinkAttack(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() defer func() { - os.RemoveAll(fcfg.Filesystem().URI()) + os.RemoveAll(fcfg.Filesystem(nil).URI()) os.Remove(w.ConfigPath()) }() @@ -301,7 +301,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { w, wCancel := createTmpWrapper(defaultCfgWrapper.RawCopy()) defer wCancel() fcfg := testFolderConfigTmp() - fss := fcfg.Filesystem() + fss := fcfg.Filesystem(nil) fcfg.Type = ft setFolder(t, w, fcfg) m := setupModel(t, w) @@ -430,7 +430,7 @@ func pullInvalidIgnored(t *testing.T, ft config.FolderType) { func TestIssue4841(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) received := make(chan []protocol.FileInfo) fc.setIndexFn(func(_ context.Context, _ string, fs []protocol.FileInfo) error { @@ -478,7 +478,7 @@ func TestIssue4841(t *testing.T) { func TestRescanIfHaveInvalidContent(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) payload := []byte("hello") @@ -544,7 +544,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { func TestParentDeletion(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - testFs := fcfg.Filesystem() + testFs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, testFs.URI()) parent := "foo" @@ -623,7 +623,7 @@ func TestRequestSymlinkWindows(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - defer cleanupModelAndRemoveDir(m, fcfg.Filesystem().URI()) + defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) received := make(chan []protocol.FileInfo) fc.setIndexFn(func(_ context.Context, folder string, fs []protocol.FileInfo) error { @@ -691,7 +691,7 @@ func equalContents(path string, contents []byte) error { func TestRequestRemoteRenameChanged(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) tmpDir := tfs.URI() defer cleanupModelAndRemoveDir(m, tfs.URI()) @@ -825,7 +825,7 @@ func TestRequestRemoteRenameChanged(t *testing.T) { func TestRequestRemoteRenameConflict(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) tmpDir := tfs.URI() defer cleanupModelAndRemoveDir(m, tmpDir) @@ -916,7 +916,7 @@ func TestRequestRemoteRenameConflict(t *testing.T) { func TestRequestDeleteChanged(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) done := make(chan struct{}) @@ -984,7 +984,7 @@ func TestRequestDeleteChanged(t *testing.T) { func TestNeedFolderFiles(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) tmpDir := tfs.URI() defer cleanupModelAndRemoveDir(m, tmpDir) @@ -1032,7 +1032,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() m := setupModel(t, w) - fss := fcfg.Filesystem() + fss := fcfg.Filesystem(nil) tmpDir := fss.URI() defer cleanupModelAndRemoveDir(m, tmpDir) @@ -1127,7 +1127,7 @@ func TestIgnoreDeleteUnignore(t *testing.T) { func TestRequestLastFileProgress(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) done := make(chan struct{}) @@ -1162,7 +1162,7 @@ func TestRequestIndexSenderPause(t *testing.T) { m, fc, fcfg, wcfgCancel := setupModelWithConnection(t) defer wcfgCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) indexChan := make(chan []protocol.FileInfo) @@ -1275,7 +1275,7 @@ func TestRequestIndexSenderPause(t *testing.T) { func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) dir1 := "foo" dir2 := "bar" @@ -1342,7 +1342,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { w, fcfg, wCancel := tmpDefaultWrapper() defer wCancel() - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) fcfg.Type = config.FolderTypeReceiveEncrypted setFolder(t, w, fcfg) @@ -1450,7 +1450,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { must(t, err) waiter.Wait() addFakeConn(m, device2, fcfg.ID) - tfs := fcfg.Filesystem() + tfs := fcfg.Filesystem(nil) defer cleanupModelAndRemoveDir(m, tfs.URI()) indexChan := make(chan []protocol.FileInfo, 1) diff --git a/lib/model/testutils_test.go b/lib/model/testutils_test.go index e16607839..954d64b00 100644 --- a/lib/model/testutils_test.go +++ b/lib/model/testutils_test.go @@ -40,7 +40,7 @@ func init() { defaultCfgWrapper, defaultCfgWrapperCancel = createTmpWrapper(config.New(myID)) defaultFolderConfig = testFolderConfig("testdata") - defaultFs = defaultFolderConfig.Filesystem() + defaultFs = defaultFolderConfig.Filesystem(nil) waiter, _ := defaultCfgWrapper.Modify(func(cfg *config.Configuration) { cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device1, "device1")) @@ -308,7 +308,7 @@ func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderCon t.Helper() m.removeFolder(fcfg) fset := newFileSet(t, fcfg.ID, m.db) - ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged())) + ignores := ignore.New(fcfg.Filesystem(nil), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged())) m.fmut.Lock() m.addAndStartFolderLockedWithIgnores(fcfg, fset, ignores) m.fmut.Unlock() diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index 4edad2b47..c673accf4 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -38,10 +38,14 @@ type testfile struct { type testfileList []testfile +const ( + testFsType = fs.FilesystemTypeBasic + testFsLocation = "testdata" +) + var ( - testFs fs.Filesystem - testFsType = fs.FilesystemTypeBasic - testdata = testfileList{ + testFs fs.Filesystem + testdata = testfileList{ {"afile", 4, "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"}, {"dir1", 128, ""}, {filepath.Join("dir1", "dfile"), 5, "49ae93732fcf8d63fe1cce759664982dbd5b23161f007dba8561862adc96d063"}, @@ -58,7 +62,7 @@ func init() { // potentially taking down the box... rdebug.SetMaxStack(10 * 1 << 20) - testFs = fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata") + testFs = fs.NewFilesystem(testFsType, testFsLocation) } func TestWalkSub(t *testing.T) { @@ -258,7 +262,7 @@ func TestNormalizationDarwinCaseFS(t *testing.T) { return } - testFs := fs.NewCaseFilesystem(testFs) + testFs := fs.NewFilesystem(testFsType, testFsLocation, new(fs.OptionDetectCaseConflicts)) testFs.RemoveAll("normalization") defer testFs.RemoveAll("normalization") diff --git a/lib/versioner/external.go b/lib/versioner/external.go index b39d4044b..2319077e4 100644 --- a/lib/versioner/external.go +++ b/lib/versioner/external.go @@ -41,7 +41,7 @@ func newExternal(cfg config.FolderConfiguration) Versioner { s := external{ command: command, - filesystem: cfg.Filesystem(), + filesystem: cfg.Filesystem(nil), } l.Debugf("instantiated %#v", s) diff --git a/lib/versioner/simple.go b/lib/versioner/simple.go index 06ab00b27..e4865116a 100644 --- a/lib/versioner/simple.go +++ b/lib/versioner/simple.go @@ -40,7 +40,7 @@ func newSimple(cfg config.FolderConfiguration) Versioner { s := simple{ keep: keep, cleanoutDays: cleanoutDays, - folderFs: cfg.Filesystem(), + folderFs: cfg.Filesystem(nil), versionsFs: versionerFsFromFolderCfg(cfg), copyRangeMethod: cfg.CopyRangeMethod, } diff --git a/lib/versioner/simple_test.go b/lib/versioner/simple_test.go index 12dc2c81a..0e2f33467 100644 --- a/lib/versioner/simple_test.go +++ b/lib/versioner/simple_test.go @@ -70,7 +70,7 @@ func TestSimpleVersioningVersionCount(t *testing.T) { }, }, } - fs := cfg.Filesystem() + fs := cfg.Filesystem(nil) v := newSimple(cfg) diff --git a/lib/versioner/staggered.go b/lib/versioner/staggered.go index 25ec71e4b..2a341590e 100644 --- a/lib/versioner/staggered.go +++ b/lib/versioner/staggered.go @@ -44,7 +44,7 @@ func newStaggered(cfg config.FolderConfiguration) Versioner { versionsFs := versionerFsFromFolderCfg(cfg) s := &staggered{ - folderFs: cfg.Filesystem(), + folderFs: cfg.Filesystem(nil), versionsFs: versionsFs, interval: [4]interval{ {30, 60 * 60}, // first hour -> 30 sec between versions diff --git a/lib/versioner/trashcan.go b/lib/versioner/trashcan.go index 2e8110259..a72d7b42e 100644 --- a/lib/versioner/trashcan.go +++ b/lib/versioner/trashcan.go @@ -33,7 +33,7 @@ func newTrashcan(cfg config.FolderConfiguration) Versioner { // On error we default to 0, "do not clean out the trash can" s := &trashcan{ - folderFs: cfg.Filesystem(), + folderFs: cfg.Filesystem(nil), versionsFs: versionerFsFromFolderCfg(cfg), cleanoutDays: cleanoutDays, copyRangeMethod: cfg.CopyRangeMethod, diff --git a/lib/versioner/trashcan_test.go b/lib/versioner/trashcan_test.go index 891c64ecf..e0c947be9 100644 --- a/lib/versioner/trashcan_test.go +++ b/lib/versioner/trashcan_test.go @@ -38,7 +38,7 @@ func TestTrashcanArchiveRestoreSwitcharoo(t *testing.T) { FSPath: tmpDir2, }, } - folderFs := cfg.Filesystem() + folderFs := cfg.Filesystem(nil) versionsFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir2) diff --git a/lib/versioner/util.go b/lib/versioner/util.go index 1ee4ae292..3dd808576 100644 --- a/lib/versioner/util.go +++ b/lib/versioner/util.go @@ -256,7 +256,7 @@ func restoreFile(method fs.CopyRangeMethod, src, dst fs.Filesystem, filePath str } func versionerFsFromFolderCfg(cfg config.FolderConfiguration) (versionsFs fs.Filesystem) { - folderFs := cfg.Filesystem() + folderFs := cfg.Filesystem(nil) if cfg.Versioning.FSPath == "" { versionsFs = fs.NewFilesystem(folderFs.Type(), filepath.Join(folderFs.URI(), ".stversions")) } else if cfg.Versioning.FSType == fs.FilesystemTypeBasic && !filepath.IsAbs(cfg.Versioning.FSPath) {