github.com/hanwen/go-fuse@v1.0.0/fuse/nodefs/fsconnector.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 nodefs
     6  
     7  // This file contains the internal logic of the
     8  // FileSystemConnector. The functions for satisfying the raw interface
     9  // are in fsops.go
    10  
    11  import (
    12  	"log"
    13  	"path/filepath"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  	"unsafe"
    18  
    19  	"github.com/hanwen/go-fuse/fuse"
    20  )
    21  
    22  // Tests should set to true.
    23  var paranoia = false
    24  
    25  // FileSystemConnector translates the raw FUSE protocol (serialized
    26  // structs of uint32/uint64) to operations on Go objects representing
    27  // files and directories.
    28  type FileSystemConnector struct {
    29  	debug bool
    30  
    31  	// Callbacks for talking back to the kernel.
    32  	server *fuse.Server
    33  
    34  	// Translate between uint64 handles and *Inode.
    35  	inodeMap handleMap
    36  
    37  	// The root of the FUSE file system.
    38  	rootNode *Inode
    39  
    40  	// This lock prevents Lookup() and Forget() from running concurrently.
    41  	// Locking at this level is a big hammer, but makes sure we don't return
    42  	// forgotten nodes to the kernel. Problems solved by this lock:
    43  	// https://github.com/hanwen/go-fuse/issues/168
    44  	// https://github.com/rfjakob/gocryptfs/issues/322
    45  	lookupLock sync.Mutex
    46  }
    47  
    48  // NewOptions generates FUSE options that correspond to libfuse's
    49  // defaults.
    50  func NewOptions() *Options {
    51  	return &Options{
    52  		NegativeTimeout: 0,
    53  		AttrTimeout:     time.Second,
    54  		EntryTimeout:    time.Second,
    55  		Owner:           fuse.CurrentOwner(),
    56  	}
    57  }
    58  
    59  // NewFileSystemConnector creates a FileSystemConnector with the given
    60  // options.
    61  func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
    62  	c = new(FileSystemConnector)
    63  	if opts == nil {
    64  		opts = NewOptions()
    65  	}
    66  	c.inodeMap = newPortableHandleMap()
    67  	c.rootNode = newInode(true, root)
    68  
    69  	c.verify()
    70  	c.mountRoot(opts)
    71  
    72  	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
    73  	// issue a forget.  This lookupUpdate is to make the counts match.
    74  	c.lookupUpdate(c.rootNode)
    75  	c.debug = opts.Debug
    76  
    77  	return c
    78  }
    79  
    80  // Server returns the fuse.Server that talking to the kernel.
    81  func (c *FileSystemConnector) Server() *fuse.Server {
    82  	return c.server
    83  }
    84  
    85  // SetDebug toggles printing of debug information. This function is
    86  // deprecated. Set the Debug option in the Options struct instead.
    87  func (c *FileSystemConnector) SetDebug(debug bool) {
    88  	c.debug = debug
    89  }
    90  
    91  // This verifies invariants of the data structure.  This routine
    92  // acquires tree locks as it walks the inode tree.
    93  func (c *FileSystemConnector) verify() {
    94  	if !paranoia {
    95  		return
    96  	}
    97  	root := c.rootNode
    98  	root.verify(c.rootNode.mountPoint)
    99  }
   100  
   101  // childLookup fills entry information for a newly created child inode
   102  func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
   103  	n.Node().GetAttr(&out.Attr, nil, context)
   104  	n.mount.fillEntry(out)
   105  	out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
   106  	if out.Ino == 0 {
   107  		out.Ino = out.NodeId
   108  	}
   109  	if out.Nlink == 0 {
   110  		// With Nlink == 0, newer kernels will refuse link
   111  		// operations.
   112  		out.Nlink = 1
   113  	}
   114  }
   115  
   116  func (c *rawBridge) toInode(nodeid uint64) *Inode {
   117  	if nodeid == fuse.FUSE_ROOT_ID {
   118  		return c.rootNode
   119  	}
   120  	i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
   121  	return i
   122  }
   123  
   124  // Must run outside treeLock.  Returns the nodeId and generation.
   125  func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) {
   126  	id, generation = c.inodeMap.Register(&node.handled)
   127  	c.verify()
   128  	return
   129  }
   130  
   131  // forgetUpdate decrements the reference counter for "nodeID" by "forgetCount".
   132  // Must run outside treeLock.
   133  func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
   134  	if nodeID == fuse.FUSE_ROOT_ID {
   135  		c.rootNode.Node().OnUnmount()
   136  
   137  		// We never got a lookup for root, so don't try to
   138  		// forget root.
   139  		return
   140  	}
   141  
   142  	// Prevent concurrent modification of the tree while we are processing
   143  	// the FORGET
   144  	node := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeID)))
   145  	node.mount.treeLock.Lock()
   146  	defer node.mount.treeLock.Unlock()
   147  
   148  	if forgotten, _ := c.inodeMap.Forget(nodeID, forgetCount); forgotten {
   149  		if len(node.children) > 0 || !node.Node().Deletable() ||
   150  			node == c.rootNode || node.mountPoint != nil {
   151  			// We cannot forget a directory that still has children as these
   152  			// would become unreachable.
   153  			return
   154  		}
   155  		// We have to remove ourself from all parents.
   156  		// Create a copy of node.parents so we can safely iterate over it
   157  		// while modifying the original.
   158  		parents := make(map[parentData]struct{}, len(node.parents))
   159  		for k, v := range node.parents {
   160  			parents[k] = v
   161  		}
   162  
   163  		for p := range parents {
   164  			// This also modifies node.parents
   165  			p.parent.rmChild(p.name)
   166  		}
   167  
   168  		node.fsInode.OnForget()
   169  	}
   170  	// TODO - try to drop children even forget was not successful.
   171  	c.verify()
   172  }
   173  
   174  // InodeCount returns the number of inodes registered with the kernel.
   175  func (c *FileSystemConnector) InodeHandleCount() int {
   176  	return c.inodeMap.Count()
   177  }
   178  
   179  // Finds a node within the currently known inodes, returns the last
   180  // known node and the remaining unknown path components.  If parent is
   181  // nil, start from FUSE mountpoint.
   182  func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
   183  	if parent == nil {
   184  		parent = c.rootNode
   185  	}
   186  	if fullPath == "" {
   187  		return parent, nil
   188  	}
   189  
   190  	sep := string(filepath.Separator)
   191  	fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
   192  	comps := strings.Split(fullPath, sep)
   193  
   194  	node := parent
   195  	if node.mountPoint == nil {
   196  		node.mount.treeLock.RLock()
   197  		defer node.mount.treeLock.RUnlock()
   198  	}
   199  
   200  	for i, component := range comps {
   201  		if len(component) == 0 {
   202  			continue
   203  		}
   204  
   205  		if node.mountPoint != nil {
   206  			node.mount.treeLock.RLock()
   207  			defer node.mount.treeLock.RUnlock()
   208  		}
   209  
   210  		next := node.children[component]
   211  		if next == nil {
   212  			return node, comps[i:]
   213  		}
   214  		node = next
   215  	}
   216  
   217  	return node, nil
   218  }
   219  
   220  // Follows the path from the given parent, doing lookups as
   221  // necessary. The path should be '/' separated without leading slash.
   222  func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
   223  	if path == "" {
   224  		return parent
   225  	}
   226  
   227  	components := strings.Split(path, "/")
   228  	for _, r := range components {
   229  		var a fuse.Attr
   230  		// This will not affect inode ID lookup counts, which
   231  		// are only update in response to kernel requests.
   232  		var dummy fuse.InHeader
   233  		child, _ := c.internalLookup(&a, parent, r, &dummy)
   234  		if child == nil {
   235  			return nil
   236  		}
   237  
   238  		parent = child
   239  	}
   240  
   241  	return parent
   242  }
   243  
   244  func (c *FileSystemConnector) mountRoot(opts *Options) {
   245  	c.rootNode.mountFs(opts)
   246  	c.rootNode.mount.connector = c
   247  	c.verify()
   248  }
   249  
   250  // Mount() generates a synthetic directory node, and mounts the file
   251  // system there.  If opts is nil, the mount options of the root file
   252  // system are inherited.  The encompassing filesystem should pretend
   253  // the mount point does not exist.
   254  //
   255  // It returns ENOENT if the directory containing the mount point does
   256  // not exist, and EBUSY if the intended mount point already exists.
   257  func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
   258  	node, code := c.lockMount(parent, name, root, opts)
   259  	if !code.Ok() {
   260  		return code
   261  	}
   262  
   263  	node.Node().OnMount(c)
   264  	return code
   265  }
   266  
   267  func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, opts *Options) (*Inode, fuse.Status) {
   268  	defer c.verify()
   269  	parent.mount.treeLock.Lock()
   270  	defer parent.mount.treeLock.Unlock()
   271  	node := parent.children[name]
   272  	if node != nil {
   273  		return nil, fuse.EBUSY
   274  	}
   275  
   276  	node = newInode(true, root)
   277  	if opts == nil {
   278  		opts = c.rootNode.mountPoint.options
   279  	}
   280  
   281  	node.mountFs(opts)
   282  	node.mount.connector = c
   283  	parent.addChild(name, node)
   284  
   285  	node.mountPoint.parentInode = parent
   286  	if c.debug {
   287  		log.Printf("Mount %T on subdir %s, parent i%d", node,
   288  			name, c.inodeMap.Handle(&parent.handled))
   289  	}
   290  	return node, fuse.OK
   291  }
   292  
   293  // Unmount() tries to unmount the given inode.  It returns EINVAL if the
   294  // path does not exist, or is not a mount point, and EBUSY if there
   295  // are open files or submounts below this node.
   296  func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
   297  	// TODO - racy.
   298  	if node.mountPoint == nil {
   299  		log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled))
   300  		return fuse.EINVAL
   301  	}
   302  
   303  	nodeID := c.inodeMap.Handle(&node.handled)
   304  
   305  	// Must lock parent to update tree structure.
   306  	parentNode := node.mountPoint.parentInode
   307  	parentNode.mount.treeLock.Lock()
   308  	defer parentNode.mount.treeLock.Unlock()
   309  
   310  	mount := node.mountPoint
   311  	name := node.mountPoint.mountName()
   312  	if mount.openFiles.Count() > 0 {
   313  		return fuse.EBUSY
   314  	}
   315  
   316  	node.mount.treeLock.Lock()
   317  	defer node.mount.treeLock.Unlock()
   318  
   319  	if mount.mountInode != node {
   320  		log.Panicf("got two different mount inodes %v vs %v",
   321  			c.inodeMap.Handle(&mount.mountInode.handled),
   322  			c.inodeMap.Handle(&node.handled))
   323  	}
   324  
   325  	if !node.canUnmount() {
   326  		return fuse.EBUSY
   327  	}
   328  
   329  	delete(parentNode.children, name)
   330  	node.Node().OnUnmount()
   331  
   332  	parentId := c.inodeMap.Handle(&parentNode.handled)
   333  	if parentNode == c.rootNode {
   334  		// TODO - test coverage. Currently covered by zipfs/multizip_test.go
   335  		parentId = fuse.FUSE_ROOT_ID
   336  	}
   337  
   338  	// We have to wait until the kernel has forgotten the
   339  	// mountpoint, so the write to node.mountPoint is no longer
   340  	// racy.
   341  	mount.treeLock.Unlock()
   342  	parentNode.mount.treeLock.Unlock()
   343  	code := c.server.DeleteNotify(parentId, nodeID, name)
   344  
   345  	if code.Ok() {
   346  		delay := 100 * time.Microsecond
   347  
   348  		for {
   349  			// This operation is rare, so we kludge it to avoid
   350  			// contention.
   351  			time.Sleep(delay)
   352  			delay = delay * 2
   353  			if !c.inodeMap.Has(nodeID) {
   354  				break
   355  			}
   356  
   357  			if delay >= time.Second {
   358  				// We limit the wait at one second. If
   359  				// it takes longer, something else is
   360  				// amiss, and we would be waiting forever.
   361  				log.Println("kernel did not issue FORGET for node on Unmount.")
   362  				break
   363  			}
   364  		}
   365  
   366  	}
   367  
   368  	parentNode.mount.treeLock.Lock()
   369  	mount.treeLock.Lock()
   370  	mount.mountInode = nil
   371  	node.mountPoint = nil
   372  
   373  	return fuse.OK
   374  }
   375  
   376  // FileNotify notifies the kernel that data and metadata of this inode
   377  // has changed.  After this call completes, the kernel will issue a
   378  // new GetAttr requests for metadata and new Read calls for content.
   379  // Use negative offset for metadata-only invalidation, and zero-length
   380  // for invalidating all content.
   381  func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status {
   382  	var nID uint64
   383  	if node == c.rootNode {
   384  		nID = fuse.FUSE_ROOT_ID
   385  	} else {
   386  		nID = c.inodeMap.Handle(&node.handled)
   387  	}
   388  
   389  	if nID == 0 {
   390  		return fuse.OK
   391  	}
   392  	return c.server.InodeNotify(nID, off, length)
   393  }
   394  
   395  // FileNotifyStoreCache notifies the kernel about changed data of the inode.
   396  //
   397  // This call is similar to FileNotify, but instead of only invalidating a data
   398  // region, it puts updated data directly to the kernel cache:
   399  //
   400  // After this call completes, the kernel has put updated data into the inode's cache,
   401  // and will use data from that cache for non direct-IO reads from the inode
   402  // in corresponding data region. After kernel's cache data is evicted, the kernel
   403  // will have to issue new Read calls on user request to get data content.
   404  //
   405  // ENOENT is returned if the kernel does not currently have entry for this
   406  // inode in its dentry cache.
   407  func (c *FileSystemConnector) FileNotifyStoreCache(node *Inode, off int64, data []byte) fuse.Status {
   408  	var nID uint64
   409  	if node == c.rootNode {
   410  		nID = fuse.FUSE_ROOT_ID
   411  	} else {
   412  		nID = c.inodeMap.Handle(&node.handled)
   413  	}
   414  
   415  	if nID == 0 {
   416  		// the kernel does not currently know about this inode.
   417  		return fuse.ENOENT
   418  	}
   419  	return c.server.InodeNotifyStoreCache(nID, off, data)
   420  }
   421  
   422  // FileRetrieveCache retrieves data from kernel's inode cache.
   423  //
   424  // This call retrieves data from kernel's inode cache @ offset and up to
   425  // len(dest) bytes. If kernel cache has fewer consecutive data starting at
   426  // offset, that fewer amount is returned. In particular if inode data at offset
   427  // is not cached (0, OK) is returned.
   428  //
   429  // If the kernel does not currently have entry for this inode in its dentry
   430  // cache (0, OK) is still returned, pretending that the inode could be known to
   431  // the kernel, but kernel's inode cache is empty.
   432  func (c *FileSystemConnector) FileRetrieveCache(node *Inode, off int64, dest []byte) (n int, st fuse.Status) {
   433  	var nID uint64
   434  	if node == c.rootNode {
   435  		nID = fuse.FUSE_ROOT_ID
   436  	} else {
   437  		nID = c.inodeMap.Handle(&node.handled)
   438  	}
   439  
   440  	if nID == 0 {
   441  		// the kernel does not currently know about this inode.
   442  		// -> we can pretend that its cache for the inode is empty.
   443  		return 0, fuse.OK
   444  	}
   445  	return c.server.InodeRetrieveCache(nID, off, dest)
   446  }
   447  
   448  // EntryNotify makes the kernel forget the entry data from the given
   449  // name from a directory.  After this call, the kernel will issue a
   450  // new lookup request for the given name when necessary. No filesystem
   451  // related locks should be held when calling this.
   452  func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status {
   453  	var nID uint64
   454  	if node == c.rootNode {
   455  		nID = fuse.FUSE_ROOT_ID
   456  	} else {
   457  		nID = c.inodeMap.Handle(&node.handled)
   458  	}
   459  
   460  	if nID == 0 {
   461  		return fuse.OK
   462  	}
   463  	return c.server.EntryNotify(nID, name)
   464  }
   465  
   466  // DeleteNotify signals to the kernel that the named entry in dir for
   467  // the child disappeared. No filesystem related locks should be held
   468  // when calling this.
   469  func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status {
   470  	var nID uint64
   471  
   472  	if dir == c.rootNode {
   473  		nID = fuse.FUSE_ROOT_ID
   474  	} else {
   475  		nID = c.inodeMap.Handle(&dir.handled)
   476  	}
   477  
   478  	if nID == 0 {
   479  		return fuse.OK
   480  	}
   481  
   482  	chId := c.inodeMap.Handle(&child.handled)
   483  
   484  	return c.server.DeleteNotify(nID, chId, name)
   485  }