Refactored fs.go.
parent
e9d3dd9d5c
commit
830f272aab
|
@ -27,22 +27,23 @@ type memFS struct {
|
||||||
// Mutable state
|
// Mutable state
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
|
// When acquiring this lock, the caller must hold no inode locks.
|
||||||
mu syncutil.InvariantMutex
|
mu syncutil.InvariantMutex
|
||||||
|
|
||||||
// The collection of all inodes that have ever been created, indexed by inode
|
// The collection of live inodes, indexed by ID. IDs of free inodes that may
|
||||||
// ID. Some inodes are not in use if they have been unlinked, and no inode
|
// be re-used have nil entries. No ID less than fuse.RootInodeID is ever used.
|
||||||
// with ID less than fuse.RootInodeID is ever used.
|
|
||||||
//
|
//
|
||||||
// INVARIANT: len(inodes) > fuse.RootInodeID
|
// INVARIANT: len(inodes) > fuse.RootInodeID
|
||||||
// INVARIANT: For all i < fuse.RootInodeID, inodes[i].impl == nil
|
// INVARIANT: For all i < fuse.RootInodeID, inodes[i] == nil
|
||||||
// INVARIANT: inodes[fuse.RootInodeID].impl is of type *memDir
|
// INVARIANT: inodes[fuse.RootInodeID] != nil
|
||||||
inodes []inode // GUARDED_BY(mu)
|
// INVARIANT: inodes[fuse.RootInodeID].dir is true
|
||||||
|
inodes []*inode // GUARDED_BY(mu)
|
||||||
|
|
||||||
// A list of inode IDs within inodes available for reuse, not including the
|
// A list of inode IDs within inodes available for reuse, not including the
|
||||||
// reserved IDs less than fuse.RootInodeID.
|
// reserved IDs less than fuse.RootInodeID.
|
||||||
//
|
//
|
||||||
// INVARIANT: This is all and only indices i of inodes such that i >
|
// INVARIANT: This is all and only indices i of 'inodes' such that i >
|
||||||
// fuse.RootInodeID and inodes[i].impl == nil
|
// fuse.RootInodeID and inodes[i] == nil
|
||||||
freeInodes []fuse.InodeID // GUARDED_BY(mu)
|
freeInodes []fuse.InodeID // GUARDED_BY(mu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,11 +53,11 @@ func NewMemFS(
|
||||||
// Set up the basic struct.
|
// Set up the basic struct.
|
||||||
fs := &memFS{
|
fs := &memFS{
|
||||||
clock: clock,
|
clock: clock,
|
||||||
inodes: make([]inode, fuse.RootInodeID+1),
|
inodes: make([]*inode, fuse.RootInodeID+1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the root inode.
|
// Set up the root inode.
|
||||||
fs.inodes[fuse.RootInodeID].impl = newDir()
|
fs.inodes[fuse.RootInodeID] = newInode(true) // dir
|
||||||
|
|
||||||
// Set up invariant checking.
|
// Set up invariant checking.
|
||||||
fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
|
fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
|
||||||
|
@ -65,27 +66,23 @@ func NewMemFS(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *memFS) checkInvariants() {
|
func (fs *memFS) checkInvariants() {
|
||||||
// Check general inode invariants.
|
|
||||||
for i := range fs.inodes {
|
|
||||||
fs.inodes[i].checkInvariants()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check reserved inodes.
|
// Check reserved inodes.
|
||||||
for i := 0; i < fuse.RootInodeID; i++ {
|
for i := 0; i < fuse.RootInodeID; i++ {
|
||||||
var inode *inode = &fs.inodes[i]
|
if fs.inodes[i] != nil {
|
||||||
if inode.impl != nil {
|
panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
|
||||||
panic(fmt.Sprintf("Non-nil impl for ID: %v", i))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the root inode.
|
// Check the root inode.
|
||||||
_ = fs.inodes[fuse.RootInodeID].impl.(*memDir)
|
if !fs.inodes[fuse.RootInodeID].dir {
|
||||||
|
panic("Expected root to be a directory.")
|
||||||
|
}
|
||||||
|
|
||||||
// Check inodes, building our own set of free IDs.
|
// Build our own list of free IDs.
|
||||||
freeIDsEncountered := make(map[fuse.InodeID]struct{})
|
freeIDsEncountered := make(map[fuse.InodeID]struct{})
|
||||||
for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
|
for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
|
||||||
var inode *inode = &fs.inodes[i]
|
inode := fs.inodes[i]
|
||||||
if inode.impl == nil {
|
if inode == nil {
|
||||||
freeIDsEncountered[fuse.InodeID(i)] = struct{}{}
|
freeIDsEncountered[fuse.InodeID(i)] = struct{}{}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -114,31 +111,17 @@ func (fs *memFS) Init(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic if out of range.
|
// Find the supplied inode and return it with its lock held for reading. Panic
|
||||||
|
// if it doesn't exist.
|
||||||
//
|
//
|
||||||
// LOCKS_EXCLUDED(fs.mu)
|
// SHARED_LOCKS_REQUIRED(fs.mu)
|
||||||
func (fs *memFS) getInodeOrDie(inodeID fuse.InodeID) (inode *inode) {
|
// SHARED_LOCK_FUNCTION(inode.mu)
|
||||||
fs.mu.RLock()
|
func (fs *memFS) getInodeForReadingOrDie(id fuse.InodeID) (inode *inode) {
|
||||||
defer fs.mu.RUnlock()
|
inode = fs.inodes[id]
|
||||||
|
if inode == nil {
|
||||||
inode = &fs.inodes[inodeID]
|
panic(fmt.Sprintf("Unknown inode: %v", id))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic if not a live dir.
|
|
||||||
//
|
|
||||||
// LOCKS_EXCLUDED(fs.mu)
|
|
||||||
func (fs *memFS) getDirOrDie(inodeID fuse.InodeID) (d *memDir) {
|
|
||||||
fs.mu.RLock()
|
|
||||||
defer fs.mu.RUnlock()
|
|
||||||
|
|
||||||
if inodeID >= fuse.InodeID(len(fs.inodes)) {
|
|
||||||
panic(fmt.Sprintf("Inode out of range: %v vs. %v", inodeID, len(fs.inodes)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inode *inode = &fs.inodes[inodeID]
|
|
||||||
d = inode.impl.(*memDir)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,21 +130,30 @@ func (fs *memFS) LookUpInode(
|
||||||
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
|
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
|
||||||
resp = &fuse.LookUpInodeResponse{}
|
resp = &fuse.LookUpInodeResponse{}
|
||||||
|
|
||||||
|
fs.mu.RLock()
|
||||||
|
defer fs.mu.RUnlock()
|
||||||
|
|
||||||
// Grab the parent directory.
|
// Grab the parent directory.
|
||||||
d := fs.getDirOrDie(req.Parent)
|
inode := fs.getInodeForReadingOrDie(req.Parent)
|
||||||
|
defer inode.mu.RUnlock()
|
||||||
|
|
||||||
|
if !inode.dir {
|
||||||
|
panic("Found non-dir.")
|
||||||
|
}
|
||||||
|
|
||||||
// Does the directory have an entry with the given name?
|
// Does the directory have an entry with the given name?
|
||||||
childID, ok := d.LookUpInode(req.Name)
|
childID, ok := inode.LookUpChild(req.Name)
|
||||||
if !ok {
|
if !ok {
|
||||||
err = fuse.ENOENT
|
err = fuse.ENOENT
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up the child.
|
// Grab the child.
|
||||||
child := fs.getInodeOrDie(childID)
|
child := fs.getInodeForReadingOrDie(childID)
|
||||||
|
defer child.mu.RUnlock()
|
||||||
|
|
||||||
// Fill in the response.
|
// Fill in the response.
|
||||||
resp.Attributes = child.Attributes()
|
resp.Attributes = child.attributes
|
||||||
|
|
||||||
// We don't spontaneously mutate, so the kernel can cache as long as it wants
|
// We don't spontaneously mutate, so the kernel can cache as long as it wants
|
||||||
// (since it also handles invalidation).
|
// (since it also handles invalidation).
|
||||||
|
@ -177,11 +169,15 @@ func (fs *memFS) GetInodeAttributes(
|
||||||
resp *fuse.GetInodeAttributesResponse, err error) {
|
resp *fuse.GetInodeAttributesResponse, err error) {
|
||||||
resp = &fuse.GetInodeAttributesResponse{}
|
resp = &fuse.GetInodeAttributesResponse{}
|
||||||
|
|
||||||
// Look up the inode.
|
fs.mu.RLock()
|
||||||
inode := fs.getInodeOrDie(req.Inode)
|
defer fs.mu.RUnlock()
|
||||||
|
|
||||||
|
// Grab the inode.
|
||||||
|
inode := fs.getInodeForReadingOrDie(req.Inode)
|
||||||
|
defer inode.mu.RUnlock()
|
||||||
|
|
||||||
// Fill in the response.
|
// Fill in the response.
|
||||||
resp.Attributes = inode.Attributes()
|
resp.Attributes = inode.attributes
|
||||||
|
|
||||||
// We don't spontaneously mutate, so the kernel can cache as long as it wants
|
// We don't spontaneously mutate, so the kernel can cache as long as it wants
|
||||||
// (since it also handles invalidation).
|
// (since it also handles invalidation).
|
||||||
|
@ -195,10 +191,18 @@ func (fs *memFS) OpenDir(
|
||||||
req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
|
req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
|
||||||
resp = &fuse.OpenDirResponse{}
|
resp = &fuse.OpenDirResponse{}
|
||||||
|
|
||||||
|
fs.mu.RLock()
|
||||||
|
defer fs.mu.RUnlock()
|
||||||
|
|
||||||
// We don't mutate spontaneosuly, so if the VFS layer has asked for an
|
// We don't mutate spontaneosuly, so if the VFS layer has asked for an
|
||||||
// inode that doesn't exist, something screwed up earlier (a lookup, a
|
// inode that doesn't exist, something screwed up earlier (a lookup, a
|
||||||
// cache invalidation, etc.).
|
// cache invalidation, etc.).
|
||||||
_ = fs.getDirOrDie(req.Inode)
|
inode := fs.getInodeForReadingOrDie(req.Inode)
|
||||||
|
defer inode.mu.RUnlock()
|
||||||
|
|
||||||
|
if !inode.dir {
|
||||||
|
panic("Found non-dir.")
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -208,15 +212,20 @@ func (fs *memFS) ReadDir(
|
||||||
req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
|
req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
|
||||||
resp = &fuse.ReadDirResponse{}
|
resp = &fuse.ReadDirResponse{}
|
||||||
|
|
||||||
// Grab the directory.
|
fs.mu.RLock()
|
||||||
d := fs.getDirOrDie(req.Inode)
|
defer fs.mu.RUnlock()
|
||||||
|
|
||||||
d.mu.RLock()
|
// Grab the directory.
|
||||||
defer d.mu.RUnlock()
|
inode := fs.getInodeForReadingOrDie(req.Inode)
|
||||||
|
defer inode.mu.RUnlock()
|
||||||
|
|
||||||
|
if !inode.dir {
|
||||||
|
panic("Found non-dir.")
|
||||||
|
}
|
||||||
|
|
||||||
// Return the entries requested.
|
// Return the entries requested.
|
||||||
for i := int(req.Offset); i < len(d.entries); i++ {
|
for i := int(req.Offset); i < len(inode.entries); i++ {
|
||||||
resp.Data = fuseutil.AppendDirent(resp.Data, d.entries[i])
|
resp.Data = fuseutil.AppendDirent(resp.Data, inode.entries[i])
|
||||||
|
|
||||||
// Trim and stop early if we've exceeded the requested size.
|
// Trim and stop early if we've exceeded the requested size.
|
||||||
if len(resp.Data) > req.Size {
|
if len(resp.Data) > req.Size {
|
||||||
|
|
|
@ -58,3 +58,9 @@ type inode struct {
|
||||||
func newInode(dir bool) (inode *inode)
|
func newInode(dir bool) (inode *inode)
|
||||||
|
|
||||||
func (inode *inode) checkInvariants()
|
func (inode *inode) checkInvariants()
|
||||||
|
|
||||||
|
// Find an entry for the given child name and return its inode ID.
|
||||||
|
//
|
||||||
|
// REQUIRES: inode.dir
|
||||||
|
// SHARED_LOCKS_REQUIRED(inode.mu)
|
||||||
|
func (inode *inode) LookUpChild(name string) (id fuse.InodeID, ok bool)
|
||||||
|
|
Loading…
Reference in New Issue