fs / archiver: convert to handle based interface

The actual implementation still relies on file paths, but with the
abstraction layer in place, an FS implementation can ensure atomic file
accesses in the future.
This commit is contained in:
Michael Eischer
2024-11-02 20:27:38 +01:00
parent 2f2ce9add2
commit 48dbefc37e
18 changed files with 356 additions and 284 deletions

View File

@@ -20,18 +20,16 @@ func (fs Local) VolumeName(path string) string {
return filepath.VolumeName(path)
}
// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func (fs Local) OpenFile(name string, flag int) (File, error) {
f, err := os.OpenFile(fixpath(name), flag, 0)
if err != nil {
return nil, err
}
_ = setFlags(f)
return f, nil
// OpenFile opens a file or directory for reading.
//
// If metadataOnly is set, an implementation MUST return a File object for
// arbitrary file types including symlinks. The implementation may internally use
// the given file path or a file handle. In particular, an implementation may
// delay actually accessing the underlying filesystem.
//
// 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)
}
// Lstat returns the FileInfo structure describing the named file.
@@ -53,10 +51,6 @@ func (fs Local) ExtendedStat(fi os.FileInfo) ExtendedFileInfo {
return ExtendedStat(fi)
}
func (fs Local) NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*restic.Node, error) {
return nodeFromFileInfo(path, fi, ignoreXattrListError)
}
// Join joins any number of path elements into a single path, adding a
// Separator if necessary. Join calls Clean on the result; in particular, all
// empty strings are ignored. On Windows, the result is a UNC path if and only
@@ -97,3 +91,87 @@ func (fs Local) Base(path string) string {
func (fs Local) Dir(path string) string {
return filepath.Dir(path)
}
type localFile struct {
name string
flag int
f *os.File
fi os.FileInfo
}
// See the File interface for a description of each method
var _ File = &localFile{}
func newLocalFile(name string, flag int, metadataOnly bool) (*localFile, error) {
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)
}
return &localFile{
name: name,
flag: flag,
f: f,
}, 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
}
var err error
if f.f != nil {
f.fi, err = f.f.Stat()
} else if f.flag&O_NOFOLLOW != 0 {
f.fi, err = os.Lstat(f.name)
} else {
f.fi, err = os.Stat(f.name)
}
return err
}
func (f *localFile) Stat() (os.FileInfo, error) {
err := f.cacheFI()
// the call to cacheFI MUST happen before reading from f.fi
return f.fi, err
}
func (f *localFile) ToNode(ignoreXattrListError bool) (*restic.Node, error) {
if err := f.cacheFI(); err != nil {
return nil, err
}
return nodeFromFileInfo(f.name, f.fi, ignoreXattrListError)
}
func (f *localFile) Read(p []byte) (n int, err error) {
return f.f.Read(p)
}
func (f *localFile) Readdirnames(n int) ([]string, error) {
return f.f.Readdirnames(n)
}
func (f *localFile) Close() error {
if f.f != nil {
return f.f.Close()
}
return nil
}