Added a FileSystem.Destroy method, called after unmount.

Fixes #9.
geesefs-0-30-9
Aaron Jacobs 2015-06-09 13:29:00 +10:00
commit 6292872c4e
5 changed files with 39 additions and 31 deletions

View File

@ -237,7 +237,7 @@ func (o *SetInodeAttributesOp) toBazilfuseResponse() (bfResp interface{}) {
// revalidating.
//
// In contrast to all other inodes, RootInodeID begins with an implicit
// reference count of one, without a corresponding op to increase it. (There
// lookup count of one, without a corresponding op to increase it. (There
// could be no such op, because the root cannot be referred to by name.) Code
// walk:
//
@ -248,10 +248,10 @@ func (o *SetInodeAttributesOp) toBazilfuseResponse() (bfResp interface{}) {
//
// * (http://goo.gl/vPD9Oh) fuse_iget increments nlookup.
//
// File systems should not make assumptions about whether they will or will not
// receive a ForgetInodeOp for the root inode. Experimentally, OS X seems to
// never send one, while Linux appears to send one only sometimes. (Cf.
// http://goo.gl/EUbxEg, fuse-devel thread "Root inode lookup count").
// File systems should tolerate but not rely on receiving forget ops for
// remaining inodes when the file system unmounts, including the root inode.
// Rather they should take fuse.Connection.ReadOp returning io.EOF as
// implicitly decrementing all lookup counts to zero.
type ForgetInodeOp struct {
commonOp

View File

@ -60,6 +60,11 @@ type FileSystem interface {
FlushFile(*fuseops.FlushFileOp) error
ReleaseFileHandle(*fuseops.ReleaseFileHandleOp) error
ReadSymlink(*fuseops.ReadSymlinkOp) error
// Regard all inodes (including the root inode) as having their lookup counts
// decremented to zero, and clean up any resources associated with the file
// system. No further calls to the file system will be made.
Destroy()
}
// Create a fuse.Server that handles ops by calling the associated FileSystem
@ -85,7 +90,12 @@ type fileSystemServer struct {
}
func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
defer s.opsInFlight.Wait()
// When we are done, we clean up by waiting for all in-flight ops then
// destroying the file system.
defer func() {
s.opsInFlight.Wait()
s.fs.Destroy()
}()
for {
op, err := c.ReadOp()

View File

@ -147,3 +147,6 @@ func (fs *NotImplementedFileSystem) ReadSymlink(
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) Destroy() {
}

View File

@ -27,7 +27,8 @@ import (
// A type that knows how to serve ops read from a connection.
type Server interface {
// Read and serve ops from the supplied connection until EOF. Do not return
// until all operations have been responded to.
// until all operations have been responded to. Must not be called more than
// once.
ServeOps(*Connection)
}

View File

@ -17,7 +17,6 @@ package forgetfs
import (
"fmt"
"os"
"runtime"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
@ -67,6 +66,13 @@ func NewFileSystem() (fs *ForgetFS) {
// The root inode starts with a lookup count of one.
impl.inodes[cannedID_Root].IncrementLookupCount()
// The canned inodes are supposed to be stable from the user's point of view,
// so we should allow them to be looked up at any point even if the kernel
// has balanced its lookups with its forgets. Ensure that they never go to
// zero until the file system is destroyed.
impl.inodes[cannedID_Foo].IncrementLookupCount()
impl.inodes[cannedID_Bar].IncrementLookupCount()
// Set up the mutex.
impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)
@ -165,6 +171,11 @@ func (in *inode) DecrementLookupCount(n uint64) {
in.lookupCount -= n
}
// Decrement the lookup count to zero.
func (in *inode) Destroy() {
in.DecrementLookupCount(in.lookupCount)
}
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
@ -184,30 +195,7 @@ func (fs *fsImpl) Check() {
fs.mu.Lock()
defer fs.mu.Unlock()
// On Linux we often don't receive forget ops, and never receive destroy ops
// (cf. http://goo.gl/EUbxEg, fuse-devel thread "Root inode lookup count").
// So there's not really much we can check here.
//
// TODO(jacobsa): Figure out why we don't receive destroy. If we can reliably
// receive it, we can treat it as "forget all".
if runtime.GOOS == "linux" {
return
}
for k, v := range fs.inodes {
// Special case: we don't require the root inode to have reached zero.
// OS X doesn't seem to send forgets for the root, and Linux only does
// sometimes. But we want to make sure it's not greater than one, which
// would be weird.
if k == fuseops.RootInodeID {
if v.lookupCount > 1 {
panic(fmt.Sprintf("Root has lookup count %v", v.lookupCount))
}
continue
}
// Check other inodes.
if v.lookupCount != 0 {
panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount))
}
@ -383,3 +371,9 @@ func (fs *fsImpl) OpenDir(
return
}
func (fs *fsImpl) Destroy() {
for _, in := range fs.inodes {
in.Destroy()
}
}