mirror of https://github.com/restic/restic.git
Merge pull request #251 from restic/fuse-ownership
fuse/mount: Use original UID/GID as owner
This commit is contained in:
commit
17ee6b1c4b
|
@ -13,6 +13,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type CmdMount struct {
|
type CmdMount struct {
|
||||||
|
Root bool `long:"owner-root" description:"use 'root' as the owner of files and dirs" default:"false"`
|
||||||
|
|
||||||
global *GlobalOptions
|
global *GlobalOptions
|
||||||
ready chan struct{}
|
ready chan struct{}
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
@ -69,7 +71,7 @@ func (cmd CmdMount) Execute(args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
root := fs.Tree{}
|
root := fs.Tree{}
|
||||||
root.Add("snapshots", fuse.NewSnapshotsDir(repo))
|
root.Add("snapshots", fuse.NewSnapshotsDir(repo, cmd.Root))
|
||||||
|
|
||||||
cmd.global.Printf("Now serving %s at %s\n", repo.Backend().Location(), mountpoint)
|
cmd.global.Printf("Now serving %s at %s\n", repo.Backend().Location(), mountpoint)
|
||||||
cmd.global.Printf("Don't forget to umount after quitting!\n")
|
cmd.global.Printf("Don't forget to umount after quitting!\n")
|
||||||
|
|
33
fuse/dir.go
33
fuse/dir.go
|
@ -19,9 +19,11 @@ type dir struct {
|
||||||
repo *repository.Repository
|
repo *repository.Repository
|
||||||
items map[string]*restic.Node
|
items map[string]*restic.Node
|
||||||
inode uint64
|
inode uint64
|
||||||
|
node *restic.Node
|
||||||
|
ownerIsRoot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDir(repo *repository.Repository, node *restic.Node) (*dir, error) {
|
func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*dir, error) {
|
||||||
tree, err := restic.LoadTree(repo, *node.Subtree)
|
tree, err := restic.LoadTree(repo, *node.Subtree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -33,12 +35,14 @@ func newDir(repo *repository.Repository, node *restic.Node) (*dir, error) {
|
||||||
|
|
||||||
return &dir{
|
return &dir{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
node: node,
|
||||||
items: items,
|
items: items,
|
||||||
inode: node.Inode,
|
inode: node.Inode,
|
||||||
|
ownerIsRoot: ownerIsRoot,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId) (*dir, error) {
|
func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId, ownerIsRoot bool) (*dir, error) {
|
||||||
tree, err := restic.LoadTree(repo, *snapshot.Tree)
|
tree, err := restic.LoadTree(repo, *snapshot.Tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -50,14 +54,31 @@ func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId) (*
|
||||||
|
|
||||||
return &dir{
|
return &dir{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
node: &restic.Node{
|
||||||
|
UID: uint32(os.Getuid()),
|
||||||
|
GID: uint32(os.Getgid()),
|
||||||
|
AccessTime: snapshot.Time,
|
||||||
|
ModTime: snapshot.Time,
|
||||||
|
ChangeTime: snapshot.Time,
|
||||||
|
Mode: os.ModeDir | 0555,
|
||||||
|
},
|
||||||
items: items,
|
items: items,
|
||||||
inode: inodeFromBackendId(snapshot.ID),
|
inode: inodeFromBackendId(snapshot.ID),
|
||||||
|
ownerIsRoot: ownerIsRoot,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dir) Attr(ctx context.Context, a *fuse.Attr) error {
|
func (d *dir) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
a.Inode = d.inode
|
a.Inode = d.inode
|
||||||
a.Mode = os.ModeDir | 0555
|
a.Mode = os.ModeDir | d.node.Mode
|
||||||
|
|
||||||
|
if !d.ownerIsRoot {
|
||||||
|
a.Uid = d.node.UID
|
||||||
|
a.Gid = d.node.GID
|
||||||
|
}
|
||||||
|
a.Atime = d.node.AccessTime
|
||||||
|
a.Ctime = d.node.ChangeTime
|
||||||
|
a.Mtime = d.node.ModTime
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +113,11 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||||
}
|
}
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
case "dir":
|
case "dir":
|
||||||
return newDir(d.repo, node)
|
return newDir(d.repo, node, d.ownerIsRoot)
|
||||||
case "file":
|
case "file":
|
||||||
return newFile(d.repo, node)
|
return newFile(d.repo, node, d.ownerIsRoot)
|
||||||
case "symlink":
|
case "symlink":
|
||||||
return newLink(d.repo, node)
|
return newLink(d.repo, node, d.ownerIsRoot)
|
||||||
default:
|
default:
|
||||||
return nil, fuse.ENOENT
|
return nil, fuse.ENOENT
|
||||||
}
|
}
|
||||||
|
|
12
fuse/file.go
12
fuse/file.go
|
@ -26,6 +26,7 @@ type BlobLoader interface {
|
||||||
type file struct {
|
type file struct {
|
||||||
repo BlobLoader
|
repo BlobLoader
|
||||||
node *restic.Node
|
node *restic.Node
|
||||||
|
ownerIsRoot bool
|
||||||
|
|
||||||
sizes []uint
|
sizes []uint
|
||||||
blobs [][]byte
|
blobs [][]byte
|
||||||
|
@ -39,7 +40,7 @@ var blobPool = sync.Pool{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFile(repo BlobLoader, node *restic.Node) (*file, error) {
|
func newFile(repo BlobLoader, node *restic.Node, ownerIsRoot bool) (*file, error) {
|
||||||
sizes := make([]uint, len(node.Content))
|
sizes := make([]uint, len(node.Content))
|
||||||
for i, id := range node.Content {
|
for i, id := range node.Content {
|
||||||
size, err := repo.LookupBlobSize(id)
|
size, err := repo.LookupBlobSize(id)
|
||||||
|
@ -55,6 +56,7 @@ func newFile(repo BlobLoader, node *restic.Node) (*file, error) {
|
||||||
node: node,
|
node: node,
|
||||||
sizes: sizes,
|
sizes: sizes,
|
||||||
blobs: make([][]byte, len(node.Content)),
|
blobs: make([][]byte, len(node.Content)),
|
||||||
|
ownerIsRoot: ownerIsRoot,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +64,14 @@ func (f *file) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
a.Inode = f.node.Inode
|
a.Inode = f.node.Inode
|
||||||
a.Mode = f.node.Mode
|
a.Mode = f.node.Mode
|
||||||
a.Size = f.node.Size
|
a.Size = f.node.Size
|
||||||
|
|
||||||
|
if !f.ownerIsRoot {
|
||||||
|
a.Uid = f.node.UID
|
||||||
|
a.Gid = f.node.GID
|
||||||
|
}
|
||||||
|
a.Atime = f.node.AccessTime
|
||||||
|
a.Ctime = f.node.ChangeTime
|
||||||
|
a.Mtime = f.node.ModTime
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package fuse
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -121,7 +120,7 @@ func TestFuseFile(t *testing.T) {
|
||||||
Size: 42,
|
Size: 42,
|
||||||
Content: ids,
|
Content: ids,
|
||||||
}
|
}
|
||||||
f, err := newFile(repo, node)
|
f, err := newFile(repo, node, false)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
attr := fuse.Attr{}
|
attr := fuse.Attr{}
|
||||||
|
@ -148,7 +147,6 @@ func TestFuseFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := memfile[offset : offset+length]
|
b := memfile[offset : offset+length]
|
||||||
fmt.Printf("test offset %d, length %d\n", offset, length)
|
|
||||||
res := testRead(t, f, offset, length, b)
|
res := testRead(t, f, offset, length, b)
|
||||||
if !bytes.Equal(b, res) {
|
if !bytes.Equal(b, res) {
|
||||||
t.Errorf("test %d failed (offset %d, length %d), wrong data returned", i, offset, length)
|
t.Errorf("test %d failed (offset %d, length %d), wrong data returned", i, offset, length)
|
||||||
|
|
13
fuse/link.go
13
fuse/link.go
|
@ -13,10 +13,11 @@ var _ = fs.NodeReadlinker(&link{})
|
||||||
|
|
||||||
type link struct {
|
type link struct {
|
||||||
node *restic.Node
|
node *restic.Node
|
||||||
|
ownerIsRoot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLink(repo *repository.Repository, node *restic.Node) (*link, error) {
|
func newLink(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*link, error) {
|
||||||
return &link{node: node}, nil
|
return &link{node: node, ownerIsRoot: ownerIsRoot}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
|
func (l *link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
|
||||||
|
@ -26,5 +27,13 @@ func (l *link) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string,
|
||||||
func (l *link) Attr(ctx context.Context, a *fuse.Attr) error {
|
func (l *link) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||||
a.Inode = l.node.Inode
|
a.Inode = l.node.Inode
|
||||||
a.Mode = l.node.Mode
|
a.Mode = l.node.Mode
|
||||||
|
|
||||||
|
if !l.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
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,22 +28,29 @@ var _ = fs.NodeStringLookuper(&SnapshotsDir{})
|
||||||
|
|
||||||
type SnapshotsDir struct {
|
type SnapshotsDir struct {
|
||||||
repo *repository.Repository
|
repo *repository.Repository
|
||||||
|
ownerIsRoot bool
|
||||||
|
|
||||||
// knownSnapshots maps snapshot timestamp to the snapshot
|
// knownSnapshots maps snapshot timestamp to the snapshot
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
knownSnapshots map[string]SnapshotWithId
|
knownSnapshots map[string]SnapshotWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSnapshotsDir(repo *repository.Repository) *SnapshotsDir {
|
func NewSnapshotsDir(repo *repository.Repository, ownerIsRoot bool) *SnapshotsDir {
|
||||||
return &SnapshotsDir{
|
return &SnapshotsDir{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
knownSnapshots: make(map[string]SnapshotWithId),
|
knownSnapshots: make(map[string]SnapshotWithId),
|
||||||
|
ownerIsRoot: ownerIsRoot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sn *SnapshotsDir) Attr(ctx context.Context, attr *fuse.Attr) error {
|
func (sn *SnapshotsDir) Attr(ctx context.Context, attr *fuse.Attr) error {
|
||||||
attr.Inode = 0
|
attr.Inode = 0
|
||||||
attr.Mode = os.ModeDir | 0555
|
attr.Mode = os.ModeDir | 0555
|
||||||
|
|
||||||
|
if !sn.ownerIsRoot {
|
||||||
|
attr.Uid = uint32(os.Getuid())
|
||||||
|
attr.Gid = uint32(os.Getgid())
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,5 +112,5 @@ func (sn *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newDirFromSnapshot(sn.repo, snapshot)
|
return newDirFromSnapshot(sn.repo, snapshot, sn.ownerIsRoot)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue