Merge pull request #1050 from restic/extend-fuse-mount

fuse: Add more directories
This commit is contained in:
Alexander Neumann 2017-06-19 19:52:45 +02:00
commit bca9566849
4 changed files with 178 additions and 88 deletions

View File

@ -22,12 +22,13 @@ type dir struct {
root *Root root *Root
items map[string]*restic.Node items map[string]*restic.Node
inode uint64 inode uint64
parentInode uint64
node *restic.Node node *restic.Node
blobsize *BlobSizeCache blobsize *BlobSizeCache
} }
func newDir(ctx context.Context, root *Root, inode uint64, node *restic.Node) (*dir, error) { func newDir(ctx context.Context, root *Root, inode, parentInode uint64, node *restic.Node) (*dir, error) {
debug.Log("new dir for %v (%v)", node.Name, node.Subtree.Str()) debug.Log("new dir for %v (%v)", node.Name, node.Subtree.Str())
tree, err := root.repo.LoadTree(ctx, *node.Subtree) tree, err := root.repo.LoadTree(ctx, *node.Subtree)
if err != nil { if err != nil {
@ -44,6 +45,7 @@ func newDir(ctx context.Context, root *Root, inode uint64, node *restic.Node) (*
node: node, node: node,
items: items, items: items,
inode: inode, inode: inode,
parentInode: parentInode,
}, nil }, nil
} }
@ -134,7 +136,19 @@ func (d *dir) calcNumberOfLinks() uint32 {
func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
debug.Log("called") debug.Log("called")
ret := make([]fuse.Dirent, 0, len(d.items)) ret := make([]fuse.Dirent, 0, len(d.items)+2)
ret = append(ret, fuse.Dirent{
Inode: d.inode,
Name: ".",
Type: fuse.DT_Dir,
})
ret = append(ret, fuse.Dirent{
Inode: d.parentInode,
Name: "..",
Type: fuse.DT_Dir,
})
for _, node := range d.items { for _, node := range d.items {
var typ fuse.DirentType var typ fuse.DirentType
@ -166,7 +180,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
} }
switch node.Type { switch node.Type {
case "dir": case "dir":
return newDir(ctx, d.root, fs.GenerateDynamicInode(d.inode, name), node) return newDir(ctx, d.root, fs.GenerateDynamicInode(d.inode, name), d.inode, node)
case "file": case "file":
return newFile(ctx, d.root, fs.GenerateDynamicInode(d.inode, name), node) return newFile(ctx, d.root, fs.GenerateDynamicInode(d.inode, name), node)
case "symlink": case "symlink":

View File

@ -0,0 +1,87 @@
// +build !openbsd
// +build !windows
package fuse
import (
"os"
"restic/debug"
"golang.org/x/net/context"
"bazil.org/fuse"
"bazil.org/fuse/fs"
)
// ensure that *DirSnapshots implements these interfaces
var _ = fs.HandleReadDirAller(&MetaDir{})
var _ = fs.NodeStringLookuper(&MetaDir{})
// MetaDir is a fuse directory which contains other directories.
type MetaDir struct {
inode uint64
root *Root
entries map[string]fs.Node
}
// NewMetaDir returns a new meta dir.
func NewMetaDir(root *Root, inode uint64, entries map[string]fs.Node) *MetaDir {
debug.Log("new meta dir with %d entries, inode %d", len(entries), inode)
return &MetaDir{
root: root,
inode: inode,
entries: entries,
}
}
// Attr returns the attributes for the root node.
func (d *MetaDir) Attr(ctx context.Context, attr *fuse.Attr) error {
attr.Inode = d.inode
attr.Mode = os.ModeDir | 0555
if !d.root.cfg.OwnerIsRoot {
attr.Uid = uint32(os.Getuid())
attr.Gid = uint32(os.Getgid())
}
debug.Log("attr: %v", attr)
return nil
}
// ReadDirAll returns all entries of the root node.
func (d *MetaDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
debug.Log("ReadDirAll()")
items := []fuse.Dirent{
{
Inode: d.inode,
Name: ".",
Type: fuse.DT_Dir,
},
{
Inode: d.root.inode,
Name: "..",
Type: fuse.DT_Dir,
},
}
for name := range d.entries {
items = append(items, fuse.Dirent{
Inode: fs.GenerateDynamicInode(d.inode, name),
Name: name,
Type: fuse.DT_Dir,
})
}
return items, nil
}
// Lookup returns a specific entry from the root node.
func (d *MetaDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
debug.Log("Lookup(%s)", name)
if dir, ok := d.entries[name]; ok {
return dir, nil
}
return nil, fuse.ENOENT
}

View File

@ -4,13 +4,11 @@
package fuse package fuse
import ( import (
"os"
"restic" "restic"
"restic/debug" "restic/debug"
"golang.org/x/net/context" "golang.org/x/net/context"
"bazil.org/fuse"
"bazil.org/fuse/fs" "bazil.org/fuse/fs"
) )
@ -28,14 +26,17 @@ type Root struct {
cfg Config cfg Config
inode uint64 inode uint64
snapshots restic.Snapshots snapshots restic.Snapshots
dirSnapshots *DirSnapshots
blobSizeCache *BlobSizeCache blobSizeCache *BlobSizeCache
*MetaDir
} }
// ensure that *Root implements these interfaces // ensure that *Root implements these interfaces
var _ = fs.HandleReadDirAller(&Root{}) var _ = fs.HandleReadDirAller(&Root{})
var _ = fs.NodeStringLookuper(&Root{}) var _ = fs.NodeStringLookuper(&Root{})
const rootInode = 1
// NewRoot initializes a new root node from a repository. // NewRoot initializes a new root node from a repository.
func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, error) { func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, error) {
debug.Log("NewRoot(), config %v", cfg) debug.Log("NewRoot(), config %v", cfg)
@ -45,77 +46,65 @@ func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, er
root := &Root{ root := &Root{
repo: repo, repo: repo,
inode: rootInode,
cfg: cfg, cfg: cfg,
inode: 1,
snapshots: snapshots, snapshots: snapshots,
blobSizeCache: NewBlobSizeCache(ctx, repo.Index()),
} }
root.dirSnapshots = NewDirSnapshots(root, fs.GenerateDynamicInode(root.inode, "snapshots"), snapshots) entries := map[string]fs.Node{
root.blobSizeCache = NewBlobSizeCache(ctx, repo.Index()) "snapshots": NewSnapshotsDir(root, fs.GenerateDynamicInode(root.inode, "snapshots"), snapshots),
"tags": NewTagsDir(root, fs.GenerateDynamicInode(root.inode, "tags"), snapshots),
"hosts": NewHostsDir(root, fs.GenerateDynamicInode(root.inode, "hosts"), snapshots),
}
root.MetaDir = NewMetaDir(root, rootInode, entries)
return root, nil return root, nil
} }
// NewTagsDir returns a new directory containing entries, which in turn contains
// snapshots with this tag set.
func NewTagsDir(root *Root, inode uint64, snapshots restic.Snapshots) fs.Node {
tags := make(map[string]restic.Snapshots)
for _, sn := range snapshots {
for _, tag := range sn.Tags {
tags[tag] = append(tags[tag], sn)
}
}
debug.Log("create tags dir with %d tags, inode %d", len(tags), inode)
entries := make(map[string]fs.Node)
for name, snapshots := range tags {
debug.Log(" tag %v has %v snapshots", name, len(snapshots))
entries[name] = NewSnapshotsDir(root, fs.GenerateDynamicInode(inode, name), snapshots)
}
return NewMetaDir(root, inode, entries)
}
// NewHostsDir returns a new directory containing hostnames, which in
// turn contains snapshots of a single host each.
func NewHostsDir(root *Root, inode uint64, snapshots restic.Snapshots) fs.Node {
hosts := make(map[string]restic.Snapshots)
for _, sn := range snapshots {
hosts[sn.Hostname] = append(hosts[sn.Hostname], sn)
}
debug.Log("create hosts dir with %d snapshots, inode %d", len(hosts), inode)
entries := make(map[string]fs.Node)
for name, snapshots := range hosts {
debug.Log(" host %v has %v snapshots", name, len(snapshots))
entries[name] = NewSnapshotsDir(root, fs.GenerateDynamicInode(inode, name), snapshots)
}
return NewMetaDir(root, inode, entries)
}
// Root is just there to satisfy fs.Root, it returns itself. // Root is just there to satisfy fs.Root, it returns itself.
func (r *Root) Root() (fs.Node, error) { func (r *Root) Root() (fs.Node, error) {
debug.Log("Root()") debug.Log("Root()")
return r, nil return r, nil
} }
// Attr returns the attributes for the root node.
func (r *Root) Attr(ctx context.Context, attr *fuse.Attr) error {
attr.Inode = r.inode
attr.Mode = os.ModeDir | 0555
if !r.cfg.OwnerIsRoot {
attr.Uid = uint32(os.Getuid())
attr.Gid = uint32(os.Getgid())
}
debug.Log("attr: %v", attr)
return nil
}
// ReadDirAll returns all entries of the root node.
func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
debug.Log("ReadDirAll()")
items := []fuse.Dirent{
{
Inode: r.inode,
Name: ".",
Type: fuse.DT_Dir,
},
{
Inode: r.inode,
Name: "..",
Type: fuse.DT_Dir,
},
{
Inode: fs.GenerateDynamicInode(r.inode, "snapshots"),
Name: "snapshots",
Type: fuse.DT_Dir,
},
// {
// Inode: fs.GenerateDynamicInode(0, "tags"),
// Name: "tags",
// Type: fuse.DT_Dir,
// },
// {
// Inode: fs.GenerateDynamicInode(0, "hosts"),
// Name: "hosts",
// Type: fuse.DT_Dir,
// },
}
return items, nil
}
// Lookup returns a specific entry from the root node.
func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
debug.Log("Lookup(%s)", name)
switch name {
case "snapshots":
return r.dirSnapshots, nil
}
return nil, fuse.ENOENT
}

View File

@ -16,8 +16,8 @@ import (
"bazil.org/fuse/fs" "bazil.org/fuse/fs"
) )
// DirSnapshots is a fuse directory which contains snapshots. // SnapshotsDir is a fuse directory which contains snapshots.
type DirSnapshots struct { type SnapshotsDir struct {
inode uint64 inode uint64
root *Root root *Root
snapshots restic.Snapshots snapshots restic.Snapshots
@ -25,13 +25,13 @@ type DirSnapshots struct {
} }
// ensure that *DirSnapshots implements these interfaces // ensure that *DirSnapshots implements these interfaces
var _ = fs.HandleReadDirAller(&DirSnapshots{}) var _ = fs.HandleReadDirAller(&SnapshotsDir{})
var _ = fs.NodeStringLookuper(&DirSnapshots{}) var _ = fs.NodeStringLookuper(&SnapshotsDir{})
// NewDirSnapshots returns a new directory containing snapshots. // NewSnapshotsDir returns a new directory containing snapshots.
func NewDirSnapshots(root *Root, inode uint64, snapshots restic.Snapshots) *DirSnapshots { func NewSnapshotsDir(root *Root, inode uint64, snapshots restic.Snapshots) *SnapshotsDir {
debug.Log("create snapshots dir with %d snapshots, inode %d", len(snapshots), inode) debug.Log("create snapshots dir with %d snapshots, inode %d", len(snapshots), inode)
d := &DirSnapshots{ d := &SnapshotsDir{
root: root, root: root,
inode: inode, inode: inode,
snapshots: snapshots, snapshots: snapshots,
@ -56,7 +56,7 @@ func NewDirSnapshots(root *Root, inode uint64, snapshots restic.Snapshots) *DirS
} }
// Attr returns the attributes for the root node. // Attr returns the attributes for the root node.
func (d *DirSnapshots) Attr(ctx context.Context, attr *fuse.Attr) error { func (d *SnapshotsDir) Attr(ctx context.Context, attr *fuse.Attr) error {
attr.Inode = d.inode attr.Inode = d.inode
attr.Mode = os.ModeDir | 0555 attr.Mode = os.ModeDir | 0555
@ -69,7 +69,7 @@ func (d *DirSnapshots) Attr(ctx context.Context, attr *fuse.Attr) error {
} }
// ReadDirAll returns all entries of the root node. // ReadDirAll returns all entries of the root node.
func (d *DirSnapshots) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
debug.Log("ReadDirAll()") debug.Log("ReadDirAll()")
items := []fuse.Dirent{ items := []fuse.Dirent{
{ {
@ -96,7 +96,7 @@ func (d *DirSnapshots) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
} }
// Lookup returns a specific entry from the root node. // Lookup returns a specific entry from the root node.
func (d *DirSnapshots) Lookup(ctx context.Context, name string) (fs.Node, error) { func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
debug.Log("Lookup(%s)", name) debug.Log("Lookup(%s)", name)
sn, ok := d.names[name] sn, ok := d.names[name]