commit
6292872c4e
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -147,3 +147,6 @@ func (fs *NotImplementedFileSystem) ReadSymlink(
|
|||
err = fuse.ENOSYS
|
||||
return
|
||||
}
|
||||
|
||||
func (fs *NotImplementedFileSystem) Destroy() {
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue