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 "io"
26 "os"
27 "path/filepath"
28 "strings"
29 "sync"
30 "time"
31
32 "perkeep.org/pkg/blob"
33 "perkeep.org/pkg/schema"
34 "perkeep.org/pkg/search"
35
36 "bazil.org/fuse"
37 "bazil.org/fuse/fs"
38 "go4.org/syncutil"
39 )
40
41
42 const populateInterval = 30 * time.Second
43
44
45
46 const deletionRefreshWindow = time.Minute
47
48 type nodeType int
49
50 const (
51 fileType nodeType = iota
52 dirType
53 symlinkType
54 )
55
56
57
58 type mutDir struct {
59 fs *CamliFileSystem
60 permanode blob.Ref
61 parent *mutDir
62 name string
63
64 localCreateTime time.Time
65
66 mu sync.Mutex
67 lastPop time.Time
68 children map[string]mutFileOrDir
69 xattrs map[string][]byte
70 deleted bool
71 }
72
73 var _ fs.Node = (*mutDir)(nil)
74 var _ fs.NodeAccesser = (*mutDir)(nil)
75 var _ fs.HandleReadDirAller = (*mutDir)(nil)
76 var _ fs.NodeStringLookuper = (*mutDir)(nil)
77 var _ fs.NodeGetxattrer = (*mutDir)(nil)
78 var _ fs.NodeListxattrer = (*mutDir)(nil)
79 var _ fs.NodeSetxattrer = (*mutDir)(nil)
80 var _ fs.NodeCreater = (*mutDir)(nil)
81 var _ fs.NodeMkdirer = (*mutDir)(nil)
82 var _ fs.NodeSymlinker = (*mutDir)(nil)
83 var _ fs.NodeRemover = (*mutDir)(nil)
84 var _ fs.NodeRenamer = (*mutDir)(nil)
85
86 func (n *mutDir) String() string {
87 return fmt.Sprintf("&mutDir{%p name=%q perm:%v}", n, n.fullPath(), n.permanode)
88 }
89
90
91 func (n *mutDir) fullPath() string {
92 if n == nil {
93 return ""
94 }
95 return filepath.Join(n.parent.fullPath(), n.name)
96 }
97
98 func (n *mutDir) Attr(ctx context.Context, a *fuse.Attr) error {
99 a.Inode = n.permanode.Sum64()
100 a.Mode = os.ModeDir | 0700
101 a.Uid = uint32(os.Getuid())
102 a.Gid = uint32(os.Getgid())
103 return nil
104 }
105
106 func (n *mutDir) Access(ctx context.Context, req *fuse.AccessRequest) error {
107 n.mu.Lock()
108 defer n.mu.Unlock()
109 if n.deleted {
110 return fuse.ENOENT
111 }
112 return nil
113 }
114
115 func (n *mutFile) Access(ctx context.Context, req *fuse.AccessRequest) error {
116 n.mu.Lock()
117 defer n.mu.Unlock()
118 if n.deleted {
119 return fuse.ENOENT
120 }
121 return nil
122 }
123
124
125 func (n *mutDir) populate(ctx context.Context) error {
126 n.mu.Lock()
127 defer n.mu.Unlock()
128
129
130 now := time.Now()
131 if n.lastPop.Add(populateInterval).After(now) {
132 return nil
133 }
134 n.lastPop = now
135
136 res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{
137 BlobRef: n.permanode,
138 Depth: 3,
139 })
140 if err != nil {
141 return err
142 }
143 db := res.Meta[n.permanode.String()]
144 if db == nil {
145 return errors.New("dir blobref not described")
146 }
147
148
149 currentChildren := map[string]bool{}
150 for k, v := range db.Permanode.Attr {
151 const p = "camliPath:"
152 if !strings.HasPrefix(k, p) || len(v) < 1 {
153 continue
154 }
155 name := k[len(p):]
156 childRef := v[0]
157 child := res.Meta[childRef]
158 if child == nil {
159 Logger.Printf("child not described: %v", childRef)
160 continue
161 }
162 if child.Permanode == nil {
163 Logger.Printf("invalid child, not a permanode: %v", childRef)
164 continue
165 }
166 if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" {
167
168 n.maybeAddChild(name, child.Permanode, &mutFile{
169 fs: n.fs,
170 permanode: blob.ParseOrZero(childRef),
171 parent: n,
172 name: name,
173 symLink: true,
174 target: target,
175 })
176 } else if isDir(child.Permanode) {
177
178 n.maybeAddChild(name, child.Permanode, &mutDir{
179 fs: n.fs,
180 permanode: blob.ParseOrZero(childRef),
181 parent: n,
182 name: name,
183 children: make(map[string]mutFileOrDir),
184 })
185 } else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" {
186
187 content := res.Meta[contentRef]
188 if content == nil {
189 Logger.Printf("child content not described: %v", childRef)
190 continue
191 }
192 if content.CamliType != schema.TypeFile {
193 Logger.Printf("child not a file: %v", childRef)
194 continue
195 }
196 if content.File == nil {
197 Logger.Printf("camlitype \"file\" child %v has no described File member", childRef)
198 continue
199 }
200 n.maybeAddChild(name, child.Permanode, &mutFile{
201 fs: n.fs,
202 permanode: blob.ParseOrZero(childRef),
203 parent: n,
204 name: name,
205 content: blob.ParseOrZero(contentRef),
206 size: content.File.Size,
207 })
208 } else {
209
210 continue
211 }
212 currentChildren[name] = true
213 }
214
215 for name, oldchild := range n.children {
216 if _, ok := currentChildren[name]; !ok {
217 if oldchild.eligibleToDelete() {
218 delete(n.children, name)
219 }
220 }
221 }
222 return nil
223 }
224
225
226
227 func (n *mutDir) maybeAddChild(name string, permanode *search.DescribedPermanode,
228 child mutFileOrDir) {
229 if current, ok := n.children[name]; !ok ||
230 current.permanodeString() != child.permanodeString() {
231
232 child.xattr().load(permanode)
233 n.children[name] = child
234 }
235 }
236
237 func isDir(d *search.DescribedPermanode) bool {
238
239 if d.Attr.Get("camliNodeType") == "directory" {
240 return true
241 }
242
243 for k := range d.Attr {
244 if strings.HasPrefix(k, "camliPath:") {
245 return true
246 }
247 }
248 return false
249 }
250
251 func (n *mutDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
252 if err := n.populate(ctx); err != nil {
253 Logger.Println("populate:", err)
254 return nil, handleEIOorEINTR(err)
255 }
256 n.mu.Lock()
257 defer n.mu.Unlock()
258 var ents []fuse.Dirent
259 for name, childNode := range n.children {
260 var ino uint64
261 switch v := childNode.(type) {
262 case *mutDir:
263 ino = v.permanode.Sum64()
264 case *mutFile:
265 ino = v.permanode.Sum64()
266 default:
267 Logger.Printf("mutDir.ReadDirAll: unknown child type %T", childNode)
268 }
269
270
271
272 dirent := fuse.Dirent{
273 Name: name,
274 Inode: ino,
275 }
276 Logger.Printf("mutDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent)
277 ents = append(ents, dirent)
278 }
279 return ents, nil
280 }
281
282 func (n *mutDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error) {
283 defer func() {
284 Logger.Printf("mutDir(%q).Lookup(%q) = %v, %v", n.fullPath(), name, ret, err)
285 }()
286 if err := n.populate(ctx); err != nil {
287 Logger.Println("populate:", err)
288 return nil, handleEIOorEINTR(err)
289 }
290 n.mu.Lock()
291 defer n.mu.Unlock()
292 if n2 := n.children[name]; n2 != nil {
293 return n2, nil
294 }
295 return nil, fuse.ENOENT
296 }
297
298
299
300
301
302
303
304
305
306
307
308 func (n *mutDir) Create(ctx context.Context, req *fuse.CreateRequest, res *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
309 child, err := n.creat(ctx, req.Name, fileType)
310 if err != nil {
311 Logger.Printf("mutDir.Create(%q): %v", req.Name, err)
312 return nil, nil, handleEIOorEINTR(err)
313 }
314
315
316 h, ferr := child.(*mutFile).newHandle(nil)
317 if ferr != nil {
318 return nil, nil, ferr
319 }
320
321 return child, h, nil
322 }
323
324 func (n *mutDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
325 child, err := n.creat(ctx, req.Name, dirType)
326 if err != nil {
327 Logger.Printf("mutDir.Mkdir(%q): %v", req.Name, err)
328 return nil, handleEIOorEINTR(err)
329 }
330 return child, nil
331 }
332
333
334 func (n *mutDir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
335 node, err := n.creat(ctx, req.NewName, symlinkType)
336 if err != nil {
337 Logger.Printf("mutDir.Symlink(%q): %v", req.NewName, err)
338 return nil, handleEIOorEINTR(err)
339 }
340 mf := node.(*mutFile)
341 mf.symLink = true
342 mf.target = req.Target
343
344 claim := schema.NewSetAttributeClaim(mf.permanode, "camliSymlinkTarget", req.Target)
345 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
346 if err != nil {
347 Logger.Printf("mutDir.Symlink(%q) upload error: %v", req.NewName, err)
348 return nil, handleEIOorEINTR(err)
349 }
350
351 return node, nil
352 }
353
354 func (n *mutDir) creat(ctx context.Context, name string, typ nodeType) (fs.Node, error) {
355
356 pr, err := n.fs.client.UploadNewPermanode(ctx)
357 if err != nil {
358 return nil, err
359 }
360
361 var grp syncutil.Group
362 grp.Go(func() (err error) {
363
364 claim := schema.NewSetAttributeClaim(n.permanode, "camliPath:"+name, pr.BlobRef.String())
365 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
366 return
367 })
368
369
370
371 if name == ".DS_Store" {
372 grp.Go(func() (err error) {
373 claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliDefVis", "hide")
374 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
375 return
376 })
377 }
378
379 if typ == dirType {
380 grp.Go(func() (err error) {
381
382 claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliNodeType", "directory")
383 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
384 return
385 })
386 grp.Go(func() (err error) {
387
388 claim := schema.NewSetAttributeClaim(pr.BlobRef, "title", name)
389 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
390 return
391 })
392 }
393 if err := grp.Err(); err != nil {
394 return nil, err
395 }
396
397
398 var child mutFileOrDir
399 switch typ {
400 case dirType:
401 child = &mutDir{
402 fs: n.fs,
403 permanode: pr.BlobRef,
404 parent: n,
405 name: name,
406 xattrs: make(map[string][]byte),
407 localCreateTime: time.Now(),
408 children: make(map[string]mutFileOrDir),
409 }
410 case fileType, symlinkType:
411 child = &mutFile{
412 fs: n.fs,
413 permanode: pr.BlobRef,
414 parent: n,
415 name: name,
416 xattrs: make(map[string][]byte),
417 localCreateTime: time.Now(),
418 }
419 default:
420 panic("bogus creat type")
421 }
422 n.mu.Lock()
423 n.children[name] = child
424 n.mu.Unlock()
425
426 Logger.Printf("Created %v in %p", child, n)
427
428 return child, nil
429 }
430
431 func (n *mutDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
432
433 claim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.Name, "")
434 _, err := n.fs.client.UploadAndSignBlob(ctx, claim)
435 if err != nil {
436 Logger.Println("mutDir.Remove:", err)
437 return handleEIOorEINTR(err)
438 }
439
440 n.mu.Lock()
441 if n.children != nil {
442 if removed, ok := n.children[req.Name]; ok {
443 removed.invalidate()
444 delete(n.children, req.Name)
445 Logger.Printf("Removed %v from %p", removed, n)
446 }
447 }
448 n.mu.Unlock()
449 return nil
450 }
451
452
453 func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
454 n2, ok := newDir.(*mutDir)
455 if !ok {
456 Logger.Printf("*mutDir newDir node isn't a *mutDir; is a %T; can't handle. returning EIO.", newDir)
457 return fuse.EIO
458 }
459
460 var wg syncutil.Group
461 wg.Go(func() error { return n.populate(ctx) })
462 wg.Go(func() error { return n2.populate(ctx) })
463 if err := wg.Err(); err != nil {
464 Logger.Printf("*mutDir.Rename src dir populate = %v", err)
465 return handleEIOorEINTR(err)
466 }
467
468 n.mu.Lock()
469 target, ok := n.children[req.OldName]
470 n.mu.Unlock()
471 if !ok {
472 Logger.Printf("*mutDir.Rename src name %q isn't known", req.OldName)
473 return fuse.ENOENT
474 }
475
476 now := time.Now()
477
478
479
480 claim := schema.NewSetAttributeClaim(n2.permanode, "camliPath:"+req.NewName, target.permanodeString())
481 claim.SetClaimDate(now)
482 _, err := n.fs.client.UploadAndSignBlob(ctx, claim)
483 if err != nil {
484 Logger.Printf("Upload rename link error: %v", err)
485 return handleEIOorEINTR(err)
486 }
487
488 var grp syncutil.Group
489
490 grp.Go(func() (err error) {
491 delClaim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.OldName, "")
492 delClaim.SetClaimDate(now)
493 _, err = n.fs.client.UploadAndSignBlob(ctx, delClaim)
494 return
495 })
496
497 if dir, ok := target.(*mutDir); ok {
498 grp.Go(func() (err error) {
499 claim := schema.NewSetAttributeClaim(dir.permanode, "title", req.NewName)
500 _, err = n.fs.client.UploadAndSignBlob(ctx, claim)
501 return
502 })
503 }
504 if err := grp.Err(); err != nil {
505 Logger.Printf("Upload rename unlink/title error: %v", err)
506 return handleEIOorEINTR(err)
507 }
508
509
510
511
512
513 n.mu.Lock()
514 if n.children[req.OldName] != target {
515 panic("Race.")
516 }
517 delete(n.children, req.OldName)
518 n.mu.Unlock()
519 n2.mu.Lock()
520 n2.children[req.NewName] = target
521 n2.mu.Unlock()
522
523 return nil
524 }
525
526
527 type mutFile struct {
528 fs *CamliFileSystem
529 permanode blob.Ref
530 parent *mutDir
531 name string
532
533 localCreateTime time.Time
534
535 mu sync.Mutex
536 symLink bool
537 target string
538 content blob.Ref
539 size int64
540 mtime, atime time.Time
541 xattrs map[string][]byte
542 deleted bool
543 }
544
545 var (
546 _ fs.Node = (*mutFile)(nil)
547 _ fs.NodeAccesser = (*mutFile)(nil)
548 _ fs.NodeGetxattrer = (*mutFile)(nil)
549 _ fs.NodeListxattrer = (*mutFile)(nil)
550 _ fs.NodeSetxattrer = (*mutFile)(nil)
551 _ fs.NodeOpener = (*mutFile)(nil)
552 _ fs.NodeFsyncer = (*mutFile)(nil)
553 _ fs.NodeReadlinker = (*mutFile)(nil)
554 _ fs.NodeSetattrer = (*mutFile)(nil)
555 )
556
557 func (n *mutFile) String() string {
558 return fmt.Sprintf("&mutFile{%p name=%q perm:%v}", n, n.fullPath(), n.permanode)
559 }
560
561
562 func (n *mutFile) fullPath() string {
563 if n == nil {
564 return ""
565 }
566 return filepath.Join(n.parent.fullPath(), n.name)
567 }
568
569 func (n *mutFile) xattr() *xattr {
570 return &xattr{"mutFile", n.fs, n.permanode, &n.mu, &n.xattrs}
571 }
572
573 func (n *mutDir) xattr() *xattr {
574 return &xattr{"mutDir", n.fs, n.permanode, &n.mu, &n.xattrs}
575 }
576
577 func (n *mutDir) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
578 return n.xattr().remove(ctx, req)
579 }
580
581 func (n *mutDir) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
582 return n.xattr().set(ctx, req)
583 }
584
585 func (n *mutDir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
586 return n.xattr().get(req, res)
587 }
588
589 func (n *mutDir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
590 return n.xattr().list(req, res)
591 }
592
593 func (n *mutFile) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
594 return n.xattr().get(req, res)
595 }
596
597 func (n *mutFile) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
598 return n.xattr().list(req, res)
599 }
600
601 func (n *mutFile) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
602 return n.xattr().remove(ctx, req)
603 }
604
605 func (n *mutFile) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
606 return n.xattr().set(ctx, req)
607 }
608
609 func (n *mutFile) Attr(ctx context.Context, a *fuse.Attr) error {
610
611 var mode os.FileMode = 0600
612
613 n.mu.Lock()
614 size := n.size
615 var blocks uint64
616 if size > 0 {
617 blocks = uint64(size)/512 + 1
618 }
619 inode := n.permanode.Sum64()
620 if n.symLink {
621 mode |= os.ModeSymlink
622 }
623 n.mu.Unlock()
624
625 a.Inode = inode
626 a.Mode = mode
627 a.Uid = uint32(os.Getuid())
628 a.Gid = uint32(os.Getgid())
629 a.Size = uint64(size)
630 a.Blocks = blocks
631 a.Mtime = n.modTime()
632 a.Atime = n.accessTime()
633 a.Ctime = serverStart
634 return nil
635 }
636
637 func (n *mutFile) accessTime() time.Time {
638 n.mu.Lock()
639 if !n.atime.IsZero() {
640 defer n.mu.Unlock()
641 return n.atime
642 }
643 n.mu.Unlock()
644 return n.modTime()
645 }
646
647 func (n *mutFile) modTime() time.Time {
648 n.mu.Lock()
649 defer n.mu.Unlock()
650 if !n.mtime.IsZero() {
651 return n.mtime
652 }
653 return serverStart
654 }
655
656
657
658
659
660
661
662
663
664
665
666 func (n *mutFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) {
667 mutFileOpen.Incr()
668
669 Logger.Printf("mutFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags)
670 r, err := schema.NewFileReader(ctx, n.fs.fetcher, n.content)
671 if err != nil {
672 mutFileOpenError.Incr()
673 Logger.Printf("mutFile.Open: %v", err)
674 return nil, handleEIOorEINTR(err)
675 }
676
677
678 if !isWriteFlags(req.Flags) {
679 mutFileOpenRO.Incr()
680 Logger.Printf("mutFile.Open returning read-only file")
681 n := &node{
682 fs: n.fs,
683 blobref: n.content,
684 }
685 return &nodeReader{n: n, fr: r}, nil
686 }
687
688 mutFileOpenRW.Incr()
689 Logger.Printf("mutFile.Open returning read-write filehandle")
690
691 defer r.Close()
692 return n.newHandle(r)
693 }
694
695 func (n *mutFile) Fsync(ctx context.Context, r *fuse.FsyncRequest) error {
696
697
698 Logger.Printf("mutFile.Fsync: TODO")
699 return nil
700 }
701
702 func (n *mutFile) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
703 n.mu.Lock()
704 defer n.mu.Unlock()
705 if !n.symLink {
706 Logger.Printf("mutFile.Readlink on node that's not a symlink?")
707 return "", fuse.EIO
708 }
709 return n.target, nil
710 }
711
712 func (n *mutFile) Setattr(ctx context.Context, req *fuse.SetattrRequest, res *fuse.SetattrResponse) error {
713 Logger.Printf("mutFile.Setattr on %q: %#v", n.fullPath(), req)
714
715
716 n.mu.Lock()
717 if req.Valid.Mtime() {
718 n.mtime = req.Mtime
719 }
720 if req.Valid.Atime() {
721 n.atime = req.Atime
722 }
723 if req.Valid.Size() {
724 n.size = int64(req.Size)
725 }
726 n.mu.Unlock()
727
728 n.Attr(ctx, &res.Attr)
729 return nil
730 }
731
732 func (n *mutFile) newHandle(body io.Reader) (fs.Handle, error) {
733 tmp, err := os.CreateTemp("", "camli-")
734 if err == nil && body != nil {
735 _, err = io.Copy(tmp, body)
736 }
737 if err != nil {
738 Logger.Printf("mutFile.newHandle: %v", err)
739 if tmp != nil {
740 tmp.Close()
741 os.Remove(tmp.Name())
742 }
743 return nil, fuse.EIO
744 }
745 return &mutFileHandle{f: n, tmp: tmp}, nil
746 }
747
748
749
750
751
752
753
754 type mutFileHandle struct {
755 f *mutFile
756 tmp *os.File
757 }
758
759 var (
760 _ fs.HandleReader = (*mutFileHandle)(nil)
761 _ fs.HandleWriter = (*mutFileHandle)(nil)
762 _ fs.HandleFlusher = (*mutFileHandle)(nil)
763 _ fs.HandleReleaser = (*mutFileHandle)(nil)
764 )
765
766 func (h *mutFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, res *fuse.ReadResponse) error {
767 h.f.mu.Lock()
768 defer h.f.mu.Unlock()
769
770 if h.tmp == nil {
771 Logger.Printf("Read called on camli mutFileHandle without a tempfile set")
772 return fuse.EIO
773 }
774
775 if req.Offset > h.f.size {
776 return nil
777 }
778 toRead := int64(req.Size)
779 if remain := h.f.size - req.Offset; remain < toRead {
780 toRead = remain
781 }
782
783 buf := make([]byte, toRead)
784 n, err := h.tmp.ReadAt(buf, req.Offset)
785 if err == io.EOF {
786 err = nil
787 }
788 if err != nil {
789 Logger.Printf("mutFileHandle.Read: %v", err)
790 return fuse.EIO
791 }
792 res.Data = buf[:n]
793 return nil
794 }
795
796 func (h *mutFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, res *fuse.WriteResponse) error {
797 h.f.mu.Lock()
798 defer h.f.mu.Unlock()
799
800 if h.tmp == nil {
801 Logger.Printf("Write called on camli mutFileHandle without a tempfile set")
802 return fuse.EIO
803 }
804
805 n, err := h.tmp.WriteAt(req.Data, req.Offset)
806 Logger.Printf("mutFileHandle.Write(%q, %d bytes at %d, flags %v) = %d, %v",
807 h.f.fullPath(), len(req.Data), req.Offset, req.Flags, n, err)
808 if err != nil {
809 Logger.Println("mutFileHandle.Write:", err)
810 return fuse.EIO
811 }
812
813 if h.f.size < req.Offset+int64(n) {
814 h.f.size = req.Offset + int64(n)
815 }
816 res.Size = n
817
818 return nil
819 }
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835 func (h *mutFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error {
836 h.f.mu.Lock()
837 defer h.f.mu.Unlock()
838
839 if h.tmp == nil {
840 Logger.Printf("Flush called on camli mutFileHandle without a tempfile set")
841 return fuse.EIO
842 }
843 _, err := h.tmp.Seek(0, 0)
844 if err != nil {
845 Logger.Println("mutFileHandle.Flush:", err)
846 return fuse.EIO
847 }
848 br, err := schema.WriteFileFromReader(ctx, h.f.fs.client, h.f.name, &io.LimitedReader{R: h.tmp, N: h.f.size})
849 if err != nil {
850 Logger.Println("mutFileHandle.Flush:", err)
851 return handleEIOorEINTR(err)
852 }
853 h.f.content = br
854 claim := schema.NewSetAttributeClaim(h.f.permanode, "camliContent", br.String())
855 if _, err := h.f.fs.client.UploadAndSignBlob(ctx, claim); err != nil {
856 Logger.Printf("mutFileHandle.Flush: %v", err)
857 return handleEIOorEINTR(err)
858 }
859
860 return nil
861 }
862
863
864
865 func (h *mutFileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
866 h.f.mu.Lock()
867 defer h.f.mu.Unlock()
868
869 h.tmp.Close()
870 os.Remove(h.tmp.Name())
871 h.tmp = nil
872
873 return nil
874 }
875
876
877 type mutFileOrDir interface {
878 fs.Node
879 invalidate()
880 permanodeString() string
881 xattr() *xattr
882 eligibleToDelete() bool
883 }
884
885 func (n *mutFile) permanodeString() string {
886 return n.permanode.String()
887 }
888
889 func (n *mutDir) permanodeString() string {
890 return n.permanode.String()
891 }
892
893 func (n *mutFile) invalidate() {
894 n.mu.Lock()
895 n.deleted = true
896 n.mu.Unlock()
897 }
898
899 func (n *mutDir) invalidate() {
900 n.mu.Lock()
901 n.deleted = true
902 n.mu.Unlock()
903 }
904
905 func (n *mutFile) eligibleToDelete() bool {
906 return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow))
907 }
908
909 func (n *mutDir) eligibleToDelete() bool {
910 return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow))
911 }