github.com/hanwen/go-fuse@v1.0.0/fuse/pathfs/pathfs.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 pathfs
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/hanwen/go-fuse/fuse"
    16  	"github.com/hanwen/go-fuse/fuse/nodefs"
    17  )
    18  
    19  // refCountedInode is used in clientInodeMap. The reference count is used to decide
    20  // if the entry in clientInodeMap can be dropped.
    21  type refCountedInode struct {
    22  	node     *pathInode
    23  	refCount int
    24  }
    25  
    26  // PathNodeFs is the file system that can translate an inode back to a
    27  // path.  The path name is then used to call into an object that has
    28  // the FileSystem interface.
    29  //
    30  // Lookups (ie. FileSystem.GetAttr) may return a inode number in its
    31  // return value. The inode number ("clientInode") is used to indicate
    32  // linked files.
    33  type PathNodeFs struct {
    34  	debug     bool
    35  	fs        FileSystem
    36  	root      *pathInode
    37  	connector *nodefs.FileSystemConnector
    38  
    39  	// protects clientInodeMap
    40  	pathLock sync.RWMutex
    41  
    42  	// This map lists all the parent links known for a given inode number.
    43  	clientInodeMap map[uint64]*refCountedInode
    44  
    45  	options *PathNodeFsOptions
    46  }
    47  
    48  // SetDebug toggles debug information: it will log path names for
    49  // each operation processed.
    50  func (fs *PathNodeFs) SetDebug(dbg bool) {
    51  	fs.debug = dbg
    52  }
    53  
    54  // Mount mounts a another node filesystem with the given root on the
    55  // path. The last component of the path should not exist yet.
    56  func (fs *PathNodeFs) Mount(path string, root nodefs.Node, opts *nodefs.Options) fuse.Status {
    57  	dir, name := filepath.Split(path)
    58  	if dir != "" {
    59  		dir = filepath.Clean(dir)
    60  	}
    61  	parent := fs.LookupNode(dir)
    62  	if parent == nil {
    63  		return fuse.ENOENT
    64  	}
    65  	return fs.connector.Mount(parent, name, root, opts)
    66  }
    67  
    68  // ForgetClientInodes forgets all known information on client inodes.
    69  func (fs *PathNodeFs) ForgetClientInodes() {
    70  	if !fs.options.ClientInodes {
    71  		return
    72  	}
    73  	fs.pathLock.Lock()
    74  	fs.clientInodeMap = map[uint64]*refCountedInode{}
    75  	fs.root.forgetClientInodes()
    76  	fs.pathLock.Unlock()
    77  }
    78  
    79  // Rereads all inode numbers for all known files.
    80  func (fs *PathNodeFs) RereadClientInodes() {
    81  	if !fs.options.ClientInodes {
    82  		return
    83  	}
    84  	fs.ForgetClientInodes()
    85  	fs.root.updateClientInodes()
    86  }
    87  
    88  // UnmountNode unmounts the node filesystem with the given root.
    89  func (fs *PathNodeFs) UnmountNode(node *nodefs.Inode) fuse.Status {
    90  	return fs.connector.Unmount(node)
    91  }
    92  
    93  // UnmountNode unmounts the node filesystem with the given root.
    94  func (fs *PathNodeFs) Unmount(path string) fuse.Status {
    95  	node := fs.Node(path)
    96  	if node == nil {
    97  		return fuse.ENOENT
    98  	}
    99  	return fs.connector.Unmount(node)
   100  }
   101  
   102  // String returns a name for this file system
   103  func (fs *PathNodeFs) String() string {
   104  	name := fs.fs.String()
   105  	if name == "defaultFileSystem" {
   106  		name = fmt.Sprintf("%T", fs.fs)
   107  		name = strings.TrimLeft(name, "*")
   108  	}
   109  	return name
   110  }
   111  
   112  // Connector returns the FileSystemConnector (the bridge to the raw
   113  // protocol) for this PathNodeFs.
   114  func (fs *PathNodeFs) Connector() *nodefs.FileSystemConnector {
   115  	return fs.connector
   116  }
   117  
   118  // Node looks up the Inode that corresponds to the given path name, or
   119  // returns nil if not found.
   120  func (fs *PathNodeFs) Node(name string) *nodefs.Inode {
   121  	n, rest := fs.LastNode(name)
   122  	if len(rest) > 0 {
   123  		return nil
   124  	}
   125  	return n
   126  }
   127  
   128  // Like Node, but use Lookup to discover inodes we may not have yet.
   129  func (fs *PathNodeFs) LookupNode(name string) *nodefs.Inode {
   130  	return fs.connector.LookupNode(fs.Root().Inode(), name)
   131  }
   132  
   133  // Path constructs a path for the given Inode. If the file system
   134  // implements hard links through client-inode numbers, the path may
   135  // not be unique.
   136  func (fs *PathNodeFs) Path(node *nodefs.Inode) string {
   137  	pNode := node.Node().(*pathInode)
   138  	return pNode.GetPath()
   139  }
   140  
   141  // LastNode finds the deepest inode known corresponding to a path. The
   142  // unknown part of the filename is also returned.
   143  func (fs *PathNodeFs) LastNode(name string) (*nodefs.Inode, []string) {
   144  	return fs.connector.Node(fs.Root().Inode(), name)
   145  }
   146  
   147  // FileNotify notifies that file contents were changed within the
   148  // given range.  Use negative offset for metadata-only invalidation,
   149  // and zero-length for invalidating all content.
   150  func (fs *PathNodeFs) FileNotify(path string, off int64, length int64) fuse.Status {
   151  	node, r := fs.connector.Node(fs.root.Inode(), path)
   152  	if len(r) > 0 {
   153  		return fuse.ENOENT
   154  	}
   155  	return fs.connector.FileNotify(node, off, length)
   156  }
   157  
   158  // EntryNotify makes the kernel forget the entry data from the given
   159  // name from a directory.  After this call, the kernel will issue a
   160  // new lookup request for the given name when necessary.
   161  func (fs *PathNodeFs) EntryNotify(dir string, name string) fuse.Status {
   162  	node, rest := fs.connector.Node(fs.root.Inode(), dir)
   163  	if len(rest) > 0 {
   164  		return fuse.ENOENT
   165  	}
   166  	return fs.connector.EntryNotify(node, name)
   167  }
   168  
   169  // Notify ensures that the path name is invalidates: if the inode is
   170  // known, it issues an file content Notify, if not, an entry notify
   171  // for the path is issued. The latter will clear out non-existence
   172  // cache entries.
   173  func (fs *PathNodeFs) Notify(path string) fuse.Status {
   174  	node, rest := fs.connector.Node(fs.root.Inode(), path)
   175  	if len(rest) > 0 {
   176  		return fs.connector.EntryNotify(node, rest[0])
   177  	}
   178  	return fs.connector.FileNotify(node, 0, 0)
   179  }
   180  
   181  // AllFiles returns all open files for the inode corresponding with
   182  // the given mask.
   183  func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags {
   184  	n := fs.Node(name)
   185  	if n == nil {
   186  		return nil
   187  	}
   188  	return n.Files(mask)
   189  }
   190  
   191  // NewPathNodeFs returns a file system that translates from inodes to
   192  // path names.
   193  func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs {
   194  	root := &pathInode{}
   195  	root.fs = fs
   196  
   197  	if opts == nil {
   198  		opts = &PathNodeFsOptions{}
   199  	}
   200  
   201  	pfs := &PathNodeFs{
   202  		fs:             fs,
   203  		root:           root,
   204  		clientInodeMap: map[uint64]*refCountedInode{},
   205  		options:        opts,
   206  	}
   207  	root.pathFs = pfs
   208  	return pfs
   209  }
   210  
   211  // Root returns the root node for the path filesystem.
   212  func (fs *PathNodeFs) Root() nodefs.Node {
   213  	return fs.root
   214  }
   215  
   216  // This is a combination of dentry (entry in the file/directory and
   217  // the inode). This structure is used to implement glue for FSes where
   218  // there is a one-to-one mapping of paths and inodes.
   219  type pathInode struct {
   220  	pathFs *PathNodeFs
   221  	fs     FileSystem
   222  
   223  	// This is to correctly resolve hardlinks of the underlying
   224  	// real filesystem.
   225  	clientInode uint64
   226  	inode       *nodefs.Inode
   227  }
   228  
   229  func (n *pathInode) OnMount(conn *nodefs.FileSystemConnector) {
   230  	n.pathFs.connector = conn
   231  	n.pathFs.fs.OnMount(n.pathFs)
   232  }
   233  
   234  func (n *pathInode) OnUnmount() {
   235  }
   236  
   237  // Drop all known client inodes. Must have the treeLock.
   238  func (n *pathInode) forgetClientInodes() {
   239  	n.clientInode = 0
   240  	for _, ch := range n.Inode().FsChildren() {
   241  		ch.Node().(*pathInode).forgetClientInodes()
   242  	}
   243  }
   244  
   245  func (fs *pathInode) Deletable() bool {
   246  	return true
   247  }
   248  
   249  func (n *pathInode) Inode() *nodefs.Inode {
   250  	return n.inode
   251  }
   252  
   253  func (n *pathInode) SetInode(node *nodefs.Inode) {
   254  	n.inode = node
   255  }
   256  
   257  // Reread all client nodes below this node.  Must run outside the treeLock.
   258  func (n *pathInode) updateClientInodes() {
   259  	n.GetAttr(&fuse.Attr{}, nil, nil)
   260  	for _, ch := range n.Inode().FsChildren() {
   261  		ch.Node().(*pathInode).updateClientInodes()
   262  	}
   263  }
   264  
   265  // GetPath returns the path relative to the mount governing this
   266  // inode.  It returns nil for mount if the file was deleted or the
   267  // filesystem unmounted.
   268  func (n *pathInode) GetPath() string {
   269  	if n == n.pathFs.root {
   270  		return ""
   271  	}
   272  
   273  	pathLen := 1
   274  
   275  	// The simple solution is to collect names, and reverse join
   276  	// them, them, but since this is a hot path, we take some
   277  	// effort to avoid allocations.
   278  
   279  	walkUp := n.Inode()
   280  
   281  	// TODO - guess depth?
   282  	segments := make([]string, 0, 10)
   283  	for {
   284  		parent, name := walkUp.Parent()
   285  		if parent == nil {
   286  			break
   287  		}
   288  		segments = append(segments, name)
   289  		pathLen += len(name) + 1
   290  		walkUp = parent
   291  	}
   292  	pathLen--
   293  
   294  	pathBytes := make([]byte, 0, pathLen)
   295  	for i := len(segments) - 1; i >= 0; i-- {
   296  		pathBytes = append(pathBytes, segments[i]...)
   297  		if i > 0 {
   298  			pathBytes = append(pathBytes, '/')
   299  		}
   300  	}
   301  
   302  	path := string(pathBytes)
   303  	if n.pathFs.debug {
   304  		log.Printf("Inode = %q (%s)", path, n.fs.String())
   305  	}
   306  
   307  	if walkUp != n.pathFs.root.Inode() {
   308  		// This might happen if the node has been removed from
   309  		// the tree using unlink, but we are forced to run
   310  		// some file system operation, because the file is
   311  		// still opened.
   312  
   313  		return ".deleted." + n.inode.String()
   314  	}
   315  
   316  	return path
   317  }
   318  
   319  func (n *pathInode) OnAdd(parent *nodefs.Inode, name string) {
   320  	// TODO it would be logical to increment the clientInodeMap reference count
   321  	// here. However, as the inode number is loaded lazily, we cannot do it
   322  	// yet.
   323  }
   324  
   325  func (n *pathInode) rmChild(name string) *pathInode {
   326  	childInode := n.Inode().RmChild(name)
   327  	if childInode == nil {
   328  		return nil
   329  	}
   330  	return childInode.Node().(*pathInode)
   331  }
   332  
   333  func (n *pathInode) OnRemove(parent *nodefs.Inode, name string) {
   334  	if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
   335  		return
   336  	}
   337  
   338  	n.pathFs.pathLock.Lock()
   339  	r := n.pathFs.clientInodeMap[n.clientInode]
   340  	if r != nil {
   341  		r.refCount--
   342  		if r.refCount == 0 {
   343  			delete(n.pathFs.clientInodeMap, n.clientInode)
   344  		}
   345  	}
   346  	n.pathFs.pathLock.Unlock()
   347  }
   348  
   349  // setClientInode sets the inode number if has not been set yet.
   350  // This function exists to allow lazy-loading of the inode number.
   351  func (n *pathInode) setClientInode(ino uint64) {
   352  	n.pathFs.pathLock.Lock()
   353  	defer n.pathFs.pathLock.Unlock()
   354  	if ino == 0 || n.clientInode != 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
   355  		return
   356  	}
   357  	n.clientInode = ino
   358  	n.pathFs.clientInodeMap[ino] = &refCountedInode{node: n, refCount: 1}
   359  }
   360  
   361  func (n *pathInode) OnForget() {
   362  	if n.clientInode == 0 || !n.pathFs.options.ClientInodes || n.Inode().IsDir() {
   363  		return
   364  	}
   365  	n.pathFs.pathLock.Lock()
   366  	delete(n.pathFs.clientInodeMap, n.clientInode)
   367  	n.pathFs.pathLock.Unlock()
   368  }
   369  
   370  ////////////////////////////////////////////////////////////////
   371  // FS operations
   372  
   373  func (n *pathInode) StatFs() *fuse.StatfsOut {
   374  	return n.fs.StatFs(n.GetPath())
   375  }
   376  
   377  func (n *pathInode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
   378  	path := n.GetPath()
   379  
   380  	val, err := n.fs.Readlink(path, c)
   381  	return []byte(val), err
   382  }
   383  
   384  func (n *pathInode) Access(mode uint32, context *fuse.Context) (code fuse.Status) {
   385  	p := n.GetPath()
   386  	return n.fs.Access(p, mode, context)
   387  }
   388  
   389  func (n *pathInode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
   390  	return n.fs.GetXAttr(n.GetPath(), attribute, context)
   391  }
   392  
   393  func (n *pathInode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status {
   394  	p := n.GetPath()
   395  	return n.fs.RemoveXAttr(p, attr, context)
   396  }
   397  
   398  func (n *pathInode) SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
   399  	return n.fs.SetXAttr(n.GetPath(), attr, data, flags, context)
   400  }
   401  
   402  func (n *pathInode) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) {
   403  	return n.fs.ListXAttr(n.GetPath(), context)
   404  }
   405  
   406  func (n *pathInode) Flush(file nodefs.File, openFlags uint32, context *fuse.Context) (code fuse.Status) {
   407  	return file.Flush()
   408  }
   409  
   410  func (n *pathInode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
   411  	return n.fs.OpenDir(n.GetPath(), context)
   412  }
   413  
   414  func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
   415  	fullPath := filepath.Join(n.GetPath(), name)
   416  	code := n.fs.Mknod(fullPath, mode, dev, context)
   417  	var child *nodefs.Inode
   418  	if code.Ok() {
   419  		pNode := n.createChild(name, false)
   420  		child = pNode.Inode()
   421  	}
   422  	return child, code
   423  }
   424  
   425  func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
   426  	fullPath := filepath.Join(n.GetPath(), name)
   427  	code := n.fs.Mkdir(fullPath, mode, context)
   428  	var child *nodefs.Inode
   429  	if code.Ok() {
   430  		pNode := n.createChild(name, true)
   431  		child = pNode.Inode()
   432  	}
   433  	return child, code
   434  }
   435  
   436  func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
   437  	code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context)
   438  	if code.Ok() {
   439  		n.Inode().RmChild(name)
   440  	}
   441  	return code
   442  }
   443  
   444  func (n *pathInode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
   445  	code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context)
   446  	if code.Ok() {
   447  		n.Inode().RmChild(name)
   448  	}
   449  	return code
   450  }
   451  
   452  func (n *pathInode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
   453  	fullPath := filepath.Join(n.GetPath(), name)
   454  	code := n.fs.Symlink(content, fullPath, context)
   455  	var child *nodefs.Inode
   456  	if code.Ok() {
   457  		pNode := n.createChild(name, false)
   458  		child = pNode.Inode()
   459  	}
   460  	return child, code
   461  }
   462  
   463  func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string, context *fuse.Context) (code fuse.Status) {
   464  	p := newParent.(*pathInode)
   465  	oldPath := filepath.Join(n.GetPath(), oldName)
   466  	newPath := filepath.Join(p.GetPath(), newName)
   467  	code = n.fs.Rename(oldPath, newPath, context)
   468  	if code.Ok() {
   469  		// The rename may have overwritten another file, remove it from the tree
   470  		p.Inode().RmChild(newName)
   471  		ch := n.Inode().RmChild(oldName)
   472  		if ch != nil {
   473  			// oldName may have been forgotten in the meantime.
   474  			p.Inode().AddChild(newName, ch)
   475  		}
   476  	}
   477  	return code
   478  }
   479  
   480  func (n *pathInode) Link(name string, existingFsnode nodefs.Node, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
   481  	if !n.pathFs.options.ClientInodes {
   482  		return nil, fuse.ENOSYS
   483  	}
   484  
   485  	newPath := filepath.Join(n.GetPath(), name)
   486  	existing := existingFsnode.(*pathInode)
   487  	oldPath := existing.GetPath()
   488  	code := n.fs.Link(oldPath, newPath, context)
   489  
   490  	var a *fuse.Attr
   491  	if code.Ok() {
   492  		a, code = n.fs.GetAttr(newPath, context)
   493  	}
   494  
   495  	var child *nodefs.Inode
   496  	if code.Ok() {
   497  		if existing.clientInode != 0 && existing.clientInode == a.Ino {
   498  			child = existing.Inode()
   499  			n.Inode().AddChild(name, existing.Inode())
   500  		} else {
   501  			pNode := n.createChild(name, false)
   502  			child = pNode.Inode()
   503  			pNode.clientInode = a.Ino
   504  		}
   505  	}
   506  	return child, code
   507  }
   508  
   509  func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, *nodefs.Inode, fuse.Status) {
   510  	var child *nodefs.Inode
   511  	fullPath := filepath.Join(n.GetPath(), name)
   512  	file, code := n.fs.Create(fullPath, flags, mode, context)
   513  	if code.Ok() {
   514  		pNode := n.createChild(name, false)
   515  		child = pNode.Inode()
   516  	}
   517  	return file, child, code
   518  }
   519  
   520  func (n *pathInode) createChild(name string, isDir bool) *pathInode {
   521  	i := &pathInode{
   522  		fs:     n.fs,
   523  		pathFs: n.pathFs,
   524  	}
   525  
   526  	n.Inode().NewChild(name, isDir, i)
   527  
   528  	return i
   529  }
   530  
   531  func (n *pathInode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
   532  	p := n.GetPath()
   533  	file, code = n.fs.Open(p, flags, context)
   534  	if n.pathFs.debug {
   535  		file = &nodefs.WithFlags{
   536  			File:        file,
   537  			Description: n.GetPath(),
   538  		}
   539  	}
   540  	return
   541  }
   542  
   543  func (n *pathInode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
   544  	fullPath := filepath.Join(n.GetPath(), name)
   545  	fi, code := n.fs.GetAttr(fullPath, context)
   546  	node := n.Inode().GetChild(name)
   547  	if node != nil && (!code.Ok() || node.IsDir() != fi.IsDir()) {
   548  		n.Inode().RmChild(name)
   549  		node = nil
   550  	}
   551  
   552  	if code.Ok() {
   553  		if node == nil {
   554  			node = n.findChild(fi, name, fullPath).Inode()
   555  		}
   556  		*out = *fi
   557  	}
   558  
   559  	return node, code
   560  }
   561  
   562  func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out *pathInode) {
   563  	if fi.Ino > 0 {
   564  		n.pathFs.pathLock.RLock()
   565  		r := n.pathFs.clientInodeMap[fi.Ino]
   566  		if r != nil {
   567  			out = r.node
   568  			r.refCount++
   569  			if fi.Nlink == 1 {
   570  				log.Printf("Found linked inode, but Nlink == 1, ino=%d, fullPath=%q", fi.Ino, fullPath)
   571  			}
   572  		}
   573  		n.pathFs.pathLock.RUnlock()
   574  	}
   575  
   576  	if out == nil {
   577  		out = n.createChild(name, fi.IsDir())
   578  		out.setClientInode(fi.Ino)
   579  	} else {
   580  		n.Inode().AddChild(name, out.Inode())
   581  	}
   582  	return out
   583  }
   584  
   585  func (n *pathInode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
   586  	var fi *fuse.Attr
   587  	if file == nil {
   588  		// Linux currently (tested on v4.4) does not pass a file descriptor for
   589  		// fstat. To be able to stat a deleted file we have to find ourselves
   590  		// an open fd.
   591  		file = n.Inode().AnyFile()
   592  	}
   593  	// If we have found an open file, try to fstat it.
   594  	if file != nil {
   595  		code = file.GetAttr(out)
   596  		if code.Ok() {
   597  			return code
   598  		}
   599  		// ENOSYS and EBADF are retried below. Error out for other codes.
   600  		if code != fuse.ENOSYS && code != fuse.EBADF {
   601  			return code
   602  		}
   603  	}
   604  	// If we don't have an open file, or fstat on it failed due to an internal
   605  	// error, stat by path.
   606  	if file == nil || code == fuse.ENOSYS || code == fuse.EBADF {
   607  		fi, code = n.fs.GetAttr(n.GetPath(), context)
   608  		if !code.Ok() {
   609  			return code
   610  		}
   611  		// This is a bug in the filesystem implementation, but let's not
   612  		// crash.
   613  		if fi == nil {
   614  			log.Printf("Bug: fs.GetAttr returned OK with nil data")
   615  			return fuse.EINVAL
   616  		}
   617  	}
   618  	// Set inode number (unless already set or disabled).
   619  	n.setClientInode(fi.Ino)
   620  	// Help filesystems that forget to set Nlink.
   621  	if !fi.IsDir() && fi.Nlink == 0 {
   622  		fi.Nlink = 1
   623  	}
   624  	*out = *fi
   625  	return code
   626  }
   627  
   628  func (n *pathInode) Chmod(file nodefs.File, perms uint32, context *fuse.Context) (code fuse.Status) {
   629  	// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
   630  	// to FUSE for fchmod. We still check because that may change in the future.
   631  	if file != nil {
   632  		code = file.Chmod(perms)
   633  		if code != fuse.ENOSYS {
   634  			return code
   635  		}
   636  	}
   637  
   638  	files := n.Inode().Files(fuse.O_ANYWRITE)
   639  	for _, f := range files {
   640  		// TODO - pass context
   641  		code = f.Chmod(perms)
   642  		if code.Ok() {
   643  			return
   644  		}
   645  	}
   646  
   647  	if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
   648  		code = n.fs.Chmod(n.GetPath(), perms, context)
   649  	}
   650  	return code
   651  }
   652  
   653  func (n *pathInode) Chown(file nodefs.File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
   654  	// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
   655  	// to FUSE for fchown. We still check because it may change in the future.
   656  	if file != nil {
   657  		code = file.Chown(uid, gid)
   658  		if code != fuse.ENOSYS {
   659  			return code
   660  		}
   661  	}
   662  
   663  	files := n.Inode().Files(fuse.O_ANYWRITE)
   664  	for _, f := range files {
   665  		// TODO - pass context
   666  		code = f.Chown(uid, gid)
   667  		if code.Ok() {
   668  			return code
   669  		}
   670  	}
   671  	if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
   672  		// TODO - can we get just FATTR_GID but not FATTR_UID ?
   673  		code = n.fs.Chown(n.GetPath(), uid, gid, context)
   674  	}
   675  	return code
   676  }
   677  
   678  func (n *pathInode) Truncate(file nodefs.File, size uint64, context *fuse.Context) (code fuse.Status) {
   679  	// A file descriptor was passed in AND the filesystem implements the
   680  	// operation on the file handle. This the common case for ftruncate.
   681  	if file != nil {
   682  		code = file.Truncate(size)
   683  		if code != fuse.ENOSYS {
   684  			return code
   685  		}
   686  	}
   687  
   688  	files := n.Inode().Files(fuse.O_ANYWRITE)
   689  	for _, f := range files {
   690  		// TODO - pass context
   691  		code = f.Truncate(size)
   692  		if code.Ok() {
   693  			return code
   694  		}
   695  	}
   696  	if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
   697  		code = n.fs.Truncate(n.GetPath(), size, context)
   698  	}
   699  	return code
   700  }
   701  
   702  func (n *pathInode) Utimens(file nodefs.File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status) {
   703  	// Note that Linux currently (Linux 4.4) DOES NOT pass a file descriptor
   704  	// to FUSE for futimens. We still check because it may change in the future.
   705  	if file != nil {
   706  		code = file.Utimens(atime, mtime)
   707  		if code != fuse.ENOSYS {
   708  			return code
   709  		}
   710  	}
   711  
   712  	files := n.Inode().Files(fuse.O_ANYWRITE)
   713  	for _, f := range files {
   714  		// TODO - pass context
   715  		code = f.Utimens(atime, mtime)
   716  		if code.Ok() {
   717  			return code
   718  		}
   719  	}
   720  	if len(files) == 0 || code == fuse.ENOSYS || code == fuse.EBADF {
   721  		code = n.fs.Utimens(n.GetPath(), atime, mtime, context)
   722  	}
   723  	return code
   724  }
   725  
   726  func (n *pathInode) Fallocate(file nodefs.File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status) {
   727  	if file != nil {
   728  		code = file.Allocate(off, size, mode)
   729  		if code.Ok() {
   730  			return code
   731  		}
   732  	}
   733  
   734  	files := n.Inode().Files(fuse.O_ANYWRITE)
   735  	for _, f := range files {
   736  		// TODO - pass context
   737  		code = f.Allocate(off, size, mode)
   738  		if code.Ok() {
   739  			return code
   740  		}
   741  	}
   742  
   743  	return code
   744  }
   745  
   746  func (n *pathInode) Read(file nodefs.File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status) {
   747  	if file != nil {
   748  		return file.Read(dest, off)
   749  	}
   750  	return nil, fuse.ENOSYS
   751  }
   752  
   753  func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status) {
   754  	if file != nil {
   755  		return file.Write(data, off)
   756  	}
   757  	return 0, fuse.ENOSYS
   758  }
   759  
   760  func (n *pathInode) GetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) {
   761  	if file != nil {
   762  		return file.GetLk(owner, lk, flags, out)
   763  	}
   764  	return fuse.ENOSYS
   765  }
   766  
   767  func (n *pathInode) SetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
   768  	if file != nil {
   769  		return file.SetLk(owner, lk, flags)
   770  	}
   771  	return fuse.ENOSYS
   772  }
   773  
   774  func (n *pathInode) SetLkw(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
   775  	if file != nil {
   776  		return file.SetLkw(owner, lk, flags)
   777  	}
   778  	return fuse.ENOSYS
   779  }