fusego/samples/memfs/fs.go

130 lines
3.2 KiB
Go

// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
package memfs
import (
"fmt"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/gcloud/syncutil"
"github.com/jacobsa/gcsfuse/timeutil"
"golang.org/x/net/context"
)
type memFS struct {
fuseutil.NotImplementedFileSystem
/////////////////////////
// Dependencies
/////////////////////////
clock timeutil.Clock
/////////////////////////
// Mutable state
/////////////////////////
mu syncutil.InvariantMutex
// The collection of all inodes that have ever been created, indexed by inode
// ID. Some inodes are not in use if they have been unlinked, and no inode
// with ID less than fuse.RootInodeID is ever used.
//
// INVARIANT: len(inodes) > fuse.RootInodeID
// INVARIANT: For all i < fuse.RootInodeID, inodes[i].impl == nil
// INVARIANT: inodes[fuse.RootInodeID].impl is of type *memDir
inodes []inode // GUARDED_BY(mu)
// A list of inode IDs within inodes available for reuse, not including the
// reserved IDs less than fuse.RootInodeID.
//
// INVARIANT: This is all and only indices i of inodes such that i >
// fuse.RootInodeID and inodes[i].impl == nil
freeInodes []fuse.InodeID // GUARDED_BY(mu)
}
// Create a file system that stores data and metadata in memory.
func NewMemFS(
clock timeutil.Clock) (fs fuse.FileSystem) {
fs = &memFS{
clock: clock,
}
fs.(*memFS).mu = syncutil.NewInvariantMutex(fs.(*memFS).checkInvariants)
return
}
func (fs *memFS) checkInvariants() {
// Check general inode invariants.
for i := range fs.inodes {
fs.inodes[i].checkInvariants()
}
// Check reserved inodes.
for i := 0; i < fuse.RootInodeID; i++ {
var inode *inode = &fs.inodes[i]
if inode.impl != nil {
panic(fmt.Sprintf("Non-nil impl for ID: %v", i))
}
}
// Check the root inode.
_ = fs.inodes[fuse.RootInodeID].impl.(*memDir)
// Check inodes, building our own set of free IDs.
freeIDsEncountered := make(map[fuse.InodeID]struct{})
for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
var inode *inode = &fs.inodes[i]
if inode.impl == nil {
freeIDsEncountered[fuse.InodeID(i)] = struct{}{}
continue
}
}
// Check fs.freeInodes.
if len(fs.freeInodes) != len(freeIDsEncountered) {
panic(
fmt.Sprintf(
"Length mismatch: %v vs. %v",
len(fs.freeInodes),
len(freeIDsEncountered)))
}
for _, id := range fs.freeInodes {
if _, ok := freeIDsEncountered[id]; !ok {
panic(fmt.Sprintf("Unexected free inode ID: %v", id))
}
}
}
func (fs *memFS) Init(
ctx context.Context,
req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
resp = &fuse.InitResponse{}
return
}
func (fs *memFS) OpenDir(
ctx context.Context,
req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
fs.mu.RLock()
defer fs.mu.RUnlock()
// 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
// cache invalidation, etc.).
if req.Inode >= fuse.InodeID(len(fs.inodes)) {
panic(fmt.Sprintf("Inode out of range: %v vs. %v", req.Inode, len(fs.inodes)))
}
var inode *inode = &fs.inodes[req.Inode]
if inode.impl == nil {
panic(fmt.Sprintf("Dead inode requested: %v", req.Inode))
}
// All is good.
return
}