diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 95facb16f..7e71a543b 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -68,7 +68,7 @@ func (s *ItemStats) Add(other ItemStats) { // ToNoder returns a restic.Node for a File. type ToNoder interface { - ToNode(ignoreXattrListError bool) (*restic.Node, error) + ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) } type archiverRepo interface { @@ -263,7 +263,9 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I // nodeFromFileInfo returns the restic node from an os.FileInfo. func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) { - node, err := meta.ToNode(ignoreXattrListError) + node, err := meta.ToNode(ignoreXattrListError, func(format string, args ...any) { + _ = arch.error(filename, fmt.Errorf(format, args...)) + }) // node does not exist. This prevents all further processing for this file. // If an error and a node are returned, then preserve as much data as possible (see below). if err != nil && node == nil { diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go index b0f338092..b52e47b18 100644 --- a/internal/archiver/archiver_test.go +++ b/internal/archiver/archiver_test.go @@ -551,7 +551,7 @@ func rename(t testing.TB, oldname, newname string) { func nodeFromFile(t testing.TB, localFs fs.FS, filename string) *restic.Node { meta, err := localFs.OpenFile(filename, fs.O_NOFOLLOW, true) rtest.OK(t, err) - node, err := meta.ToNode(false) + node, err := meta.ToNode(false, t.Logf) rtest.OK(t, err) rtest.OK(t, meta.Close()) @@ -2287,9 +2287,9 @@ func (f overrideFile) MakeReadable() error { return f.File.MakeReadable() } -func (f overrideFile) ToNode(ignoreXattrListError bool) (*restic.Node, error) { +func (f overrideFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) { if f.ofs.overrideNode == nil { - return f.File.ToNode(ignoreXattrListError) + return f.File.ToNode(ignoreXattrListError, warnf) } return f.ofs.overrideNode, f.ofs.overrideErr } @@ -2321,7 +2321,7 @@ func TestMetadataChanged(t *testing.T) { localFS := &fs.Local{} meta, err := localFS.OpenFile("testfile", fs.O_NOFOLLOW, true) rtest.OK(t, err) - want, err := meta.ToNode(false) + want, err := meta.ToNode(false, t.Logf) rtest.OK(t, err) rtest.OK(t, meta.Close()) @@ -2455,7 +2455,7 @@ type mockToNoder struct { err error } -func (m *mockToNoder) ToNode(_ bool) (*restic.Node, error) { +func (m *mockToNoder) ToNode(_ bool, _ func(format string, args ...any)) (*restic.Node, error) { return m.node, m.err } diff --git a/internal/archiver/file_saver_test.go b/internal/archiver/file_saver_test.go index af4ed0157..a5d090c05 100644 --- a/internal/archiver/file_saver_test.go +++ b/internal/archiver/file_saver_test.go @@ -50,7 +50,7 @@ func startFileSaver(ctx context.Context, t testing.TB, _ fs.FS) (*fileSaver, con s := newFileSaver(ctx, wg, saveBlob, pol, workers, workers) s.NodeFromFileInfo = func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) { - return meta.ToNode(ignoreXattrListError) + return meta.ToNode(ignoreXattrListError, t.Logf) } return s, ctx, wg diff --git a/internal/fs/fs_local.go b/internal/fs/fs_local.go index fc6c69cf2..dfbdab3b0 100644 --- a/internal/fs/fs_local.go +++ b/internal/fs/fs_local.go @@ -152,11 +152,11 @@ func (f *localFile) Stat() (*ExtendedFileInfo, error) { return f.fi, err } -func (f *localFile) ToNode(ignoreXattrListError bool) (*restic.Node, error) { +func (f *localFile) ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) { if err := f.cacheFI(); err != nil { return nil, err } - return nodeFromFileInfo(f.name, f.fi, ignoreXattrListError) + return nodeFromFileInfo(f.name, f.fi, ignoreXattrListError, warnf) } func (f *localFile) Read(p []byte) (n int, err error) { diff --git a/internal/fs/fs_local_test.go b/internal/fs/fs_local_test.go index 8fd8eb136..aff0b7a50 100644 --- a/internal/fs/fs_local_test.go +++ b/internal/fs/fs_local_test.go @@ -86,7 +86,7 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest rtest.OK(t, err) assertFIEqual(t, fi2, fi) - node, err := f.ToNode(false) + node, err := f.ToNode(false, t.Logf) rtest.OK(t, err) // ModTime is likely unique per file, thus it provides a good indication that it is from the correct file diff --git a/internal/fs/fs_local_vss_test.go b/internal/fs/fs_local_vss_test.go index 32f9f4cfd..bae08bd10 100644 --- a/internal/fs/fs_local_vss_test.go +++ b/internal/fs/fs_local_vss_test.go @@ -333,7 +333,7 @@ func TestVSSFS(t *testing.T) { rtest.OK(t, err) rtest.Equals(t, "example", string(data), "unexpected file content") - node, err := f.ToNode(false) + node, err := f.ToNode(false, t.Logf) rtest.OK(t, err) rtest.Equals(t, node.Mode, lstatFi.Mode) diff --git a/internal/fs/fs_reader.go b/internal/fs/fs_reader.go index c4e98be0f..32a4e8fee 100644 --- a/internal/fs/fs_reader.go +++ b/internal/fs/fs_reader.go @@ -267,7 +267,7 @@ func (f fakeFile) Stat() (*ExtendedFileInfo, error) { return f.fi, nil } -func (f fakeFile) ToNode(_ bool) (*restic.Node, error) { +func (f fakeFile) ToNode(_ bool, _ func(format string, args ...any)) (*restic.Node, error) { node := buildBasicNode(f.name, f.fi) // fill minimal info with current values for uid, gid diff --git a/internal/fs/interface.go b/internal/fs/interface.go index d75b0a91d..19d2eb1f7 100644 --- a/internal/fs/interface.go +++ b/internal/fs/interface.go @@ -48,5 +48,5 @@ type File interface { // ToNode returns a restic.Node for the File. The internally used os.FileInfo // must be consistent with that returned by Stat(). In particular, the metadata // returned by consecutive calls to Stat() and ToNode() must match. - ToNode(ignoreXattrListError bool) (*restic.Node, error) + ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) } diff --git a/internal/fs/node.go b/internal/fs/node.go index ab2aca957..ac8e58203 100644 --- a/internal/fs/node.go +++ b/internal/fs/node.go @@ -15,7 +15,7 @@ import ( // nodeFromFileInfo returns a new node from the given path and FileInfo. It // returns the first error that is encountered, together with a node. -func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bool) (*restic.Node, error) { +func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error) { node := buildBasicNode(path, fi) if err := nodeFillExtendedStat(node, path, fi); err != nil { @@ -23,7 +23,7 @@ func nodeFromFileInfo(path string, fi *ExtendedFileInfo, ignoreXattrListError bo } err := nodeFillGenericAttributes(node, path, fi) - err = errors.Join(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError)) + err = errors.Join(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError, warnf)) return node, err } diff --git a/internal/fs/node_noxattr.go b/internal/fs/node_noxattr.go index 2dbd72c9d..2923aea74 100644 --- a/internal/fs/node_noxattr.go +++ b/internal/fs/node_noxattr.go @@ -13,6 +13,6 @@ func nodeRestoreExtendedAttributes(_ *restic.Node, _ string, _ func(xattrName st } // nodeFillExtendedAttributes is a no-op -func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool) error { +func nodeFillExtendedAttributes(_ *restic.Node, _ string, _ bool, _ func(format string, args ...any)) error { return nil } diff --git a/internal/fs/node_test.go b/internal/fs/node_test.go index 176abc382..2b4c76c92 100644 --- a/internal/fs/node_test.go +++ b/internal/fs/node_test.go @@ -31,7 +31,7 @@ func BenchmarkNodeFromFileInfo(t *testing.B) { t.ResetTimer() for i := 0; i < t.N; i++ { - _, err := f.ToNode(false) + _, err := f.ToNode(false, t.Logf) rtest.OK(t, err) } @@ -223,9 +223,9 @@ func TestNodeRestoreAt(t *testing.T) { fs := &Local{} meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true) rtest.OK(t, err) - n2, err := meta.ToNode(false) + n2, err := meta.ToNode(false, t.Logf) rtest.OK(t, err) - n3, err := meta.ToNode(true) + n3, err := meta.ToNode(true, t.Logf) rtest.OK(t, err) rtest.OK(t, meta.Close()) rtest.Assert(t, n2.Equals(*n3), "unexpected node info mismatch %v", cmp.Diff(n2, n3)) diff --git a/internal/fs/node_unix_test.go b/internal/fs/node_unix_test.go index 1eb1ee506..7d5f7fa98 100644 --- a/internal/fs/node_unix_test.go +++ b/internal/fs/node_unix_test.go @@ -117,7 +117,7 @@ func TestNodeFromFileInfo(t *testing.T) { fs := &Local{} meta, err := fs.OpenFile(test.filename, O_NOFOLLOW, true) rtest.OK(t, err) - node, err := meta.ToNode(false) + node, err := meta.ToNode(false, t.Logf) rtest.OK(t, err) rtest.OK(t, meta.Close()) diff --git a/internal/fs/node_windows.go b/internal/fs/node_windows.go index df0a7ea65..416b5c7f8 100644 --- a/internal/fs/node_windows.go +++ b/internal/fs/node_windows.go @@ -91,7 +91,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi // fill extended attributes in the node // It also checks if the volume supports extended attributes and stores the result in a map // so that it does not have to be checked again for subsequent calls for paths in the same volume. -func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool) (err error) { +func nodeFillExtendedAttributes(node *restic.Node, path string, _ bool, _ func(format string, args ...any)) (err error) { if strings.Contains(filepath.Base(path), ":") { // Do not process for Alternate Data Streams in Windows return nil diff --git a/internal/fs/node_windows_test.go b/internal/fs/node_windows_test.go index 458a7bcb1..2d71e178e 100644 --- a/internal/fs/node_windows_test.go +++ b/internal/fs/node_windows_test.go @@ -224,7 +224,7 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn fs := &Local{} meta, err := fs.OpenFile(testPath, O_NOFOLLOW, true) test.OK(t, err) - nodeFromFileInfo, err := meta.ToNode(false) + nodeFromFileInfo, err := meta.ToNode(false, t.Logf) test.OK(t, errors.Wrapf(err, "Could not get NodeFromFileInfo for path: %s", testPath)) test.OK(t, meta.Close()) diff --git a/internal/fs/node_xattr.go b/internal/fs/node_xattr.go index f1119fe51..9ebd3524b 100644 --- a/internal/fs/node_xattr.go +++ b/internal/fs/node_xattr.go @@ -4,7 +4,6 @@ package fs import ( - "fmt" "os" "syscall" @@ -97,7 +96,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi return nil } -func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError bool) error { +func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError bool, warnf func(format string, args ...any)) error { xattrs, err := listxattr(path) debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err) if err != nil { @@ -111,7 +110,7 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError for _, attr := range xattrs { attrVal, err := getxattr(path, attr) if err != nil { - fmt.Fprintf(os.Stderr, "can not obtain extended attribute %v for %v:\n", attr, path) + warnf("can not obtain extended attribute %v for %v:\n", attr, path) continue } attr := restic.ExtendedAttribute{ diff --git a/internal/fs/node_xattr_all_test.go b/internal/fs/node_xattr_all_test.go index 6a9a2e4bf..9e35582ba 100644 --- a/internal/fs/node_xattr_all_test.go +++ b/internal/fs/node_xattr_all_test.go @@ -34,7 +34,7 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu nodeActual := &restic.Node{ Type: restic.NodeTypeFile, } - rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false)) + rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false, t.Logf)) rtest.Assert(t, nodeActual.Equals(*node), "xattr mismatch got %v expected %v", nodeActual.ExtendedAttributes, node.ExtendedAttributes) } @@ -59,7 +59,7 @@ func setAndVerifyXattrWithSelectFilter(t *testing.T, file string, testAttr []tes nodeActual := &restic.Node{ Type: restic.NodeTypeFile, } - rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false)) + rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false, t.Logf)) // Check nodeActual to make sure only xattrs we expect are there for _, testAttr := range testAttr { diff --git a/internal/restic/tree_test.go b/internal/restic/tree_test.go index 5c9c0739c..08ebe3984 100644 --- a/internal/restic/tree_test.go +++ b/internal/restic/tree_test.go @@ -86,7 +86,7 @@ func TestNodeMarshal(t *testing.T) { func nodeForFile(t *testing.T, name string) *restic.Node { f, err := (&fs.Local{}).OpenFile(name, fs.O_NOFOLLOW, true) rtest.OK(t, err) - node, err := f.ToNode(false) + node, err := f.ToNode(false, t.Logf) rtest.OK(t, err) rtest.OK(t, f.Close()) return node