From 4b33540148d880852d1222df01b320f902606f84 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 17 Nov 2024 13:52:42 +0100 Subject: [PATCH] fs: wire up fd-based handles for fs.Local --- internal/fs/fs_local.go | 143 +++++++++++++++++++++++++---------- internal/fs/fs_local_test.go | 3 +- internal/fs/meta_fd.go | 12 +-- 3 files changed, 112 insertions(+), 46 deletions(-) diff --git a/internal/fs/fs_local.go b/internal/fs/fs_local.go index b2dd454b5..062ee47e4 100644 --- a/internal/fs/fs_local.go +++ b/internal/fs/fs_local.go @@ -29,7 +29,7 @@ func (fs Local) VolumeName(path string) string { // // Only the O_NOFOLLOW and O_DIRECTORY flags are supported. func (fs Local) OpenFile(name string, flag int, metadataOnly bool) (File, error) { - return newLocalFile(name, flag, metadataOnly) + return buildLocalFile(name, flag, metadataOnly) } // Lstat returns the FileInfo structure describing the named file. @@ -85,52 +85,14 @@ func (fs Local) Dir(path string) string { return filepath.Dir(path) } +// See the File interface for a description of each method type localFile struct { name string - flag int f *os.File fi *ExtendedFileInfo meta metadataHandle } -// See the File interface for a description of each method -var _ File = &localFile{} - -func newLocalFile(name string, flag int, metadataOnly bool) (*localFile, error) { - meta := newPathMetadataHandle(name, flag) - var f *os.File - if !metadataOnly { - var err error - f, err = os.OpenFile(fixpath(name), flag, 0) - if err != nil { - return nil, err - } - _ = setFlags(f) - } - file := &localFile{ - name: name, - flag: flag, - f: f, - meta: meta, - } - - return file, nil -} - -func (f *localFile) MakeReadable() error { - if f.f != nil { - panic("file is already readable") - } - - newF, err := newLocalFile(f.name, f.flag, false) - if err != nil { - return err - } - // replace state and also reset cached FileInfo - *f = *newF - return nil -} - func (f *localFile) cacheFI() error { if f.fi != nil { return nil @@ -186,3 +148,104 @@ type cachedMetadataHandle struct { func (c *cachedMetadataHandle) Stat() (*ExtendedFileInfo, error) { return c.f.Stat() } + +type pathLocalFile struct { + localFile + flag int +} + +var _ File = &pathLocalFile{} + +func newPathLocalFile(name string, flag int, metadataOnly bool) (*pathLocalFile, error) { + var f *os.File + var meta metadataHandle + + if !metadataOnly { + var err error + f, err = os.OpenFile(fixpath(name), flag, 0) + if err != nil { + return nil, err + } + _ = setFlags(f) + } + meta = newPathMetadataHandle(name, flag) + + return &pathLocalFile{ + localFile: localFile{ + name: name, + f: f, + meta: meta, + }, + flag: flag, + }, nil +} + +func (f *pathLocalFile) MakeReadable() error { + if f.f != nil { + panic("file is already readable") + } + + newF, err := newPathLocalFile(f.name, f.flag, false) + if err != nil { + return err + } + // replace state and also reset cached FileInfo + *f = *newF + return nil +} + +type fdLocalFile struct { + localFile + flag int + metadataOnly bool +} + +var _ File = &fdLocalFile{} + +func newFdLocalFile(name string, flag int, metadataOnly bool) (*fdLocalFile, error) { + var f *os.File + var err error + if metadataOnly { + f, err = openMetadataHandle(name, flag) + } else { + f, err = openReadHandle(name, flag) + } + if err != nil { + return nil, err + } + meta := newFdMetadataHandle(name, f) + return &fdLocalFile{ + localFile: localFile{ + name: name, + f: f, + meta: meta, + }, + flag: flag, + metadataOnly: metadataOnly, + }, nil +} + +func (f *fdLocalFile) MakeReadable() error { + if !f.metadataOnly { + panic("file is already readable") + } + + newF, err := reopenMetadataHandle(f.f) + // old handle is no longer usable + f.f = nil + if err != nil { + return err + } + f.f = newF + f.meta = newFdMetadataHandle(f.name, newF) + f.metadataOnly = false + return nil +} + +func buildLocalFile(name string, flag int, metadataOnly bool) (File, error) { + useFd := true // FIXME + if useFd { + return newFdLocalFile(name, flag, metadataOnly) + } + return newPathLocalFile(name, flag, metadataOnly) +} diff --git a/internal/fs/fs_local_test.go b/internal/fs/fs_local_test.go index 8fd8eb136..83f5b6365 100644 --- a/internal/fs/fs_local_test.go +++ b/internal/fs/fs_local_test.go @@ -96,7 +96,8 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest func assertFIEqual(t *testing.T, want os.FileInfo, got *ExtendedFileInfo) { t.Helper() - rtest.Equals(t, want.Name(), got.Name, "Name") + // do not compare the name as it can differ in testcases that rename the test file + // a fd-based implementation will still keep the original file name rtest.Equals(t, want.ModTime(), got.ModTime, "ModTime") rtest.Equals(t, want.Mode(), got.Mode, "Mode") rtest.Equals(t, want.Size(), got.Size, "Size") diff --git a/internal/fs/meta_fd.go b/internal/fs/meta_fd.go index e6bbd6c58..458f8ca3a 100644 --- a/internal/fs/meta_fd.go +++ b/internal/fs/meta_fd.go @@ -23,11 +23,13 @@ func (p *fdMetadataHandle) Name() string { } func (p *fdMetadataHandle) Stat() (*ExtendedFileInfo, error) { - fi, err := p.f.Stat() - if err != nil { - return nil, err - } - return extendedStat(fi), nil + // localFile.cacheFI() already internally calls stat on the filehandle + panic("should never be called") + // fi, err := p.f.Stat() + // if err != nil { + // return nil, err + // } + // return extendedStat(fi), nil } func (p *fdMetadataHandle) Readlink() (string, error) {