github.com/hanwen/go-fuse@v1.0.0/unionfs/unionfs.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package unionfs
     6  
     7  import (
     8  	"crypto/md5"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"strings"
    15  	"sync"
    16  	"syscall"
    17  	"time"
    18  
    19  	"github.com/hanwen/go-fuse/fuse"
    20  	"github.com/hanwen/go-fuse/fuse/nodefs"
    21  	"github.com/hanwen/go-fuse/fuse/pathfs"
    22  )
    23  
    24  func filePathHash(path string) string {
    25  	dir, base := filepath.Split(path)
    26  
    27  	h := md5.New()
    28  	h.Write([]byte(dir))
    29  	return fmt.Sprintf("%x-%s", h.Sum(nil)[:8], base)
    30  }
    31  
    32  /*
    33  
    34   UnionFs implements a user-space union file system, which is
    35   stateless but efficient even if the writable branch is on NFS.
    36  
    37  
    38   Assumptions:
    39  
    40   * It uses a list of branches, the first of which (index 0) is thought
    41   to be writable, and the rest read-only.
    42  
    43   * It assumes that the number of deleted files is small relative to
    44   the total tree size.
    45  
    46  
    47   Implementation notes.
    48  
    49   * It overlays arbitrary writable FileSystems with any number of
    50     readonly FileSystems.
    51  
    52   * Deleting a file will put a file named
    53   /DELETIONS/HASH-OF-FULL-FILENAME into the writable overlay,
    54   containing the full filename itself.
    55  
    56   This is optimized for NFS usage: we want to minimize the number of
    57   NFS operations, which are slow.  By putting all whiteouts in one
    58   place, we can cheaply fetch the list of all deleted files.  Even
    59   without caching on our side, the kernel's negative dentry cache can
    60   answer is-deleted queries quickly.
    61  
    62  */
    63  type unionFS struct {
    64  	pathfs.FileSystem
    65  
    66  	// The same, but as interfaces.
    67  	fileSystems []pathfs.FileSystem
    68  
    69  	// A file-existence cache.
    70  	deletionCache *dirCache
    71  
    72  	// A file -> branch cache.
    73  	branchCache *TimedCache
    74  
    75  	// Map of files to hide.
    76  	hiddenFiles map[string]bool
    77  
    78  	options *UnionFsOptions
    79  	nodeFs  *pathfs.PathNodeFs
    80  }
    81  
    82  type UnionFsOptions struct {
    83  	BranchCacheTTL   time.Duration
    84  	DeletionCacheTTL time.Duration
    85  	DeletionDirName  string
    86  	HiddenFiles      []string
    87  }
    88  
    89  const (
    90  	_DROP_CACHE = ".drop_cache"
    91  )
    92  
    93  func NewUnionFs(fileSystems []pathfs.FileSystem, options UnionFsOptions) (pathfs.FileSystem, error) {
    94  	g := &unionFS{
    95  		options:     &options,
    96  		fileSystems: fileSystems,
    97  		FileSystem:  pathfs.NewDefaultFileSystem(),
    98  	}
    99  
   100  	writable := g.fileSystems[0]
   101  	code := g.createDeletionStore()
   102  	if !code.Ok() {
   103  		return nil, fmt.Errorf("could not create deletion path %v: %v", options.DeletionDirName, code)
   104  	}
   105  
   106  	g.deletionCache = newDirCache(writable, options.DeletionDirName, options.DeletionCacheTTL)
   107  	g.branchCache = NewTimedCache(
   108  		func(n string) (interface{}, bool) { return g.getBranchAttrNoCache(n), true },
   109  		options.BranchCacheTTL)
   110  
   111  	g.hiddenFiles = make(map[string]bool)
   112  	for _, name := range options.HiddenFiles {
   113  		g.hiddenFiles[name] = true
   114  	}
   115  
   116  	return g, nil
   117  }
   118  
   119  func (fs *unionFS) OnMount(nodeFs *pathfs.PathNodeFs) {
   120  	fs.nodeFs = nodeFs
   121  }
   122  
   123  ////////////////
   124  // Deal with all the caches.
   125  
   126  // The isDeleted() method tells us if a path has a marker in the deletion store.
   127  // It may return an error code if the store could not be accessed.
   128  func (fs *unionFS) isDeleted(name string) (deleted bool, code fuse.Status) {
   129  	marker := fs.deletionPath(name)
   130  	haveCache, found := fs.deletionCache.HasEntry(filepath.Base(marker))
   131  	if haveCache {
   132  		return found, fuse.OK
   133  	}
   134  
   135  	_, code = fs.fileSystems[0].GetAttr(marker, nil)
   136  
   137  	if code == fuse.OK {
   138  		return true, code
   139  	}
   140  	if code == fuse.ENOENT {
   141  		return false, fuse.OK
   142  	}
   143  
   144  	log.Printf("error accessing deletion marker %s: %v", marker, code)
   145  	return false, fuse.Status(syscall.EROFS)
   146  }
   147  
   148  func (fs *unionFS) createDeletionStore() (code fuse.Status) {
   149  	writable := fs.fileSystems[0]
   150  	fi, code := writable.GetAttr(fs.options.DeletionDirName, nil)
   151  	if code == fuse.ENOENT {
   152  		code = writable.Mkdir(fs.options.DeletionDirName, 0755, nil)
   153  		if code.Ok() {
   154  			fi, code = writable.GetAttr(fs.options.DeletionDirName, nil)
   155  		}
   156  	}
   157  
   158  	if !code.Ok() || !fi.IsDir() {
   159  		code = fuse.Status(syscall.EROFS)
   160  	}
   161  
   162  	return code
   163  }
   164  
   165  func (fs *unionFS) getBranch(name string) branchResult {
   166  	name = stripSlash(name)
   167  	r := fs.branchCache.Get(name)
   168  	return r.(branchResult)
   169  }
   170  
   171  func (fs *unionFS) setBranch(name string, r branchResult) {
   172  	if !r.valid() {
   173  		log.Panicf("entry %q setting illegal branchResult %v", name, r)
   174  	}
   175  	fs.branchCache.Set(name, r)
   176  }
   177  
   178  type branchResult struct {
   179  	attr   *fuse.Attr
   180  	code   fuse.Status
   181  	branch int
   182  }
   183  
   184  func (r *branchResult) valid() bool {
   185  	return (r.branch >= 0 && r.attr != nil && r.code.Ok()) ||
   186  		(r.branch < 0 && r.attr == nil && !r.code.Ok())
   187  }
   188  
   189  func (fs branchResult) String() string {
   190  	return fmt.Sprintf("{%v %v branch %d}", fs.attr, fs.code, fs.branch)
   191  }
   192  
   193  func (fs *unionFS) getBranchAttrNoCache(name string) branchResult {
   194  	name = stripSlash(name)
   195  
   196  	parent, base := path.Split(name)
   197  	parent = stripSlash(parent)
   198  
   199  	parentBranch := 0
   200  	if base != "" {
   201  		parentBranch = fs.getBranch(parent).branch
   202  	}
   203  	for i, fs := range fs.fileSystems {
   204  		if i < parentBranch {
   205  			continue
   206  		}
   207  
   208  		a, s := fs.GetAttr(name, nil)
   209  		if s.Ok() {
   210  			if i > 0 {
   211  				// Needed to make hardlinks work.
   212  				a.Ino = 0
   213  			}
   214  			return branchResult{
   215  				attr:   a,
   216  				code:   s,
   217  				branch: i,
   218  			}
   219  		} else {
   220  			if s != fuse.ENOENT {
   221  				log.Printf("getattr: %v:  Got error %v from branch %v", name, s, i)
   222  			}
   223  		}
   224  	}
   225  	return branchResult{nil, fuse.ENOENT, -1}
   226  }
   227  
   228  ////////////////
   229  // Deletion.
   230  
   231  func (fs *unionFS) deletionPath(name string) string {
   232  	return filepath.Join(fs.options.DeletionDirName, filePathHash(name))
   233  }
   234  
   235  func (fs *unionFS) removeDeletion(name string) {
   236  	marker := fs.deletionPath(name)
   237  
   238  	// os.Remove tries to be smart and issues a Remove() and
   239  	// Rmdir() sequentially.  We want to skip the 2nd system call,
   240  	// so use syscall.Unlink() directly.
   241  
   242  	code := fs.fileSystems[0].Unlink(marker, nil)
   243  	if !code.Ok() && code != fuse.ENOENT {
   244  		log.Printf("error unlinking %s: %v", marker, code)
   245  	}
   246  
   247  	// Update in-memory cache as last step, so we avoid caching a
   248  	// state from before the storage update.
   249  	fs.deletionCache.RemoveEntry(path.Base(marker))
   250  }
   251  
   252  func (fs *unionFS) putDeletion(name string) (code fuse.Status) {
   253  	code = fs.createDeletionStore()
   254  	if !code.Ok() {
   255  		return code
   256  	}
   257  
   258  	marker := fs.deletionPath(name)
   259  
   260  	// Is there a WriteStringToFileOrDie ?
   261  	writable := fs.fileSystems[0]
   262  	fi, code := writable.GetAttr(marker, nil)
   263  	if code.Ok() && fi.Size == uint64(len(name)) {
   264  		return fuse.OK
   265  	}
   266  
   267  	var f nodefs.File
   268  	if code == fuse.ENOENT {
   269  		f, code = writable.Create(marker, uint32(os.O_TRUNC|os.O_WRONLY), 0644, nil)
   270  	} else {
   271  		writable.Chmod(marker, 0644, nil)
   272  		f, code = writable.Open(marker, uint32(os.O_TRUNC|os.O_WRONLY), nil)
   273  	}
   274  	if !code.Ok() {
   275  		log.Printf("could not create deletion file %v: %v", marker, code)
   276  		return fuse.EPERM
   277  	}
   278  	defer f.Release()
   279  	defer f.Flush()
   280  	n, code := f.Write([]byte(name), 0)
   281  	if int(n) != len(name) || !code.Ok() {
   282  		panic(fmt.Sprintf("Error for writing %v: %v, %v (exp %v) %v", name, marker, n, len(name), code))
   283  	}
   284  
   285  	// Update the in-memory deletion cache as the last step,
   286  	// to ensure that the new state stays in memory
   287  	fs.deletionCache.AddEntry(path.Base(marker))
   288  
   289  	return fuse.OK
   290  }
   291  
   292  ////////////////
   293  // Promotion.
   294  
   295  func (fs *unionFS) Promote(name string, srcResult branchResult, context *fuse.Context) (code fuse.Status) {
   296  	writable := fs.fileSystems[0]
   297  	sourceFs := fs.fileSystems[srcResult.branch]
   298  
   299  	// Promote directories.
   300  	fs.promoteDirsTo(name)
   301  
   302  	if srcResult.attr.IsRegular() {
   303  		code = pathfs.CopyFile(sourceFs, writable, name, name, context)
   304  
   305  		if code.Ok() {
   306  			code = writable.Chmod(name, srcResult.attr.Mode&07777|0200, context)
   307  		}
   308  		if code.Ok() {
   309  			aTime := srcResult.attr.AccessTime()
   310  			mTime := srcResult.attr.ModTime()
   311  			code = writable.Utimens(name, &aTime, &mTime, context)
   312  		}
   313  
   314  		files := fs.nodeFs.AllFiles(name, 0)
   315  		for _, fileWrapper := range files {
   316  			if !code.Ok() {
   317  				break
   318  			}
   319  			var uf *unionFsFile
   320  			f := fileWrapper.File
   321  			for f != nil {
   322  				ok := false
   323  				uf, ok = f.(*unionFsFile)
   324  				if ok {
   325  					break
   326  				}
   327  				f = f.InnerFile()
   328  			}
   329  			if uf == nil {
   330  				panic("no unionFsFile found inside")
   331  			}
   332  
   333  			if uf.layer > 0 {
   334  				uf.layer = 0
   335  				f := uf.File
   336  				uf.File, code = fs.fileSystems[0].Open(name, fileWrapper.OpenFlags, context)
   337  				f.Flush()
   338  				f.Release()
   339  			}
   340  		}
   341  	} else if srcResult.attr.IsSymlink() {
   342  		link := ""
   343  		link, code = sourceFs.Readlink(name, context)
   344  		if !code.Ok() {
   345  			log.Println("can't read link in source fs", name)
   346  		} else {
   347  			code = writable.Symlink(link, name, context)
   348  		}
   349  	} else if srcResult.attr.IsDir() {
   350  		code = writable.Mkdir(name, srcResult.attr.Mode&07777|0200, context)
   351  	} else {
   352  		log.Println("Unknown file type:", srcResult.attr)
   353  		return fuse.ENOSYS
   354  	}
   355  
   356  	if !code.Ok() {
   357  		fs.branchCache.GetFresh(name)
   358  		return code
   359  	} else {
   360  		r := fs.getBranch(name)
   361  		r.branch = 0
   362  		fs.setBranch(name, r)
   363  	}
   364  
   365  	return fuse.OK
   366  }
   367  
   368  ////////////////////////////////////////////////////////////////
   369  // Below: implement interface for a FileSystem.
   370  
   371  func (fs *unionFS) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) {
   372  	origResult := fs.getBranch(orig)
   373  	code = origResult.code
   374  	if code.Ok() && origResult.branch > 0 {
   375  		code = fs.Promote(orig, origResult, context)
   376  	}
   377  	if code.Ok() && origResult.branch > 0 {
   378  		// Hairy: for the link to be hooked up to the existing
   379  		// inode, PathNodeFs must see a client inode for the
   380  		// original.  We force a refresh of the attribute (so
   381  		// the Ino is filled in.), and then force PathNodeFs
   382  		// to see the Inode number.
   383  		fs.branchCache.GetFresh(orig)
   384  		inode := fs.nodeFs.Node(orig)
   385  		var a fuse.Attr
   386  		inode.Node().GetAttr(&a, nil, nil)
   387  	}
   388  	if code.Ok() {
   389  		code = fs.promoteDirsTo(newName)
   390  	}
   391  	if code.Ok() {
   392  		code = fs.fileSystems[0].Link(orig, newName, context)
   393  	}
   394  	if code.Ok() {
   395  		fs.removeDeletion(newName)
   396  		fs.branchCache.GetFresh(newName)
   397  	}
   398  	return code
   399  }
   400  
   401  func (fs *unionFS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
   402  	r := fs.getBranch(path)
   403  	if r.code != fuse.OK {
   404  		return r.code
   405  	}
   406  	if !r.attr.IsDir() {
   407  		return fuse.Status(syscall.ENOTDIR)
   408  	}
   409  
   410  	stream, code := fs.OpenDir(path, context)
   411  	found := false
   412  	for _ = range stream {
   413  		found = true
   414  	}
   415  	if found {
   416  		return fuse.Status(syscall.ENOTEMPTY)
   417  	}
   418  
   419  	if r.branch > 0 {
   420  		code = fs.putDeletion(path)
   421  		return code
   422  	}
   423  	code = fs.fileSystems[0].Rmdir(path, context)
   424  	if code != fuse.OK {
   425  		return code
   426  	}
   427  
   428  	r = fs.branchCache.GetFresh(path).(branchResult)
   429  	if r.branch > 0 {
   430  		code = fs.putDeletion(path)
   431  	}
   432  	return code
   433  }
   434  
   435  func (fs *unionFS) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
   436  	deleted, code := fs.isDeleted(path)
   437  	if !code.Ok() {
   438  		return code
   439  	}
   440  
   441  	if !deleted {
   442  		r := fs.getBranch(path)
   443  		if r.code != fuse.ENOENT {
   444  			return fuse.Status(syscall.EEXIST)
   445  		}
   446  	}
   447  
   448  	code = fs.promoteDirsTo(path)
   449  	if code.Ok() {
   450  		code = fs.fileSystems[0].Mkdir(path, mode, context)
   451  	}
   452  	if code.Ok() {
   453  		fs.removeDeletion(path)
   454  		attr := &fuse.Attr{
   455  			Mode: fuse.S_IFDIR | mode,
   456  		}
   457  		fs.setBranch(path, branchResult{attr, fuse.OK, 0})
   458  	}
   459  
   460  	var stream []fuse.DirEntry
   461  	stream, code = fs.OpenDir(path, context)
   462  	if code.Ok() {
   463  		// This shouldn't happen, but let's be safe.
   464  		for _, entry := range stream {
   465  			fs.putDeletion(filepath.Join(path, entry.Name))
   466  		}
   467  	}
   468  
   469  	return code
   470  }
   471  
   472  func (fs *unionFS) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
   473  	code = fs.promoteDirsTo(linkName)
   474  	if code.Ok() {
   475  		code = fs.fileSystems[0].Symlink(pointedTo, linkName, context)
   476  	}
   477  	if code.Ok() {
   478  		fs.removeDeletion(linkName)
   479  		fs.branchCache.GetFresh(linkName)
   480  	}
   481  	return code
   482  }
   483  
   484  func (fs *unionFS) Truncate(path string, size uint64, context *fuse.Context) (code fuse.Status) {
   485  	if path == _DROP_CACHE {
   486  		return fuse.OK
   487  	}
   488  
   489  	r := fs.getBranch(path)
   490  	if r.branch > 0 {
   491  		code = fs.Promote(path, r, context)
   492  		r.branch = 0
   493  	}
   494  
   495  	if code.Ok() {
   496  		code = fs.fileSystems[0].Truncate(path, size, context)
   497  	}
   498  	if code.Ok() {
   499  		newAttr := *r.attr
   500  
   501  		r.attr = &newAttr
   502  		r.attr.Size = size
   503  		now := time.Now()
   504  		r.attr.SetTimes(nil, &now, &now)
   505  		fs.setBranch(path, r)
   506  	}
   507  	return code
   508  }
   509  
   510  func (fs *unionFS) Utimens(name string, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
   511  	name = stripSlash(name)
   512  	r := fs.getBranch(name)
   513  
   514  	code = r.code
   515  	if code.Ok() && r.branch > 0 {
   516  		code = fs.Promote(name, r, context)
   517  		r.branch = 0
   518  	}
   519  	if code.Ok() {
   520  		code = fs.fileSystems[0].Utimens(name, atime, mtime, context)
   521  	}
   522  	if code.Ok() {
   523  		now := time.Now()
   524  		newAttr := *r.attr
   525  		r.attr = &newAttr
   526  		r.attr.SetTimes(atime, mtime, &now)
   527  		fs.setBranch(name, r)
   528  	}
   529  	return code
   530  }
   531  
   532  func (fs *unionFS) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
   533  	name = stripSlash(name)
   534  	r := fs.getBranch(name)
   535  	if r.attr == nil || r.code != fuse.OK {
   536  		return r.code
   537  	}
   538  
   539  	newAttr := *r.attr
   540  	r.attr = &newAttr
   541  
   542  	if os.Geteuid() != 0 {
   543  		return fuse.EPERM
   544  	}
   545  
   546  	if r.attr.Uid != uid || r.attr.Gid != gid {
   547  		if r.branch > 0 {
   548  			code := fs.Promote(name, r, context)
   549  			if code != fuse.OK {
   550  				return code
   551  			}
   552  			r.branch = 0
   553  		}
   554  		fs.fileSystems[0].Chown(name, uid, gid, context)
   555  	}
   556  	r.attr.Uid = uid
   557  	r.attr.Gid = gid
   558  	now := time.Now()
   559  	r.attr.SetTimes(nil, nil, &now)
   560  	fs.setBranch(name, r)
   561  	return fuse.OK
   562  }
   563  
   564  func (fs *unionFS) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
   565  	name = stripSlash(name)
   566  	r := fs.getBranch(name)
   567  	if r.attr == nil {
   568  		return r.code
   569  	}
   570  	newAttr := *r.attr
   571  	r.attr = &newAttr
   572  	if r.code != fuse.OK {
   573  		return r.code
   574  	}
   575  
   576  	permMask := uint32(07777)
   577  
   578  	// Always be writable.
   579  	oldMode := r.attr.Mode & permMask
   580  
   581  	if oldMode != mode {
   582  		if r.branch > 0 {
   583  			code := fs.Promote(name, r, context)
   584  			if code != fuse.OK {
   585  				return code
   586  			}
   587  			r.branch = 0
   588  		}
   589  		fs.fileSystems[0].Chmod(name, mode, context)
   590  	}
   591  	r.attr.Mode = (r.attr.Mode &^ permMask) | mode
   592  	now := time.Now()
   593  	r.attr.SetTimes(nil, nil, &now)
   594  	fs.setBranch(name, r)
   595  	return fuse.OK
   596  }
   597  
   598  func (fs *unionFS) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
   599  	// We always allow writing.
   600  	mode = mode &^ fuse.W_OK
   601  	if name == "" || name == _DROP_CACHE {
   602  		return fuse.OK
   603  	}
   604  	r := fs.getBranch(name)
   605  	if r.branch >= 0 {
   606  		return fs.fileSystems[r.branch].Access(name, mode, context)
   607  	}
   608  	return fuse.ENOENT
   609  }
   610  
   611  func (fs *unionFS) Unlink(name string, context *fuse.Context) (code fuse.Status) {
   612  	r := fs.getBranch(name)
   613  	if r.branch == 0 {
   614  		code = fs.fileSystems[0].Unlink(name, context)
   615  		if code != fuse.OK {
   616  			return code
   617  		}
   618  		r = fs.branchCache.GetFresh(name).(branchResult)
   619  	}
   620  
   621  	if r.branch > 0 {
   622  		// It would be nice to do the putDeletion async.
   623  		code = fs.putDeletion(name)
   624  	}
   625  	return code
   626  }
   627  
   628  func (fs *unionFS) Readlink(name string, context *fuse.Context) (out string, code fuse.Status) {
   629  	r := fs.getBranch(name)
   630  	if r.branch >= 0 {
   631  		return fs.fileSystems[r.branch].Readlink(name, context)
   632  	}
   633  	return "", fuse.ENOENT
   634  }
   635  
   636  func stripSlash(fn string) string {
   637  	return strings.TrimRight(fn, string(filepath.Separator))
   638  }
   639  
   640  func (fs *unionFS) promoteDirsTo(filename string) fuse.Status {
   641  	dirName, _ := filepath.Split(filename)
   642  	dirName = stripSlash(dirName)
   643  
   644  	var todo []string
   645  	var results []branchResult
   646  	for dirName != "" {
   647  		r := fs.getBranch(dirName)
   648  
   649  		if !r.code.Ok() {
   650  			log.Println("path component does not exist", filename, dirName)
   651  		}
   652  		if !r.attr.IsDir() {
   653  			log.Println("path component is not a directory.", dirName, r)
   654  			return fuse.EPERM
   655  		}
   656  		if r.branch == 0 {
   657  			break
   658  		}
   659  		todo = append(todo, dirName)
   660  		results = append(results, r)
   661  		dirName, _ = filepath.Split(dirName)
   662  		dirName = stripSlash(dirName)
   663  	}
   664  
   665  	for i := range todo {
   666  		j := len(todo) - i - 1
   667  		d := todo[j]
   668  		r := results[j]
   669  		code := fs.fileSystems[0].Mkdir(d, r.attr.Mode&07777|0200, nil)
   670  		if code != fuse.OK {
   671  			log.Println("Error creating dir leading to path", d, code, fs.fileSystems[0])
   672  			return fuse.EPERM
   673  		}
   674  
   675  		aTime := r.attr.AccessTime()
   676  		mTime := r.attr.ModTime()
   677  		fs.fileSystems[0].Utimens(d, &aTime, &mTime, nil)
   678  		r.branch = 0
   679  		fs.setBranch(d, r)
   680  	}
   681  	return fuse.OK
   682  }
   683  
   684  func (fs *unionFS) Create(name string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
   685  	writable := fs.fileSystems[0]
   686  
   687  	code = fs.promoteDirsTo(name)
   688  	if code != fuse.OK {
   689  		return nil, code
   690  	}
   691  	fuseFile, code = writable.Create(name, flags, mode, context)
   692  	if code.Ok() {
   693  		fuseFile = fs.newUnionFsFile(fuseFile, 0)
   694  		fs.removeDeletion(name)
   695  
   696  		now := time.Now()
   697  		a := fuse.Attr{
   698  			Mode: fuse.S_IFREG | mode,
   699  		}
   700  		a.SetTimes(nil, &now, &now)
   701  		fs.setBranch(name, branchResult{&a, fuse.OK, 0})
   702  	}
   703  	return fuseFile, code
   704  }
   705  
   706  func (fs *unionFS) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s fuse.Status) {
   707  	_, hidden := fs.hiddenFiles[name]
   708  	if hidden {
   709  		return nil, fuse.ENOENT
   710  	}
   711  	if name == _DROP_CACHE {
   712  		return &fuse.Attr{
   713  			Mode: fuse.S_IFREG | 0777,
   714  		}, fuse.OK
   715  	}
   716  	if name == fs.options.DeletionDirName {
   717  		return nil, fuse.ENOENT
   718  	}
   719  	isDel, s := fs.isDeleted(name)
   720  	if !s.Ok() {
   721  		return nil, s
   722  	}
   723  
   724  	if isDel {
   725  		return nil, fuse.ENOENT
   726  	}
   727  	r := fs.getBranch(name)
   728  	if r.branch < 0 {
   729  		return nil, fuse.ENOENT
   730  	}
   731  	fi := *r.attr
   732  	// Make everything appear writable.
   733  	fi.Mode |= 0200
   734  	return &fi, r.code
   735  }
   736  
   737  func (fs *unionFS) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
   738  	if name == _DROP_CACHE {
   739  		return nil, fuse.ENOATTR
   740  	}
   741  	r := fs.getBranch(name)
   742  	if r.branch >= 0 {
   743  		return fs.fileSystems[r.branch].GetXAttr(name, attr, context)
   744  	}
   745  	return nil, fuse.ENOENT
   746  }
   747  
   748  func (fs *unionFS) OpenDir(directory string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
   749  	dirBranch := fs.getBranch(directory)
   750  	if dirBranch.branch < 0 {
   751  		return nil, fuse.ENOENT
   752  	}
   753  
   754  	// We could try to use the cache, but we have a delay, so
   755  	// might as well get the fresh results async.
   756  	var wg sync.WaitGroup
   757  	var deletions map[string]struct{}
   758  
   759  	wg.Add(1)
   760  	go func() {
   761  		deletions = newDirnameMap(fs.fileSystems[0], fs.options.DeletionDirName)
   762  		wg.Done()
   763  	}()
   764  
   765  	entries := make([]map[string]uint32, len(fs.fileSystems))
   766  	for i := range fs.fileSystems {
   767  		entries[i] = make(map[string]uint32)
   768  	}
   769  
   770  	statuses := make([]fuse.Status, len(fs.fileSystems))
   771  	for i, l := range fs.fileSystems {
   772  		if i >= dirBranch.branch {
   773  			wg.Add(1)
   774  			go func(j int, pfs pathfs.FileSystem) {
   775  				ch, s := pfs.OpenDir(directory, context)
   776  				statuses[j] = s
   777  				for _, v := range ch {
   778  					entries[j][v.Name] = v.Mode
   779  				}
   780  				wg.Done()
   781  			}(i, l)
   782  		}
   783  	}
   784  
   785  	wg.Wait()
   786  	if deletions == nil {
   787  		_, code := fs.fileSystems[0].GetAttr(fs.options.DeletionDirName, context)
   788  		if code == fuse.ENOENT {
   789  			deletions = map[string]struct{}{}
   790  		} else {
   791  			return nil, fuse.Status(syscall.EROFS)
   792  		}
   793  	}
   794  
   795  	results := entries[0]
   796  
   797  	// TODO(hanwen): should we do anything with the return
   798  	// statuses?
   799  	for i, m := range entries {
   800  		if statuses[i] != fuse.OK {
   801  			continue
   802  		}
   803  		if i == 0 {
   804  			// We don't need to further process the first
   805  			// branch: it has no deleted files.
   806  			continue
   807  		}
   808  		for k, v := range m {
   809  			_, ok := results[k]
   810  			if ok {
   811  				continue
   812  			}
   813  
   814  			_, deleted := deletions[filePathHash(filepath.Join(directory, k))]
   815  			if !deleted {
   816  				results[k] = v
   817  			}
   818  		}
   819  	}
   820  	if directory == "" {
   821  		delete(results, fs.options.DeletionDirName)
   822  		for name, _ := range fs.hiddenFiles {
   823  			delete(results, name)
   824  		}
   825  	}
   826  
   827  	stream = make([]fuse.DirEntry, 0, len(results))
   828  	for k, v := range results {
   829  		stream = append(stream, fuse.DirEntry{
   830  			Name: k,
   831  			Mode: v,
   832  		})
   833  	}
   834  	return stream, fuse.OK
   835  }
   836  
   837  // recursivePromote promotes path, and if a directory, everything
   838  // below that directory.  It returns a list of all promoted paths, in
   839  // full, including the path itself.
   840  func (fs *unionFS) recursivePromote(path string, pathResult branchResult, context *fuse.Context) (names []string, code fuse.Status) {
   841  	names = []string{}
   842  	if pathResult.branch > 0 {
   843  		code = fs.Promote(path, pathResult, context)
   844  	}
   845  
   846  	if code.Ok() {
   847  		names = append(names, path)
   848  	}
   849  
   850  	if code.Ok() && pathResult.attr != nil && pathResult.attr.IsDir() {
   851  		var stream []fuse.DirEntry
   852  		stream, code = fs.OpenDir(path, context)
   853  		for _, e := range stream {
   854  			if !code.Ok() {
   855  				break
   856  			}
   857  			subnames := []string{}
   858  			p := filepath.Join(path, e.Name)
   859  			r := fs.getBranch(p)
   860  			subnames, code = fs.recursivePromote(p, r, context)
   861  			names = append(names, subnames...)
   862  		}
   863  	}
   864  
   865  	if !code.Ok() {
   866  		names = nil
   867  	}
   868  	return names, code
   869  }
   870  
   871  func (fs *unionFS) renameDirectory(srcResult branchResult, srcDir string, dstDir string, context *fuse.Context) (code fuse.Status) {
   872  	names := []string{}
   873  	if code.Ok() {
   874  		names, code = fs.recursivePromote(srcDir, srcResult, context)
   875  	}
   876  	if code.Ok() {
   877  		code = fs.promoteDirsTo(dstDir)
   878  	}
   879  
   880  	if code.Ok() {
   881  		writable := fs.fileSystems[0]
   882  		code = writable.Rename(srcDir, dstDir, context)
   883  	}
   884  
   885  	if code.Ok() {
   886  		for _, srcName := range names {
   887  			relative := strings.TrimLeft(srcName[len(srcDir):], string(filepath.Separator))
   888  			dst := filepath.Join(dstDir, relative)
   889  			fs.removeDeletion(dst)
   890  
   891  			srcResult := fs.getBranch(srcName)
   892  			srcResult.branch = 0
   893  			fs.setBranch(dst, srcResult)
   894  
   895  			srcResult = fs.branchCache.GetFresh(srcName).(branchResult)
   896  			if srcResult.branch > 0 {
   897  				code = fs.putDeletion(srcName)
   898  			}
   899  		}
   900  	}
   901  	return code
   902  }
   903  
   904  func (fs *unionFS) Rename(src string, dst string, context *fuse.Context) fuse.Status {
   905  	srcResult := fs.getBranch(src)
   906  	if !srcResult.code.Ok() {
   907  		return srcResult.code
   908  	}
   909  
   910  	if srcResult.attr.IsDir() {
   911  		return fs.renameDirectory(srcResult, src, dst, context)
   912  	}
   913  
   914  	if srcResult.branch > 0 {
   915  		if code := fs.Promote(src, srcResult, context); !code.Ok() {
   916  			return code
   917  		}
   918  	}
   919  	if code := fs.promoteDirsTo(dst); !code.Ok() {
   920  		return code
   921  	}
   922  
   923  	if code := fs.fileSystems[0].Rename(src, dst, context); !code.Ok() {
   924  		return code
   925  	}
   926  
   927  	fs.removeDeletion(dst)
   928  	// Rename is racy; avoid racing with unionFsFile.Release().
   929  	fs.branchCache.DropEntry(dst)
   930  
   931  	srcResult = fs.branchCache.GetFresh(src).(branchResult)
   932  	if srcResult.branch > 0 {
   933  		return fs.putDeletion(src)
   934  	}
   935  	return fuse.OK
   936  }
   937  
   938  func (fs *unionFS) DropBranchCache(names []string) {
   939  	fs.branchCache.DropAll(names)
   940  }
   941  
   942  func (fs *unionFS) DropDeletionCache() {
   943  	fs.deletionCache.DropCache()
   944  }
   945  
   946  func (fs *unionFS) DropSubFsCaches() {
   947  	for _, fs := range fs.fileSystems {
   948  		a, code := fs.GetAttr(_DROP_CACHE, nil)
   949  		if code.Ok() && a.IsRegular() {
   950  			f, _ := fs.Open(_DROP_CACHE, uint32(os.O_WRONLY), nil)
   951  			if f != nil {
   952  				f.Flush()
   953  				f.Release()
   954  			}
   955  		}
   956  	}
   957  }
   958  
   959  func (fs *unionFS) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
   960  	if name == _DROP_CACHE {
   961  		if flags&fuse.O_ANYWRITE != 0 {
   962  			log.Println("Forced cache drop on", fs)
   963  			fs.DropBranchCache(nil)
   964  			fs.DropDeletionCache()
   965  			fs.DropSubFsCaches()
   966  			fs.nodeFs.ForgetClientInodes()
   967  		}
   968  		return nodefs.NewDevNullFile(), fuse.OK
   969  	}
   970  	r := fs.getBranch(name)
   971  	if r.branch < 0 {
   972  		// This should not happen, as a GetAttr() should have
   973  		// already verified existence.
   974  		log.Println("UnionFs: open of non-existent file:", name)
   975  		return nil, fuse.ENOENT
   976  	}
   977  	if flags&fuse.O_ANYWRITE != 0 && r.branch > 0 {
   978  		code := fs.Promote(name, r, context)
   979  		if code != fuse.OK {
   980  			return nil, code
   981  		}
   982  		r.branch = 0
   983  		now := time.Now()
   984  		r.attr.SetTimes(nil, &now, nil)
   985  		fs.setBranch(name, r)
   986  	}
   987  	fuseFile, status = fs.fileSystems[r.branch].Open(name, uint32(flags), context)
   988  	if fuseFile != nil {
   989  		fuseFile = fs.newUnionFsFile(fuseFile, r.branch)
   990  	}
   991  	return fuseFile, status
   992  }
   993  
   994  func (fs *unionFS) String() string {
   995  	names := []string{}
   996  	for _, fs := range fs.fileSystems {
   997  		names = append(names, fs.String())
   998  	}
   999  	return fmt.Sprintf("UnionFs(%v)", names)
  1000  }
  1001  
  1002  func (fs *unionFS) StatFs(name string) *fuse.StatfsOut {
  1003  	return fs.fileSystems[0].StatFs("")
  1004  }
  1005  
  1006  type unionFsFile struct {
  1007  	nodefs.File
  1008  	ufs   *unionFS
  1009  	node  *nodefs.Inode
  1010  	layer int
  1011  }
  1012  
  1013  func (fs *unionFsFile) String() string {
  1014  	return fmt.Sprintf("unionFsFile(%s)", fs.File.String())
  1015  }
  1016  
  1017  func (fs *unionFS) newUnionFsFile(f nodefs.File, branch int) *unionFsFile {
  1018  	return &unionFsFile{
  1019  		File:  f,
  1020  		ufs:   fs,
  1021  		layer: branch,
  1022  	}
  1023  }
  1024  
  1025  func (fs *unionFsFile) InnerFile() (file nodefs.File) {
  1026  	return fs.File
  1027  }
  1028  
  1029  // We can't hook on Release. Release has no response, so it is not
  1030  // ordered wrt any following calls.
  1031  func (fs *unionFsFile) Flush() (code fuse.Status) {
  1032  	code = fs.File.Flush()
  1033  	path := fs.ufs.nodeFs.Path(fs.node)
  1034  	fs.ufs.branchCache.GetFresh(path)
  1035  	return code
  1036  }
  1037  
  1038  func (fs *unionFsFile) SetInode(node *nodefs.Inode) {
  1039  	fs.node = node
  1040  }
  1041  
  1042  func (fs *unionFsFile) GetAttr(out *fuse.Attr) fuse.Status {
  1043  	code := fs.File.GetAttr(out)
  1044  	if code.Ok() {
  1045  		out.Mode |= 0200
  1046  	}
  1047  	return code
  1048  }