Home Download Docs Code Community
     1	//go:build linux
     2	
     3	/*
     4	Copyright 2013 The Perkeep Authors
     5	
     6	Licensed under the Apache License, Version 2.0 (the "License");
     7	you may not use this file except in compliance with the License.
     8	You may obtain a copy of the License at
     9	
    10	     http://www.apache.org/licenses/LICENSE-2.0
    11	
    12	Unless required by applicable law or agreed to in writing, software
    13	distributed under the License is distributed on an "AS IS" BASIS,
    14	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15	See the License for the specific language governing permissions and
    16	limitations under the License.
    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	// How often to refresh directory nodes by reading from the blobstore.
    42	const populateInterval = 30 * time.Second
    43	
    44	// How long an item that was created locally will be present
    45	// regardless of its presence in the indexing server.
    46	const deletionRefreshWindow = time.Minute
    47	
    48	type nodeType int
    49	
    50	const (
    51		fileType nodeType = iota
    52		dirType
    53		symlinkType
    54	)
    55	
    56	// mutDir is a mutable directory.
    57	// Its br is the permanode with camliPath:entname attributes.
    58	type mutDir struct {
    59		fs        *CamliFileSystem
    60		permanode blob.Ref
    61		parent    *mutDir // or nil, if the root within its roots.go root.
    62		name      string  // ent name (base name within parent)
    63	
    64		localCreateTime time.Time // time this node was created locally (iff it was)
    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	// for debugging
    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	// populate hits the blobstore to populate map of child nodes.
   125	func (n *mutDir) populate(ctx context.Context) error {
   126		n.mu.Lock()
   127		defer n.mu.Unlock()
   128	
   129		// Only re-populate if we haven't done so recently.
   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		// Find all child permanodes and stick them in n.children
   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				// This is a symlink.
   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				// This is a directory.
   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				// This is a file.
   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				// unhandled type...
   210				continue
   211			}
   212			currentChildren[name] = true
   213		}
   214		// Remove unreferenced children
   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	// maybeAddChild adds a child directory to this mutable directory
   226	// unless it already has one with this name and permanode.
   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		// Explicit
   239		if d.Attr.Get("camliNodeType") == "directory" {
   240			return true
   241		}
   242		// Implied
   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			// TODO: figure out what Dirent.Type means.
   271			// fuse.go says "Type uint32 // ?"
   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	// Create of regular file. (not a dir)
   299	//
   300	// Flags are always 514:  O_CREAT is 0x200 | O_RDWR is 0x2.
   301	// From fuse_vnops.c:
   302	//
   303	//	/* XXX: We /always/ creat() like this. Wish we were on Linux. */
   304	//	foi->flags = O_CREAT | O_RDWR;
   305	//
   306	// 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}
   307	// 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}}
   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		// Create and return a file handle.
   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	// &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"}
   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		// Create a Permanode for the file/directory.
   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			// Add a camliPath:name attribute to the directory permanode.
   364			claim := schema.NewSetAttributeClaim(n.permanode, "camliPath:"+name, pr.BlobRef.String())
   365			_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
   366			return
   367		})
   368	
   369		// Hide OS X Finder .DS_Store junk.  This is distinct from
   370		// extended attributes.
   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				// Set a directory type on the permanode
   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				// Set the permanode title to the directory name
   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		// Add a child node to this node.
   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		// Remove the camliPath:name attribute from the directory permanode.
   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		// Remove child from map.
   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	// &RenameRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210048180), ID:0x2, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x5edb}, NewDir:0x8, OldName:"1", NewName:"2"}
   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		// Add a camliPath:name attribute to the dest permanode before unlinking it from
   479		// the source.
   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		// Unlink the dest permanode from the source.
   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		// If target is a directory then update its title.
   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		// TODO(bradfitz): this locking would be racy, if the kernel
   510		// doesn't do it properly. (It should) Let's just trust the
   511		// kernel for now. Later we can verify and remove this
   512		// comment.
   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	// mutFile is a mutable file, or symlink.
   527	type mutFile struct {
   528		fs        *CamliFileSystem
   529		permanode blob.Ref
   530		parent    *mutDir
   531		name      string // ent name (base name within parent)
   532	
   533		localCreateTime time.Time // time this node was created locally (iff it was)
   534	
   535		mu           sync.Mutex // protects all following fields
   536		symLink      bool       // if true, is a symlink
   537		target       string     // if a symlink
   538		content      blob.Ref   // if a regular file
   539		size         int64
   540		mtime, atime time.Time // if zero, use serverStart
   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	// for debugging
   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		// TODO: don't grab n.mu three+ times in here.
   611		var mode os.FileMode = 0600 // writable
   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	// Empirically:
   657	//
   658	//	open for read:   req.Flags == 0
   659	//	open for append: req.Flags == 1
   660	//	open for write:  req.Flags == 1
   661	//	open for read/write (+<)   == 2 (bitmask? of?)
   662	//
   663	// open flags are O_WRONLY (1), O_RDONLY (0), or O_RDWR (2). and also
   664	// bitmaks of O_SYMLINK (0x200000) maybe. (from
   665	// fuse_filehandle_xlate_to_oflags in macosx/kext/fuse_file.h)
   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		// Read-only.
   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		// TODO(adg): in the fuse package, plumb through fsync to mutFileHandle
   697		// in the same way we did Truncate.
   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		// 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}
   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	// mutFileHandle represents an open mutable file.
   749	// It stores the file contents in a temporary file, and
   750	// delegates reads and writes directly to the temporary file.
   751	// When the handle is released, it writes the contents of the
   752	// temporary file to the blobstore, and instructs the parent
   753	// mutFile to update the file permanode.
   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	// Flush is called to let the file system clean up any data buffers
   822	// and to pass any errors in the process of closing a file to the user
   823	// application.
   824	//
   825	// Flush *may* be called more than once in the case where a file is
   826	// opened more than once, but it's not possible to detect from the
   827	// call itself whether this is a final flush.
   828	//
   829	// This is generally the last opportunity to finalize data and the
   830	// return value sets the return value of the Close that led to the
   831	// calling of Flush.
   832	//
   833	// Note that this is distinct from Fsync -- which is a user-requested
   834	// flush (fsync, etc...)
   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	// Release is called when a file handle is no longer needed.  This is
   864	// called asynchronously after the last handle to a file is closed.
   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	// mutFileOrDir is a *mutFile or *mutDir
   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	}
Website layout inspired by memcached.
Content by the authors.