diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index fb03c88fc..e88c15bd9 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -605,22 +605,6 @@ func join(elem ...string) string { return path.Join(elem...) } -// statDir returns the file info for the directory. Symbolic links are -// resolved. If the target directory is not a directory, an error is returned. -func (arch *Archiver) statDir(dir string) (os.FileInfo, error) { - fi, err := arch.FS.Stat(dir) - if err != nil { - return nil, errors.WithStack(err) - } - - tpe := fi.Mode() & (os.ModeType | os.ModeCharDevice) - if tpe != os.ModeDir { - return fi, errors.Errorf("path is not a directory: %v", dir) - } - - return fi, nil -} - // saveTree stores a Tree in the repo, returned is the tree. snPath is the path // within the current snapshot. func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree, previous *restic.Tree, complete fileCompleteFunc) (futureNode, int, error) { @@ -631,15 +615,8 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree, return futureNode{}, 0, errors.Errorf("FileInfoPath for %v is empty", snPath) } - fi, err := arch.statDir(atree.FileInfoPath) - if err != nil { - return futureNode{}, 0, err - } - - debug.Log("%v, dir node data loaded from %v", snPath, atree.FileInfoPath) - // in some cases reading xattrs for directories above the backup source is not allowed - // thus ignore errors for such folders. - node, err = arch.nodeFromFileInfo(snPath, atree.FileInfoPath, fi, true) + var err error + node, err = arch.dirPathToNode(snPath, atree.FileInfoPath) if err != nil { return futureNode{}, 0, err } @@ -710,6 +687,36 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree, return fn, len(nodes), nil } +func (arch *Archiver) dirPathToNode(snPath, target string) (node *restic.Node, err error) { + meta, err := arch.FS.OpenFile(target, fs.O_RDONLY) + if err != nil { + return nil, err + } + defer func() { + cerr := meta.Close() + if err == nil { + err = cerr + } + }() + + debug.Log("%v, reading dir node data from %v", snPath, target) + fi, err := meta.Stat() + if err != nil { + return nil, errors.WithStack(err) + } + + // in some cases reading xattrs for directories above the backup source is not allowed + // thus ignore errors for such folders. + node, err = arch.nodeFromFileInfo(snPath, target, fi, true) + if err != nil { + return nil, err + } + if node.Type != restic.NodeTypeDir { + return nil, errors.Errorf("path is not a directory: %v", target) + } + return node, err +} + // resolveRelativeTargets replaces targets that only contain relative // directories ("." or "../../") with the contents of the directory. Each // element of target is processed with fs.Clean(). diff --git a/internal/fs/fs_local.go b/internal/fs/fs_local.go index 045edf02f..4f8b67090 100644 --- a/internal/fs/fs_local.go +++ b/internal/fs/fs_local.go @@ -34,12 +34,6 @@ func (fs Local) OpenFile(name string, flag int) (File, error) { return f, nil } -// Stat returns a FileInfo describing the named file. If there is an error, it -// will be of type *PathError. -func (fs Local) Stat(name string) (os.FileInfo, error) { - return os.Stat(fixpath(name)) -} - // Lstat returns the FileInfo structure describing the named file. // If the file is a symbolic link, the returned FileInfo // describes the symbolic link. Lstat makes no attempt to follow the link. diff --git a/internal/fs/fs_local_vss.go b/internal/fs/fs_local_vss.go index aa9f2b89d..e9bc66657 100644 --- a/internal/fs/fs_local_vss.go +++ b/internal/fs/fs_local_vss.go @@ -131,11 +131,6 @@ func (fs *LocalVss) OpenFile(name string, flag int) (File, error) { return fs.FS.OpenFile(fs.snapshotPath(name), flag) } -// Stat wraps the Stat method of the underlying file system. -func (fs *LocalVss) Stat(name string) (os.FileInfo, error) { - return fs.FS.Stat(fs.snapshotPath(name)) -} - // Lstat wraps the Lstat method of the underlying file system. func (fs *LocalVss) Lstat(name string) (os.FileInfo, error) { return fs.FS.Lstat(fs.snapshotPath(name)) diff --git a/internal/fs/fs_local_vss_test.go b/internal/fs/fs_local_vss_test.go index 7856767ba..db8d4b133 100644 --- a/internal/fs/fs_local_vss_test.go +++ b/internal/fs/fs_local_vss_test.go @@ -317,16 +317,12 @@ func TestVSSFS(t *testing.T) { // trigger snapshot creation and // capture FI while file still exists (should already be within the snapshot) - origFi, err := localVss.Stat(tempfile) + origFi, err := localVss.Lstat(tempfile) rtest.OK(t, err) // remove original file rtest.OK(t, os.Remove(tempfile)) - statFi, err := localVss.Stat(tempfile) - rtest.OK(t, err) - rtest.Equals(t, origFi.Mode(), statFi.Mode()) - lstatFi, err := localVss.Lstat(tempfile) rtest.OK(t, err) rtest.Equals(t, origFi.Mode(), lstatFi.Mode()) @@ -336,9 +332,10 @@ func TestVSSFS(t *testing.T) { data, err := io.ReadAll(f) rtest.OK(t, err) rtest.Equals(t, "example", string(data), "unexpected file content") - rtest.OK(t, f.Close()) - node, err := localVss.NodeFromFileInfo(tempfile, statFi, false) + node, err := f.ToNode(false) rtest.OK(t, err) - rtest.Equals(t, node.Mode, statFi.Mode()) + rtest.Equals(t, node.Mode, lstatFi.Mode()) + + rtest.OK(t, f.Close()) } diff --git a/internal/fs/fs_reader.go b/internal/fs/fs_reader.go index ed8b9a347..a4efa8dea 100644 --- a/internal/fs/fs_reader.go +++ b/internal/fs/fs_reader.go @@ -81,12 +81,6 @@ func (fs *Reader) OpenFile(name string, flag int) (f File, err error) { return nil, pathError("open", name, syscall.ENOENT) } -// Stat returns a FileInfo describing the named file. If there is an error, it -// will be of type *os.PathError. -func (fs *Reader) Stat(name string) (os.FileInfo, error) { - return fs.Lstat(name) -} - // Lstat returns the FileInfo structure describing the named file. // If the file is a symbolic link, the returned FileInfo // describes the symbolic link. Lstat makes no attempt to follow the link. diff --git a/internal/fs/interface.go b/internal/fs/interface.go index 58744bd1d..899888fb0 100644 --- a/internal/fs/interface.go +++ b/internal/fs/interface.go @@ -10,7 +10,6 @@ import ( // FS bundles all methods needed for a file system. type FS interface { OpenFile(name string, flag int) (File, error) - Stat(name string) (os.FileInfo, error) Lstat(name string) (os.FileInfo, error) DeviceID(fi os.FileInfo) (deviceID uint64, err error) ExtendedStat(fi os.FileInfo) ExtendedFileInfo