Commit 8cadc873 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Move treeLock into mountData too.

Further simplifies code.

Deal with nonexistent mountpoints both in mount and umount
parent e975a7da
...@@ -680,12 +680,19 @@ func TestRecursiveMount(t *testing.T) { ...@@ -680,12 +680,19 @@ func TestRecursiveMount(t *testing.T) {
t.Error("expect EBUSY") t.Error("expect EBUSY")
} }
err = os.Rename(ts.mountPoint + "/mnt", ts.mountPoint + "/foobar")
CheckSuccess(err)
f.Close() f.Close()
log.Println("Waiting for kernel to flush file-close to fuse...") log.Println("Waiting for kernel to flush file-close to fuse...")
time.Sleep(1.5e9 * testTtl) time.Sleep(1.5e9 * testTtl)
code = ts.connector.Unmount("/doesnotexist")
if code != EINVAL {
t.Fatal("expect EINVAL", code)
}
code = ts.connector.Unmount("/mnt") code = ts.connector.Unmount("/foobar")
if code != OK { if code != OK {
t.Error("umount failed.", code) t.Error("umount failed.", code)
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"math" "math"
"reflect" "reflect"
"regexp" "regexp"
"strings"
"syscall" "syscall"
"unsafe" "unsafe"
"io/ioutil" "io/ioutil"
......
...@@ -21,7 +21,6 @@ package fuse ...@@ -21,7 +21,6 @@ package fuse
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
...@@ -56,6 +55,10 @@ type mountData struct { ...@@ -56,6 +55,10 @@ type mountData struct {
// multi-mount filesystems. // multi-mount filesystems.
options *FileSystemOptions options *FileSystemOptions
// Protects parent/child relations within the mount.
// treeLock should be acquired before openFilesLock
treeLock sync.RWMutex
// Protects openFiles // Protects openFiles
openFilesLock sync.RWMutex openFilesLock sync.RWMutex
...@@ -133,10 +136,14 @@ type inode struct { ...@@ -133,10 +136,14 @@ type inode struct {
mount *mountData mount *mountData
} }
// Should be called with treeLock and fileLock held. // TotalOpenCount counts open files. It should only be entered from
func (me *inode) totalOpenCount() int { // an inode which is a mountpoint.
func (me *inode) TotalOpenCount() int {
o := 0 o := 0
if me.mountPoint != nil { if me.mountPoint != nil {
me.mountPoint.treeLock.RLock()
defer me.mountPoint.treeLock.RUnlock()
me.mountPoint.openFilesLock.RLock() me.mountPoint.openFilesLock.RLock()
defer me.mountPoint.openFilesLock.RUnlock() defer me.mountPoint.openFilesLock.RUnlock()
...@@ -144,19 +151,27 @@ func (me *inode) totalOpenCount() int { ...@@ -144,19 +151,27 @@ func (me *inode) totalOpenCount() int {
} }
for _, v := range me.Children { for _, v := range me.Children {
o += v.totalOpenCount() o += v.TotalOpenCount()
} }
return o return o
} }
// Should be called with treeLock held. // TotalMountCount counts mountpoints. It should only be entered from
func (me *inode) totalMountCount() int { // an inode which is a mountpoint.
func (me *inode) TotalMountCount() int {
o := 0 o := 0
if me.mountPoint != nil && !me.mountPoint.unmountPending { if me.mountPoint != nil {
if me.mountPoint.unmountPending {
return 0
}
o++ o++
me.mountPoint.treeLock.RLock()
defer me.mountPoint.treeLock.RUnlock()
} }
for _, v := range me.Children { for _, v := range me.Children {
o += v.totalMountCount() o += v.TotalMountCount()
} }
return o return o
} }
...@@ -207,6 +222,9 @@ func (me *inode) GetFullPath() (path string) { ...@@ -207,6 +222,9 @@ func (me *inode) GetFullPath() (path string) {
return ReverseJoin(rev_components, "/") return ReverseJoin(rev_components, "/")
} }
// GetPath returns the path relative to the mount governing this
// inode. It returns nil for mount if the file was deleted or the
// filesystem unmounted.
func (me *inode) GetPath() (path string, mount *mountData) { func (me *inode) GetPath() (path string, mount *mountData) {
if me.NodeId != FUSE_ROOT_ID && me.Parent == nil { if me.NodeId != FUSE_ROOT_ID && me.Parent == nil {
// Deleted node. Treat as if the filesystem was unmounted. // Deleted node. Treat as if the filesystem was unmounted.
...@@ -230,7 +248,7 @@ func (me *inode) GetPath() (path string, mount *mountData) { ...@@ -230,7 +248,7 @@ func (me *inode) GetPath() (path string, mount *mountData) {
return ReverseJoin(rev_components, "/"), mount return ReverseJoin(rev_components, "/"), mount
} }
// Must be called with treeLock held. // Must be called with treeLock for the mount held.
func (me *inode) setParent(newParent *inode) { func (me *inode) setParent(newParent *inode) {
oldParent := me.Parent oldParent := me.Parent
if oldParent == newParent { if oldParent == newParent {
...@@ -285,9 +303,8 @@ type FileSystemConnector struct { ...@@ -285,9 +303,8 @@ type FileSystemConnector struct {
//////////////// ////////////////
// Protects the inodeMap and each node's Children/Parent // inodeMapMutex protects inodeMap
// relations. inodeMapMutex sync.Mutex
treeLock sync.RWMutex
// Invariants: see the verify() method. // Invariants: see the verify() method.
inodeMap map[uint64]*inode inodeMap map[uint64]*inode
...@@ -295,13 +312,12 @@ type FileSystemConnector struct { ...@@ -295,13 +312,12 @@ type FileSystemConnector struct {
} }
func (me *FileSystemConnector) Statistics() string { func (me *FileSystemConnector) Statistics() string {
me.treeLock.RLock()
defer me.treeLock.RUnlock()
root := me.rootNode root := me.rootNode
me.inodeMapMutex.Lock()
defer me.inodeMapMutex.Unlock()
return fmt.Sprintf("Mounts %20d\nFiles %20d\nInodes %20d\n", return fmt.Sprintf("Mounts %20d\nFiles %20d\nInodes %20d\n",
root.totalMountCount(), root.TotalMountCount(),
root.totalOpenCount(), len(me.inodeMap)) root.TotalOpenCount(), len(me.inodeMap))
} }
func (me *FileSystemConnector) decodeFileHandle(h uint64) *fileBridge { func (me *FileSystemConnector) decodeFileHandle(h uint64) *fileBridge {
...@@ -328,8 +344,8 @@ func (me *FileSystemConnector) verify() { ...@@ -328,8 +344,8 @@ func (me *FileSystemConnector) verify() {
if !paranoia { if !paranoia {
return return
} }
me.treeLock.Lock() me.inodeMapMutex.Lock()
defer me.treeLock.Unlock() defer me.inodeMapMutex.Unlock()
for k, v := range me.inodeMap { for k, v := range me.inodeMap {
if v.NodeId != k { if v.NodeId != k {
...@@ -360,8 +376,8 @@ func (me *FileSystemConnector) newInode(root bool, isDir bool) *inode { ...@@ -360,8 +376,8 @@ func (me *FileSystemConnector) newInode(root bool, isDir bool) *inode {
func (me *FileSystemConnector) lookupUpdate(parent *inode, name string, isDir bool, lookupCount int) *inode { func (me *FileSystemConnector) lookupUpdate(parent *inode, name string, isDir bool, lookupCount int) *inode {
defer me.verify() defer me.verify()
me.treeLock.Lock() parent.mount.treeLock.Lock()
defer me.treeLock.Unlock() defer parent.mount.treeLock.Unlock()
data, ok := parent.Children[name] data, ok := parent.Children[name]
if !ok { if !ok {
...@@ -384,28 +400,34 @@ func (me *FileSystemConnector) getInodeData(nodeid uint64) *inode { ...@@ -384,28 +400,34 @@ func (me *FileSystemConnector) getInodeData(nodeid uint64) *inode {
func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) { func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) {
defer me.verify() defer me.verify()
me.treeLock.Lock()
defer me.treeLock.Unlock()
data, ok := me.inodeMap[nodeId] node := me.getInodeData(nodeId)
if ok {
data.LookupCount -= forgetCount node.LookupCount -= forgetCount
me.considerDropInode(data) me.considerDropInode(node)
}
} }
func (me *FileSystemConnector) considerDropInode(n *inode) { func (me *FileSystemConnector) considerDropInode(n *inode) {
n.mount.treeLock.Lock()
defer n.mount.treeLock.Unlock()
if n.LookupCount <= 0 && len(n.Children) == 0 && (n.mountPoint == nil || n.mountPoint.unmountPending) && if n.LookupCount <= 0 && len(n.Children) == 0 && (n.mountPoint == nil || n.mountPoint.unmountPending) &&
n.OpenCount <= 0 { n.OpenCount <= 0 {
n.setParent(nil) n.setParent(nil)
me.inodeMapMutex.Lock()
defer me.inodeMapMutex.Unlock()
me.inodeMap[n.NodeId] = nil, false me.inodeMap[n.NodeId] = nil, false
} }
} }
func (me *FileSystemConnector) renameUpdate(oldParent *inode, oldName string, newParent *inode, newName string) { func (me *FileSystemConnector) renameUpdate(oldParent *inode, oldName string, newParent *inode, newName string) {
defer me.verify() defer me.verify()
me.treeLock.Lock() if oldParent.mount != newParent.mount {
defer me.treeLock.Unlock() panic("Cross mount rename")
}
oldParent.mount.treeLock.Lock()
defer oldParent.mount.treeLock.Unlock()
node := oldParent.Children[oldName] node := oldParent.Children[oldName]
if node == nil { if node == nil {
...@@ -423,31 +445,35 @@ func (me *FileSystemConnector) renameUpdate(oldParent *inode, oldName string, ne ...@@ -423,31 +445,35 @@ func (me *FileSystemConnector) renameUpdate(oldParent *inode, oldName string, ne
func (me *FileSystemConnector) unlinkUpdate(parent *inode, name string) { func (me *FileSystemConnector) unlinkUpdate(parent *inode, name string) {
defer me.verify() defer me.verify()
me.treeLock.Lock()
defer me.treeLock.Unlock() parent.mount.treeLock.Lock()
defer parent.mount.treeLock.Unlock()
node := parent.Children[name] node := parent.Children[name]
node.setParent(nil) node.setParent(nil)
node.Name = ".deleted" node.Name = ".deleted"
} }
// Walk the file system starting from the root. // Walk the file system starting from the root. Will return nil if
// node not found.
func (me *FileSystemConnector) findInode(fullPath string) *inode { func (me *FileSystemConnector) findInode(fullPath string) *inode {
fullPath = strings.TrimLeft(filepath.Clean(fullPath), "/") fullPath = strings.TrimLeft(filepath.Clean(fullPath), "/")
comps := strings.Split(fullPath, "/", -1) comps := strings.Split(fullPath, "/", -1)
me.treeLock.RLock()
defer me.treeLock.RUnlock()
node := me.rootNode node := me.rootNode
for i, component := range comps { for _, component := range comps {
if len(component) == 0 { if len(component) == 0 {
continue continue
} }
if node.mountPoint != nil {
node.mountPoint.treeLock.RLock()
defer node.mountPoint.treeLock.RUnlock()
}
node = node.Children[component] node = node.Children[component]
if node == nil { if node == nil {
panic(fmt.Sprintf("findInode: %v %v", i, fullPath)) return nil
} }
} }
return node return node
...@@ -472,14 +498,17 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil ...@@ -472,14 +498,17 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil
if mountPoint != "/" { if mountPoint != "/" {
dirParent, base := filepath.Split(mountPoint) dirParent, base := filepath.Split(mountPoint)
dirParentNode := me.findInode(dirParent) dirParentNode := me.findInode(dirParent)
if dirParentNode == nil {
log.Println("Could not find mountpoint:", mountPoint)
return ENOENT
}
// Make sure we know the mount point. // Make sure we know the mount point.
_, _, node = me.internalLookupWithNode(dirParentNode, base, 0) _, _, node = me.internalLookupWithNode(dirParentNode, base, 0)
} else { } else {
node = me.rootNode node = me.rootNode
} }
if node == nil { if node == nil {
log.Println("Could not find mountpoint?", mountPoint) log.Println("Could not find mountpoint:", mountPoint)
return ENOENT return ENOENT
} }
...@@ -487,12 +516,16 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil ...@@ -487,12 +516,16 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil
return EINVAL return EINVAL
} }
me.treeLock.RLock() if node != me.rootNode {
node.mount.treeLock.RLock()
}
hasChildren := len(node.Children) > 0 hasChildren := len(node.Children) > 0
// don't use defer, as we dont want to hold the lock during // don't use defer, as we don't want to hold the lock during
// fs.Mount(). // fs.Mount().
me.treeLock.RUnlock() if node != me.rootNode {
node.mount.treeLock.RUnlock()
}
if hasChildren { if hasChildren {
return EBUSY return EBUSY
} }
...@@ -519,12 +552,11 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil ...@@ -519,12 +552,11 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil
func (me *FileSystemConnector) Unmount(path string) Status { func (me *FileSystemConnector) Unmount(path string) Status {
node := me.findInode(path) node := me.findInode(path)
if node == nil { if node == nil {
panic(path) log.Println("Could not find mountpoint:", path)
return EINVAL
} }
// Need to lock to look at node.Children // Need to lock to look at node.Children
me.treeLock.RLock()
unmountError := OK unmountError := OK
mount := node.mountPoint mount := node.mountPoint
...@@ -534,7 +566,7 @@ func (me *FileSystemConnector) Unmount(path string) Status { ...@@ -534,7 +566,7 @@ func (me *FileSystemConnector) Unmount(path string) Status {
// don't use defer: we don't want to call out to // don't use defer: we don't want to call out to
// mount.fs.Unmount() with lock held. // mount.fs.Unmount() with lock held.
if unmountError.Ok() && (node.totalOpenCount() > 0 || node.totalMountCount() > 1) { if unmountError.Ok() && (node.TotalOpenCount() > 0 || node.TotalMountCount() > 1) {
unmountError = EBUSY unmountError = EBUSY
} }
...@@ -542,7 +574,6 @@ func (me *FileSystemConnector) Unmount(path string) Status { ...@@ -542,7 +574,6 @@ func (me *FileSystemConnector) Unmount(path string) Status {
// We settle for eventual consistency. // We settle for eventual consistency.
mount.unmountPending = true mount.unmountPending = true
} }
me.treeLock.RUnlock()
if unmountError.Ok() { if unmountError.Ok() {
if me.Debug { if me.Debug {
...@@ -558,8 +589,8 @@ func (me *FileSystemConnector) GetPath(nodeid uint64) (path string, mount *mount ...@@ -558,8 +589,8 @@ func (me *FileSystemConnector) GetPath(nodeid uint64) (path string, mount *mount
n := me.getInodeData(nodeid) n := me.getInodeData(nodeid)
// Need to lock because renames create invalid states. // Need to lock because renames create invalid states.
me.treeLock.RLock() n.mount.treeLock.RLock()
defer me.treeLock.RUnlock() defer n.mount.treeLock.RUnlock()
p, m := n.GetPath() p, m := n.GetPath()
if me.Debug { if me.Debug {
...@@ -575,10 +606,10 @@ func (me *FileSystemConnector) getOpenFileData(nodeid uint64, fh uint64) (f File ...@@ -575,10 +606,10 @@ func (me *FileSystemConnector) getOpenFileData(nodeid uint64, fh uint64) (f File
f, bridge = me.getFile(fh) f, bridge = me.getFile(fh)
m = bridge.mountData m = bridge.mountData
} }
me.treeLock.RLock()
defer me.treeLock.RUnlock()
node := me.getInodeData(nodeid) node := me.getInodeData(nodeid)
node.mount.treeLock.RLock()
defer node.mount.treeLock.RUnlock()
if node.Parent != nil { if node.Parent != nil {
p, m = node.GetPath() p, m = node.GetPath()
} }
......
...@@ -391,11 +391,11 @@ func (me *FileSystemConnector) Flush(input *FlushIn) Status { ...@@ -391,11 +391,11 @@ func (me *FileSystemConnector) Flush(input *FlushIn) Status {
// open could have changed things. // open could have changed things.
var path string var path string
var mount *mountData var mount *mountData
me.treeLock.RLock() b.mount.treeLock.RLock()
if b.inode.Parent != nil { if b.inode.Parent != nil {
path, mount = b.inode.GetPath() path, mount = b.inode.GetPath()
} }
me.treeLock.RUnlock() b.mount.treeLock.RUnlock()
if mount != nil { if mount != nil {
code = mount.fs.Flush(path) code = mount.fs.Flush(path)
...@@ -492,3 +492,4 @@ func (me *FileSystemConnector) Ioctl(header *InHeader, input *IoctlIn) (out *Ioc ...@@ -492,3 +492,4 @@ func (me *FileSystemConnector) Ioctl(header *InHeader, input *IoctlIn) (out *Ioc
} }
return f.Ioctl(input) return f.Ioctl(input)
} }
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