mirror of
https://github.com/restic/restic.git
synced 2025-12-11 18:47:50 +00:00
node: report error on xattr retrieval using standard error logging
This commit is contained in:
@@ -68,7 +68,7 @@ func (s *ItemStats) Add(other ItemStats) {
|
|||||||
|
|
||||||
// ToNoder returns a restic.Node for a File.
|
// ToNoder returns a restic.Node for a File.
|
||||||
type ToNoder interface {
|
type ToNoder interface {
|
||||||
ToNode(ignoreXattrListError bool) (*restic.Node, error)
|
ToNode(ignoreXattrListError bool, warnf func(format string, args ...any)) (*restic.Node, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type archiverRepo interface {
|
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.
|
// nodeFromFileInfo returns the restic node from an os.FileInfo.
|
||||||
func (arch *Archiver) nodeFromFileInfo(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) {
|
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.
|
// 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 an error and a node are returned, then preserve as much data as possible (see below).
|
||||||
if err != nil && node == nil {
|
if err != nil && node == nil {
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ func rename(t testing.TB, oldname, newname string) {
|
|||||||
func nodeFromFile(t testing.TB, localFs fs.FS, filename string) *restic.Node {
|
func nodeFromFile(t testing.TB, localFs fs.FS, filename string) *restic.Node {
|
||||||
meta, err := localFs.OpenFile(filename, fs.O_NOFOLLOW, true)
|
meta, err := localFs.OpenFile(filename, fs.O_NOFOLLOW, true)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
node, err := meta.ToNode(false)
|
node, err := meta.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.OK(t, meta.Close())
|
rtest.OK(t, meta.Close())
|
||||||
|
|
||||||
@@ -2287,9 +2287,9 @@ func (f overrideFile) MakeReadable() error {
|
|||||||
return f.File.MakeReadable()
|
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 {
|
if f.ofs.overrideNode == nil {
|
||||||
return f.File.ToNode(ignoreXattrListError)
|
return f.File.ToNode(ignoreXattrListError, warnf)
|
||||||
}
|
}
|
||||||
return f.ofs.overrideNode, f.ofs.overrideErr
|
return f.ofs.overrideNode, f.ofs.overrideErr
|
||||||
}
|
}
|
||||||
@@ -2321,7 +2321,7 @@ func TestMetadataChanged(t *testing.T) {
|
|||||||
localFS := &fs.Local{}
|
localFS := &fs.Local{}
|
||||||
meta, err := localFS.OpenFile("testfile", fs.O_NOFOLLOW, true)
|
meta, err := localFS.OpenFile("testfile", fs.O_NOFOLLOW, true)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
want, err := meta.ToNode(false)
|
want, err := meta.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.OK(t, meta.Close())
|
rtest.OK(t, meta.Close())
|
||||||
|
|
||||||
@@ -2455,7 +2455,7 @@ type mockToNoder struct {
|
|||||||
err error
|
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
|
return m.node, m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 := newFileSaver(ctx, wg, saveBlob, pol, workers, workers)
|
||||||
s.NodeFromFileInfo = func(snPath, filename string, meta ToNoder, ignoreXattrListError bool) (*restic.Node, error) {
|
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
|
return s, ctx, wg
|
||||||
|
|||||||
@@ -152,11 +152,11 @@ func (f *localFile) Stat() (*ExtendedFileInfo, error) {
|
|||||||
return f.fi, err
|
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 {
|
if err := f.cacheFI(); err != nil {
|
||||||
return nil, err
|
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) {
|
func (f *localFile) Read(p []byte) (n int, err error) {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest
|
|||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
assertFIEqual(t, fi2, fi)
|
assertFIEqual(t, fi2, fi)
|
||||||
|
|
||||||
node, err := f.ToNode(false)
|
node, err := f.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
// ModTime is likely unique per file, thus it provides a good indication that it is from the correct file
|
// ModTime is likely unique per file, thus it provides a good indication that it is from the correct file
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ func TestVSSFS(t *testing.T) {
|
|||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.Equals(t, "example", string(data), "unexpected file content")
|
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.OK(t, err)
|
||||||
rtest.Equals(t, node.Mode, lstatFi.Mode)
|
rtest.Equals(t, node.Mode, lstatFi.Mode)
|
||||||
|
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ func (f fakeFile) Stat() (*ExtendedFileInfo, error) {
|
|||||||
return f.fi, nil
|
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)
|
node := buildBasicNode(f.name, f.fi)
|
||||||
|
|
||||||
// fill minimal info with current values for uid, gid
|
// fill minimal info with current values for uid, gid
|
||||||
|
|||||||
@@ -48,5 +48,5 @@ type File interface {
|
|||||||
// ToNode returns a restic.Node for the File. The internally used os.FileInfo
|
// 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
|
// must be consistent with that returned by Stat(). In particular, the metadata
|
||||||
// returned by consecutive calls to Stat() and ToNode() must match.
|
// 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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
// nodeFromFileInfo returns a new node from the given path and FileInfo. It
|
// nodeFromFileInfo returns a new node from the given path and FileInfo. It
|
||||||
// returns the first error that is encountered, together with a node.
|
// 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)
|
node := buildBasicNode(path, fi)
|
||||||
|
|
||||||
if err := nodeFillExtendedStat(node, path, fi); err != nil {
|
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 := nodeFillGenericAttributes(node, path, fi)
|
||||||
err = errors.Join(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError))
|
err = errors.Join(err, nodeFillExtendedAttributes(node, path, ignoreXattrListError, warnf))
|
||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ func nodeRestoreExtendedAttributes(_ *restic.Node, _ string, _ func(xattrName st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// nodeFillExtendedAttributes is a no-op
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func BenchmarkNodeFromFileInfo(t *testing.B) {
|
|||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < t.N; i++ {
|
for i := 0; i < t.N; i++ {
|
||||||
_, err := f.ToNode(false)
|
_, err := f.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,9 +223,9 @@ func TestNodeRestoreAt(t *testing.T) {
|
|||||||
fs := &Local{}
|
fs := &Local{}
|
||||||
meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true)
|
meta, err := fs.OpenFile(nodePath, O_NOFOLLOW, true)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
n2, err := meta.ToNode(false)
|
n2, err := meta.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
n3, err := meta.ToNode(true)
|
n3, err := meta.ToNode(true, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.OK(t, meta.Close())
|
rtest.OK(t, meta.Close())
|
||||||
rtest.Assert(t, n2.Equals(*n3), "unexpected node info mismatch %v", cmp.Diff(n2, n3))
|
rtest.Assert(t, n2.Equals(*n3), "unexpected node info mismatch %v", cmp.Diff(n2, n3))
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func TestNodeFromFileInfo(t *testing.T) {
|
|||||||
fs := &Local{}
|
fs := &Local{}
|
||||||
meta, err := fs.OpenFile(test.filename, O_NOFOLLOW, true)
|
meta, err := fs.OpenFile(test.filename, O_NOFOLLOW, true)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
node, err := meta.ToNode(false)
|
node, err := meta.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.OK(t, meta.Close())
|
rtest.OK(t, meta.Close())
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi
|
|||||||
// fill extended attributes in the node
|
// fill extended attributes in the node
|
||||||
// It also checks if the volume supports extended attributes and stores the result in a map
|
// 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.
|
// 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), ":") {
|
if strings.Contains(filepath.Base(path), ":") {
|
||||||
// Do not process for Alternate Data Streams in Windows
|
// Do not process for Alternate Data Streams in Windows
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn
|
|||||||
fs := &Local{}
|
fs := &Local{}
|
||||||
meta, err := fs.OpenFile(testPath, O_NOFOLLOW, true)
|
meta, err := fs.OpenFile(testPath, O_NOFOLLOW, true)
|
||||||
test.OK(t, err)
|
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, errors.Wrapf(err, "Could not get NodeFromFileInfo for path: %s", testPath))
|
||||||
test.OK(t, meta.Close())
|
test.OK(t, meta.Close())
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@@ -97,7 +96,7 @@ func nodeRestoreExtendedAttributes(node *restic.Node, path string, xattrSelectFi
|
|||||||
return nil
|
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)
|
xattrs, err := listxattr(path)
|
||||||
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -111,7 +110,7 @@ func nodeFillExtendedAttributes(node *restic.Node, path string, ignoreListError
|
|||||||
for _, attr := range xattrs {
|
for _, attr := range xattrs {
|
||||||
attrVal, err := getxattr(path, attr)
|
attrVal, err := getxattr(path, attr)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
attr := restic.ExtendedAttribute{
|
attr := restic.ExtendedAttribute{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
|
|||||||
nodeActual := &restic.Node{
|
nodeActual := &restic.Node{
|
||||||
Type: restic.NodeTypeFile,
|
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)
|
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{
|
nodeActual := &restic.Node{
|
||||||
Type: restic.NodeTypeFile,
|
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
|
// Check nodeActual to make sure only xattrs we expect are there
|
||||||
for _, testAttr := range testAttr {
|
for _, testAttr := range testAttr {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func TestNodeMarshal(t *testing.T) {
|
|||||||
func nodeForFile(t *testing.T, name string) *restic.Node {
|
func nodeForFile(t *testing.T, name string) *restic.Node {
|
||||||
f, err := (&fs.Local{}).OpenFile(name, fs.O_NOFOLLOW, true)
|
f, err := (&fs.Local{}).OpenFile(name, fs.O_NOFOLLOW, true)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
node, err := f.ToNode(false)
|
node, err := f.ToNode(false, t.Logf)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.OK(t, f.Close())
|
rtest.OK(t, f.Close())
|
||||||
return node
|
return node
|
||||||
|
|||||||
Reference in New Issue
Block a user