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 "errors"
24 "fmt"
25 "os"
26 "path/filepath"
27 "strings"
28 "sync"
29 "time"
30
31 "bazil.org/fuse"
32 "bazil.org/fuse/fs"
33 "go4.org/types"
34 "perkeep.org/pkg/blob"
35 "perkeep.org/pkg/schema"
36 "perkeep.org/pkg/search"
37 )
38
39
40
41 type roDir struct {
42 fs *CamliFileSystem
43 permanode blob.Ref
44 parent *roDir
45 name string
46 at time.Time
47
48 mu sync.Mutex
49 children map[string]roFileOrDir
50 xattrs map[string][]byte
51 }
52
53 var _ fs.Node = (*roDir)(nil)
54 var _ fs.HandleReadDirAller = (*roDir)(nil)
55 var _ fs.NodeGetxattrer = (*roDir)(nil)
56 var _ fs.NodeListxattrer = (*roDir)(nil)
57
58 func newRODir(fs *CamliFileSystem, permanode blob.Ref, name string, at time.Time) *roDir {
59 return &roDir{
60 fs: fs,
61 permanode: permanode,
62 name: name,
63 at: at,
64 }
65 }
66
67
68 func (n *roDir) fullPath() string {
69 if n == nil {
70 return ""
71 }
72 return filepath.Join(n.parent.fullPath(), n.name)
73 }
74
75 func (n *roDir) Attr(ctx context.Context, a *fuse.Attr) error {
76 *a = fuse.Attr{
77 Inode: n.permanode.Sum64(),
78 Mode: os.ModeDir | 0500,
79 Uid: uint32(os.Getuid()),
80 Gid: uint32(os.Getgid()),
81 }
82 return nil
83 }
84
85
86 func (n *roDir) populate(ctx context.Context) error {
87 n.mu.Lock()
88 defer n.mu.Unlock()
89
90
91 if n.children != nil {
92 return nil
93 }
94
95 Logger.Printf("roDir.populate(%q) - Sending request At %v", n.fullPath(), n.at)
96
97 res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{
98 BlobRef: n.permanode,
99 Depth: 3,
100 At: types.Time3339(n.at),
101 })
102 if err != nil {
103 Logger.Println("roDir.paths:", err)
104 return fmt.Errorf("error while describing permanode: %w", err)
105 }
106 db := res.Meta[n.permanode.String()]
107 if db == nil {
108 return errors.New("dir blobref not described")
109 }
110
111
112 n.children = make(map[string]roFileOrDir)
113 for k, v := range db.Permanode.Attr {
114 const p = "camliPath:"
115 if !strings.HasPrefix(k, p) || len(v) < 1 {
116 continue
117 }
118 name := k[len(p):]
119 childRef := v[0]
120 child := res.Meta[childRef]
121 if child == nil {
122 Logger.Printf("child not described: %v", childRef)
123 continue
124 }
125 if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" {
126
127 n.children[name] = &roFile{
128 fs: n.fs,
129 permanode: blob.ParseOrZero(childRef),
130 parent: n,
131 name: name,
132 symLink: true,
133 target: target,
134 }
135 } else if isDir(child.Permanode) {
136
137 n.children[name] = &roDir{
138 fs: n.fs,
139 permanode: blob.ParseOrZero(childRef),
140 parent: n,
141 name: name,
142 at: n.at,
143 }
144 } else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" {
145
146 content := res.Meta[contentRef]
147 if content == nil {
148 Logger.Printf("child content not described: %v", childRef)
149 continue
150 }
151 if content.CamliType != "file" {
152 Logger.Printf("child not a file: %v", childRef)
153 continue
154 }
155 n.children[name] = &roFile{
156 fs: n.fs,
157 permanode: blob.ParseOrZero(childRef),
158 parent: n,
159 name: name,
160 content: blob.ParseOrZero(contentRef),
161 size: content.File.Size,
162 }
163 } else {
164
165 continue
166 }
167 n.children[name].xattr().load(child.Permanode)
168 }
169 return nil
170 }
171
172 func (n *roDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
173 if err := n.populate(ctx); err != nil {
174 Logger.Println("populate:", err)
175 return nil, handleEIOorEINTR(err)
176 }
177 n.mu.Lock()
178 defer n.mu.Unlock()
179 var ents []fuse.Dirent
180 for name, childNode := range n.children {
181 var ino uint64
182 switch v := childNode.(type) {
183 case *roDir:
184 ino = v.permanode.Sum64()
185 case *roFile:
186 ino = v.permanode.Sum64()
187 default:
188 Logger.Printf("roDir.ReadDirAll: unknown child type %T", childNode)
189 }
190
191
192
193 dirent := fuse.Dirent{
194 Name: name,
195 Inode: ino,
196 }
197 Logger.Printf("roDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent)
198 ents = append(ents, dirent)
199 }
200 return ents, nil
201 }
202
203 var _ fs.NodeStringLookuper = (*roDir)(nil)
204
205 func (n *roDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error) {
206 defer func() {
207 Logger.Printf("roDir(%q).Lookup(%q) = %#v, %v", n.fullPath(), name, ret, err)
208 }()
209 if err := n.populate(ctx); err != nil {
210 Logger.Println("populate:", err)
211 return nil, handleEIOorEINTR(err)
212 }
213 n.mu.Lock()
214 defer n.mu.Unlock()
215 if n2 := n.children[name]; n2 != nil {
216 return n2, nil
217 }
218 return nil, fuse.ENOENT
219 }
220
221
222 type roFile struct {
223 fs *CamliFileSystem
224 permanode blob.Ref
225 parent *roDir
226 name string
227
228 mu sync.Mutex
229 symLink bool
230 target string
231 content blob.Ref
232 size int64
233 mtime, atime time.Time
234 xattrs map[string][]byte
235 }
236
237 var _ fs.Node = (*roFile)(nil)
238 var _ fs.NodeGetxattrer = (*roFile)(nil)
239 var _ fs.NodeListxattrer = (*roFile)(nil)
240 var _ fs.NodeSetxattrer = (*roFile)(nil)
241 var _ fs.NodeRemovexattrer = (*roFile)(nil)
242 var _ fs.NodeOpener = (*roFile)(nil)
243 var _ fs.NodeFsyncer = (*roFile)(nil)
244 var _ fs.NodeReadlinker = (*roFile)(nil)
245
246 func (n *roDir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
247 return n.xattr().get(req, res)
248 }
249
250 func (n *roDir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
251 return n.xattr().list(req, res)
252 }
253
254 func (n *roFile) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
255 return n.xattr().get(req, res)
256 }
257
258 func (n *roFile) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
259 return n.xattr().list(req, res)
260 }
261
262 func (n *roFile) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
263 return fuse.EPERM
264 }
265
266 func (n *roFile) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
267 return fuse.EPERM
268 }
269
270
271 func (n *roFile) fullPath() string {
272 if n == nil {
273 return ""
274 }
275 return filepath.Join(n.parent.fullPath(), n.name)
276 }
277
278 func (n *roFile) Attr(ctx context.Context, a *fuse.Attr) error {
279
280 var mode os.FileMode = 0400
281
282 n.mu.Lock()
283 size := n.size
284 var blocks uint64
285 if size > 0 {
286 blocks = uint64(size)/512 + 1
287 }
288 inode := n.permanode.Sum64()
289 if n.symLink {
290 mode |= os.ModeSymlink
291 }
292 n.mu.Unlock()
293
294 *a = fuse.Attr{
295 Inode: inode,
296 Mode: mode,
297 Uid: uint32(os.Getuid()),
298 Gid: uint32(os.Getgid()),
299 Size: uint64(size),
300 Blocks: blocks,
301 Mtime: n.modTime(),
302 Atime: n.accessTime(),
303 Ctime: serverStart,
304 }
305 return nil
306 }
307
308 func (n *roFile) accessTime() time.Time {
309 n.mu.Lock()
310 if !n.atime.IsZero() {
311 defer n.mu.Unlock()
312 return n.atime
313 }
314 n.mu.Unlock()
315 return n.modTime()
316 }
317
318 func (n *roFile) modTime() time.Time {
319 n.mu.Lock()
320 defer n.mu.Unlock()
321 if !n.mtime.IsZero() {
322 return n.mtime
323 }
324 return serverStart
325 }
326
327
328
329
330
331
332
333
334
335
336
337 func (n *roFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) {
338 roFileOpen.Incr()
339
340 if isWriteFlags(req.Flags) {
341 return nil, fuse.EPERM
342 }
343
344 Logger.Printf("roFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags)
345 r, err := schema.NewFileReader(ctx, n.fs.fetcher, n.content)
346 if err != nil {
347 roFileOpenError.Incr()
348 Logger.Printf("roFile.Open: %v", err)
349 return nil, handleEIOorEINTR(err)
350 }
351
352
353
354 res.Flags &= ^fuse.OpenDirectIO
355
356
357 nod := &node{
358 fs: n.fs,
359 blobref: n.content,
360 }
361 return &nodeReader{n: nod, fr: r}, nil
362 }
363
364 func (n *roFile) Fsync(ctx context.Context, r *fuse.FsyncRequest) error {
365
366 return nil
367 }
368
369 func (n *roFile) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
370 Logger.Printf("roFile.Readlink(%q)", n.fullPath())
371 n.mu.Lock()
372 defer n.mu.Unlock()
373 if !n.symLink {
374 Logger.Printf("roFile.Readlink on node that's not a symlink?")
375 return "", fuse.EIO
376 }
377 return n.target, nil
378 }
379
380
381 type roFileOrDir interface {
382 fs.Node
383 permanodeString() string
384 xattr() *xattr
385 }
386
387 func (n *roFile) permanodeString() string {
388 return n.permanode.String()
389 }
390
391 func (n *roDir) permanodeString() string {
392 return n.permanode.String()
393 }
394
395 func (n *roFile) xattr() *xattr {
396 return &xattr{"roFile", n.fs, n.permanode, &n.mu, &n.xattrs}
397 }
398
399 func (n *roDir) xattr() *xattr {
400 return &xattr{"roDir", n.fs, n.permanode, &n.mu, &n.xattrs}
401 }