From 8c02ebb0299cf517aa7a48bfc27bdf1ad5a5b7ba Mon Sep 17 00:00:00 2001 From: Andrew Gunnerson Date: Mon, 19 Jun 2023 14:24:47 -0400 Subject: [PATCH] Add support for extended attributes on symlinks Linux allows the use of non-`user.` extended attributes on symlinks. One of the main users of this functionality is SELinux's `security.selinux` xattr for storing a path's label. By storing symlink xattrs, restic is now suitable for backing up the root filesystem on Linux distributions that use SELinux. This commit adds support for symlink xattrs when backing up data, restoring data, and mounting snapshots via a fuse mount. All calls to the xattr library have been updated to the use `L` variants of the various functions, which always operate on the path given, without following symlinks. Fixes: #4375 Signed-off-by: Andrew Gunnerson --- changelog/unreleased/issue-4375 | 8 ++++++++ internal/fuse/link.go | 20 ++++++++++++++++++++ internal/restic/node.go | 4 ---- internal/restic/node_xattr.go | 6 +++--- 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/issue-4375 diff --git a/changelog/unreleased/issue-4375 b/changelog/unreleased/issue-4375 new file mode 100644 index 000000000..6ce68c2ba --- /dev/null +++ b/changelog/unreleased/issue-4375 @@ -0,0 +1,8 @@ +Enhancement: Add support for extended attributes on symlinks + +Restic now supports extended attributes on symlinks when backing up, +restoring, or FUSE-mounting snapshots. This includes, for example, the +`security.selinux` xattr on Linux distributions that use SELinux. + +https://github.com/restic/restic/issues/4375 +https://github.com/restic/restic/pull/4379 diff --git a/internal/fuse/link.go b/internal/fuse/link.go index c89451602..43b00a855 100644 --- a/internal/fuse/link.go +++ b/internal/fuse/link.go @@ -6,6 +6,8 @@ package fuse import ( "context" + "github.com/restic/restic/internal/debug" + "github.com/anacrolix/fuse" "github.com/anacrolix/fuse/fs" "github.com/restic/restic/internal/restic" @@ -46,3 +48,21 @@ func (l *link) Attr(_ context.Context, a *fuse.Attr) error { return nil } + +func (l *link) Listxattr(_ context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error { + debug.Log("Listxattr(%v, %v)", l.node.Name, req.Size) + for _, attr := range l.node.ExtendedAttributes { + resp.Append(attr.Name) + } + return nil +} + +func (l *link) Getxattr(_ context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error { + debug.Log("Getxattr(%v, %v, %v)", l.node.Name, req.Name, req.Size) + attrval := l.node.GetExtendedAttribute(req.Name) + if attrval != nil { + resp.Xattr = attrval + return nil + } + return fuse.ErrNoXattr +} diff --git a/internal/restic/node.go b/internal/restic/node.go index 7d2a1434e..f2d9f2315 100644 --- a/internal/restic/node.go +++ b/internal/restic/node.go @@ -609,10 +609,6 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error { } func (node *Node) fillExtendedAttributes(path string) error { - if node.Type == "symlink" { - return nil - } - xattrs, err := Listxattr(path) debug.Log("fillExtendedAttributes(%v) %v %v", path, xattrs, err) if err != nil { diff --git a/internal/restic/node_xattr.go b/internal/restic/node_xattr.go index a2eed39c0..ea9eafe94 100644 --- a/internal/restic/node_xattr.go +++ b/internal/restic/node_xattr.go @@ -13,20 +13,20 @@ import ( // Getxattr retrieves extended attribute data associated with path. func Getxattr(path, name string) ([]byte, error) { - b, err := xattr.Get(path, name) + b, err := xattr.LGet(path, name) return b, handleXattrErr(err) } // Listxattr retrieves a list of names of extended attributes associated with the // given path in the file system. func Listxattr(path string) ([]string, error) { - l, err := xattr.List(path) + l, err := xattr.LList(path) return l, handleXattrErr(err) } // Setxattr associates name and data together as an attribute of path. func Setxattr(path, name string, data []byte) error { - return handleXattrErr(xattr.Set(path, name, data)) + return handleXattrErr(xattr.LSet(path, name, data)) } func handleXattrErr(err error) error {