diff --git a/src/restic/fuse/hosts_dir.go b/src/restic/fuse/hosts_dir.go new file mode 100644 index 000000000..0f5f4a8af --- /dev/null +++ b/src/restic/fuse/hosts_dir.go @@ -0,0 +1,99 @@ +// +build !openbsd +// +build !windows + +package fuse + +import ( + "os" + "restic" + "restic/debug" + + "golang.org/x/net/context" + + "bazil.org/fuse" + "bazil.org/fuse/fs" +) + +// HostsDir is a fuse directory which contains hostnames. +type HostsDir struct { + inode uint64 + root *Root + snapshots restic.Snapshots + hosts map[string]*SnapshotsDir +} + +// 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) *HostsDir { + 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) + + d := &HostsDir{ + root: root, + inode: inode, + snapshots: snapshots, + hosts: make(map[string]*SnapshotsDir), + } + + for hostname, snapshots := range hosts { + debug.Log(" host %v has %v snapshots", hostname, len(snapshots)) + d.hosts[hostname] = NewSnapshotsDir(root, fs.GenerateDynamicInode(inode, hostname), snapshots) + } + + return d +} + +// Attr returns the attributes for the root node. +func (d *HostsDir) 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 *HostsDir) 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.hosts { + 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 *HostsDir) Lookup(ctx context.Context, name string) (fs.Node, error) { + debug.Log("Lookup(%s)", name) + + if dir, ok := d.hosts[name]; ok { + return dir, nil + } + + return nil, fuse.ENOENT +} diff --git a/src/restic/fuse/root.go b/src/restic/fuse/root.go index 39a148c19..45d287043 100644 --- a/src/restic/fuse/root.go +++ b/src/restic/fuse/root.go @@ -28,8 +28,10 @@ type Root struct { cfg Config inode uint64 snapshots restic.Snapshots - dirSnapshots *SnapshotsDir blobSizeCache *BlobSizeCache + + dirSnapshots *SnapshotsDir + dirHosts *HostsDir } // ensure that *Root implements these interfaces @@ -50,7 +52,8 @@ func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, er snapshots: snapshots, } - root.dirSnapshots = NewDirSnapshots(root, fs.GenerateDynamicInode(root.inode, "snapshots"), snapshots) + root.dirSnapshots = NewSnapshotsDir(root, fs.GenerateDynamicInode(root.inode, "snapshots"), snapshots) + root.dirHosts = NewHostsDir(root, fs.GenerateDynamicInode(root.inode, "hosts"), snapshots) root.blobSizeCache = NewBlobSizeCache(ctx, repo.Index()) return root, nil @@ -94,16 +97,11 @@ func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { 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, - // }, + { + Inode: fs.GenerateDynamicInode(0, "hosts"), + Name: "hosts", + Type: fuse.DT_Dir, + }, } return items, nil @@ -115,6 +113,8 @@ func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { switch name { case "snapshots": return r.dirSnapshots, nil + case "hosts": + return r.dirHosts, nil } return nil, fuse.ENOENT diff --git a/src/restic/fuse/snapshots_dir.go b/src/restic/fuse/snapshots_dir.go index 39f07f3ba..44afd6bdb 100644 --- a/src/restic/fuse/snapshots_dir.go +++ b/src/restic/fuse/snapshots_dir.go @@ -28,8 +28,8 @@ type SnapshotsDir struct { var _ = fs.HandleReadDirAller(&SnapshotsDir{}) var _ = fs.NodeStringLookuper(&SnapshotsDir{}) -// NewDirSnapshots returns a new directory containing snapshots. -func NewDirSnapshots(root *Root, inode uint64, snapshots restic.Snapshots) *SnapshotsDir { +// NewSnapshotsDir returns a new directory containing snapshots. +func NewSnapshotsDir(root *Root, inode uint64, snapshots restic.Snapshots) *SnapshotsDir { debug.Log("create snapshots dir with %d snapshots, inode %d", len(snapshots), inode) d := &SnapshotsDir{ root: root,