From f6e2ba95fd384e5ab47deb288446058c3f97a850 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys <hanwen@google.com> Date: Thu, 10 Mar 2011 19:37:08 -0300 Subject: [PATCH] Fix mount raciness. --- fuse/loopback_test.go | 2 +- fuse/pathfilesystem.go | 61 +++++++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/fuse/loopback_test.go b/fuse/loopback_test.go index 69fde00..8314205 100644 --- a/fuse/loopback_test.go +++ b/fuse/loopback_test.go @@ -559,7 +559,7 @@ func TestRecursiveMount(t *testing.T) { f.Close() - // The close takes some time to propagate through + log.Println("Waiting for kernel to flush file-close to fuse...") time.Sleep(1e9) code = ts.connector.Unmount("/mnt") diff --git a/fuse/pathfilesystem.go b/fuse/pathfilesystem.go index 84ecea2..9e18335 100644 --- a/fuse/pathfilesystem.go +++ b/fuse/pathfilesystem.go @@ -2,23 +2,33 @@ package fuse import ( "bytes" - "sync" "fmt" "log" "path" "strings" + "sync" ) type mountData struct { // If non-nil the file system mounted here. fs PathFilesystem + // Protects the variables below. + mutex sync.RWMutex + // If yes, we are looking to unmount the mounted fs. unmountPending bool - openFiles int - openDirs int - subMounts int + // Count files, dirs and mounts. + openCount int +} + +// TODO - should we call async or not? what is the goroutine creation +// overhead vs. lock acquisition. +func (me *mountData) incOpenCount(delta int) { + me.mutex.Lock() + defer me.mutex.Unlock() + me.openCount += delta } func newMount(fs PathFilesystem) *mountData { @@ -66,12 +76,15 @@ func (me *inodeData) GetPath() (path string, mount *mountData) { if inode == nil { panic("did not find parent with mount") } - - fullPath := strings.Join(components[j:], "/") mount = inode.mount + + mount.mutex.RLock() + defer mount.mutex.RUnlock() if mount.unmountPending { - mount = nil + return "", nil } + + fullPath := strings.Join(components[j:], "/") return fullPath, mount } @@ -193,6 +206,12 @@ func (me *PathFileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) data, ok := me.inodePathMapByInode[nodeId] if ok { data.LookupCount -= forgetCount + + if data.mount != nil { + data.mount.mutex.RLock() + defer data.mount.mutex.RUnlock() + } + if data.LookupCount <= 0 && data.RefCount <= 0 && (data.mount == nil || data.mount.unmountPending) { me.inodePathMap[data.Key()] = nil, false } @@ -344,11 +363,10 @@ func (me *PathFileSystemConnector) Mount(mountPoint string, fs PathFilesystem) S log.Println("Mount: ", fs, "on", mountPoint, node) } - // TODO - this is technically a race-condition? node.mount = newMount(fs) if node.Parent != nil { _, parentMount := node.Parent.GetPath() - parentMount.subMounts++ + parentMount.incOpenCount(1) } return OK @@ -365,7 +383,9 @@ func (me *PathFileSystemConnector) Unmount(path string) Status { panic(path) } - if mount.openFiles+mount.openDirs+mount.subMounts > 0 { + mount.mutex.Lock() + defer mount.mutex.Unlock() + if mount.openCount > 0 { log.Println("busy: ", mount) return EBUSY } @@ -373,7 +393,7 @@ func (me *PathFileSystemConnector) Unmount(path string) Status { if me.Debug { log.Println("Unmount: ", mount) } - // node manipulations are racy? + if node.RefCount > 0 { mount.fs.Unmount() mount.unmountPending = true @@ -383,7 +403,7 @@ func (me *PathFileSystemConnector) Unmount(path string) Status { if node.Parent != nil { _, parentMount := node.Parent.GetPath() - parentMount.subMounts-- + parentMount.incOpenCount(-1) } return OK } @@ -476,8 +496,7 @@ func (me *PathFileSystemConnector) OpenDir(header *InHeader, input *OpenIn) (fla return 0, nil, err } - // TODO - racy? - mount.openDirs++ + mount.incOpenCount(1) de := new(FuseDir) de.connector = me @@ -497,8 +516,7 @@ func (me *PathFileSystemConnector) Open(header *InHeader, input *OpenIn) (flags return 0, nil, err } - // TODO - racy? - mount.openFiles++ + mount.incOpenCount(1) return 0, f, OK } @@ -678,19 +696,24 @@ func (me *PathFileSystemConnector) Create(header *InHeader, input *CreateIn, nam return 0, nil, nil, err } - mount.openFiles++ + mount.incOpenCount(1) + out, code = me.Lookup(header, name) return 0, f, out, code } func (me *PathFileSystemConnector) Release(header *InHeader, f RawFuseFile) { _, mount := me.GetPath(header.NodeId) - mount.openFiles-- + if mount != nil { + mount.incOpenCount(-1) + } } func (me *PathFileSystemConnector) ReleaseDir(header *InHeader, f RawFuseDir) { _, mount := me.GetPath(header.NodeId) - mount.openDirs-- + if mount != nil { + mount.incOpenCount(-1) + } } //////////////////////////////////////////////////////////////// -- 2.30.9