fusego/samples/memfs/fs.go

339 lines
8.4 KiB
Go
Raw Normal View History

2015-02-27 08:32:01 +03:00
// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
package memfs
import (
2015-03-02 06:22:59 +03:00
"fmt"
2015-03-02 08:03:44 +03:00
"os"
2015-03-02 07:18:23 +03:00
"time"
2015-03-02 06:22:59 +03:00
2015-02-27 08:32:01 +03:00
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/gcloud/syncutil"
"github.com/jacobsa/gcsfuse/timeutil"
2015-03-02 06:14:49 +03:00
"golang.org/x/net/context"
2015-02-27 08:32:01 +03:00
)
type memFS struct {
fuseutil.NotImplementedFileSystem
/////////////////////////
// Dependencies
/////////////////////////
clock timeutil.Clock
/////////////////////////
// Mutable state
/////////////////////////
2015-03-02 07:50:34 +03:00
// When acquiring this lock, the caller must hold no inode locks.
2015-02-27 08:32:01 +03:00
mu syncutil.InvariantMutex
2015-03-02 07:50:34 +03:00
// The collection of live inodes, indexed by ID. IDs of free inodes that may
// be re-used have nil entries. No ID less than fuse.RootInodeID is ever used.
2015-02-27 08:32:01 +03:00
//
// INVARIANT: len(inodes) > fuse.RootInodeID
2015-03-02 07:50:34 +03:00
// INVARIANT: For all i < fuse.RootInodeID, inodes[i] == nil
// INVARIANT: inodes[fuse.RootInodeID] != nil
// INVARIANT: inodes[fuse.RootInodeID].dir is true
inodes []*inode // GUARDED_BY(mu)
2015-02-27 08:32:01 +03:00
// A list of inode IDs within inodes available for reuse, not including the
// reserved IDs less than fuse.RootInodeID.
2015-02-27 08:32:01 +03:00
//
2015-03-02 07:50:34 +03:00
// INVARIANT: This is all and only indices i of 'inodes' such that i >
// fuse.RootInodeID and inodes[i] == nil
freeInodes []fuse.InodeID // GUARDED_BY(mu)
2015-02-27 08:32:01 +03:00
}
2015-02-27 08:33:18 +03:00
// Create a file system that stores data and metadata in memory.
func NewMemFS(
clock timeutil.Clock) fuse.FileSystem {
// Set up the basic struct.
fs := &memFS{
clock: clock,
2015-03-02 07:50:34 +03:00
inodes: make([]*inode, fuse.RootInodeID+1),
2015-03-02 06:14:02 +03:00
}
2015-03-03 03:03:03 +03:00
// Set up the root inode. Its ownership information will later be modified in
// Init.
2015-03-03 01:47:36 +03:00
rootAttrs := fuse.InodeAttributes{
2015-03-03 03:03:03 +03:00
Mode: 0700 | os.ModeDir,
2015-03-03 01:47:36 +03:00
}
fs.inodes[fuse.RootInodeID] = newInode(rootAttrs)
// Set up invariant checking.
fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
return fs
2015-03-02 06:14:02 +03:00
}
2015-03-03 03:03:03 +03:00
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
2015-03-02 06:14:02 +03:00
func (fs *memFS) checkInvariants() {
// Check reserved inodes.
for i := 0; i < fuse.RootInodeID; i++ {
2015-03-02 07:50:34 +03:00
if fs.inodes[i] != nil {
panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
}
}
// Check the root inode.
2015-03-02 07:50:34 +03:00
if !fs.inodes[fuse.RootInodeID].dir {
panic("Expected root to be a directory.")
}
2015-03-02 07:50:34 +03:00
// Build our own list of free IDs.
freeIDsEncountered := make(map[fuse.InodeID]struct{})
2015-03-02 06:37:01 +03:00
for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
2015-03-02 07:50:34 +03:00
inode := fs.inodes[i]
if inode == nil {
2015-03-02 06:37:01 +03:00
freeIDsEncountered[fuse.InodeID(i)] = struct{}{}
continue
}
2015-03-02 06:37:01 +03:00
}
2015-03-02 06:37:01 +03:00
// Check fs.freeInodes.
if len(fs.freeInodes) != len(freeIDsEncountered) {
panic(
fmt.Sprintf(
"Length mismatch: %v vs. %v",
len(fs.freeInodes),
len(freeIDsEncountered)))
}
2015-03-02 06:37:01 +03:00
for _, id := range fs.freeInodes {
if _, ok := freeIDsEncountered[id]; !ok {
panic(fmt.Sprintf("Unexected free inode ID: %v", id))
}
}
2015-03-02 06:00:48 +03:00
}
2015-03-02 06:14:49 +03:00
2015-03-03 01:33:33 +03:00
// Find the given inode and return it with its lock held. Panic if it doesn't
// exist.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// EXCLUSIVE_LOCK_FUNCTION(inode.mu)
func (fs *memFS) getInodeForModifyingOrDie(id fuse.InodeID) (inode *inode) {
inode = fs.inodes[id]
if inode == nil {
panic(fmt.Sprintf("Unknown inode: %v", id))
}
inode.mu.Lock()
return
}
// Find the given inode and return it with its lock held for reading. Panic if
// it doesn't exist.
2015-03-02 06:52:29 +03:00
//
2015-03-02 07:50:34 +03:00
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(inode.mu)
func (fs *memFS) getInodeForReadingOrDie(id fuse.InodeID) (inode *inode) {
inode = fs.inodes[id]
if inode == nil {
panic(fmt.Sprintf("Unknown inode: %v", id))
2015-03-02 06:52:29 +03:00
}
2015-03-02 08:04:21 +03:00
inode.mu.RLock()
2015-03-02 06:52:29 +03:00
return
}
2015-03-03 01:33:33 +03:00
// Allocate a new inode, assigning it an ID that is not in use. Return it with
// its lock held.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
// EXCLUSIVE_LOCK_FUNCTION(inode.mu)
2015-03-03 01:39:01 +03:00
func (fs *memFS) allocateInode(
2015-03-03 01:47:36 +03:00
attrs fuse.InodeAttributes) (id fuse.InodeID, inode *inode) {
2015-03-03 01:39:45 +03:00
// Create and lock the inode.
2015-03-03 01:47:36 +03:00
inode = newInode(attrs)
2015-03-03 01:39:45 +03:00
inode.mu.Lock()
2015-03-03 01:39:01 +03:00
// Re-use a free ID if possible. Otherwise mint a new one.
numFree := len(fs.freeInodes)
if numFree != 0 {
id = fs.freeInodes[numFree-1]
fs.freeInodes = fs.freeInodes[:numFree-1]
fs.inodes[id] = inode
} else {
id = fuse.InodeID(len(fs.inodes))
fs.inodes = append(fs.inodes, inode)
}
return
}
2015-03-03 01:33:33 +03:00
2015-03-03 03:03:03 +03:00
////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////
func (fs *memFS) Init(
ctx context.Context,
req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
resp = &fuse.InitResponse{}
fs.mu.RLock()
defer fs.mu.RUnlock()
// Update the root inode's ownership information to match the credentials of
// the mounting process.
root := fs.getInodeForModifyingOrDie(fuse.RootInodeID)
defer root.mu.Unlock()
root.attributes.Uid = req.Header.Uid
root.attributes.Gid = req.Header.Gid
return
}
2015-03-02 07:18:23 +03:00
func (fs *memFS) LookUpInode(
ctx context.Context,
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
resp = &fuse.LookUpInodeResponse{}
2015-03-02 07:50:34 +03:00
fs.mu.RLock()
defer fs.mu.RUnlock()
2015-03-02 07:18:23 +03:00
// Grab the parent directory.
2015-03-02 07:50:34 +03:00
inode := fs.getInodeForReadingOrDie(req.Parent)
defer inode.mu.RUnlock()
2015-03-02 07:18:23 +03:00
// Does the directory have an entry with the given name?
2015-03-02 07:50:34 +03:00
childID, ok := inode.LookUpChild(req.Name)
2015-03-02 07:18:23 +03:00
if !ok {
err = fuse.ENOENT
return
}
2015-03-02 07:50:34 +03:00
// Grab the child.
child := fs.getInodeForReadingOrDie(childID)
defer child.mu.RUnlock()
2015-03-02 07:18:23 +03:00
// Fill in the response.
resp.Entry.Child = childID
resp.Entry.Attributes = child.attributes
2015-03-02 07:18:23 +03:00
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp.Entry.AttributesExpiration = fs.clock.Now().Add(365 * 24 * time.Hour)
resp.Entry.EntryExpiration = resp.Entry.EntryExpiration
2015-03-02 07:18:23 +03:00
return
}
2015-03-02 07:23:17 +03:00
func (fs *memFS) GetInodeAttributes(
ctx context.Context,
req *fuse.GetInodeAttributesRequest) (
resp *fuse.GetInodeAttributesResponse, err error) {
resp = &fuse.GetInodeAttributesResponse{}
2015-03-02 07:50:34 +03:00
fs.mu.RLock()
defer fs.mu.RUnlock()
// Grab the inode.
inode := fs.getInodeForReadingOrDie(req.Inode)
defer inode.mu.RUnlock()
2015-03-02 07:23:17 +03:00
// Fill in the response.
2015-03-02 07:50:34 +03:00
resp.Attributes = inode.attributes
2015-03-02 07:23:17 +03:00
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp.AttributesExpiration = fs.clock.Now().Add(365 * 24 * time.Hour)
return
}
2015-03-03 01:22:51 +03:00
func (fs *memFS) MkDir(
ctx context.Context,
req *fuse.MkDirRequest) (resp *fuse.MkDirResponse, err error) {
resp = &fuse.MkDirResponse{}
fs.mu.Lock()
defer fs.mu.Unlock()
2015-03-03 01:30:25 +03:00
// Grab the parent, which we will update shortly.
2015-03-03 01:33:33 +03:00
parent := fs.getInodeForModifyingOrDie(req.Parent)
2015-03-03 01:30:25 +03:00
defer parent.mu.Unlock()
2015-03-03 03:04:17 +03:00
// Set up attributes from the child, using the credientials of the calling
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
2015-03-03 01:48:12 +03:00
now := fs.clock.Now()
2015-03-03 01:47:36 +03:00
childAttrs := fuse.InodeAttributes{
2015-03-03 01:48:12 +03:00
Mode: req.Mode,
Atime: now,
Mtime: now,
2015-03-03 02:48:26 +03:00
Ctime: now,
2015-03-03 01:48:12 +03:00
Crtime: now,
2015-03-03 03:04:17 +03:00
Uid: req.Header.Uid,
Gid: req.Header.Gid,
2015-03-03 01:47:36 +03:00
}
2015-03-03 03:04:17 +03:00
// Allocate a child.
2015-03-03 01:47:36 +03:00
childID, child := fs.allocateInode(childAttrs)
2015-03-03 01:30:25 +03:00
defer child.mu.Unlock()
// Add an entry in the parent.
2015-03-03 01:33:33 +03:00
parent.AddChild(childID, req.Name, fuseutil.DT_Directory)
2015-03-03 01:30:25 +03:00
// Fill in the response.
resp.Entry.Child = childID
2015-03-03 01:30:25 +03:00
resp.Entry.Attributes = child.attributes
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp.Entry.AttributesExpiration = fs.clock.Now().Add(365 * 24 * time.Hour)
resp.Entry.EntryExpiration = resp.Entry.EntryExpiration
return
2015-03-03 01:22:51 +03:00
}
2015-03-02 06:22:59 +03:00
func (fs *memFS) OpenDir(
ctx context.Context,
req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
2015-03-02 06:45:14 +03:00
resp = &fuse.OpenDirResponse{}
2015-03-02 07:50:34 +03:00
fs.mu.RLock()
defer fs.mu.RUnlock()
2015-03-02 06:22:59 +03:00
// 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.).
2015-03-02 07:50:34 +03:00
inode := fs.getInodeForReadingOrDie(req.Inode)
defer inode.mu.RUnlock()
if !inode.dir {
panic("Found non-dir.")
}
2015-03-02 06:52:29 +03:00
return
}
2015-03-02 06:22:59 +03:00
2015-03-02 06:52:29 +03:00
func (fs *memFS) ReadDir(
ctx context.Context,
req *fuse.ReadDirRequest) (resp *fuse.ReadDirResponse, err error) {
resp = &fuse.ReadDirResponse{}
2015-03-02 07:50:34 +03:00
fs.mu.RLock()
defer fs.mu.RUnlock()
2015-03-02 06:52:29 +03:00
// Grab the directory.
2015-03-02 07:50:34 +03:00
inode := fs.getInodeForReadingOrDie(req.Inode)
defer inode.mu.RUnlock()
2015-03-02 06:52:29 +03:00
2015-03-02 07:54:56 +03:00
// Serve the request.
resp.Data, err = inode.ReadDir(int(req.Offset), req.Size)
if err != nil {
err = fmt.Errorf("inode.ReadDir: %v", err)
return
2015-03-02 06:22:59 +03:00
}
return
}