diff --git a/src/restic/fuse/hosts_dir.go b/src/restic/fuse/hosts_dir.go deleted file mode 100644 index 0f5f4a8af..000000000 --- a/src/restic/fuse/hosts_dir.go +++ /dev/null @@ -1,99 +0,0 @@ -// +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/meta_dir.go b/src/restic/fuse/meta_dir.go new file mode 100644 index 000000000..8542b14c8 --- /dev/null +++ b/src/restic/fuse/meta_dir.go @@ -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 +} diff --git a/src/restic/fuse/root.go b/src/restic/fuse/root.go index d1f43c6c3..b82502c7a 100644 --- a/src/restic/fuse/root.go +++ b/src/restic/fuse/root.go @@ -4,13 +4,11 @@ package fuse import ( - "os" "restic" "restic/debug" "golang.org/x/net/context" - "bazil.org/fuse" "bazil.org/fuse/fs" ) @@ -30,15 +28,15 @@ type Root struct { snapshots restic.Snapshots blobSizeCache *BlobSizeCache - dirSnapshots *SnapshotsDir - dirHosts *HostsDir - dirTags *TagsDir + *MetaDir } // ensure that *Root implements these interfaces var _ = fs.HandleReadDirAller(&Root{}) var _ = fs.NodeStringLookuper(&Root{}) +const rootInode = 1 + // NewRoot initializes a new root node from a repository. func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, error) { debug.Log("NewRoot(), config %v", cfg) @@ -47,84 +45,66 @@ func NewRoot(ctx context.Context, repo restic.Repository, cfg Config) (*Root, er debug.Log("found %d matching snapshots", len(snapshots)) root := &Root{ - repo: repo, - cfg: cfg, - inode: 1, - snapshots: snapshots, + repo: repo, + inode: rootInode, + cfg: cfg, + snapshots: snapshots, + blobSizeCache: NewBlobSizeCache(ctx, repo.Index()), } - root.dirSnapshots = NewSnapshotsDir(root, fs.GenerateDynamicInode(root.inode, "snapshots"), snapshots) - root.dirHosts = NewHostsDir(root, fs.GenerateDynamicInode(root.inode, "hosts"), snapshots) - root.dirTags = NewTagsDir(root, fs.GenerateDynamicInode(root.inode, "tags"), snapshots) - root.blobSizeCache = NewBlobSizeCache(ctx, repo.Index()) + entries := map[string]fs.Node{ + "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 } +// 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. func (r *Root) Root() (fs.Node, error) { debug.Log("Root()") 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(r.inode, "hosts"), - Name: "hosts", - Type: fuse.DT_Dir, - }, - { - Inode: fs.GenerateDynamicInode(r.inode, "tags"), - Name: "tags", - 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 - case "hosts": - return r.dirHosts, nil - case "tags": - return r.dirTags, nil - } - - return nil, fuse.ENOENT -} diff --git a/src/restic/fuse/tags_dir.go b/src/restic/fuse/tags_dir.go deleted file mode 100644 index 30edd8b0b..000000000 --- a/src/restic/fuse/tags_dir.go +++ /dev/null @@ -1,101 +0,0 @@ -// +build !openbsd -// +build !windows - -package fuse - -import ( - "os" - "restic" - "restic/debug" - - "golang.org/x/net/context" - - "bazil.org/fuse" - "bazil.org/fuse/fs" -) - -// TagsDir is a fuse directory which contains hostnames. -type TagsDir struct { - inode uint64 - root *Root - snapshots restic.Snapshots - tags map[string]*SnapshotsDir -} - -// NewTagsDir returns a new directory containing tags, which in turn contains -// snapshots with this tag set. -func NewTagsDir(root *Root, inode uint64, snapshots restic.Snapshots) *TagsDir { - 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 snapshots, inode %d", len(tags), inode) - - d := &TagsDir{ - root: root, - inode: inode, - snapshots: snapshots, - tags: make(map[string]*SnapshotsDir), - } - - for name, snapshots := range tags { - debug.Log(" tag %v has %v snapshots", name, len(snapshots)) - d.tags[name] = NewSnapshotsDir(root, fs.GenerateDynamicInode(inode, name), snapshots) - } - - return d -} - -// Attr returns the attributes for the root node. -func (d *TagsDir) 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 *TagsDir) 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.tags { - 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 *TagsDir) Lookup(ctx context.Context, name string) (fs.Node, error) { - debug.Log("Lookup(%s)", name) - - if dir, ok := d.tags[name]; ok { - return dir, nil - } - - return nil, fuse.ENOENT -}