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