archiver: use ExtendedStat from FS interface

With this change, NodeFromFileInfo is the last function that bypasses
the FS interface in the archiver.
This commit is contained in:
Michael Eischer 2024-08-27 14:35:40 +02:00
parent e79dca644e
commit 7bb92dc7bd
5 changed files with 20 additions and 7 deletions

View file

@ -459,7 +459,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
// check if the file has not changed before performing a fopen operation (more expensive, specially // check if the file has not changed before performing a fopen operation (more expensive, specially
// in network filesystems) // in network filesystems)
if previous != nil && !fileChanged(fi, previous, arch.ChangeIgnoreFlags) { if previous != nil && !fileChanged(arch.FS, fi, previous, arch.ChangeIgnoreFlags) {
if arch.allBlobsPresent(previous) { if arch.allBlobsPresent(previous) {
debug.Log("%v hasn't changed, using old list of blobs", target) debug.Log("%v hasn't changed, using old list of blobs", target)
arch.trackItem(snPath, previous, previous, ItemStats{}, time.Since(start)) arch.trackItem(snPath, previous, previous, ItemStats{}, time.Since(start))
@ -579,7 +579,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
// fileChanged tries to detect whether a file's content has changed compared // fileChanged tries to detect whether a file's content has changed compared
// to the contents of node, which describes the same path in the parent backup. // to the contents of node, which describes the same path in the parent backup.
// It should only be run for regular files. // It should only be run for regular files.
func fileChanged(fi os.FileInfo, node *restic.Node, ignoreFlags uint) bool { func fileChanged(fs fs.FS, fi os.FileInfo, node *restic.Node, ignoreFlags uint) bool {
switch { switch {
case node == nil: case node == nil:
return true return true

View file

@ -686,10 +686,11 @@ func TestFileChanged(t *testing.T) {
} }
save(t, filename, content) save(t, filename, content)
fs := &fs.Local{}
fiBefore := lstat(t, filename) fiBefore := lstat(t, filename)
node := nodeFromFI(t, filename, fiBefore) node := nodeFromFI(t, filename, fiBefore)
if fileChanged(fiBefore, node, 0) { if fileChanged(fs, fiBefore, node, 0) {
t.Fatalf("unchanged file detected as changed") t.Fatalf("unchanged file detected as changed")
} }
@ -699,12 +700,12 @@ func TestFileChanged(t *testing.T) {
if test.SameFile { if test.SameFile {
// file should be detected as unchanged // file should be detected as unchanged
if fileChanged(fiAfter, node, test.ChangeIgnore) { if fileChanged(fs, fiAfter, node, test.ChangeIgnore) {
t.Fatalf("unmodified file detected as changed") t.Fatalf("unmodified file detected as changed")
} }
} else { } else {
// file should be detected as changed // file should be detected as changed
if !fileChanged(fiAfter, node, test.ChangeIgnore) && !test.SameFile { if !fileChanged(fs, fiAfter, node, test.ChangeIgnore) && !test.SameFile {
t.Fatalf("modified file detected as unchanged") t.Fatalf("modified file detected as unchanged")
} }
} }
@ -721,7 +722,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
t.Run("nil-node", func(t *testing.T) { t.Run("nil-node", func(t *testing.T) {
fi := lstat(t, filename) fi := lstat(t, filename)
if !fileChanged(fi, nil, 0) { if !fileChanged(&fs.Local{}, fi, nil, 0) {
t.Fatal("nil node detected as unchanged") t.Fatal("nil node detected as unchanged")
} }
}) })
@ -730,7 +731,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
fi := lstat(t, filename) fi := lstat(t, filename)
node := nodeFromFI(t, filename, fi) node := nodeFromFI(t, filename, fi)
node.Type = "symlink" node.Type = "symlink"
if !fileChanged(fi, node, 0) { if !fileChanged(&fs.Local{}, fi, node, 0) {
t.Fatal("node with changed type detected as unchanged") t.Fatal("node with changed type detected as unchanged")
} }
}) })

View file

@ -52,6 +52,11 @@ func (fs Local) DeviceID(fi os.FileInfo) (id uint64, err error) {
return deviceID(fi) return deviceID(fi)
} }
// ExtendedStat converts the give FileInfo into ExtendedFileInfo.
func (fs Local) ExtendedStat(fi os.FileInfo) ExtendedFileInfo {
return ExtendedStat(fi)
}
// Join joins any number of path elements into a single path, adding a // 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 // 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 // empty strings are ignored. On Windows, the result is a UNC path if and only

View file

@ -126,6 +126,12 @@ func (fs *Reader) DeviceID(_ os.FileInfo) (deviceID uint64, err error) {
return 0, errors.New("Device IDs are not supported") return 0, errors.New("Device IDs are not supported")
} }
func (fs *Reader) ExtendedStat(fi os.FileInfo) ExtendedFileInfo {
return ExtendedFileInfo{
FileInfo: fi,
}
}
// Join joins any number of path elements into a single path, adding a // 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 // 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 // empty strings are ignored. On Windows, the result is a UNC path if and only

View file

@ -11,6 +11,7 @@ type FS interface {
Stat(name string) (os.FileInfo, error) Stat(name string) (os.FileInfo, error)
Lstat(name string) (os.FileInfo, error) Lstat(name string) (os.FileInfo, error)
DeviceID(fi os.FileInfo) (deviceID uint64, err error) DeviceID(fi os.FileInfo) (deviceID uint64, err error)
ExtendedStat(fi os.FileInfo) ExtendedFileInfo
Join(elem ...string) string Join(elem ...string) string
Separator() string Separator() string