diff --git a/cmd/restic/cmd_mount.go b/cmd/restic/cmd_mount.go
index 033c98850..d06274d00 100644
--- a/cmd/restic/cmd_mount.go
+++ b/cmd/restic/cmd_mount.go
@@ -3,21 +3,12 @@
 package main
 
 import (
-	"encoding/binary"
 	"fmt"
 	"os"
-	"sync"
-	"time"
 
-	"golang.org/x/net/context"
+	"github.com/restic/restic/cmd/restic/fuse"
 
-	"github.com/restic/restic"
-	"github.com/restic/restic/backend"
-	"github.com/restic/restic/crypto"
-	"github.com/restic/restic/pack"
-	"github.com/restic/restic/repository"
-
-	"bazil.org/fuse"
+	systemFuse "bazil.org/fuse"
 	"bazil.org/fuse/fs"
 )
 
@@ -66,20 +57,17 @@ func (cmd CmdMount) Execute(args []string) error {
 			return err
 		}
 	}
-	c, err := fuse.Mount(
+	c, err := systemFuse.Mount(
 		mountpoint,
-		fuse.ReadOnly(),
-		fuse.FSName("restic"),
+		systemFuse.ReadOnly(),
+		systemFuse.FSName("restic"),
 	)
 	if err != nil {
 		return err
 	}
 
 	root := fs.Tree{}
-	root.Add("snapshots", &snapshots{
-		repo:           repo,
-		knownSnapshots: make(map[string]snapshotWithId),
-	})
+	root.Add("snapshots", fuse.NewSnapshotsDir(repo))
 
 	cmd.global.Printf("Now serving %s at %s\n", repo.Backend().Location(), mountpoint)
 	cmd.global.Printf("Don't forget to umount after quitting!\n")
@@ -94,261 +82,3 @@ func (cmd CmdMount) Execute(args []string) error {
 	<-c.Ready
 	return c.MountError
 }
-
-type snapshotWithId struct {
-	*restic.Snapshot
-	backend.ID
-}
-
-// These lines statically ensure that a *snapshots implement the given
-// interfaces; a misplaced refactoring of the implementation that breaks
-// the interface will be catched by the compiler
-var _ = fs.HandleReadDirAller(&snapshots{})
-var _ = fs.NodeStringLookuper(&snapshots{})
-
-type snapshots struct {
-	repo *repository.Repository
-
-	// knownSnapshots maps snapshot timestamp to the snapshot
-	sync.RWMutex
-	knownSnapshots map[string]snapshotWithId
-}
-
-func (sn *snapshots) Attr(ctx context.Context, attr *fuse.Attr) error {
-	attr.Inode = 0
-	attr.Mode = os.ModeDir | 0555
-	return nil
-}
-
-func (sn *snapshots) updateCache(ctx context.Context) error {
-	sn.Lock()
-	defer sn.Unlock()
-
-	for id := range sn.repo.List(backend.Snapshot, ctx.Done()) {
-		snapshot, err := restic.LoadSnapshot(sn.repo, id)
-		if err != nil {
-			return err
-		}
-		sn.knownSnapshots[snapshot.Time.Format(time.RFC3339)] = snapshotWithId{snapshot, id}
-	}
-	return nil
-}
-func (sn *snapshots) get(name string) (snapshot snapshotWithId, ok bool) {
-	sn.Lock()
-	snapshot, ok = sn.knownSnapshots[name]
-	sn.Unlock()
-	return snapshot, ok
-}
-
-func (sn *snapshots) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
-	err := sn.updateCache(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	sn.RLock()
-	defer sn.RUnlock()
-
-	ret := make([]fuse.Dirent, 0)
-	for _, snapshot := range sn.knownSnapshots {
-		ret = append(ret, fuse.Dirent{
-			Inode: binary.BigEndian.Uint64(snapshot.ID[:8]),
-			Type:  fuse.DT_Dir,
-			Name:  snapshot.Time.Format(time.RFC3339),
-		})
-	}
-
-	return ret, nil
-}
-
-func (sn *snapshots) Lookup(ctx context.Context, name string) (fs.Node, error) {
-	snapshot, ok := sn.get(name)
-
-	if !ok {
-		// We don't know about it, update the cache
-		err := sn.updateCache(ctx)
-		if err != nil {
-			return nil, err
-		}
-		snapshot, ok = sn.get(name)
-		if !ok {
-			// We still don't know about it, this time it really doesn't exist
-			return nil, fuse.ENOENT
-		}
-	}
-
-	return newDirFromSnapshot(sn.repo, snapshot)
-}
-
-// Statically ensure that *dir implement those interface
-var _ = fs.HandleReadDirAller(&dir{})
-var _ = fs.NodeStringLookuper(&dir{})
-
-type dir struct {
-	repo     *repository.Repository
-	children map[string]*restic.Node
-	inode    uint64
-}
-
-func newDir(repo *repository.Repository, node *restic.Node) (*dir, error) {
-	tree, err := restic.LoadTree(repo, node.Subtree)
-	if err != nil {
-		return nil, err
-	}
-	children := make(map[string]*restic.Node)
-	for _, child := range tree.Nodes {
-		children[child.Name] = child
-	}
-
-	return &dir{
-		repo:     repo,
-		children: children,
-		inode:    node.Inode,
-	}, nil
-}
-
-func newDirFromSnapshot(repo *repository.Repository, snapshot snapshotWithId) (*dir, error) {
-	tree, err := restic.LoadTree(repo, snapshot.Tree)
-	if err != nil {
-		return nil, err
-	}
-	children := make(map[string]*restic.Node)
-	for _, node := range tree.Nodes {
-		children[node.Name] = node
-	}
-
-	return &dir{
-		repo:     repo,
-		children: children,
-		inode:    binary.BigEndian.Uint64(snapshot.ID),
-	}, nil
-}
-
-func (d *dir) Attr(ctx context.Context, a *fuse.Attr) error {
-	a.Inode = d.inode
-	a.Mode = os.ModeDir | 0555
-	return nil
-}
-
-func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
-	ret := make([]fuse.Dirent, 0, len(d.children))
-
-	for _, node := range d.children {
-		var typ fuse.DirentType
-		switch {
-		case node.Mode.IsDir():
-			typ = fuse.DT_Dir
-		case node.Mode.IsRegular():
-			typ = fuse.DT_File
-		}
-
-		ret = append(ret, fuse.Dirent{
-			Inode: node.Inode,
-			Type:  typ,
-			Name:  node.Name,
-		})
-	}
-
-	return ret, nil
-}
-
-func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
-	child, ok := d.children[name]
-	if !ok {
-		return nil, fuse.ENOENT
-	}
-	switch {
-	case child.Mode.IsDir():
-		return newDir(d.repo, child)
-	case child.Mode.IsRegular():
-		return newFile(d.repo, child)
-	default:
-		return nil, fuse.ENOENT
-	}
-}
-
-// Statically ensure that *file implements the given interface
-var _ = fs.HandleReader(&file{})
-
-type file struct {
-	repo *repository.Repository
-	node *restic.Node
-
-	sizes []uint32
-	blobs [][]byte
-}
-
-func newFile(repo *repository.Repository, node *restic.Node) (*file, error) {
-	sizes := make([]uint32, len(node.Content))
-	for i, blobId := range node.Content {
-		_, _, _, length, err := repo.Index().Lookup(blobId)
-		if err != nil {
-			return nil, err
-		}
-		sizes[i] = uint32(length) - crypto.Extension
-	}
-
-	return &file{
-		repo:  repo,
-		node:  node,
-		sizes: sizes,
-		blobs: make([][]byte, len(node.Content)),
-	}, nil
-}
-
-func (f *file) Attr(ctx context.Context, a *fuse.Attr) error {
-	a.Inode = f.node.Inode
-	a.Mode = f.node.Mode
-	a.Size = f.node.Size
-	return nil
-}
-
-func (f *file) getBlobAt(i int) (blob []byte, err error) {
-	if f.blobs[i] != nil {
-		blob = f.blobs[i]
-	} else {
-		blob, err = f.repo.LoadBlob(pack.Data, f.node.Content[i])
-		if err != nil {
-			return nil, err
-		}
-		f.blobs[i] = blob
-	}
-
-	return blob, nil
-}
-
-func (f *file) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
-	off := req.Offset
-
-	// Skip blobs before the offset
-	startContent := 0
-	for off > int64(f.sizes[startContent]) {
-		off -= int64(f.sizes[startContent])
-		startContent++
-	}
-
-	content := make([]byte, req.Size)
-	allContent := content
-	for i := startContent; i < len(f.sizes); i++ {
-		blob, err := f.getBlobAt(i)
-		if err != nil {
-			return err
-		}
-
-		blob = blob[off:]
-		off = 0
-
-		var copied int
-		if len(blob) > len(content) {
-			copied = copy(content[0:], blob[:len(content)])
-		} else {
-			copied = copy(content[0:], blob)
-		}
-		content = content[copied:]
-		if len(content) == 0 {
-			break
-		}
-	}
-	resp.Data = allContent
-	return nil
-}
diff --git a/cmd/restic/fuse/dir.go b/cmd/restic/fuse/dir.go
new file mode 100644
index 000000000..ef9ad9a73
--- /dev/null
+++ b/cmd/restic/fuse/dir.go
@@ -0,0 +1,100 @@
+package fuse
+
+import (
+	"encoding/binary"
+	"os"
+
+	"bazil.org/fuse"
+	"bazil.org/fuse/fs"
+	"golang.org/x/net/context"
+
+	"github.com/restic/restic"
+	"github.com/restic/restic/repository"
+)
+
+// Statically ensure that *dir implement those interface
+var _ = fs.HandleReadDirAller(&dir{})
+var _ = fs.NodeStringLookuper(&dir{})
+
+type dir struct {
+	repo     *repository.Repository
+	children map[string]*restic.Node
+	inode    uint64
+}
+
+func newDir(repo *repository.Repository, node *restic.Node) (*dir, error) {
+	tree, err := restic.LoadTree(repo, node.Subtree)
+	if err != nil {
+		return nil, err
+	}
+	children := make(map[string]*restic.Node)
+	for _, child := range tree.Nodes {
+		children[child.Name] = child
+	}
+
+	return &dir{
+		repo:     repo,
+		children: children,
+		inode:    node.Inode,
+	}, nil
+}
+
+func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId) (*dir, error) {
+	tree, err := restic.LoadTree(repo, snapshot.Tree)
+	if err != nil {
+		return nil, err
+	}
+	children := make(map[string]*restic.Node)
+	for _, node := range tree.Nodes {
+		children[node.Name] = node
+	}
+
+	return &dir{
+		repo:     repo,
+		children: children,
+		inode:    binary.BigEndian.Uint64(snapshot.ID),
+	}, nil
+}
+
+func (d *dir) Attr(ctx context.Context, a *fuse.Attr) error {
+	a.Inode = d.inode
+	a.Mode = os.ModeDir | 0555
+	return nil
+}
+
+func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
+	ret := make([]fuse.Dirent, 0, len(d.children))
+
+	for _, node := range d.children {
+		var typ fuse.DirentType
+		switch {
+		case node.Mode.IsDir():
+			typ = fuse.DT_Dir
+		case node.Mode.IsRegular():
+			typ = fuse.DT_File
+		}
+
+		ret = append(ret, fuse.Dirent{
+			Inode: node.Inode,
+			Type:  typ,
+			Name:  node.Name,
+		})
+	}
+
+	return ret, nil
+}
+
+func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
+	child, ok := d.children[name]
+	if !ok {
+		return nil, fuse.ENOENT
+	}
+	switch {
+	case child.Mode.IsDir():
+		return newDir(d.repo, child)
+	case child.Mode.IsRegular():
+		return newFile(d.repo, child)
+	default:
+		return nil, fuse.ENOENT
+	}
+}
diff --git a/cmd/restic/fuse/file.go b/cmd/restic/fuse/file.go
new file mode 100644
index 000000000..ff28ebf85
--- /dev/null
+++ b/cmd/restic/fuse/file.go
@@ -0,0 +1,98 @@
+package fuse
+
+import (
+	"github.com/restic/restic"
+	"github.com/restic/restic/crypto"
+	"github.com/restic/restic/pack"
+	"github.com/restic/restic/repository"
+
+	"bazil.org/fuse"
+	"bazil.org/fuse/fs"
+	"golang.org/x/net/context"
+)
+
+// Statically ensure that *file implements the given interface
+var _ = fs.HandleReader(&file{})
+
+type file struct {
+	repo *repository.Repository
+	node *restic.Node
+
+	sizes []uint32
+	blobs [][]byte
+}
+
+func newFile(repo *repository.Repository, node *restic.Node) (*file, error) {
+	sizes := make([]uint32, len(node.Content))
+	for i, blobId := range node.Content {
+		_, _, _, length, err := repo.Index().Lookup(blobId)
+		if err != nil {
+			return nil, err
+		}
+		sizes[i] = uint32(length) - crypto.Extension
+	}
+
+	return &file{
+		repo:  repo,
+		node:  node,
+		sizes: sizes,
+		blobs: make([][]byte, len(node.Content)),
+	}, nil
+}
+
+func (f *file) Attr(ctx context.Context, a *fuse.Attr) error {
+	a.Inode = f.node.Inode
+	a.Mode = f.node.Mode
+	a.Size = f.node.Size
+	return nil
+}
+
+func (f *file) getBlobAt(i int) (blob []byte, err error) {
+	if f.blobs[i] != nil {
+		blob = f.blobs[i]
+	} else {
+		blob, err = f.repo.LoadBlob(pack.Data, f.node.Content[i])
+		if err != nil {
+			return nil, err
+		}
+		f.blobs[i] = blob
+	}
+
+	return blob, nil
+}
+
+func (f *file) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
+	off := req.Offset
+
+	// Skip blobs before the offset
+	startContent := 0
+	for off > int64(f.sizes[startContent]) {
+		off -= int64(f.sizes[startContent])
+		startContent++
+	}
+
+	content := make([]byte, req.Size)
+	allContent := content
+	for i := startContent; i < len(f.sizes); i++ {
+		blob, err := f.getBlobAt(i)
+		if err != nil {
+			return err
+		}
+
+		blob = blob[off:]
+		off = 0
+
+		var copied int
+		if len(blob) > len(content) {
+			copied = copy(content[0:], blob[:len(content)])
+		} else {
+			copied = copy(content[0:], blob)
+		}
+		content = content[copied:]
+		if len(content) == 0 {
+			break
+		}
+	}
+	resp.Data = allContent
+	return nil
+}