Skip to content

Commit

Permalink
fuse/nodefs: mount root node.Node directly.
Browse files Browse the repository at this point in the history
This obviates the nodefs.FileSystem type. To adjust for this API change,

  * call nodefs.MountRoot() on the Root() node of a to-be-mounted
    filesystem.

  * Move OnMount/OnUnmount methods to the type of the root node.

  * Stop embedding NewDefaultFileSystem() into file system
    objects. Usually FS objects no longer have to be exposed.
  • Loading branch information
hanwen committed Jan 5, 2014
1 parent 8b78faa commit 570a84c
Show file tree
Hide file tree
Showing 36 changed files with 161 additions and 195 deletions.
2 changes: 1 addition & 1 deletion benchmark/stat_test.go
Expand Up @@ -24,7 +24,7 @@ func setupFs(fs pathfs.FileSystem) (string, func()) {
}
mountPoint, _ := ioutil.TempDir("", "stat_test")
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(mountPoint, nfs, opts)
state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), opts)
if err != nil {
panic(fmt.Sprintf("cannot mount %v", err)) // ugh - benchmark has no error methods.
}
Expand Down
2 changes: 1 addition & 1 deletion example/autounionfs/main.go
Expand Up @@ -55,7 +55,7 @@ func main() {
}
gofs := unionfs.NewAutoUnionFs(flag.Arg(1), options)
pathfs := pathfs.NewPathNodeFs(gofs, nil)
state, conn, err := nodefs.MountFileSystem(flag.Arg(0), pathfs, &fsOpts)
state, conn, err := nodefs.MountRoot(flag.Arg(0), pathfs.Root(), &fsOpts)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion example/hello/main.go
Expand Up @@ -53,7 +53,7 @@ func main() {
log.Fatal("Usage:\n hello MOUNTPOINT")
}
nfs := pathfs.NewPathNodeFs(&HelloFs{FileSystem: pathfs.NewDefaultFileSystem()}, nil)
server, _, err := nodefs.MountFileSystem(flag.Arg(0), nfs, nil)
server, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), nil)
if err != nil {
log.Fatal("Mount fail: %v\n", err)
}
Expand Down
2 changes: 1 addition & 1 deletion example/loopback/main.go
Expand Up @@ -38,7 +38,7 @@ func main() {
EntryTimeout: time.Second,
}
pathFs := pathfs.NewPathNodeFs(finalFs, nil)
conn := nodefs.NewFileSystemConnector(pathFs, opts)
conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts)
mountPoint := flag.Arg(0)
mOpts := &fuse.MountOptions{
AllowOther: *other,
Expand Down
4 changes: 2 additions & 2 deletions example/memfs/main.go
Expand Up @@ -23,8 +23,8 @@ func main() {

mountPoint := flag.Arg(0)
prefix := flag.Arg(1)
fs := nodefs.NewMemNodeFs(prefix)
conn := nodefs.NewFileSystemConnector(fs, nil)
root := nodefs.NewMemNodeFSRoot(prefix)
conn := nodefs.NewFileSystemConnector(root, nil)
server, err := fuse.NewServer(conn.RawFS(), mountPoint, nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
Expand Down
2 changes: 1 addition & 1 deletion example/multizip/main.go
Expand Up @@ -23,7 +23,7 @@ func main() {

fs := zipfs.NewMultiZipFs()
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(flag.Arg(0), nfs, nil)
state, _, err := nodefs.MountRoot(flag.Arg(0), nfs.Root(), nil)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion example/unionfs/main.go
Expand Up @@ -48,7 +48,7 @@ func main() {
NegativeTimeout: time.Duration(*negative_ttl * float64(time.Second)),
PortableInodes: *portable,
}
mountState, _, err := nodefs.MountFileSystem(flag.Arg(0), nodeFs, &mOpts)
mountState, _, err := nodefs.MountRoot(flag.Arg(0), nodeFs.Root(), &mOpts)
if err != nil {
log.Fatal("Mount fail:", err)
}
Expand Down
5 changes: 2 additions & 3 deletions example/zipfs/main.go
Expand Up @@ -44,8 +44,7 @@ func main() {
}
}

var fs nodefs.FileSystem
fs, err = zipfs.NewArchiveFileSystem(flag.Arg(1))
root, err := zipfs.NewArchiveFileSystem(flag.Arg(1))
if err != nil {
fmt.Fprintf(os.Stderr, "NewArchiveFileSystem failed: %v\n", err)
os.Exit(1)
Expand All @@ -55,7 +54,7 @@ func main() {
AttrTimeout: time.Duration(*ttl * float64(time.Second)),
EntryTimeout: time.Duration(*ttl * float64(time.Second)),
}
state, _, err := nodefs.MountFileSystem(flag.Arg(0), fs, opts)
state, _, err := nodefs.MountRoot(flag.Arg(0), root, opts)
if err != nil {
fmt.Printf("Mount fail: %v\n", err)
os.Exit(1)
Expand Down
27 changes: 13 additions & 14 deletions fuse/nodefs/api.go
Expand Up @@ -11,24 +11,11 @@ import (
"github.com/hanwen/go-fuse/fuse"
)

// The FileSystem is the unit that can be mounted. It's essential
// function is the Root() method, which provides access to the file
// system tree.
// This is a legacy type.
type FileSystem interface {
// Root should return the inode for root of this file system.
Root() Node

// OnMount is called just after a mount is executed, either
// when the root is mounted, or when other filesystem are
// mounted in-process. The passed-in FileSystemConnector gives
// access to Notify methods and Debug settings.
OnMount(conn *FileSystemConnector)

// OnUnmount is executed just before a submount is removed,
// and when the process receives a forget for the FUSE root
// node.
OnUnmount()

// Used for debug outputs
String() string

Expand All @@ -46,6 +33,18 @@ type Node interface {
Inode() *Inode
SetInode(node *Inode)

// OnMount is called on the root node just after a mount is
// executed, either when the actual root is mounted, or when a
// filesystem is mounted in-process. The passed-in
// FileSystemConnector gives access to Notify methods and
// Debug settings.
OnMount(conn *FileSystemConnector)

// OnUnmount is executed just before a submount is removed,
// and when the process receives a forget for the FUSE root
// node.
OnUnmount()

// Lookup finds a child node to this node; it is only called
// for directory Nodes.
Lookup(out *fuse.Attr, name string, context *fuse.Context) (node Node, code fuse.Status)
Expand Down
33 changes: 6 additions & 27 deletions fuse/nodefs/defaultnode.go
Expand Up @@ -6,33 +6,6 @@ import (
"github.com/hanwen/go-fuse/fuse"
)

// NewDefaultNodeFileSystem returns a dummy implementation of
// NodeFileSystem, for embedding in structs.
func NewDefaultFileSystem() FileSystem {
return (*defaultFileSystem)(nil)
}

type defaultFileSystem struct {
}

func (fs *defaultFileSystem) OnUnmount() {
}

func (fs *defaultFileSystem) OnMount(conn *FileSystemConnector) {

}

func (fs *defaultFileSystem) Root() Node {
return NewDefaultNode()
}

func (fs *defaultFileSystem) String() string {
return "defaultFileSystem"
}

func (fs *defaultFileSystem) SetDebug(dbg bool) {
}

// NewDefaultNode returns an implementation of Node that returns
// ENOSYS for all operations.
func NewDefaultNode() Node {
Expand All @@ -43,6 +16,12 @@ type defaultNode struct {
inode *Inode
}

func (fs *defaultNode) OnUnmount() {
}

func (fs *defaultNode) OnMount(conn *FileSystemConnector) {
}

func (n *defaultNode) StatFs() *fuse.StatfsOut {
return nil
}
Expand Down
31 changes: 14 additions & 17 deletions fuse/nodefs/fsconnector.go
Expand Up @@ -31,8 +31,6 @@ type FileSystemConnector struct {
// Callbacks for talking back to the kernel.
server *fuse.Server

nodeFs FileSystem

// Translate between uint64 handles and *Inode.
inodeMap handleMap

Expand All @@ -53,20 +51,19 @@ func NewOptions() *Options {

// NewFileSystemConnector creates a FileSystemConnector with the given
// options.
func NewFileSystemConnector(nodeFs FileSystem, opts *Options) (c *FileSystemConnector) {
func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
c = new(FileSystemConnector)
if opts == nil {
opts = NewOptions()
}
c.nodeFs = nodeFs
c.inodeMap = newHandleMap(opts.PortableInodes)
c.rootNode = newInode(true, nodeFs.Root())
c.rootNode = newInode(true, root)

// Make sure we don't reuse generation numbers.
c.generation = uint64(time.Now().UnixNano())

c.verify()
c.mountRoot(nodeFs, opts)
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.
Expand Down Expand Up @@ -130,7 +127,7 @@ func (c *FileSystemConnector) lookupUpdate(node *Inode) (id uint64) {
// Must run outside treeLock.
func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
if nodeID == fuse.FUSE_ROOT_ID {
c.nodeFs.OnUnmount()
c.rootNode.Node().OnUnmount()

// We never got a lookup for root, so don't try to
// forget root.
Expand Down Expand Up @@ -250,10 +247,10 @@ func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
return parent
}

func (c *FileSystemConnector) mountRoot(nodeFs FileSystem, opts *Options) {
c.rootNode.mountFs(nodeFs, opts)
func (c *FileSystemConnector) mountRoot(opts *Options) {
c.rootNode.mountFs(opts)
c.rootNode.mount.connector = c
nodeFs.OnMount(c)
c.rootNode.Node().OnMount(c)
c.verify()
}

Expand All @@ -264,7 +261,7 @@ func (c *FileSystemConnector) mountRoot(nodeFs FileSystem, opts *Options) {
//
// 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, nodeFs FileSystem, opts *Options) fuse.Status {
func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
defer c.verify()
parent.mount.treeLock.Lock()
defer parent.mount.treeLock.Unlock()
Expand All @@ -273,21 +270,21 @@ func (c *FileSystemConnector) Mount(parent *Inode, name string, nodeFs FileSyste
return fuse.EBUSY
}

node = newInode(true, nodeFs.Root())
node = newInode(true, root)
if opts == nil {
opts = c.rootNode.mountPoint.options
}

node.mountFs(nodeFs, opts)
node.mountFs(opts)
node.mount.connector = c
parent.addChild(name, node)

node.mountPoint.parentInode = parent
if c.debug {
log.Println("Mount: ", nodeFs, "on subdir", name,
"parent", c.inodeMap.Handle(&parent.handled))
log.Printf("Mount %T on subdir %s, parent %d", node,
name, c.inodeMap.Handle(&parent.handled))
}
nodeFs.OnMount(c)
node.Node().OnMount(c)
return fuse.OK
}

Expand Down Expand Up @@ -328,7 +325,7 @@ func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
}

delete(parentNode.children, name)
mount.fs.OnUnmount()
node.Node().OnUnmount()

parentId := c.inodeMap.Handle(&parentNode.handled)
if parentNode == c.rootNode {
Expand Down
3 changes: 0 additions & 3 deletions fuse/nodefs/fsmount.go
Expand Up @@ -18,9 +18,6 @@ type openedFile struct {
}

type fileSystemMount struct {
// The file system we mounted here.
fs FileSystem

// Node that we were mounted on.
mountInode *Inode

Expand Down
10 changes: 3 additions & 7 deletions fuse/nodefs/fsops.go
Expand Up @@ -41,12 +41,8 @@ func (c *rawBridge) String() string {
return "go-fuse:unmounted"
}

fs := c.rootNode.mount.fs
name := fs.String()
if name == "DefaultNodeFileSystem" {
name = fmt.Sprintf("%T", fs)
name = strings.TrimLeft(name, "*")
}
name := fmt.Sprintf("%T", c.rootNode.Node())
name = strings.TrimLeft(name, "*")
return name
}

Expand All @@ -55,7 +51,7 @@ func (c *rawBridge) Init(s *fuse.Server) {
}

func (c *FileSystemConnector) lookupMountUpdate(out *fuse.Attr, mount *fileSystemMount) (node *Inode, code fuse.Status) {
code = mount.fs.Root().GetAttr(out, nil, nil)
code = mount.mountInode.Node().GetAttr(out, nil, nil)
if !code.Ok() {
log.Println("Root getattr should not return error", code)
out.Mode = fuse.S_IFDIR | 0755
Expand Down
10 changes: 8 additions & 2 deletions fuse/nodefs/fuse.go
Expand Up @@ -4,11 +4,17 @@ import (
"github.com/hanwen/go-fuse/fuse"
)

func MountFileSystem(mountpoint string, nodeFs FileSystem, opts *Options) (*fuse.Server, *FileSystemConnector, error) {
conn := NewFileSystemConnector(nodeFs, opts)
// Mounts a filesystem with the given root node on the given directory
func MountRoot(mountpoint string, root Node, opts *Options) (*fuse.Server, *FileSystemConnector, error) {
conn := NewFileSystemConnector(root, opts)
s, err := fuse.NewServer(conn.RawFS(), mountpoint, nil)
if err != nil {
return nil, nil, err
}
return s, conn, nil
}

// Mounts a filesystem on the given directory
func MountFileSystem(mountpoint string, fs FileSystem, opts *Options) (*fuse.Server, *FileSystemConnector, error) {
return MountRoot(mountpoint, fs.Root(), opts)
}
5 changes: 2 additions & 3 deletions fuse/nodefs/inode.go
Expand Up @@ -173,10 +173,9 @@ func (n *Inode) rmChild(name string) (ch *Inode) {
return ch
}

// Can only be called on untouched inodes.
func (n *Inode) mountFs(fs FileSystem, opts *Options) {
// Can only be called on untouched root inodes.
func (n *Inode) mountFs(opts *Options) {
n.mountPoint = &fileSystemMount{
fs: fs,
openFiles: newHandleMap(false),
mountInode: n,
options: opts,
Expand Down
8 changes: 4 additions & 4 deletions fuse/nodefs/memnode.go
Expand Up @@ -10,14 +10,14 @@ import (
"github.com/hanwen/go-fuse/fuse"
)

// NewMemNodeFs creates an in-memory node-based filesystem. Files are
// written into a backing store under the given prefix.
func NewMemNodeFs(prefix string) FileSystem {
// NewMemNodeFSRoot creates an in-memory node-based filesystem. Files
// are written into a backing store under the given prefix.
func NewMemNodeFSRoot(prefix string) Node {
fs := &memNodeFs{
backingStorePrefix: prefix,
}
fs.root = fs.newNode()
return fs
return fs.root
}

type memNodeFs struct {
Expand Down
8 changes: 4 additions & 4 deletions fuse/nodefs/memnode_test.go
Expand Up @@ -11,18 +11,18 @@ import (

const testTtl = 100 * time.Millisecond

func setupMemNodeTest(t *testing.T) (wd string, fs FileSystem, clean func()) {
func setupMemNodeTest(t *testing.T) (wd string, root Node, clean func()) {
tmp, err := ioutil.TempDir("", "go-fuse-memnode_test")
if err != nil {
t.Fatalf("TempDir failed: %v", err)
}
back := tmp + "/backing"
os.Mkdir(back, 0700)
fs = NewMemNodeFs(back)
root = NewMemNodeFSRoot(back)
mnt := tmp + "/mnt"
os.Mkdir(mnt, 0700)

connector := NewFileSystemConnector(fs,
connector := NewFileSystemConnector(root,
&Options{
EntryTimeout: testTtl,
AttrTimeout: testTtl,
Expand All @@ -39,7 +39,7 @@ func setupMemNodeTest(t *testing.T) (wd string, fs FileSystem, clean func()) {

// Unthreaded, but in background.
go state.Serve()
return mnt, fs, func() {
return mnt, root, func() {
state.Unmount()
os.RemoveAll(tmp)
}
Expand Down

0 comments on commit 570a84c

Please sign in to comment.