Commit f9ab0286 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Use Inode arguments in FileSystemConnector.{Mount,Unmount}.

parent 70a92e3a
...@@ -45,7 +45,7 @@ func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (me ...@@ -45,7 +45,7 @@ func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (me
me.rootNode = me.newInode(true) me.rootNode = me.newInode(true)
me.rootNode.nodeId = FUSE_ROOT_ID me.rootNode.nodeId = FUSE_ROOT_ID
me.verify() me.verify()
me.mountRoot(nodeFs, opts) me.MountRoot(nodeFs, opts)
return me return me
} }
...@@ -223,6 +223,12 @@ func (me *FileSystemConnector) findInode(fullPath string) *Inode { ...@@ -223,6 +223,12 @@ func (me *FileSystemConnector) findInode(fullPath string) *Inode {
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
func (me *FileSystemConnector) MountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) {
me.rootNode.mountFs(nodeFs, opts)
nodeFs.Mount(me)
me.verify()
}
// Mount() generates a synthetic directory node, and mounts the file // Mount() generates a synthetic directory node, and mounts the file
// system there. If opts is nil, the mount options of the root file // system there. If opts is nil, the mount options of the root file
// system are inherited. The encompassing filesystem should pretend // system are inherited. The encompassing filesystem should pretend
...@@ -239,25 +245,10 @@ func (me *FileSystemConnector) findInode(fullPath string) *Inode { ...@@ -239,25 +245,10 @@ func (me *FileSystemConnector) findInode(fullPath string) *Inode {
// mount management in FileSystemConnector, so AutoUnionFs and // mount management in FileSystemConnector, so AutoUnionFs and
// MultiZipFs don't have to do it separately, with the risk of // MultiZipFs don't have to do it separately, with the risk of
// inconsistencies. // inconsistencies.
func (me *FileSystemConnector) Mount(mountPoint string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status { func (me *FileSystemConnector) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
if mountPoint == "/" || mountPoint == "" {
me.mountRoot(nodeFs, opts)
return OK
}
dirParent, base := filepath.Split(mountPoint)
parent := me.findInode(dirParent)
if parent == nil {
log.Println("Could not find mountpoint parent:", dirParent)
return ENOENT
}
parent.treeLock.Lock() parent.treeLock.Lock()
defer parent.treeLock.Unlock() defer parent.treeLock.Unlock()
if parent.mount == nil { node := parent.children[name]
return ENOENT
}
node := parent.children[base]
if node != nil { if node != nil {
return EBUSY return EBUSY
} }
...@@ -268,51 +259,42 @@ func (me *FileSystemConnector) Mount(mountPoint string, nodeFs NodeFileSystem, o ...@@ -268,51 +259,42 @@ func (me *FileSystemConnector) Mount(mountPoint string, nodeFs NodeFileSystem, o
} }
node.mountFs(nodeFs, opts) node.mountFs(nodeFs, opts)
parent.addChild(base, node) parent.addChild(name, node)
if parent.mounts == nil { if parent.mounts == nil {
parent.mounts = make(map[string]*fileSystemMount) parent.mounts = make(map[string]*fileSystemMount)
} }
parent.mounts[base] = node.mountPoint parent.mounts[name] = node.mountPoint
node.mountPoint.parentInode = parent
if me.Debug { if me.Debug {
log.Println("Mount: ", nodeFs, "on dir", mountPoint, log.Println("Mount: ", nodeFs, "on subdir", name,
"parent", parent) "parent", parent.nodeId)
} }
nodeFs.Mount(me) nodeFs.Mount(me)
me.verify() me.verify()
return OK return OK
} }
func (me *FileSystemConnector) mountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) { // Unmount() tries to unmount the given inode.
me.rootNode.mountFs(nodeFs, opts)
nodeFs.Mount(me)
me.verify()
}
// Unmount() tries to unmount the given path.
// //
// Returns the following error codes: // Returns the following error codes:
// //
// EINVAL: path does not exist, or is not a mount point. // EINVAL: path does not exist, or is not a mount point.
// //
// EBUSY: there are open files, or submounts below this node. // EBUSY: there are open files, or submounts below this node.
func (me *FileSystemConnector) Unmount(path string) Status { func (me *FileSystemConnector) Unmount(node *Inode) Status {
dir, name := filepath.Split(path) if node.mountPoint == nil {
parentNode := me.findInode(dir) log.Println("not a mountpoint:", node.nodeId)
if parentNode == nil {
log.Println("Could not find parent of mountpoint:", path)
return EINVAL return EINVAL
} }
// Must lock parent to update tree structure. // Must lock parent to update tree structure.
parentNode := node.mountPoint.parentInode
parentNode.treeLock.Lock() parentNode.treeLock.Lock()
defer parentNode.treeLock.Unlock() defer parentNode.treeLock.Unlock()
mount := parentNode.mounts[name] mount := node.mountPoint
if mount == nil { name := node.mountPoint.mountName()
return EINVAL
}
if mount.openFiles.Count() > 0 { if mount.openFiles.Count() > 0 {
return EBUSY return EBUSY
} }
......
...@@ -25,6 +25,9 @@ type fileSystemMount struct { ...@@ -25,6 +25,9 @@ type fileSystemMount struct {
// Node that we were mounted on. // Node that we were mounted on.
mountInode *Inode mountInode *Inode
// Parent to the mountInode.
parentInode *Inode
// Options for the mount. // Options for the mount.
options *FileSystemOptions options *FileSystemOptions
...@@ -36,6 +39,19 @@ type fileSystemMount struct { ...@@ -36,6 +39,19 @@ type fileSystemMount struct {
openFiles HandleMap openFiles HandleMap
} }
// Must called with lock for parent held.
func (me *fileSystemMount) mountName() string {
for k, v := range me.parentInode.mounts {
if me == v {
return k
}
}
panic("not found")
return ""
}
func (me *fileSystemMount) setOwner(attr *Attr) { func (me *fileSystemMount) setOwner(attr *Attr) {
if me.options.Owner != nil { if me.options.Owner != nil {
attr.Owner = *me.options.Owner attr.Owner = *me.options.Owner
......
...@@ -33,6 +33,7 @@ type testCase struct { ...@@ -33,6 +33,7 @@ type testCase struct {
origSubdir string origSubdir string
tester *testing.T tester *testing.T
state *MountState state *MountState
nodeFs *PathNodeFs
connector *FileSystemConnector connector *FileSystemConnector
} }
...@@ -68,8 +69,8 @@ func NewTestCase(t *testing.T) *testCase { ...@@ -68,8 +69,8 @@ func NewTestCase(t *testing.T) *testCase {
pfs = NewLockingFileSystem(pfs) pfs = NewLockingFileSystem(pfs)
var rfs RawFileSystem var rfs RawFileSystem
nfs := NewPathNodeFs(pfs) me.nodeFs = NewPathNodeFs(pfs)
me.connector = NewFileSystemConnector(nfs, me.connector = NewFileSystemConnector(me.nodeFs,
&FileSystemOptions{ &FileSystemOptions{
EntryTimeout: testTtl, EntryTimeout: testTtl,
AttrTimeout: testTtl, AttrTimeout: testTtl,
...@@ -99,7 +100,8 @@ func (me *testCase) Cleanup() { ...@@ -99,7 +100,8 @@ func (me *testCase) Cleanup() {
os.Remove(me.tmpDir) os.Remove(me.tmpDir)
} }
func (me *testCase) writeOrigFile() { func (me *testCase) rootNode() *Inode {
return me.nodeFs.Root().Inode()
} }
//////////////// ////////////////
......
...@@ -16,35 +16,25 @@ func TestMountOnExisting(t *testing.T) { ...@@ -16,35 +16,25 @@ func TestMountOnExisting(t *testing.T) {
err := os.Mkdir(ts.mnt+"/mnt", 0777) err := os.Mkdir(ts.mnt+"/mnt", 0777)
CheckSuccess(err) CheckSuccess(err)
nfs := &DefaultNodeFileSystem{} nfs := &DefaultNodeFileSystem{}
code := ts.connector.Mount("/mnt", nfs, nil) code := ts.connector.Mount(ts.rootNode(), "mnt", nfs, nil)
if code != EBUSY { if code != EBUSY {
t.Fatal("expect EBUSY:", code) t.Fatal("expect EBUSY:", code)
} }
err = os.Remove(ts.mnt + "/mnt") err = os.Remove(ts.mnt + "/mnt")
CheckSuccess(err) CheckSuccess(err)
code = ts.connector.Mount("/mnt", nfs, nil) code = ts.connector.Mount(ts.rootNode(), "mnt", nfs, nil)
if !code.Ok() { if !code.Ok() {
t.Fatal("expect OK:", code) t.Fatal("expect OK:", code)
} }
} }
func TestUnmountNoExist(t *testing.T) {
ts := NewTestCase(t)
defer ts.Cleanup()
code := ts.connector.Unmount("/doesnotexist")
if code != EINVAL {
t.Fatal("expect EINVAL", code)
}
}
func TestMountRename(t *testing.T) { func TestMountRename(t *testing.T) {
ts := NewTestCase(t) ts := NewTestCase(t)
defer ts.Cleanup() defer ts.Cleanup()
fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig)) fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig))
code := ts.connector.Mount("/mnt", fs, nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
if !code.Ok() { if !code.Ok() {
t.Fatal("mount should succeed") t.Fatal("mount should succeed")
} }
...@@ -59,7 +49,7 @@ func TestMountReaddir(t *testing.T) { ...@@ -59,7 +49,7 @@ func TestMountReaddir(t *testing.T) {
defer ts.Cleanup() defer ts.Cleanup()
fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig)) fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig))
code := ts.connector.Mount("/mnt", fs, nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
if !code.Ok() { if !code.Ok() {
t.Fatal("mount should succeed") t.Fatal("mount should succeed")
} }
...@@ -79,7 +69,7 @@ func TestRecursiveMount(t *testing.T) { ...@@ -79,7 +69,7 @@ func TestRecursiveMount(t *testing.T) {
CheckSuccess(err) CheckSuccess(err)
fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig)) fs := NewPathNodeFs(NewLoopbackFileSystem(ts.orig))
code := ts.connector.Mount("/mnt", fs, nil) code := ts.connector.Mount(ts.rootNode(), "mnt", fs, nil)
if !code.Ok() { if !code.Ok() {
t.Fatal("mount should succeed") t.Fatal("mount should succeed")
} }
...@@ -93,7 +83,7 @@ func TestRecursiveMount(t *testing.T) { ...@@ -93,7 +83,7 @@ func TestRecursiveMount(t *testing.T) {
f, err := os.Open(filepath.Join(submnt, "hello.txt")) f, err := os.Open(filepath.Join(submnt, "hello.txt"))
CheckSuccess(err) CheckSuccess(err)
log.Println("Attempting unmount, should fail") log.Println("Attempting unmount, should fail")
code = ts.connector.Unmount("/mnt") code = ts.connector.Unmount(ts.nodeFs.Node("mnt"))
if code != EBUSY { if code != EBUSY {
t.Error("expect EBUSY") t.Error("expect EBUSY")
} }
...@@ -104,7 +94,7 @@ func TestRecursiveMount(t *testing.T) { ...@@ -104,7 +94,7 @@ func TestRecursiveMount(t *testing.T) {
time.Sleep(1.5e9 * testTtl) time.Sleep(1.5e9 * testTtl)
log.Println("Attempting unmount, should succeed") log.Println("Attempting unmount, should succeed")
code = ts.connector.Unmount("/mnt") code = ts.connector.Unmount(ts.nodeFs.Node("mnt"))
if code != OK { if code != OK {
t.Error("umount failed.", code) t.Error("umount failed.", code)
} }
...@@ -116,7 +106,7 @@ func TestDeletedUnmount(t *testing.T) { ...@@ -116,7 +106,7 @@ func TestDeletedUnmount(t *testing.T) {
submnt := filepath.Join(ts.mnt, "mnt") submnt := filepath.Join(ts.mnt, "mnt")
pfs2 := NewPathNodeFs(NewLoopbackFileSystem(ts.orig)) pfs2 := NewPathNodeFs(NewLoopbackFileSystem(ts.orig))
code := ts.connector.Mount("/mnt", pfs2, nil) code := ts.connector.Mount(ts.rootNode(), "mnt", pfs2, nil)
if !code.Ok() { if !code.Ok() {
t.Fatal("Mount error", code) t.Fatal("Mount error", code)
} }
...@@ -131,14 +121,14 @@ func TestDeletedUnmount(t *testing.T) { ...@@ -131,14 +121,14 @@ func TestDeletedUnmount(t *testing.T) {
_, err = f.Write([]byte("bla")) _, err = f.Write([]byte("bla"))
CheckSuccess(err) CheckSuccess(err)
code = ts.connector.Unmount("/mnt") code = ts.connector.Unmount(ts.nodeFs.Node("mnt"))
if code != EBUSY { if code != EBUSY {
t.Error("expect EBUSY for unmount with open files", code) t.Error("expect EBUSY for unmount with open files", code)
} }
f.Close() f.Close()
time.Sleep(1.5e9 * testTtl) time.Sleep(1.5e9 * testTtl)
code = ts.connector.Unmount("/mnt") code = ts.connector.Unmount(ts.nodeFs.Node("mnt"))
if !code.Ok() { if !code.Ok() {
t.Error("should succeed", code) t.Error("should succeed", code)
} }
......
...@@ -3,16 +3,21 @@ package unionfs ...@@ -3,16 +3,21 @@ package unionfs
import ( import (
"fmt" "fmt"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"sync" "sync"
"syscall"
"time" "time"
"io/ioutil"
) )
type knownFs struct {
*UnionFs
*fuse.PathNodeFs
}
// Creates unions for all files under a given directory, // Creates unions for all files under a given directory,
// walking the tree and looking for directories D which have a // walking the tree and looking for directories D which have a
// D/READONLY symlink. // D/READONLY symlink.
...@@ -22,12 +27,12 @@ type AutoUnionFs struct { ...@@ -22,12 +27,12 @@ type AutoUnionFs struct {
fuse.DefaultFileSystem fuse.DefaultFileSystem
lock sync.RWMutex lock sync.RWMutex
knownFileSystems map[string]*UnionFs knownFileSystems map[string]knownFs
nameRootMap map[string]string nameRootMap map[string]string
root string root string
connector *fuse.FileSystemConnector connector *fuse.FileSystemConnector
nodeFs *fuse.PathNodeFs
options *AutoUnionFsOptions options *AutoUnionFsOptions
} }
...@@ -50,7 +55,7 @@ const ( ...@@ -50,7 +55,7 @@ const (
func NewAutoUnionFs(directory string, options AutoUnionFsOptions) *AutoUnionFs { func NewAutoUnionFs(directory string, options AutoUnionFsOptions) *AutoUnionFs {
a := new(AutoUnionFs) a := new(AutoUnionFs)
a.knownFileSystems = make(map[string]*UnionFs) a.knownFileSystems = make(map[string]knownFs)
a.nameRootMap = make(map[string]string) a.nameRootMap = make(map[string]string)
a.options = &options a.options = &options
directory, err := filepath.Abs(directory) directory, err := filepath.Abs(directory)
...@@ -66,6 +71,7 @@ func (me *AutoUnionFs) Name() string { ...@@ -66,6 +71,7 @@ func (me *AutoUnionFs) Name() string {
} }
func (me *AutoUnionFs) Mount(nodeFs *fuse.PathNodeFs, connector *fuse.FileSystemConnector) { func (me *AutoUnionFs) Mount(nodeFs *fuse.PathNodeFs, connector *fuse.FileSystemConnector) {
me.nodeFs = nodeFs
me.connector = connector me.connector = connector
if me.options.UpdateOnMount { if me.options.UpdateOnMount {
time.AfterFunc(0.1e9, func() { me.updateKnownFses() }) time.AfterFunc(0.1e9, func() { me.updateKnownFses() })
...@@ -93,8 +99,8 @@ func (me *AutoUnionFs) createFs(name string, roots []string) fuse.Status { ...@@ -93,8 +99,8 @@ func (me *AutoUnionFs) createFs(name string, roots []string) fuse.Status {
} }
} }
ufs := me.knownFileSystems[name] known := me.knownFileSystems[name]
if ufs != nil { if known.UnionFs != nil {
log.Println("Already have a workspace:", name) log.Println("Already have a workspace:", name)
return fuse.EBUSY return fuse.EBUSY
} }
...@@ -107,9 +113,12 @@ func (me *AutoUnionFs) createFs(name string, roots []string) fuse.Status { ...@@ -107,9 +113,12 @@ func (me *AutoUnionFs) createFs(name string, roots []string) fuse.Status {
log.Printf("Adding workspace %v for roots %v", name, ufs.Name()) log.Printf("Adding workspace %v for roots %v", name, ufs.Name())
nfs := fuse.NewPathNodeFs(ufs) nfs := fuse.NewPathNodeFs(ufs)
code := me.connector.Mount("/"+name, nfs, &me.options.FileSystemOptions) code := me.connector.Mount(me.nodeFs.Root().Inode(), name, nfs, &me.options.FileSystemOptions)
if code.Ok() { if code.Ok() {
me.knownFileSystems[name] = ufs me.knownFileSystems[name] = knownFs{
ufs,
nfs,
}
me.nameRootMap[name] = roots[0] me.nameRootMap[name] = roots[0]
} }
return code return code
...@@ -119,14 +128,14 @@ func (me *AutoUnionFs) rmFs(name string) (code fuse.Status) { ...@@ -119,14 +128,14 @@ func (me *AutoUnionFs) rmFs(name string) (code fuse.Status) {
me.lock.Lock() me.lock.Lock()
defer me.lock.Unlock() defer me.lock.Unlock()
fs := me.knownFileSystems[name] known := me.knownFileSystems[name]
if fs == nil { if known.UnionFs == nil {
return fuse.ENOENT return fuse.ENOENT
} }
code = me.connector.Unmount(name) code = me.connector.Unmount(known.PathNodeFs.Root().Inode())
if code.Ok() { if code.Ok() {
me.knownFileSystems[name] = nil, false me.knownFileSystems[name] = knownFs{}, false
me.nameRootMap[name] = "", false me.nameRootMap[name] = "", false
} else { } else {
log.Printf("Unmount failed for %s. Code %v", name, code) log.Printf("Unmount failed for %s. Code %v", name, code)
...@@ -208,7 +217,7 @@ func (me *AutoUnionFs) Readlink(path string, context *fuse.Context) (out string, ...@@ -208,7 +217,7 @@ func (me *AutoUnionFs) Readlink(path string, context *fuse.Context) (out string,
func (me *AutoUnionFs) getUnionFs(name string) *UnionFs { func (me *AutoUnionFs) getUnionFs(name string) *UnionFs {
me.lock.RLock() me.lock.RLock()
defer me.lock.RUnlock() defer me.lock.RUnlock()
return me.knownFileSystems[name] return me.knownFileSystems[name].UnionFs
} }
func (me *AutoUnionFs) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) { func (me *AutoUnionFs) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
......
...@@ -35,6 +35,7 @@ type MultiZipFs struct { ...@@ -35,6 +35,7 @@ type MultiZipFs struct {
zips map[string]*MemTreeFs zips map[string]*MemTreeFs
dirZipFileMap map[string]string dirZipFileMap map[string]string
nodeFs *fuse.PathNodeFs
fuse.DefaultFileSystem fuse.DefaultFileSystem
} }
...@@ -50,6 +51,7 @@ func (me *MultiZipFs) Name() string { ...@@ -50,6 +51,7 @@ func (me *MultiZipFs) Name() string {
} }
func (me *MultiZipFs) Mount(nodeFs *fuse.PathNodeFs, connector *fuse.FileSystemConnector) { func (me *MultiZipFs) Mount(nodeFs *fuse.PathNodeFs, connector *fuse.FileSystemConnector) {
me.nodeFs = nodeFs
me.Connector = connector me.Connector = connector
} }
...@@ -119,9 +121,9 @@ func (me *MultiZipFs) Unlink(name string, context *fuse.Context) (code fuse.Stat ...@@ -119,9 +121,9 @@ func (me *MultiZipFs) Unlink(name string, context *fuse.Context) (code fuse.Stat
me.lock.Lock() me.lock.Lock()
defer me.lock.Unlock() defer me.lock.Unlock()
_, ok := me.zips[basename] zfs, ok := me.zips[basename]
if ok { if ok {
code = me.Connector.Unmount("/" + basename) code = me.Connector.Unmount(zfs.Root().Inode())
if !code.Ok() { if !code.Ok() {
return code return code
} }
...@@ -171,7 +173,7 @@ func (me *MultiZipFs) Symlink(value string, linkName string, context *fuse.Conte ...@@ -171,7 +173,7 @@ func (me *MultiZipFs) Symlink(value string, linkName string, context *fuse.Conte
return fuse.EINVAL return fuse.EINVAL
} }
code = me.Connector.Mount("/"+base, fs, nil) code = me.Connector.Mount(me.nodeFs.Root().Inode(), base, fs, nil)
if !code.Ok() { if !code.Ok() {
return code return code
} }
......
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