1// +build linux darwin
2
3/*
4Copyright 2013 The Perkeep Authors
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10     http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17*/
18
19package fs
20
21import (
22	"context"
23	"errors"
24	"fmt"
25	"io"
26	"io/ioutil"
27	"os"
28	"path/filepath"
29	"strings"
30	"sync"
31	"time"
32
33	"perkeep.org/pkg/blob"
34	"perkeep.org/pkg/schema"
35	"perkeep.org/pkg/search"
36
37	"bazil.org/fuse"
38	"bazil.org/fuse/fs"
39	"go4.org/readerutil"
40	"go4.org/syncutil"
41)
42
43// How often to refresh directory nodes by reading from the blobstore.
44const populateInterval = 30 * time.Second
45
46// How long an item that was created locally will be present
47// regardless of its presence in the indexing server.
48const deletionRefreshWindow = time.Minute
49
50type nodeType int
51
52const (
53	fileType nodeType = iota
54	dirType
55	symlinkType
56)
57
58// mutDir is a mutable directory.
59// Its br is the permanode with camliPath:entname attributes.
60type mutDir struct {
61	fs        *CamliFileSystem
62	permanode blob.Ref
63	parent    *mutDir // or nil, if the root within its roots.go root.
64	name      string  // ent name (base name within parent)
65
66	localCreateTime time.Time // time this node was created locally (iff it was)
67
68	mu       sync.Mutex
69	lastPop  time.Time
70	children map[string]mutFileOrDir
71	xattrs   map[string][]byte
72	deleted  bool
73}
74
75var _ fs.Node = (*mutDir)(nil)
76var _ fs.NodeAccesser = (*mutDir)(nil)
77var _ fs.HandleReadDirAller = (*mutDir)(nil)
78var _ fs.NodeStringLookuper = (*mutDir)(nil)
79var _ fs.NodeGetxattrer = (*mutDir)(nil)
80var _ fs.NodeListxattrer = (*mutDir)(nil)
81var _ fs.NodeSetxattrer = (*mutDir)(nil)
82var _ fs.NodeCreater = (*mutDir)(nil)
83var _ fs.NodeMkdirer = (*mutDir)(nil)
84var _ fs.NodeSymlinker = (*mutDir)(nil)
85var _ fs.NodeRemover = (*mutDir)(nil)
86var _ fs.NodeRenamer = (*mutDir)(nil)
87
88func (n *mutDir) String() string {
89	return fmt.Sprintf("&mutDir{%p name=%q perm:%v}", n, n.fullPath(), n.permanode)
90}
91
92// for debugging
93func (n *mutDir) fullPath() string {
94	if n == nil {
95		return ""
96	}
97	return filepath.Join(n.parent.fullPath(), n.name)
98}
99
100func (n *mutDir) Attr(ctx context.Context, a *fuse.Attr) error {
101	a.Inode = n.permanode.Sum64()
102	a.Mode = os.ModeDir | 0700
103	a.Uid = uint32(os.Getuid())
104	a.Gid = uint32(os.Getgid())
105	return nil
106}
107
108func (n *mutDir) Access(ctx context.Context, req *fuse.AccessRequest) error {
109	n.mu.Lock()
110	defer n.mu.Unlock()
111	if n.deleted {
112		return fuse.ENOENT
113	}
114	return nil
115}
116
117func (n *mutFile) Access(ctx context.Context, req *fuse.AccessRequest) error {
118	n.mu.Lock()
119	defer n.mu.Unlock()
120	if n.deleted {
121		return fuse.ENOENT
122	}
123	return nil
124}
125
126// populate hits the blobstore to populate map of child nodes.
127func (n *mutDir) populate() error {
128	n.mu.Lock()
129	defer n.mu.Unlock()
130	ctx := context.TODO()
131
132	// Only re-populate if we haven't done so recently.
133	now := time.Now()
134	if n.lastPop.Add(populateInterval).After(now) {
135		return nil
136	}
137	n.lastPop = now
138
139	res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{
140		BlobRef: n.permanode,
141		Depth:   3,
142	})
143	if err != nil {
144		Logger.Println("mutDir.paths:", err)
145		return nil
146	}
147	db := res.Meta[n.permanode.String()]
148	if db == nil {
149		return errors.New("dir blobref not described")
150	}
151
152	// Find all child permanodes and stick them in n.children
153	if n.children == nil {
154		n.children = make(map[string]mutFileOrDir)
155	}
156	currentChildren := map[string]bool{}
157	for k, v := range db.Permanode.Attr {
158		const p = "camliPath:"
159		if !strings.HasPrefix(k, p) || len(v) < 1 {
160			continue
161		}
162		name := k[len(p):]
163		childRef := v[0]
164		child := res.Meta[childRef]
165		if child == nil {
166			Logger.Printf("child not described: %v", childRef)
167			continue
168		}
169		if child.Permanode == nil {
170			Logger.Printf("invalid child, not a permanode: %v", childRef)
171			continue
172		}
173		if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" {
174			// This is a symlink.
175			n.maybeAddChild(name, child.Permanode, &mutFile{
176				fs:        n.fs,
177				permanode: blob.ParseOrZero(childRef),
178				parent:    n,
179				name:      name,
180				symLink:   true,
181				target:    target,
182			})
183		} else if isDir(child.Permanode) {
184			// This is a directory.
185			n.maybeAddChild(name, child.Permanode, &mutDir{
186				fs:        n.fs,
187				permanode: blob.ParseOrZero(childRef),
188				parent:    n,
189				name:      name,
190			})
191		} else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" {
192			// This is a file.
193			content := res.Meta[contentRef]
194			if content == nil {
195				Logger.Printf("child content not described: %v", childRef)
196				continue
197			}
198			if content.CamliType != "file" {
199				Logger.Printf("child not a file: %v", childRef)
200				continue
201			}
202			if content.File == nil {
203				Logger.Printf("camlitype \"file\" child %v has no described File member", childRef)
204				continue
205			}
206			n.maybeAddChild(name, child.Permanode, &mutFile{
207				fs:        n.fs,
208				permanode: blob.ParseOrZero(childRef),
209				parent:    n,
210				name:      name,
211				content:   blob.ParseOrZero(contentRef),
212				size:      content.File.Size,
213			})
214		} else {
215			// unhandled type...
216			continue
217		}
218		currentChildren[name] = true
219	}
220	// Remove unreferenced children
221	for name, oldchild := range n.children {
222		if _, ok := currentChildren[name]; !ok {
223			if oldchild.eligibleToDelete() {
224				delete(n.children, name)
225			}
226		}
227	}
228	return nil
229}
230
231// maybeAddChild adds a child directory to this mutable directory
232// unless it already has one with this name and permanode.
233func (n *mutDir) maybeAddChild(name string, permanode *search.DescribedPermanode,
234	child mutFileOrDir) {
235	if current, ok := n.children[name]; !ok ||
236		current.permanodeString() != child.permanodeString() {
237
238		child.xattr().load(permanode)
239		n.children[name] = child
240	}
241}
242
243func isDir(d *search.DescribedPermanode) bool {
244	// Explicit
245	if d.Attr.Get("camliNodeType") == "directory" {
246		return true
247	}
248	// Implied
249	for k := range d.Attr {
250		if strings.HasPrefix(k, "camliPath:") {
251			return true
252		}
253	}
254	return false
255}
256
257func (n *mutDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
258	if err := n.populate(); err != nil {
259		Logger.Println("populate:", err)
260		return nil, fuse.EIO
261	}
262	n.mu.Lock()
263	defer n.mu.Unlock()
264	var ents []fuse.Dirent
265	for name, childNode := range n.children {
266		var ino uint64
267		switch v := childNode.(type) {
268		case *mutDir:
269			ino = v.permanode.Sum64()
270		case *mutFile:
271			ino = v.permanode.Sum64()
272		default:
273			Logger.Printf("mutDir.ReadDirAll: unknown child type %T", childNode)
274		}
275
276		// TODO: figure out what Dirent.Type means.
277		// fuse.go says "Type uint32 // ?"
278		dirent := fuse.Dirent{
279			Name:  name,
280			Inode: ino,
281		}
282		Logger.Printf("mutDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent)
283		ents = append(ents, dirent)
284	}
285	return ents, nil
286}
287
288func (n *mutDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error) {
289	defer func() {
290		Logger.Printf("mutDir(%q).Lookup(%q) = %v, %v", n.fullPath(), name, ret, err)
291	}()
292	if err := n.populate(); err != nil {
293		Logger.Println("populate:", err)
294		return nil, fuse.EIO
295	}
296	n.mu.Lock()
297	defer n.mu.Unlock()
298	if n2 := n.children[name]; n2 != nil {
299		return n2, nil
300	}
301	return nil, fuse.ENOENT
302}
303
304// Create of regular file. (not a dir)
305//
306// Flags are always 514:  O_CREAT is 0x200 | O_RDWR is 0x2.
307// From fuse_vnops.c:
308//    /* XXX: We /always/ creat() like this. Wish we were on Linux. */
309//    foi->flags = O_CREAT | O_RDWR;
310//
311// 2013/07/21 05:26:35 <- &{Create [ID=0x3 Node=0x8 Uid=61652 Gid=5000 Pid=13115] "x" fl=514 mode=-rw-r--r-- fuse.Intr}
312// 2013/07/21 05:26:36 -> 0x3 Create {LookupResponse:{Node:23 Generation:0 EntryValid:1m0s AttrValid:1m0s Attr:{Inode:15976986887557313215 Size:0 Blocks:0 Atime:2013-07-21 05:23:51.537251251 +1200 NZST Mtime:2013-07-21 05:23:51.537251251 +1200 NZST Ctime:2013-07-21 05:23:51.537251251 +1200 NZST Crtime:2013-07-21 05:23:51.537251251 +1200 NZST Mode:-rw------- Nlink:1 Uid:61652 Gid:5000 Rdev:0 Flags:0}} OpenResponse:{Handle:1 Flags:0}}
313func (n *mutDir) Create(ctx context.Context, req *fuse.CreateRequest, res *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
314	child, err := n.creat(ctx, req.Name, fileType)
315	if err != nil {
316		Logger.Printf("mutDir.Create(%q): %v", req.Name, err)
317		return nil, nil, fuse.EIO
318	}
319
320	// Create and return a file handle.
321	h, ferr := child.(*mutFile).newHandle(nil)
322	if ferr != nil {
323		return nil, nil, ferr
324	}
325
326	return child, h, nil
327}
328
329func (n *mutDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
330	child, err := n.creat(ctx, req.Name, dirType)
331	if err != nil {
332		Logger.Printf("mutDir.Mkdir(%q): %v", req.Name, err)
333		return nil, fuse.EIO
334	}
335	return child, nil
336}
337
338// &fuse.SymlinkRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210047180), ID:0x4, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x7e88}, NewName:"some-link", Target:"../../some-target"}
339func (n *mutDir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
340	node, err := n.creat(ctx, req.NewName, symlinkType)
341	if err != nil {
342		Logger.Printf("mutDir.Symlink(%q): %v", req.NewName, err)
343		return nil, fuse.EIO
344	}
345	mf := node.(*mutFile)
346	mf.symLink = true
347	mf.target = req.Target
348
349	claim := schema.NewSetAttributeClaim(mf.permanode, "camliSymlinkTarget", req.Target)
350	_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
351	if err != nil {
352		Logger.Printf("mutDir.Symlink(%q) upload error: %v", req.NewName, err)
353		return nil, fuse.EIO
354	}
355
356	return node, nil
357}
358
359func (n *mutDir) creat(ctx context.Context, name string, typ nodeType) (fs.Node, error) {
360	// Create a Permanode for the file/directory.
361	pr, err := n.fs.client.UploadNewPermanode(ctx)
362	if err != nil {
363		return nil, err
364	}
365
366	var grp syncutil.Group
367	grp.Go(func() (err error) {
368		// Add a camliPath:name attribute to the directory permanode.
369		claim := schema.NewSetAttributeClaim(n.permanode, "camliPath:"+name, pr.BlobRef.String())
370		_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
371		return
372	})
373
374	// Hide OS X Finder .DS_Store junk.  This is distinct from
375	// extended attributes.
376	if name == ".DS_Store" {
377		grp.Go(func() (err error) {
378			claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliDefVis", "hide")
379			_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
380			return
381		})
382	}
383
384	if typ == dirType {
385		grp.Go(func() (err error) {
386			// Set a directory type on the permanode
387			claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliNodeType", "directory")
388			_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
389			return
390		})
391		grp.Go(func() (err error) {
392			// Set the permanode title to the directory name
393			claim := schema.NewSetAttributeClaim(pr.BlobRef, "title", name)
394			_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
395			return
396		})
397	}
398	if err := grp.Err(); err != nil {
399		return nil, err
400	}
401
402	// Add a child node to this node.
403	var child mutFileOrDir
404	switch typ {
405	case dirType:
406		child = &mutDir{
407			fs:              n.fs,
408			permanode:       pr.BlobRef,
409			parent:          n,
410			name:            name,
411			xattrs:          map[string][]byte{},
412			localCreateTime: time.Now(),
413		}
414	case fileType, symlinkType:
415		child = &mutFile{
416			fs:              n.fs,
417			permanode:       pr.BlobRef,
418			parent:          n,
419			name:            name,
420			xattrs:          map[string][]byte{},
421			localCreateTime: time.Now(),
422		}
423	default:
424		panic("bogus creat type")
425	}
426	n.mu.Lock()
427	if n.children == nil {
428		n.children = make(map[string]mutFileOrDir)
429	}
430	n.children[name] = child
431	n.mu.Unlock()
432
433	Logger.Printf("Created %v in %p", child, n)
434
435	return child, nil
436}
437
438func (n *mutDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
439	// Remove the camliPath:name attribute from the directory permanode.
440	claim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.Name, "")
441	_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
442	if err != nil {
443		Logger.Println("mutDir.Remove:", err)
444		return fuse.EIO
445	}
446	// Remove child from map.
447	n.mu.Lock()
448	if n.children != nil {
449		if removed, ok := n.children[req.Name]; ok {
450			removed.invalidate()
451			delete(n.children, req.Name)
452			Logger.Printf("Removed %v from %p", removed, n)
453		}
454	}
455	n.mu.Unlock()
456	return nil
457}
458
459// &RenameRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210048180), ID:0x2, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x5edb}, NewDir:0x8, OldName:"1", NewName:"2"}
460func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
461	n2, ok := newDir.(*mutDir)
462	if !ok {
463		Logger.Printf("*mutDir newDir node isn't a *mutDir; is a %T; can't handle. returning EIO.", newDir)
464		return fuse.EIO
465	}
466
467	var wg syncutil.Group
468	wg.Go(n.populate)
469	wg.Go(n2.populate)
470	if err := wg.Err(); err != nil {
471		Logger.Printf("*mutDir.Rename src dir populate = %v", err)
472		return fuse.EIO
473	}
474
475	n.mu.Lock()
476	target, ok := n.children[req.OldName]
477	n.mu.Unlock()
478	if !ok {
479		Logger.Printf("*mutDir.Rename src name %q isn't known", req.OldName)
480		return fuse.ENOENT
481	}
482
483	now := time.Now()
484
485	// Add a camliPath:name attribute to the dest permanode before unlinking it from
486	// the source.
487	claim := schema.NewSetAttributeClaim(n2.permanode, "camliPath:"+req.NewName, target.permanodeString())
488	claim.SetClaimDate(now)
489	_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
490	if err != nil {
491		Logger.Printf("Upload rename link error: %v", err)
492		return fuse.EIO
493	}
494
495	var grp syncutil.Group
496	// Unlink the dest permanode from the source.
497	grp.Go(func() (err error) {
498		delClaim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.OldName, "")
499		delClaim.SetClaimDate(now)
500		_, err = n.fs.client.UploadAndSignBlob(ctx, delClaim)
501		return
502	})
503	// If target is a directory then update its title.
504	if dir, ok := target.(*mutDir); ok {
505		grp.Go(func() (err error) {
506			claim := schema.NewSetAttributeClaim(dir.permanode, "title", req.NewName)
507			_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
508			return
509		})
510	}
511	if err := grp.Err(); err != nil {
512		Logger.Printf("Upload rename unlink/title error: %v", err)
513		return fuse.EIO
514	}
515
516	// TODO(bradfitz): this locking would be racy, if the kernel
517	// doesn't do it properly. (It should) Let's just trust the
518	// kernel for now. Later we can verify and remove this
519	// comment.
520	n.mu.Lock()
521	if n.children[req.OldName] != target {
522		panic("Race.")
523	}
524	delete(n.children, req.OldName)
525	n.mu.Unlock()
526	n2.mu.Lock()
527	n2.children[req.NewName] = target
528	n2.mu.Unlock()
529
530	return nil
531}
532
533// mutFile is a mutable file, or symlink.
534type mutFile struct {
535	fs        *CamliFileSystem
536	permanode blob.Ref
537	parent    *mutDir
538	name      string // ent name (base name within parent)
539
540	localCreateTime time.Time // time this node was created locally (iff it was)
541
542	mu           sync.Mutex // protects all following fields
543	symLink      bool       // if true, is a symlink
544	target       string     // if a symlink
545	content      blob.Ref   // if a regular file
546	size         int64
547	mtime, atime time.Time // if zero, use serverStart
548	xattrs       map[string][]byte
549	deleted      bool
550}
551
552var (
553	_ fs.Node            = (*mutFile)(nil)
554	_ fs.NodeAccesser    = (*mutFile)(nil)
555	_ fs.NodeGetxattrer  = (*mutFile)(nil)
556	_ fs.NodeListxattrer = (*mutFile)(nil)
557	_ fs.NodeSetxattrer  = (*mutFile)(nil)
558	_ fs.NodeOpener      = (*mutFile)(nil)
559	_ fs.NodeFsyncer     = (*mutFile)(nil)
560	_ fs.NodeReadlinker  = (*mutFile)(nil)
561	_ fs.NodeSetattrer   = (*mutFile)(nil)
562)
563
564func (n *mutFile) String() string {
565	return fmt.Sprintf("&mutFile{%p name=%q perm:%v}", n, n.fullPath(), n.permanode)
566}
567
568// for debugging
569func (n *mutFile) fullPath() string {
570	if n == nil {
571		return ""
572	}
573	return filepath.Join(n.parent.fullPath(), n.name)
574}
575
576func (n *mutFile) xattr() *xattr {
577	return &xattr{"mutFile", n.fs, n.permanode, &n.mu, &n.xattrs}
578}
579
580func (n *mutDir) xattr() *xattr {
581	return &xattr{"mutDir", n.fs, n.permanode, &n.mu, &n.xattrs}
582}
583
584func (n *mutDir) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
585	return n.xattr().remove(ctx, req)
586}
587
588func (n *mutDir) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
589	return n.xattr().set(ctx, req)
590}
591
592func (n *mutDir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
593	return n.xattr().get(req, res)
594}
595
596func (n *mutDir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
597	return n.xattr().list(req, res)
598}
599
600func (n *mutFile) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
601	return n.xattr().get(req, res)
602}
603
604func (n *mutFile) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
605	return n.xattr().list(req, res)
606}
607
608func (n *mutFile) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
609	return n.xattr().remove(ctx, req)
610}
611
612func (n *mutFile) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
613	return n.xattr().set(ctx, req)
614}
615
616func (n *mutFile) Attr(ctx context.Context, a *fuse.Attr) error {
617	// TODO: don't grab n.mu three+ times in here.
618	var mode os.FileMode = 0600 // writable
619
620	n.mu.Lock()
621	size := n.size
622	var blocks uint64
623	if size > 0 {
624		blocks = uint64(size)/512 + 1
625	}
626	inode := n.permanode.Sum64()
627	if n.symLink {
628		mode |= os.ModeSymlink
629	}
630	n.mu.Unlock()
631
632	a.Inode = inode
633	a.Mode = mode
634	a.Uid = uint32(os.Getuid())
635	a.Gid = uint32(os.Getgid())
636	a.Size = uint64(size)
637	a.Blocks = blocks
638	a.Mtime = n.modTime()
639	a.Atime = n.accessTime()
640	a.Ctime = serverStart
641	a.Crtime = serverStart
642	return nil
643}
644
645func (n *mutFile) accessTime() time.Time {
646	n.mu.Lock()
647	if !n.atime.IsZero() {
648		defer n.mu.Unlock()
649		return n.atime
650	}
651	n.mu.Unlock()
652	return n.modTime()
653}
654
655func (n *mutFile) modTime() time.Time {
656	n.mu.Lock()
657	defer n.mu.Unlock()
658	if !n.mtime.IsZero() {
659		return n.mtime
660	}
661	return serverStart
662}
663
664func (n *mutFile) setContent(ctx context.Context, br blob.Ref, size int64) error {
665	n.mu.Lock()
666	defer n.mu.Unlock()
667	n.content = br
668	n.size = size
669	claim := schema.NewSetAttributeClaim(n.permanode, "camliContent", br.String())
670	_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
671	return err
672}
673
674func (n *mutFile) setSizeAtLeast(size int64) {
675	n.mu.Lock()
676	defer n.mu.Unlock()
677	Logger.Printf("mutFile.setSizeAtLeast(%d). old size = %d", size, n.size)
678	if size > n.size {
679		n.size = size
680	}
681}
682
683// Empirically:
684//  open for read:   req.Flags == 0
685//  open for append: req.Flags == 1
686//  open for write:  req.Flags == 1
687//  open for read/write (+<)   == 2 (bitmask? of?)
688//
689// open flags are O_WRONLY (1), O_RDONLY (0), or O_RDWR (2). and also
690// bitmaks of O_SYMLINK (0x200000) maybe. (from
691// fuse_filehandle_xlate_to_oflags in macosx/kext/fuse_file.h)
692func (n *mutFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) {
693	mutFileOpen.Incr()
694
695	Logger.Printf("mutFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags)
696	r, err := schema.NewFileReader(ctx, n.fs.fetcher, n.content)
697	if err != nil {
698		mutFileOpenError.Incr()
699		Logger.Printf("mutFile.Open: %v", err)
700		return nil, fuse.EIO
701	}
702
703	// Read-only.
704	if !isWriteFlags(req.Flags) {
705		mutFileOpenRO.Incr()
706		Logger.Printf("mutFile.Open returning read-only file")
707		n := &node{
708			fs:      n.fs,
709			blobref: n.content,
710		}
711		return &nodeReader{n: n, fr: r}, nil
712	}
713
714	mutFileOpenRW.Incr()
715	Logger.Printf("mutFile.Open returning read-write filehandle")
716
717	defer r.Close()
718	return n.newHandle(r)
719}
720
721func (n *mutFile) Fsync(ctx context.Context, r *fuse.FsyncRequest) error {
722	// TODO(adg): in the fuse package, plumb through fsync to mutFileHandle
723	// in the same way we did Truncate.
724	Logger.Printf("mutFile.Fsync: TODO")
725	return nil
726}
727
728func (n *mutFile) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
729	n.mu.Lock()
730	defer n.mu.Unlock()
731	if !n.symLink {
732		Logger.Printf("mutFile.Readlink on node that's not a symlink?")
733		return "", fuse.EIO
734	}
735	return n.target, nil
736}
737
738func (n *mutFile) Setattr(ctx context.Context, req *fuse.SetattrRequest, res *fuse.SetattrResponse) error {
739	Logger.Printf("mutFile.Setattr on %q: %#v", n.fullPath(), req)
740	// 2013/07/17 19:43:41 mutFile.Setattr on "foo": &fuse.SetattrRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210047180), ID:0x3, Node:0x3d, Uid:0xf0d4, Gid:0x1388, Pid:0x75e8}, Valid:0x30, Handle:0x0, Size:0x0, Atime:time.Time{sec:63509651021, nsec:0x4aec6b8, loc:(*time.Location)(0x47f7600)}, Mtime:time.Time{sec:63509651021, nsec:0x4aec6b8, loc:(*time.Location)(0x47f7600)}, Mode:0x4000000, Uid:0x0, Gid:0x0, Bkuptime:time.Time{sec:62135596800, nsec:0x0, loc:(*time.Location)(0x47f7600)}, Chgtime:time.Time{sec:62135596800, nsec:0x0, loc:(*time.Location)(0x47f7600)}, Crtime:time.Time{sec:0, nsec:0x0, loc:(*time.Location)(nil)}, Flags:0x0}
741
742	n.mu.Lock()
743	if req.Valid&fuse.SetattrMtime != 0 {
744		n.mtime = req.Mtime
745	}
746	if req.Valid&fuse.SetattrAtime != 0 {
747		n.atime = req.Atime
748	}
749	if req.Valid&fuse.SetattrSize != 0 {
750		// TODO(bradfitz): truncate?
751		n.size = int64(req.Size)
752	}
753	n.mu.Unlock()
754
755	n.Attr(ctx, &res.Attr)
756	return nil
757}
758
759func (n *mutFile) newHandle(body io.Reader) (fs.Handle, error) {
760	tmp, err := ioutil.TempFile("", "camli-")
761	if err == nil && body != nil {
762		_, err = io.Copy(tmp, body)
763	}
764	if err != nil {
765		Logger.Printf("mutFile.newHandle: %v", err)
766		if tmp != nil {
767			tmp.Close()
768			os.Remove(tmp.Name())
769		}
770		return nil, fuse.EIO
771	}
772	return &mutFileHandle{f: n, tmp: tmp}, nil
773}
774
775// mutFileHandle represents an open mutable file.
776// It stores the file contents in a temporary file, and
777// delegates reads and writes directly to the temporary file.
778// When the handle is released, it writes the contents of the
779// temporary file to the blobstore, and instructs the parent
780// mutFile to update the file permanode.
781type mutFileHandle struct {
782	f   *mutFile
783	tmp *os.File
784}
785
786var (
787	_ fs.HandleReader   = (*mutFileHandle)(nil)
788	_ fs.HandleWriter   = (*mutFileHandle)(nil)
789	_ fs.HandleFlusher  = (*mutFileHandle)(nil)
790	_ fs.HandleReleaser = (*mutFileHandle)(nil)
791)
792
793func (h *mutFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, res *fuse.ReadResponse) error {
794	if h.tmp == nil {
795		Logger.Printf("Read called on camli mutFileHandle without a tempfile set")
796		return fuse.EIO
797	}
798
799	buf := make([]byte, req.Size)
800	n, err := h.tmp.ReadAt(buf, req.Offset)
801	if err == io.EOF {
802		err = nil
803	}
804	if err != nil {
805		Logger.Printf("mutFileHandle.Read: %v", err)
806		return fuse.EIO
807	}
808	res.Data = buf[:n]
809	return nil
810}
811
812func (h *mutFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, res *fuse.WriteResponse) error {
813	if h.tmp == nil {
814		Logger.Printf("Write called on camli mutFileHandle without a tempfile set")
815		return fuse.EIO
816	}
817
818	n, err := h.tmp.WriteAt(req.Data, req.Offset)
819	Logger.Printf("mutFileHandle.Write(%q, %d bytes at %d, flags %v) = %d, %v",
820		h.f.fullPath(), len(req.Data), req.Offset, req.Flags, n, err)
821	if err != nil {
822		Logger.Println("mutFileHandle.Write:", err)
823		return fuse.EIO
824	}
825	res.Size = n
826	h.f.setSizeAtLeast(req.Offset + int64(n))
827	return nil
828}
829
830// Flush is called to let the file system clean up any data buffers
831// and to pass any errors in the process of closing a file to the user
832// application.
833//
834// Flush *may* be called more than once in the case where a file is
835// opened more than once, but it's not possible to detect from the
836// call itself whether this is a final flush.
837//
838// This is generally the last opportunity to finalize data and the
839// return value sets the return value of the Close that led to the
840// calling of Flush.
841//
842// Note that this is distinct from Fsync -- which is a user-requested
843// flush (fsync, etc...)
844func (h *mutFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error {
845	if h.tmp == nil {
846		Logger.Printf("Flush called on camli mutFileHandle without a tempfile set")
847		return fuse.EIO
848	}
849	_, err := h.tmp.Seek(0, 0)
850	if err != nil {
851		Logger.Println("mutFileHandle.Flush:", err)
852		return fuse.EIO
853	}
854	var n int64
855	br, err := schema.WriteFileFromReader(ctx, h.f.fs.client, h.f.name, readerutil.CountingReader{Reader: h.tmp, N: &n})
856	if err != nil {
857		Logger.Println("mutFileHandle.Flush:", err)
858		return fuse.EIO
859	}
860	err = h.f.setContent(ctx, br, n)
861	if err != nil {
862		Logger.Printf("mutFileHandle.Flush: %v", err)
863		return fuse.EIO
864	}
865
866	return nil
867}
868
869// Release is called when a file handle is no longer needed.  This is
870// called asynchronously after the last handle to a file is closed.
871func (h *mutFileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
872	h.tmp.Close()
873	os.Remove(h.tmp.Name())
874	h.tmp = nil
875
876	return nil
877}
878
879// mutFileOrDir is a *mutFile or *mutDir
880type mutFileOrDir interface {
881	fs.Node
882	invalidate()
883	permanodeString() string
884	xattr() *xattr
885	eligibleToDelete() bool
886}
887
888func (n *mutFile) permanodeString() string {
889	return n.permanode.String()
890}
891
892func (n *mutDir) permanodeString() string {
893	return n.permanode.String()
894}
895
896func (n *mutFile) invalidate() {
897	n.mu.Lock()
898	n.deleted = true
899	n.mu.Unlock()
900}
901
902func (n *mutDir) invalidate() {
903	n.mu.Lock()
904	n.deleted = true
905	n.mu.Unlock()
906}
907
908func (n *mutFile) eligibleToDelete() bool {
909	return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow))
910}
911
912func (n *mutDir) eligibleToDelete() bool {
913	return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow))
914}
915