chore(fs): changes to allow Filesystem to be implemented externally (#10040)

### Purpose

The `fs.Filesystem` interface contains two parts that cannot be
implemented externally because they are private:

* `filesystemWrapperType`: this PR changes `unwrapFilesystem` to
downcast to a specific concrete type
* `underlying`: this PR simply moves it to an unexported interface

### Testing

Regular tests pass.
This commit is contained in:
Tommy van der Vorst
2025-04-08 12:39:39 +00:00
committed by GitHub
parent d7ca483df1
commit d23cd197e1
11 changed files with 33 additions and 69 deletions
-4
View File
@@ -336,10 +336,6 @@ func (*BasicFilesystem) underlying() (Filesystem, bool) {
return nil, false return nil, false
} }
func (*BasicFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
// basicFile implements the fs.File interface on top of an os.File // basicFile implements the fs.File interface on top of an os.File
type basicFile struct { type basicFile struct {
*os.File *os.File
-4
View File
@@ -357,10 +357,6 @@ func (f *caseFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true return f.Filesystem, true
} }
func (*caseFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeCase
}
func (f *caseFilesystem) checkCase(name string) error { func (f *caseFilesystem) checkCase(name string) error {
var err error var err error
if name, err = Canonicalize(name); err != nil { if name, err = Canonicalize(name); err != nil {
+13 -9
View File
@@ -161,10 +161,11 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
b.Fatal(err) b.Fatal(err)
} }
b.Run("rawfs", func(b *testing.B) { b.Run("rawfs", func(b *testing.B) {
var fakefs *fakeFS fakefs, ok := unwrapFilesystem[*fakeFS](fsys)
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok { if !ok {
fakefs = ffs.(*fakeFS) panic("expected unwrap to fakefs")
} }
fakefs.resetCounters() fakefs.resetCounters()
benchmarkWalkFakeFS(b, fsys, paths, 0, "") benchmarkWalkFakeFS(b, fsys, paths, 0, "")
fakefs.reportMetricsPerOp(b) fakefs.reportMetricsPerOp(b)
@@ -180,9 +181,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
cache: newCaseCache(), cache: newCaseCache(),
}, },
} }
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok { fakefs, ok := unwrapFilesystem[*fakeFS](fsys)
fakefs = ffs.(*fakeFS) if !ok {
panic("expected unwrap to fakefs")
} }
fakefs.resetCounters() fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, 0, "") benchmarkWalkFakeFS(b, casefs, paths, 0, "")
@@ -209,10 +211,12 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
cache: newCaseCache(), cache: newCaseCache(),
}, },
} }
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok { fakefs, ok := unwrapFilesystem[*fakeFS](fsys)
fakefs = ffs.(*fakeFS) if !ok {
panic("expected unwrap to fakefs")
} }
fakefs.resetCounters() fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, otherOpEvery, otherOpPath) benchmarkWalkFakeFS(b, casefs, paths, otherOpEvery, otherOpPath)
fakefs.reportMetricsPerOp(b) fakefs.reportMetricsPerOp(b)
-4
View File
@@ -69,7 +69,3 @@ func (fs *errorFilesystem) PlatformData(_ string, _, _ bool, _ XattrFilter) (pro
func (*errorFilesystem) underlying() (Filesystem, bool) { func (*errorFilesystem) underlying() (Filesystem, bool) {
return nil, false return nil, false
} }
func (*errorFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeError
}
-4
View File
@@ -724,10 +724,6 @@ func (*fakeFS) underlying() (Filesystem, bool) {
return nil, false return nil, false
} }
func (*fakeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
func (fs *fakeFS) resetCounters() { func (fs *fakeFS) resetCounters() {
fs.mut.Lock() fs.mut.Lock()
fs.counters = fakeFSCounters{} fs.counters = fakeFSCounters{}
+16 -20
View File
@@ -21,18 +21,6 @@ import (
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
) )
type filesystemWrapperType int32
const (
filesystemWrapperTypeNone filesystemWrapperType = iota
filesystemWrapperTypeMtime
filesystemWrapperTypeCase
filesystemWrapperTypeError
filesystemWrapperTypeWalk
filesystemWrapperTypeLog
filesystemWrapperTypeMetrics
)
type XattrFilter interface { type XattrFilter interface {
Permit(string) bool Permit(string) bool
GetMaxSingleEntrySize() int GetMaxSingleEntrySize() int
@@ -75,10 +63,11 @@ type Filesystem interface {
PlatformData(name string, withOwnership, withXattrs bool, xattrFilter XattrFilter) (protocol.PlatformData, error) PlatformData(name string, withOwnership, withXattrs bool, xattrFilter XattrFilter) (protocol.PlatformData, error)
GetXattr(name string, xattrFilter XattrFilter) ([]protocol.Xattr, error) GetXattr(name string, xattrFilter XattrFilter) ([]protocol.Xattr, error)
SetXattr(path string, xattrs []protocol.Xattr, xattrFilter XattrFilter) error SetXattr(path string, xattrs []protocol.Xattr, xattrFilter XattrFilter) error
}
type wrappingFilesystem interface {
// Used for unwrapping things // Used for unwrapping things
underlying() (Filesystem, bool) underlying() (Filesystem, bool)
wrapperType() filesystemWrapperType
} }
// The File interface abstracts access to a regular file, being a somewhat // The File interface abstracts access to a regular file, being a somewhat
@@ -353,16 +342,23 @@ func Canonicalize(file string) (string, error) {
return file, nil return file, nil
} }
// unwrapFilesystem removes "wrapping" filesystems to expose the filesystem of the requested wrapperType, if it exists. // unwrapFilesystem removes "wrapping" filesystems to expose the filesystem of the requested wrapper type T, if it exists.
func unwrapFilesystem(fs Filesystem, wrapperType filesystemWrapperType) (Filesystem, bool) { func unwrapFilesystem[T Filesystem](fs Filesystem) (T, bool) {
var ok bool
for { for {
if fs.wrapperType() == wrapperType { if unwrapped, ok := fs.(T); ok {
return fs, true return unwrapped, true
} }
fs, ok = fs.underlying()
wrappingFs, ok := fs.(wrappingFilesystem)
if !ok { if !ok {
return nil, false var x T
return x, false
}
fs, ok = wrappingFs.underlying()
if !ok {
var x T
return x, false
} }
} }
} }
-4
View File
@@ -177,7 +177,3 @@ func (fs *logFilesystem) Usage(name string) (Usage, error) {
func (fs *logFilesystem) underlying() (Filesystem, bool) { func (fs *logFilesystem) underlying() (Filesystem, bool) {
return fs.Filesystem, true return fs.Filesystem, true
} }
func (*logFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeLog
}
-4
View File
@@ -273,10 +273,6 @@ func (m *metricsFS) underlying() (Filesystem, bool) {
return m.next, true return m.next, true
} }
func (m *metricsFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeMetrics
}
type metricsFile struct { type metricsFile struct {
fs *metricsFS fs *metricsFS
next File next File
+1 -9
View File
@@ -146,10 +146,6 @@ func (f *mtimeFS) underlying() (Filesystem, bool) {
return f.Filesystem, true return f.Filesystem, true
} }
func (*mtimeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeMtime
}
func (f *mtimeFS) save(name string, real, virtual time.Time) { func (f *mtimeFS) save(name string, real, virtual time.Time) {
if f.caseInsensitive { if f.caseInsensitive {
name = UnicodeLowercaseNormalized(name) name = UnicodeLowercaseNormalized(name)
@@ -255,13 +251,9 @@ func (t *MtimeMapping) Unmarshal(bs []byte) error {
} }
func GetMtimeMapping(fs Filesystem, file string) (MtimeMapping, error) { func GetMtimeMapping(fs Filesystem, file string) (MtimeMapping, error) {
fs, ok := unwrapFilesystem(fs, filesystemWrapperTypeMtime) mtimeFs, ok := unwrapFilesystem[*mtimeFS](fs)
if !ok { if !ok {
return MtimeMapping{}, errors.New("failed to unwrap") return MtimeMapping{}, errors.New("failed to unwrap")
} }
mtimeFs, ok := fs.(*mtimeFS)
if !ok {
return MtimeMapping{}, errors.New("unwrapping failed")
}
return mtimeFs.load(file) return mtimeFs.load(file)
} }
+3 -3
View File
@@ -261,7 +261,7 @@ func newMtimeFS(path string, db database, options ...MtimeFSOption) *mtimeFS {
func newMtimeFSWithWalk(path string, db database, options ...MtimeFSOption) (*mtimeFS, *walkFilesystem) { func newMtimeFSWithWalk(path string, db database, options ...MtimeFSOption) (*mtimeFS, *walkFilesystem) {
fs := NewFilesystem(FilesystemTypeBasic, path, NewMtimeOption(db, options...)) fs := NewFilesystem(FilesystemTypeBasic, path, NewMtimeOption(db, options...))
wfs, _ := unwrapFilesystem(fs, filesystemWrapperTypeWalk) wfs, _ := unwrapFilesystem[*walkFilesystem](fs)
mfs, _ := unwrapFilesystem(fs, filesystemWrapperTypeMtime) mfs, _ := unwrapFilesystem[*mtimeFS](fs)
return mfs.(*mtimeFS), wfs.(*walkFilesystem) return mfs, wfs
} }
-4
View File
@@ -153,7 +153,3 @@ func (f *walkFilesystem) Walk(root string, walkFn WalkFunc) error {
func (f *walkFilesystem) underlying() (Filesystem, bool) { func (f *walkFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true return f.Filesystem, true
} }
func (*walkFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeWalk
}