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 <accounts+github@chiller3.com>
This commit is contained in:
Andrew Gunnerson 2023-06-19 14:24:47 -04:00
parent 74ca82a6f8
commit 8c02ebb029
No known key found for this signature in database
4 changed files with 31 additions and 7 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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 {