From 4a9603d253308ee20dbf1d6e13a2866f78aa19d6 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 29 Nov 2024 23:45:17 +0100 Subject: [PATCH] fs: test correct xattr handling for symlinks --- internal/fs/fs_local_test.go | 11 +++-- internal/fs/fs_local_xattr_test.go | 76 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 internal/fs/fs_local_xattr_test.go diff --git a/internal/fs/fs_local_test.go b/internal/fs/fs_local_test.go index 9e6f7fcae..e747418ea 100644 --- a/internal/fs/fs_local_test.go +++ b/internal/fs/fs_local_test.go @@ -17,6 +17,7 @@ type fsLocalMetadataTestcase struct { follow bool setup func(t *testing.T, path string) nodeType restic.NodeType + check func(t *testing.T, node *restic.Node) } func testHandleVariants(t *testing.T, test func(t *testing.T)) { @@ -90,13 +91,15 @@ func runFSLocalTestcase(t *testing.T, test fsLocalMetadataTestcase) { } f, err := testFs.OpenFile(path, flags, true) rtest.OK(t, err) - checkMetadata(t, f, path, test.follow, test.nodeType) + node := checkMetadata(t, f, path, test.follow, test.nodeType) + if test.check != nil { + test.check(t, node) + } rtest.OK(t, f.Close()) }) - } -func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType restic.NodeType) { +func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType restic.NodeType) *restic.Node { fi, err := f.Stat() rtest.OK(t, err) var fi2 os.FileInfo @@ -114,6 +117,8 @@ func checkMetadata(t *testing.T, f File, path string, follow bool, nodeType rest // ModTime is likely unique per file, thus it provides a good indication that it is from the correct file rtest.Equals(t, fi.ModTime, node.ModTime, "node ModTime") rtest.Equals(t, nodeType, node.Type, "node Type") + + return node } func assertFIEqual(t *testing.T, want os.FileInfo, got *ExtendedFileInfo) { diff --git a/internal/fs/fs_local_xattr_test.go b/internal/fs/fs_local_xattr_test.go new file mode 100644 index 000000000..581271da5 --- /dev/null +++ b/internal/fs/fs_local_xattr_test.go @@ -0,0 +1,76 @@ +//go:build darwin || freebsd || linux || solaris || windows + +package fs + +import ( + "os" + "runtime" + "strings" + "testing" + + "github.com/restic/restic/internal/restic" + rtest "github.com/restic/restic/internal/test" +) + +func TestXattrNoFollow(t *testing.T) { + xattrs := []restic.ExtendedAttribute{ + { + Name: "user.foo", + Value: []byte("bar"), + }, + } + if runtime.GOOS == "windows" { + // windows seems to convert the xattr name to upper case + for i := range xattrs { + xattrs[i].Name = strings.ToUpper(xattrs[i].Name) + } + } + + setXattrs := func(path string) { + node := &restic.Node{ + Type: restic.NodeTypeFile, + ExtendedAttributes: xattrs, + } + rtest.OK(t, nodeRestoreExtendedAttributes(node, path)) + } + checkXattrs := func(expected []restic.ExtendedAttribute) func(t *testing.T, node *restic.Node) { + return func(t *testing.T, node *restic.Node) { + rtest.Equals(t, expected, node.ExtendedAttributes, "xattr mismatch for file") + } + } + + setupSymlinkTest := func(t *testing.T, path string) { + rtest.OK(t, os.WriteFile(path+"file", []byte("example"), 0o600)) + setXattrs(path + "file") + rtest.OK(t, os.Symlink(path+"file", path)) + } + + for _, test := range []fsLocalMetadataTestcase{ + { + name: "file", + setup: func(t *testing.T, path string) { + rtest.OK(t, os.WriteFile(path, []byte("example"), 0o600)) + setXattrs(path) + }, + nodeType: restic.NodeTypeFile, + check: checkXattrs(xattrs), + }, + { + name: "symlink", + setup: setupSymlinkTest, + nodeType: restic.NodeTypeSymlink, + check: checkXattrs([]restic.ExtendedAttribute{}), + }, + { + name: "symlink file", + follow: true, + setup: setupSymlinkTest, + nodeType: restic.NodeTypeFile, + check: checkXattrs(xattrs), + }, + } { + testHandleVariants(t, func(t *testing.T) { + runFSLocalTestcase(t, test) + }) + } +}