fusego/samples/forgetfs/forget_fs.go

386 lines
8.6 KiB
Go
Raw Permalink Normal View History

2015-03-30 08:09:25 +03:00
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package forgetfs
2015-03-30 08:20:02 +03:00
import (
"context"
2015-03-30 08:29:21 +03:00
"fmt"
2015-03-31 01:39:27 +03:00
"os"
2015-03-30 08:29:21 +03:00
2015-03-30 08:20:02 +03:00
"github.com/jacobsa/fuse"
2015-03-30 08:20:42 +03:00
"github.com/jacobsa/fuse/fuseops"
2015-03-30 08:20:02 +03:00
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/syncutil"
2015-03-30 08:20:02 +03:00
)
2015-03-30 08:09:25 +03:00
// Create a file system whose sole contents are a file named "foo" and a
// directory named "bar".
//
// The file "foo" may be opened for reading and/or writing, but reads and
2015-03-30 08:15:13 +03:00
// writes aren't supported. Additionally, any non-existent file or directory
// name may be created within any directory, but the resulting inode will
// appear to have been unlinked immediately.
2015-03-30 08:09:25 +03:00
//
// The file system maintains reference counts for the inodes involved. It will
// panic if a reference count becomes negative or if an inode ID is re-used
// after we expect it to be dead. Its Check method may be used to check that
// there are no inodes with unexpected reference counts remaining, after
2015-03-30 08:09:25 +03:00
// unmounting.
func NewFileSystem() *ForgetFS {
2015-03-30 08:26:52 +03:00
// Set up the actual file system.
impl := &fsImpl{
inodes: map[fuseops.InodeID]*inode{
2015-03-31 01:39:27 +03:00
cannedID_Root: &inode{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0777 | os.ModeDir,
},
},
cannedID_Foo: &inode{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0777,
},
},
cannedID_Bar: &inode{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0777 | os.ModeDir,
},
},
2015-03-30 08:26:52 +03:00
},
nextInodeID: cannedID_Next,
}
2015-03-31 02:15:06 +03:00
// 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()
2015-03-31 02:15:06 +03:00
// Set up the mutex.
2015-03-30 08:26:52 +03:00
impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)
2015-03-30 08:20:02 +03:00
2015-03-30 08:26:52 +03:00
// Set up a wrapper that exposes only certain methods.
return &ForgetFS{
2015-03-30 08:20:02 +03:00
impl: impl,
server: fuseutil.NewFileSystemServer(impl),
}
2015-03-30 08:09:25 +03:00
}
2015-03-30 08:20:02 +03:00
////////////////////////////////////////////////////////////////////////
// ForgetFS
////////////////////////////////////////////////////////////////////////
2015-03-30 08:09:25 +03:00
type ForgetFS struct {
2015-03-30 08:20:02 +03:00
impl *fsImpl
server fuse.Server
2015-03-30 08:17:53 +03:00
}
func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
2015-03-30 08:20:02 +03:00
fs.server.ServeOps(c)
2015-03-30 08:09:25 +03:00
}
// Panic if there are any inodes that have a non-zero reference count. For use
// after unmounting.
func (fs *ForgetFS) Check() {
2015-03-30 08:29:21 +03:00
fs.impl.Check()
2015-03-30 08:09:25 +03:00
}
2015-03-30 08:20:02 +03:00
////////////////////////////////////////////////////////////////////////
// Actual implementation
////////////////////////////////////////////////////////////////////////
2015-03-30 08:26:52 +03:00
const (
cannedID_Root = fuseops.RootInodeID + iota
cannedID_Foo
cannedID_Bar
cannedID_Next
)
2015-03-30 08:20:02 +03:00
type fsImpl struct {
fuseutil.NotImplementedFileSystem
2015-03-30 08:26:52 +03:00
/////////////////////////
// Mutable state
/////////////////////////
mu syncutil.InvariantMutex
// An index of inode by ID, for all IDs we have issued.
//
// GUARDED_BY(mu)
inodes map[fuseops.InodeID]*inode
// The next ID to issue.
//
// INVARIANT: For each k in inodes, k < nextInodeID
//
// GUARDED_BY(mu)
nextInodeID fuseops.InodeID
2015-03-30 08:20:02 +03:00
}
2015-03-30 08:20:42 +03:00
2015-03-31 01:48:50 +03:00
////////////////////////////////////////////////////////////////////////
// inode
////////////////////////////////////////////////////////////////////////
2015-03-30 08:26:52 +03:00
type inode struct {
2015-03-31 01:39:27 +03:00
attributes fuseops.InodeAttributes
2015-03-30 08:26:52 +03:00
// The current lookup count.
2015-03-31 02:01:09 +03:00
lookupCount uint64
2015-03-31 01:48:50 +03:00
// true if lookupCount has ever been positive.
lookedUp bool
}
func (in *inode) Forgotten() bool {
2015-03-31 02:01:09 +03:00
return in.lookedUp && in.lookupCount == 0
2015-03-31 01:48:50 +03:00
}
func (in *inode) IncrementLookupCount() {
in.lookupCount++
in.lookedUp = true
2015-03-30 08:26:52 +03:00
}
2015-03-31 02:01:09 +03:00
func (in *inode) DecrementLookupCount(n uint64) {
if in.lookupCount < n {
panic(fmt.Sprintf(
"Overly large decrement: %v, %v",
in.lookupCount,
n))
}
in.lookupCount -= n
}
2015-06-09 04:10:39 +03:00
// Decrement the lookup count to zero.
func (in *inode) Destroy() {
in.DecrementLookupCount(in.lookupCount)
}
2015-03-31 01:46:01 +03:00
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
2015-03-30 08:26:52 +03:00
// LOCKS_REQUIRED(fs.mu)
2015-03-30 08:28:02 +03:00
func (fs *fsImpl) checkInvariants() {
// INVARIANT: For each k in inodes, k < nextInodeID
for k, _ := range fs.inodes {
if !(k < fs.nextInodeID) {
panic("Unexpectedly large inode ID")
}
}
}
2015-03-30 08:26:52 +03:00
2015-03-30 08:29:21 +03:00
// LOCKS_EXCLUDED(fs.mu)
func (fs *fsImpl) Check() {
fs.mu.Lock()
defer fs.mu.Unlock()
for k, v := range fs.inodes {
if v.lookupCount != 0 {
panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount))
}
}
}
2015-03-31 01:46:01 +03:00
// Look up the inode and verify it hasn't been forgotten.
//
// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) findInodeByID(id fuseops.InodeID) *inode {
in := fs.inodes[id]
2015-03-31 01:46:01 +03:00
if in == nil {
panic(fmt.Sprintf("Unknown inode: %v", id))
}
2015-03-31 01:48:50 +03:00
if in.Forgotten() {
2015-03-31 01:46:01 +03:00
panic(fmt.Sprintf("Forgotten inode: %v", id))
}
return in
2015-03-31 01:46:01 +03:00
}
////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////
2015-09-09 15:55:39 +03:00
func (fs *fsImpl) StatFS(
ctx context.Context,
op *fuseops.StatFSOp) error {
return nil
2015-09-09 15:55:39 +03:00
}
2015-03-31 01:46:01 +03:00
func (fs *fsImpl) LookUpInode(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.LookUpInodeOp) error {
2015-03-31 01:39:27 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
2015-03-31 01:46:01 +03:00
// Make sure the parent exists and has not been forgotten.
_ = fs.findInodeByID(op.Parent)
// Handle the names we support.
var childID fuseops.InodeID
switch {
case op.Parent == cannedID_Root && op.Name == "foo":
childID = cannedID_Foo
case op.Parent == cannedID_Root && op.Name == "bar":
childID = cannedID_Bar
default:
return fuse.ENOENT
2015-03-31 01:39:27 +03:00
}
2015-03-31 01:48:50 +03:00
// Look up the child.
2015-03-31 01:46:01 +03:00
child := fs.findInodeByID(childID)
2015-03-31 01:48:50 +03:00
child.IncrementLookupCount()
2015-03-31 01:46:01 +03:00
// Return an appropriate entry.
op.Entry = fuseops.ChildInodeEntry{
Child: childID,
Attributes: child.attributes,
2015-03-31 01:39:27 +03:00
}
return nil
2015-03-31 01:46:01 +03:00
}
func (fs *fsImpl) GetInodeAttributes(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.GetInodeAttributesOp) error {
2015-03-31 01:46:01 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Find the inode, verifying that it has not been forgotten.
in := fs.findInodeByID(op.Inode)
2015-03-31 01:39:27 +03:00
// Return appropriate attributes.
op.Attributes = in.attributes
return nil
2015-03-31 01:39:27 +03:00
}
2015-03-31 01:50:01 +03:00
2015-03-31 02:01:09 +03:00
func (fs *fsImpl) ForgetInode(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.ForgetInodeOp) error {
2015-03-31 02:01:09 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Find the inode and decrement its count.
in := fs.findInodeByID(op.Inode)
in.DecrementLookupCount(op.N)
return nil
2015-03-31 02:01:09 +03:00
}
2015-03-31 02:15:46 +03:00
func (fs *fsImpl) MkDir(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.MkDirOp) error {
2015-03-31 02:15:46 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Make sure the parent exists and has not been forgotten.
_ = fs.findInodeByID(op.Parent)
// Mint a child inode.
childID := fs.nextInodeID
fs.nextInodeID++
child := &inode{
attributes: fuseops.InodeAttributes{
Nlink: 0,
Mode: 0777 | os.ModeDir,
},
}
fs.inodes[childID] = child
child.IncrementLookupCount()
// Return an appropriate entry.
op.Entry = fuseops.ChildInodeEntry{
Child: childID,
Attributes: child.attributes,
}
return nil
2015-03-31 02:15:46 +03:00
}
2015-03-31 02:15:06 +03:00
func (fs *fsImpl) CreateFile(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.CreateFileOp) error {
2015-03-31 02:15:06 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Make sure the parent exists and has not been forgotten.
_ = fs.findInodeByID(op.Parent)
// Mint a child inode.
childID := fs.nextInodeID
fs.nextInodeID++
child := &inode{
attributes: fuseops.InodeAttributes{
Nlink: 0,
Mode: 0777,
},
}
fs.inodes[childID] = child
child.IncrementLookupCount()
// Return an appropriate entry.
op.Entry = fuseops.ChildInodeEntry{
Child: childID,
Attributes: child.attributes,
}
return nil
2015-03-31 02:15:06 +03:00
}
2015-03-31 01:50:01 +03:00
func (fs *fsImpl) OpenFile(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.OpenFileOp) error {
2015-03-31 01:50:01 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Verify that the inode has not been forgotten.
_ = fs.findInodeByID(op.Inode)
return nil
2015-03-31 01:50:01 +03:00
}
2015-03-31 02:11:00 +03:00
func (fs *fsImpl) OpenDir(
2015-07-27 08:47:45 +03:00
ctx context.Context,
op *fuseops.OpenDirOp) error {
2015-03-31 02:11:00 +03:00
fs.mu.Lock()
defer fs.mu.Unlock()
// Verify that the inode has not been forgotten.
_ = fs.findInodeByID(op.Inode)
return nil
2015-03-31 02:11:00 +03:00
}
2015-06-09 04:10:39 +03:00
func (fs *fsImpl) Destroy() {
for _, in := range fs.inodes {
in.Destroy()
}
}