1
2
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18
19 package fs
20
21 import (
22 "context"
23 "log"
24 "os"
25 "sync"
26
27 "bazil.org/fuse"
28 "bazil.org/fuse/fs"
29 "perkeep.org/pkg/blob"
30 )
31
32
33
34
35 type root struct {
36 fs *CamliFileSystem
37
38 mu sync.Mutex
39 recent *recentDir
40 roots *rootsDir
41 atDir *atDir
42 versionsDir *versionsDir
43 }
44
45 var (
46 _ fs.Node = (*root)(nil)
47 _ fs.HandleReadDirAller = (*root)(nil)
48 _ fs.NodeStringLookuper = (*root)(nil)
49 )
50
51 func (n *root) Attr(ctx context.Context, a *fuse.Attr) error {
52 a.Mode = os.ModeDir | 0700
53 a.Uid = uint32(os.Getuid())
54 a.Gid = uint32(os.Getgid())
55 return nil
56 }
57
58 func (n *root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
59 return []fuse.Dirent{
60 {Name: "WELCOME.txt"},
61 {Name: "tag"},
62 {Name: "date"},
63 {Name: "recent"},
64 {Name: "roots"},
65 {Name: "at"},
66 {Name: "sha1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
67 {Name: "sha224-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
68 {Name: "versions"},
69 }, nil
70 }
71
72 func (n *root) getRecentDir() *recentDir {
73 n.mu.Lock()
74 defer n.mu.Unlock()
75 if n.recent == nil {
76 n.recent = &recentDir{fs: n.fs}
77 }
78 return n.recent
79 }
80
81 func (n *root) getRootsDir() *rootsDir {
82 n.mu.Lock()
83 defer n.mu.Unlock()
84 if n.roots == nil {
85 n.roots = &rootsDir{fs: n.fs}
86 }
87 return n.roots
88 }
89
90 func (n *root) getAtDir() *atDir {
91 n.mu.Lock()
92 defer n.mu.Unlock()
93 if n.atDir == nil {
94 n.atDir = &atDir{fs: n.fs}
95 }
96 return n.atDir
97 }
98
99 func (n *root) getVersionsDir() *versionsDir {
100 n.mu.Lock()
101 defer n.mu.Unlock()
102 if n.versionsDir == nil {
103 n.versionsDir = &versionsDir{fs: n.fs}
104 }
105 return n.versionsDir
106 }
107
108 func (n *root) Lookup(ctx context.Context, name string) (fs.Node, error) {
109 Logger.Printf("root.Lookup(%s)", name)
110 switch name {
111 case ".quitquitquit":
112 log.Fatalf("Shutting down due to root .quitquitquit lookup.")
113 case "WELCOME.txt":
114 return staticFileNode("Welcome to PerkeepFS.\n\nMore information is available in the pk-mount documentation.\n\nSee https://perkeep.org/cmd/pk-mount/ , or run 'go doc perkeep.org/cmd/pk-mount'.\n"), nil
115 case "recent":
116 return n.getRecentDir(), nil
117 case "tag", "date":
118 return notImplementDirNode{}, nil
119 case "at":
120 return n.getAtDir(), nil
121 case "roots":
122 return n.getRootsDir(), nil
123 case "versions":
124 return n.getVersionsDir(), nil
125 case "sha1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
126 return notImplementDirNode{}, nil
127 case "sha224-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
128 return notImplementDirNode{}, nil
129 case ".camli_fs_stats":
130 return statsDir{}, nil
131 case "mach_kernel", ".hidden", "._.":
132
133 return nil, fuse.ENOENT
134 }
135
136 if br, ok := blob.Parse(name); ok {
137 Logger.Printf("Root lookup of blobref. %q => %v", name, br)
138 node := &node{fs: n.fs, blobref: br}
139 if _, err := node.schema(ctx); err != nil {
140 if os.IsNotExist(err) {
141 return nil, fuse.ENOENT
142 } else {
143 return nil, handleEIOorEINTR(err)
144 }
145 }
146 return node, nil
147 }
148 Logger.Printf("Bogus root lookup of %q", name)
149 return nil, fuse.ENOENT
150 }