restic/internal/fuse/link.go
Andrew Gunnerson 8c02ebb029
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>
2023-06-19 14:37:31 -04:00

68 lines
1.6 KiB
Go

//go:build darwin || freebsd || linux
// +build darwin freebsd linux
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"
)
// Statically ensure that *link implements the given interface
var _ = fs.NodeReadlinker(&link{})
type link struct {
root *Root
node *restic.Node
inode uint64
}
func newLink(root *Root, inode uint64, node *restic.Node) (*link, error) {
return &link{root: root, inode: inode, node: node}, nil
}
func (l *link) Readlink(_ context.Context, _ *fuse.ReadlinkRequest) (string, error) {
return l.node.LinkTarget, nil
}
func (l *link) Attr(_ context.Context, a *fuse.Attr) error {
a.Inode = l.inode
a.Mode = l.node.Mode
if !l.root.cfg.OwnerIsRoot {
a.Uid = l.node.UID
a.Gid = l.node.GID
}
a.Atime = l.node.AccessTime
a.Ctime = l.node.ChangeTime
a.Mtime = l.node.ModTime
a.Nlink = uint32(l.node.Links)
a.Size = uint64(len(l.node.LinkTarget))
a.Blocks = (a.Size + blockSize - 1) / blockSize
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
}