1// FUSE directory tree, for servers that wish to use it with the service loop.
2
3package fs
4
5import (
6	"os"
7	pathpkg "path"
8	"strings"
9
10	"golang.org/x/net/context"
11)
12
13import (
14	"bazil.org/fuse"
15)
16
17// A Tree implements a basic read-only directory tree for FUSE.
18// The Nodes contained in it may still be writable.
19type Tree struct {
20	tree
21}
22
23func (t *Tree) Root() (Node, error) {
24	return &t.tree, nil
25}
26
27// Add adds the path to the tree, resolving to the given node.
28// If path or a prefix of path has already been added to the tree,
29// Add panics.
30//
31// Add is only safe to call before starting to serve requests.
32func (t *Tree) Add(path string, node Node) {
33	path = pathpkg.Clean("/" + path)[1:]
34	elems := strings.Split(path, "/")
35	dir := Node(&t.tree)
36	for i, elem := range elems {
37		dt, ok := dir.(*tree)
38		if !ok {
39			panic("fuse: Tree.Add for " + strings.Join(elems[:i], "/") + " and " + path)
40		}
41		n := dt.lookup(elem)
42		if n != nil {
43			if i+1 == len(elems) {
44				panic("fuse: Tree.Add for " + path + " conflicts with " + elem)
45			}
46			dir = n
47		} else {
48			if i+1 == len(elems) {
49				dt.add(elem, node)
50			} else {
51				dir = &tree{}
52				dt.add(elem, dir)
53			}
54		}
55	}
56}
57
58type treeDir struct {
59	name string
60	node Node
61}
62
63type tree struct {
64	dir []treeDir
65}
66
67func (t *tree) lookup(name string) Node {
68	for _, d := range t.dir {
69		if d.name == name {
70			return d.node
71		}
72	}
73	return nil
74}
75
76func (t *tree) add(name string, n Node) {
77	t.dir = append(t.dir, treeDir{name, n})
78}
79
80func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error {
81	a.Mode = os.ModeDir | 0555
82	return nil
83}
84
85func (t *tree) Lookup(ctx context.Context, name string) (Node, error) {
86	n := t.lookup(name)
87	if n != nil {
88		return n, nil
89	}
90	return nil, fuse.ENOENT
91}
92
93func (t *tree) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
94	var out []fuse.Dirent
95	for _, d := range t.dir {
96		out = append(out, fuse.Dirent{Name: d.name})
97	}
98	return out, nil
99}
100