Commit 570a84cd authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fuse/nodefs: mount root node.Node directly.

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.
parent 8b78faac
......@@ -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.
}
......
......@@ -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)
......
......@@ -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)
}
......
......@@ -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,
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
}
......
......@@ -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)
......@@ -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)
......
......@@ -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
......@@ -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)
......
......@@ -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 {
......@@ -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
}
......
......@@ -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
......@@ -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.
......@@ -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.
......@@ -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()
}
......@@ -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()
......@@ -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
}
......@@ -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 {
......
......@@ -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
......
......@@ -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 := fmt.Sprintf("%T", c.rootNode.Node())
name = strings.TrimLeft(name, "*")
}
return name
}
......@@ -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
......
......@@ -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)
}
......@@ -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,
......
......@@ -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 {
......
......@@ -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,
......@@ -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)
}
......
......@@ -35,7 +35,7 @@ func setupOwnerTest(t *testing.T, opts *nodefs.Options) (workdir string, cleanup
fs := &ownerFs{NewDefaultFileSystem()}
nfs := NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(wd, nfs, opts)
state, _, err := nodefs.MountRoot(wd, nfs.Root(), opts)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -47,7 +47,7 @@ func (fs *PathNodeFs) SetDebug(dbg bool) {
fs.debug = dbg
}
func (fs *PathNodeFs) Mount(path string, nodeFs nodefs.FileSystem, opts *nodefs.Options) fuse.Status {
func (fs *PathNodeFs) Mount(path string, root nodefs.Node, opts *nodefs.Options) fuse.Status {
dir, name := filepath.Split(path)
if dir != "" {
dir = filepath.Clean(dir)
......@@ -56,7 +56,7 @@ func (fs *PathNodeFs) Mount(path string, nodeFs nodefs.FileSystem, opts *nodefs.
if parent == nil {
return fuse.ENOENT
}
return fs.connector.Mount(parent, name, nodeFs, opts)
return fs.connector.Mount(parent, name, root, opts)
}
// Forgets all known information on client inodes.
......@@ -91,9 +91,6 @@ func (fs *PathNodeFs) Unmount(path string) fuse.Status {
return fs.connector.Unmount(node)
}
func (fs *PathNodeFs) OnUnmount() {
}
func (fs *PathNodeFs) String() string {
name := fs.fs.String()
if name == "defaultFileSystem" {
......@@ -107,11 +104,6 @@ func (fs *PathNodeFs) Connector() *nodefs.FileSystemConnector {
return fs.connector
}
func (fs *PathNodeFs) OnMount(conn *nodefs.FileSystemConnector) {
fs.connector = conn
fs.fs.OnMount(fs)
}
func (fs *PathNodeFs) Node(name string) *nodefs.Inode {
n, rest := fs.LastNode(name)
if len(rest) > 0 {
......@@ -206,6 +198,14 @@ type pathInode struct {
inode *nodefs.Inode
}
func (n *pathInode) OnMount(conn *nodefs.FileSystemConnector) {
n.pathFs.connector = conn
n.pathFs.fs.OnMount(n.pathFs)
}
func (n *pathInode) OnUnmount() {
}
// Drop all known client inodes. Must have the treeLock.
func (n *pathInode) forgetClientInodes() {
n.clientInode = 0
......
......@@ -112,7 +112,7 @@ func xattrTestCase(t *testing.T, nm string) (mountPoint string, cleanup func())
}
nfs := NewPathNodeFs(xfs, nil)
state, _, err := nodefs.MountFileSystem(mountPoint, nfs, nil)
state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), nil)
if err != nil {
t.Fatalf("TempDir failed: %v", err)
}
......
......@@ -40,7 +40,7 @@ func setupCacheTest(t *testing.T) (string, *pathfs.PathNodeFs, func()) {
pathfs.NewLoopbackFileSystem(dir + "/orig"),
}
pfs := pathfs.NewPathNodeFs(fs, nil)
state, conn, err := nodefs.MountFileSystem(dir+"/mnt", pfs, nil)
state, conn, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(), nil)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......@@ -140,7 +140,7 @@ func TestNonseekable(t *testing.T) {
}
defer os.RemoveAll(dir)
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(dir, nfs, nil)
state, _, err := nodefs.MountRoot(dir, nfs.Root(), nil)
if err != nil {
t.Fatalf("failed: %v", err)
}
......@@ -173,7 +173,7 @@ func TestGetAttrRace(t *testing.T) {
fs := pathfs.NewLoopbackFileSystem(dir + "/orig")
pfs := pathfs.NewPathNodeFs(fs, nil)
state, conn, err := nodefs.MountFileSystem(dir+"/mnt", pfs,
state, conn, err := nodefs.MountRoot(dir+"/mnt", pfs.Root(),
&nodefs.Options{})
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
......
......@@ -41,7 +41,7 @@ func defaultReadTest(t *testing.T) (root string, cleanup func()) {
t.Fatalf("TempDir failed: %v", err)
}
pathfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(dir, pathfs, nil)
state, _, err := nodefs.MountRoot(dir, pathfs.Root(), nil)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -34,8 +34,8 @@ func TestDeleteNotify(t *testing.T) {
t.Fatalf("TempDir failed %v", err)
}
defer os.RemoveAll(dir)
fs := nodefs.NewMemNodeFs(dir + "/backing")
conn := nodefs.NewFileSystemConnector(fs,
root := nodefs.NewMemNodeFSRoot(dir + "/backing")
conn := nodefs.NewFileSystemConnector(root,
&nodefs.Options{PortableInodes: true})
mnt := dir + "/mnt"
err = os.Mkdir(mnt, 0755)
......@@ -51,18 +51,18 @@ func TestDeleteNotify(t *testing.T) {
go state.Serve()
defer state.Unmount()
_, code := fs.Root().Mkdir("testdir", 0755, nil)
_, code := root.Mkdir("testdir", 0755, nil)
if !code.Ok() {
t.Fatal(code)
}
ch := fs.Root().Inode().RmChild("testdir")
ch := root.Inode().RmChild("testdir")
ch.Node().SetInode(nil)
flip := flipNode{
Node: ch.Node(),
ok: make(chan int),
}
fs.Root().Inode().NewChild("testdir", true, &flip)
root.Inode().NewChild("testdir", true, &flip)
err = ioutil.WriteFile(mnt+"/testdir/testfile", []byte{42}, 0644)
if err != nil {
......@@ -99,12 +99,12 @@ func TestDeleteNotify(t *testing.T) {
// Simulate deletion+mkdir coming from the network
close(flip.ok)
oldCh := fs.Root().Inode().RmChild("testdir")
_, code = fs.Root().Inode().Node().Mkdir("testdir", 0755, nil)
oldCh := root.Inode().RmChild("testdir")
_, code = root.Inode().Node().Mkdir("testdir", 0755, nil)
if !code.Ok() {
t.Fatal("mkdir status", code)
}
conn.DeleteNotify(fs.Root().Inode(), oldCh, "testdir")
conn.DeleteNotify(root.Inode(), oldCh, "testdir")
_, err = os.Lstat(mnt + "/testdir")
if err != nil {
......
......@@ -138,7 +138,7 @@ func setupFAttrTest(t *testing.T, fs pathfs.FileSystem) (dir string, clean func(
t.Fatalf("TempDir failed: %v", err)
}
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(dir, nfs, nil)
state, _, err := nodefs.MountRoot(dir, nfs.Root(), nil)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -70,7 +70,7 @@ func NewTestCase(t *testing.T) *testCase {
me.pathFs = pathfs.NewPathNodeFs(pfs, &pathfs.PathNodeFsOptions{
ClientInodes: true})
me.connector = nodefs.NewFileSystemConnector(me.pathFs,
me.connector = nodefs.NewFileSystemConnector(me.pathFs.Root(),
&nodefs.Options{
EntryTimeout: testTtl,
AttrTimeout: testTtl,
......@@ -923,7 +923,7 @@ func TestOriginalIsSymlink(t *testing.T) {
fs := pathfs.NewLoopbackFileSystem(link)
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(mnt, nfs, nil)
state, _, err := nodefs.MountRoot(mnt, nfs.Root(), nil)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -20,7 +20,7 @@ func TestMountOnExisting(t *testing.T) {
if err != nil {
t.Fatalf("Mkdir failed: %v", err)
}
nfs := nodefs.NewDefaultFileSystem()
nfs := nodefs.NewDefaultNode()
code := ts.connector.Mount(ts.rootNode(), "mnt", nfs, nil)
if code != fuse.EBUSY {
t.Fatal("expect EBUSY:", code)
......@@ -46,7 +46,7 @@ func TestMountRename(t *testing.T) {
defer ts.Cleanup()
fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil)
if !code.Ok() {
t.Fatal("mount should succeed")
}
......@@ -62,7 +62,7 @@ func TestMountReaddir(t *testing.T) {
defer ts.Cleanup()
fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil)
if !code.Ok() {
t.Fatal("mount should succeed")
}
......@@ -87,7 +87,7 @@ func TestRecursiveMount(t *testing.T) {
}
fs := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", fs.Root(), nil)
if !code.Ok() {
t.Fatal("mount should succeed")
}
......@@ -129,7 +129,7 @@ func TestDeletedUnmount(t *testing.T) {
submnt := filepath.Join(ts.mnt, "mnt")
pfs2 := pathfs.NewPathNodeFs(pathfs.NewLoopbackFileSystem(ts.orig), nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", pfs2, nil)
code := ts.connector.Mount(ts.rootNode(), "mnt", pfs2.Root(), nil)
if !code.Ok() {
t.Fatal("Mount error", code)
}
......@@ -163,38 +163,21 @@ func TestDeletedUnmount(t *testing.T) {
}
}
type defaultFS struct {
nodefs.FileSystem
root nodefs.Node
}
func (fs *defaultFS) Root() nodefs.Node {
return fs.root
}
func TestDefaultNodeMount(t *testing.T) {
fs := &defaultFS{
nodefs.NewDefaultFileSystem(),
nodefs.NewDefaultNode(),
}
dir, err := ioutil.TempDir("", "go-fuse")
if err != nil {
t.Fatalf("TempDir: %v", err)
}
defer os.RemoveAll(dir)
s, conn, err := nodefs.MountFileSystem(dir, fs, nil)
root := nodefs.NewDefaultNode()
s, conn, err := nodefs.MountRoot(dir, root, nil)
if err != nil {
t.Fatalf("MountFileSystem: %v", err)
t.Fatalf("MountRoot: %v", err)
}
go s.Serve()
defer s.Unmount()
sub := &defaultFS{
nodefs.NewDefaultFileSystem(),
nodefs.NewDefaultNode(),
}
if err := conn.Mount(fs.Root().Inode(), "sub", sub, nil); !err.Ok() {
if err := conn.Mount(root.Inode(), "sub", nodefs.NewDefaultNode(), nil); !err.Ok() {
t.Fatalf("Mount: %v", err)
}
......
......@@ -87,7 +87,7 @@ func NewNotifyTest(t *testing.T) *NotifyTest {
}
me.pathfs = pathfs.NewPathNodeFs(me.fs, nil)
me.state, me.connector, err = nodefs.MountFileSystem(me.dir, me.pathfs, opts)
me.state, me.connector, err = nodefs.MountRoot(me.dir, me.pathfs.Root(), opts)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -131,7 +131,7 @@ func (fs *autoUnionFs) createFs(name string, roots []string) fuse.Status {
log.Printf("Adding workspace %v for roots %v", name, ufs.String())
nfs := pathfs.NewPathNodeFs(ufs, &fs.options.PathNodeFsOptions)
code := fs.nodeFs.Mount(name, nfs, &fs.options.Options)
code := fs.nodeFs.Mount(name, nfs.Root(), &fs.options.Options)
if code.Ok() {
fs.knownFileSystems[name] = knownFs{
ufs,
......
......@@ -54,7 +54,7 @@ func setup(t *testing.T) (workdir string, cleanup func()) {
fs := NewAutoUnionFs(wd+"/store", testAOpts)
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(wd+"/mnt", nfs, &testAOpts.Options)
state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), &testAOpts.Options)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -93,7 +93,7 @@ func setupUfs(t *testing.T) (workdir string, cleanup func()) {
pathfs := pathfs.NewPathNodeFs(ufs,
&pathfs.PathNodeFsOptions{ClientInodes: true})
state, conn, err := nodefs.MountFileSystem(wd+"/mnt", pathfs, opts)
state, conn, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......@@ -1159,7 +1159,7 @@ func TestUnionFsDisappearing(t *testing.T) {
}
nfs := pathfs.NewPathNodeFs(ufs, nil)
state, _, err := nodefs.MountFileSystem(wd+"/mnt", nfs, opts)
state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), opts)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -64,7 +64,7 @@ func TestXAttrCaching(t *testing.T) {
pathfs := pathfs.NewPathNodeFs(ufs,
&pathfs.PathNodeFsOptions{ClientInodes: true})
server, conn, err := nodefs.MountFileSystem(wd+"/mnt", pathfs, opts)
server, conn, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts)
if err != nil {
t.Fatalf("MountNodeFileSystem failed: %v", err)
}
......
......@@ -16,38 +16,47 @@ type MemFile interface {
type memNode struct {
nodefs.Node
file MemFile
fs *MemTreeFs
}
// MemTreeFs creates a tree of internal Inodes. Since the tree is
// loaded in memory completely at startup, it does not need to inode
// memTreeFs creates a tree of internal Inodes. Since the tree is
// loaded in memory completely at startup, it does not need inode
// discovery through Lookup() at serve time.
type MemTreeFs struct {
nodefs.FileSystem
root memNode
root *memNode
files map[string]MemFile
Name string
}
func NewMemTreeFs() *MemTreeFs {
return &MemTreeFs{
FileSystem: nodefs.NewDefaultFileSystem(),
root: memNode{Node: nodefs.NewDefaultNode()},
func NewMemTreeFs(files map[string]MemFile) *MemTreeFs {
fs := &MemTreeFs{
root: &memNode{Node: nodefs.NewDefaultNode()},
files: files,
}
fs.root.fs = fs
return fs
}
func (fs *MemTreeFs) String() string {
return fs.Name
}
func (fs *MemTreeFs) OnMount(conn *nodefs.FileSystemConnector) {
func (fs *MemTreeFs) SetDebug(bool) {
}
func (fs *MemTreeFs) Root() nodefs.Node {
return fs.root
}
func (fs *MemTreeFs) onMount() {
for k, v := range fs.files {
fs.addFile(k, v)
}
fs.files = nil
}
func (fs *MemTreeFs) Root() nodefs.Node {
return &fs.root
func (n *memNode) OnMount(c *nodefs.FileSystemConnector) {
n.fs.onMount()
}
func (n *memNode) Print(indent int) {
......@@ -114,7 +123,10 @@ func (n *MemTreeFs) addFile(name string, f MemFile) {
for i, c := range comps {
child := node.GetChild(c)
if child == nil {
fsnode := &memNode{Node: nodefs.NewDefaultNode()}
fsnode := &memNode{
Node: nodefs.NewDefaultNode(),
fs: n,
}
if i == len(comps)-1 {
fsnode.file = f
}
......
......@@ -16,6 +16,7 @@ import (
"sync"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
......@@ -28,7 +29,7 @@ const (
// MultiZipFs is a path filesystem that mounts zipfiles.
type MultiZipFs struct {
lock sync.RWMutex
zips map[string]*MemTreeFs
zips map[string]nodefs.Node
dirZipFileMap map[string]string
nodeFs *pathfs.PathNodeFs
......@@ -37,7 +38,7 @@ type MultiZipFs struct {
func NewMultiZipFs() *MultiZipFs {
m := &MultiZipFs{
zips: make(map[string]*MemTreeFs),
zips: make(map[string]nodefs.Node),
dirZipFileMap: make(map[string]string),
FileSystem: pathfs.NewDefaultFileSystem(),
}
......@@ -116,9 +117,9 @@ func (fs *MultiZipFs) Unlink(name string, context *fuse.Context) (code fuse.Stat
fs.lock.Lock()
defer fs.lock.Unlock()
zfs, ok := fs.zips[basename]
root, ok := fs.zips[basename]
if ok {
code = fs.nodeFs.UnmountNode(zfs.Root().Inode())
code = fs.nodeFs.UnmountNode(root.Inode())
if !code.Ok() {
return code
}
......@@ -162,18 +163,18 @@ func (fs *MultiZipFs) Symlink(value string, linkName string, context *fuse.Conte
return fuse.EBUSY
}
afs, err := NewArchiveFileSystem(value)
root, err := NewArchiveFileSystem(value)
if err != nil {
log.Println("NewZipArchiveFileSystem failed.", err)
return fuse.EINVAL
}
code = fs.nodeFs.Mount(base, afs, nil)
code = fs.nodeFs.Mount(base, root, nil)
if !code.Ok() {
return code
}
fs.dirZipFileMap[base] = value
fs.zips[base] = afs
fs.zips[base] = root
return fuse.OK
}
......@@ -23,7 +23,7 @@ func setupMzfs(t *testing.T) (mountPoint string, cleanup func()) {
fs := NewMultiZipFs()
mountPoint, _ = ioutil.TempDir("", "")
nfs := pathfs.NewPathNodeFs(fs, nil)
state, _, err := nodefs.MountFileSystem(mountPoint, nfs, &nodefs.Options{
state, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), &nodefs.Options{
EntryTimeout: testTtl,
AttrTimeout: testTtl,
NegativeTimeout: 0.0,
......
......@@ -3,13 +3,14 @@ package zipfs
import (
"archive/zip"
"bytes"
"errors"
"fmt"
"github.com/hanwen/go-fuse/fuse"
"io"
"os"
"path/filepath"
"strings"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
)
type ZipFile struct {
......@@ -57,33 +58,30 @@ func NewZipTree(name string) (map[string]MemFile, error) {
return out, nil
}
func NewArchiveFileSystem(name string) (mfs *MemTreeFs, err error) {
mfs = NewMemTreeFs()
mfs.Name = fmt.Sprintf("fs(%s)", name)
if strings.HasSuffix(name, ".zip") {
mfs.files, err = NewZipTree(name)
}
if strings.HasSuffix(name, ".tar.gz") {
mfs.files, err = NewTarCompressedTree(name, "gz")
}
if strings.HasSuffix(name, ".tar.bz2") {
mfs.files, err = NewTarCompressedTree(name, "bz2")
}
if strings.HasSuffix(name, ".tar") {
func NewArchiveFileSystem(name string) (root nodefs.Node, err error) {
var files map[string]MemFile
switch {
case strings.HasSuffix(name, ".zip"):
files, err = NewZipTree(name)
case strings.HasSuffix(name, ".tar.gz"):
files, err = NewTarCompressedTree(name, "gz")
case strings.HasSuffix(name, ".tar.bz2"):
files, err = NewTarCompressedTree(name, "bz2")
case strings.HasSuffix(name, ".tar"):
f, err := os.Open(name)
if err != nil {
return nil, err
}
mfs.files = NewTarTree(f)
files = NewTarTree(f)
default:
return nil, fmt.Errorf("unknown archive format %q", name)
}
if err != nil {
return nil, err
}
if mfs.files == nil {
return nil, errors.New(fmt.Sprintf("Unknown type for %v", name))
}
return mfs, nil
mfs := NewMemTreeFs(files)
mfs.Name = fmt.Sprintf("fs(%s)", name)
return mfs.Root(), nil
}
......@@ -21,13 +21,13 @@ func testZipFile() string {
}
func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) {
zfs, err := NewArchiveFileSystem(testZipFile())
root, err := NewArchiveFileSystem(testZipFile())
if err != nil {
t.Fatalf("NewArchiveFileSystem failed: %v", err)
}
mountPoint, _ = ioutil.TempDir("", "")
state, _, err := nodefs.MountFileSystem(mountPoint, zfs, nil)
state, _, err := nodefs.MountRoot(mountPoint, root, nil)
state.SetDebug(VerboseTest())
go state.Serve()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment