dep ensure
This commit is contained in:
parent
1158ac4760
commit
c8c18403e4
371 changed files with 168673 additions and 1 deletions
424
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go
generated
vendored
Normal file
424
vendor/github.com/hanwen/go-fuse/fuse/nodefs/fsconnector.go
generated
vendored
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package nodefs
|
||||
|
||||
// This file contains the internal logic of the
|
||||
// FileSystemConnector. The functions for satisfying the raw interface
|
||||
// are in fsops.go
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
)
|
||||
|
||||
// Tests should set to true.
|
||||
var paranoia = false
|
||||
|
||||
// FilesystemConnector translates the raw FUSE protocol (serialized
|
||||
// structs of uint32/uint64) to operations on Go objects representing
|
||||
// files and directories.
|
||||
type FileSystemConnector struct {
|
||||
debug bool
|
||||
|
||||
// Callbacks for talking back to the kernel.
|
||||
server *fuse.Server
|
||||
|
||||
// Translate between uint64 handles and *Inode.
|
||||
inodeMap handleMap
|
||||
|
||||
// The root of the FUSE file system.
|
||||
rootNode *Inode
|
||||
}
|
||||
|
||||
// NewOptions generates FUSE options that correspond to libfuse's
|
||||
// defaults.
|
||||
func NewOptions() *Options {
|
||||
return &Options{
|
||||
NegativeTimeout: 0,
|
||||
AttrTimeout: time.Second,
|
||||
EntryTimeout: time.Second,
|
||||
Owner: fuse.CurrentOwner(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileSystemConnector creates a FileSystemConnector with the given
|
||||
// options.
|
||||
func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
|
||||
c = new(FileSystemConnector)
|
||||
if opts == nil {
|
||||
opts = NewOptions()
|
||||
}
|
||||
c.inodeMap = newPortableHandleMap()
|
||||
c.rootNode = newInode(true, root)
|
||||
|
||||
c.verify()
|
||||
c.mountRoot(opts)
|
||||
|
||||
// FUSE does not issue a LOOKUP for 1 (obviously), but it does
|
||||
// issue a forget. This lookupUpdate is to make the counts match.
|
||||
c.lookupUpdate(c.rootNode)
|
||||
c.debug = opts.Debug
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Server returns the fuse.Server that talking to the kernel.
|
||||
func (c *FileSystemConnector) Server() *fuse.Server {
|
||||
return c.server
|
||||
}
|
||||
|
||||
// SetDebug toggles printing of debug information. This function is
|
||||
// deprecated. Set the Debug option in the Options struct instead.
|
||||
func (c *FileSystemConnector) SetDebug(debug bool) {
|
||||
c.debug = debug
|
||||
}
|
||||
|
||||
// This verifies invariants of the data structure. This routine
|
||||
// acquires tree locks as it walks the inode tree.
|
||||
func (c *FileSystemConnector) verify() {
|
||||
if !paranoia {
|
||||
return
|
||||
}
|
||||
root := c.rootNode
|
||||
root.verify(c.rootNode.mountPoint)
|
||||
}
|
||||
|
||||
// childLookup fills entry information for a newly created child inode
|
||||
func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
|
||||
n.Node().GetAttr((*fuse.Attr)(&out.Attr), nil, context)
|
||||
n.mount.fillEntry(out)
|
||||
out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
|
||||
if out.Ino == 0 {
|
||||
out.Ino = out.NodeId
|
||||
}
|
||||
if out.Nlink == 0 {
|
||||
// With Nlink == 0, newer kernels will refuse link
|
||||
// operations.
|
||||
out.Nlink = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (c *rawBridge) toInode(nodeid uint64) *Inode {
|
||||
if nodeid == fuse.FUSE_ROOT_ID {
|
||||
return c.rootNode
|
||||
}
|
||||
i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
|
||||
return i
|
||||
}
|
||||
|
||||
// Must run outside treeLock. Returns the nodeId and generation.
|
||||
func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) {
|
||||
id, generation = c.inodeMap.Register(&node.handled)
|
||||
c.verify()
|
||||
return
|
||||
}
|
||||
|
||||
// forgetUpdate decrements the reference counter for "nodeID" by "forgetCount".
|
||||
// Must run outside treeLock.
|
||||
func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
|
||||
if nodeID == fuse.FUSE_ROOT_ID {
|
||||
c.rootNode.Node().OnUnmount()
|
||||
|
||||
// We never got a lookup for root, so don't try to
|
||||
// forget root.
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent concurrent modification of the tree while we are processing
|
||||
// the FORGET
|
||||
node := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeID)))
|
||||
node.mount.treeLock.Lock()
|
||||
defer node.mount.treeLock.Unlock()
|
||||
|
||||
if forgotten, _ := c.inodeMap.Forget(nodeID, forgetCount); forgotten {
|
||||
if len(node.children) > 0 || !node.Node().Deletable() ||
|
||||
node == c.rootNode || node.mountPoint != nil {
|
||||
// We cannot forget a directory that still has children as these
|
||||
// would become unreachable.
|
||||
return
|
||||
}
|
||||
// We have to remove ourself from all parents.
|
||||
// Create a copy of node.parents so we can safely iterate over it
|
||||
// while modifying the original.
|
||||
parents := make(map[parentData]struct{}, len(node.parents))
|
||||
for k, v := range node.parents {
|
||||
parents[k] = v
|
||||
}
|
||||
|
||||
for p := range parents {
|
||||
// This also modifies node.parents
|
||||
p.parent.rmChild(p.name)
|
||||
}
|
||||
|
||||
node.fsInode.OnForget()
|
||||
}
|
||||
// TODO - try to drop children even forget was not successful.
|
||||
c.verify()
|
||||
}
|
||||
|
||||
// InodeCount returns the number of inodes registered with the kernel.
|
||||
func (c *FileSystemConnector) InodeHandleCount() int {
|
||||
return c.inodeMap.Count()
|
||||
}
|
||||
|
||||
// Finds a node within the currently known inodes, returns the last
|
||||
// known node and the remaining unknown path components. If parent is
|
||||
// nil, start from FUSE mountpoint.
|
||||
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
|
||||
if parent == nil {
|
||||
parent = c.rootNode
|
||||
}
|
||||
if fullPath == "" {
|
||||
return parent, nil
|
||||
}
|
||||
|
||||
sep := string(filepath.Separator)
|
||||
fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
|
||||
comps := strings.Split(fullPath, sep)
|
||||
|
||||
node := parent
|
||||
if node.mountPoint == nil {
|
||||
node.mount.treeLock.RLock()
|
||||
defer node.mount.treeLock.RUnlock()
|
||||
}
|
||||
|
||||
for i, component := range comps {
|
||||
if len(component) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if node.mountPoint != nil {
|
||||
node.mount.treeLock.RLock()
|
||||
defer node.mount.treeLock.RUnlock()
|
||||
}
|
||||
|
||||
next := node.children[component]
|
||||
if next == nil {
|
||||
return node, comps[i:]
|
||||
}
|
||||
node = next
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// Follows the path from the given parent, doing lookups as
|
||||
// necessary. The path should be '/' separated without leading slash.
|
||||
func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
|
||||
if path == "" {
|
||||
return parent
|
||||
}
|
||||
|
||||
components := strings.Split(path, "/")
|
||||
for _, r := range components {
|
||||
var a fuse.Attr
|
||||
// This will not affect inode ID lookup counts, which
|
||||
// are only update in response to kernel requests.
|
||||
var dummy fuse.InHeader
|
||||
child, _ := c.internalLookup(&a, parent, r, &dummy)
|
||||
if child == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parent = child
|
||||
}
|
||||
|
||||
return parent
|
||||
}
|
||||
|
||||
func (c *FileSystemConnector) mountRoot(opts *Options) {
|
||||
c.rootNode.mountFs(opts)
|
||||
c.rootNode.mount.connector = c
|
||||
c.verify()
|
||||
}
|
||||
|
||||
// Mount() generates a synthetic directory node, and mounts the file
|
||||
// system there. If opts is nil, the mount options of the root file
|
||||
// system are inherited. The encompassing filesystem should pretend
|
||||
// the mount point does not exist.
|
||||
//
|
||||
// It returns ENOENT if the directory containing the mount point does
|
||||
// not exist, and EBUSY if the intended mount point already exists.
|
||||
func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
|
||||
node, code := c.lockMount(parent, name, root, opts)
|
||||
if !code.Ok() {
|
||||
return code
|
||||
}
|
||||
|
||||
node.Node().OnMount(c)
|
||||
return code
|
||||
}
|
||||
|
||||
func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, opts *Options) (*Inode, fuse.Status) {
|
||||
defer c.verify()
|
||||
parent.mount.treeLock.Lock()
|
||||
defer parent.mount.treeLock.Unlock()
|
||||
node := parent.children[name]
|
||||
if node != nil {
|
||||
return nil, fuse.EBUSY
|
||||
}
|
||||
|
||||
node = newInode(true, root)
|
||||
if opts == nil {
|
||||
opts = c.rootNode.mountPoint.options
|
||||
}
|
||||
|
||||
node.mountFs(opts)
|
||||
node.mount.connector = c
|
||||
parent.addChild(name, node)
|
||||
|
||||
node.mountPoint.parentInode = parent
|
||||
if c.debug {
|
||||
log.Printf("Mount %T on subdir %s, parent %d", node,
|
||||
name, c.inodeMap.Handle(&parent.handled))
|
||||
}
|
||||
return node, fuse.OK
|
||||
}
|
||||
|
||||
// Unmount() tries to unmount the given inode. It returns EINVAL if the
|
||||
// path does not exist, or is not a mount point, and EBUSY if there
|
||||
// are open files or submounts below this node.
|
||||
func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
|
||||
// TODO - racy.
|
||||
if node.mountPoint == nil {
|
||||
log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled))
|
||||
return fuse.EINVAL
|
||||
}
|
||||
|
||||
nodeID := c.inodeMap.Handle(&node.handled)
|
||||
|
||||
// Must lock parent to update tree structure.
|
||||
parentNode := node.mountPoint.parentInode
|
||||
parentNode.mount.treeLock.Lock()
|
||||
defer parentNode.mount.treeLock.Unlock()
|
||||
|
||||
mount := node.mountPoint
|
||||
name := node.mountPoint.mountName()
|
||||
if mount.openFiles.Count() > 0 {
|
||||
return fuse.EBUSY
|
||||
}
|
||||
|
||||
node.mount.treeLock.Lock()
|
||||
defer node.mount.treeLock.Unlock()
|
||||
|
||||
if mount.mountInode != node {
|
||||
log.Panicf("got two different mount inodes %v vs %v",
|
||||
c.inodeMap.Handle(&mount.mountInode.handled),
|
||||
c.inodeMap.Handle(&node.handled))
|
||||
}
|
||||
|
||||
if !node.canUnmount() {
|
||||
return fuse.EBUSY
|
||||
}
|
||||
|
||||
delete(parentNode.children, name)
|
||||
node.Node().OnUnmount()
|
||||
|
||||
parentId := c.inodeMap.Handle(&parentNode.handled)
|
||||
if parentNode == c.rootNode {
|
||||
// TODO - test coverage. Currently covered by zipfs/multizip_test.go
|
||||
parentId = fuse.FUSE_ROOT_ID
|
||||
}
|
||||
|
||||
// We have to wait until the kernel has forgotten the
|
||||
// mountpoint, so the write to node.mountPoint is no longer
|
||||
// racy.
|
||||
mount.treeLock.Unlock()
|
||||
parentNode.mount.treeLock.Unlock()
|
||||
code := c.server.DeleteNotify(parentId, nodeID, name)
|
||||
|
||||
if code.Ok() {
|
||||
delay := 100 * time.Microsecond
|
||||
|
||||
for {
|
||||
// This operation is rare, so we kludge it to avoid
|
||||
// contention.
|
||||
time.Sleep(delay)
|
||||
delay = delay * 2
|
||||
if !c.inodeMap.Has(nodeID) {
|
||||
break
|
||||
}
|
||||
|
||||
if delay >= time.Second {
|
||||
// We limit the wait at one second. If
|
||||
// it takes longer, something else is
|
||||
// amiss, and we would be waiting forever.
|
||||
log.Println("kernel did not issue FORGET for node on Unmount.")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parentNode.mount.treeLock.Lock()
|
||||
mount.treeLock.Lock()
|
||||
mount.mountInode = nil
|
||||
node.mountPoint = nil
|
||||
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
// FileNotify notifies the kernel that data and metadata of this inode
|
||||
// has changed. After this call completes, the kernel will issue a
|
||||
// new GetAttr requests for metadata and new Read calls for content.
|
||||
// Use negative offset for metadata-only invalidation, and zero-length
|
||||
// for invalidating all content.
|
||||
func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status {
|
||||
var nId uint64
|
||||
if node == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&node.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
return c.server.InodeNotify(nId, off, length)
|
||||
}
|
||||
|
||||
// EntryNotify makes the kernel forget the entry data from the given
|
||||
// name from a directory. After this call, the kernel will issue a
|
||||
// new lookup request for the given name when necessary. No filesystem
|
||||
// related locks should be held when calling this.
|
||||
func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status {
|
||||
var nId uint64
|
||||
if node == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&node.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
return c.server.EntryNotify(nId, name)
|
||||
}
|
||||
|
||||
// DeleteNotify signals to the kernel that the named entry in dir for
|
||||
// the child disappeared. No filesystem related locks should be held
|
||||
// when calling this.
|
||||
func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status {
|
||||
var nId uint64
|
||||
|
||||
if dir == c.rootNode {
|
||||
nId = fuse.FUSE_ROOT_ID
|
||||
} else {
|
||||
nId = c.inodeMap.Handle(&dir.handled)
|
||||
}
|
||||
|
||||
if nId == 0 {
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
chId := c.inodeMap.Handle(&child.handled)
|
||||
|
||||
return c.server.DeleteNotify(nId, chId, name)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue