fusego/samples/forgetfs/forget_fs.go

156 lines
3.8 KiB
Go

// 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
import (
"fmt"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/gcloud/syncutil"
)
// 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
// 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.
//
// 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 non-zero reference counts remaining, after
// unmounting.
func NewFileSystem() (fs *ForgetFS) {
// Set up the actual file system.
impl := &fsImpl{
inodes: map[fuseops.InodeID]*inode{
cannedID_Root: &inode{lookupCount: 1},
cannedID_Foo: &inode{},
cannedID_Bar: &inode{},
},
nextInodeID: cannedID_Next,
}
impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants)
// Set up a wrapper that exposes only certain methods.
fs = &ForgetFS{
impl: impl,
server: fuseutil.NewFileSystemServer(impl),
}
return
}
////////////////////////////////////////////////////////////////////////
// ForgetFS
////////////////////////////////////////////////////////////////////////
type ForgetFS struct {
impl *fsImpl
server fuse.Server
}
func (fs *ForgetFS) ServeOps(c *fuse.Connection) {
fs.server.ServeOps(c)
}
// Panic if there are any inodes that have a non-zero reference count. For use
// after unmounting.
func (fs *ForgetFS) Check() {
fs.impl.Check()
}
////////////////////////////////////////////////////////////////////////
// Actual implementation
////////////////////////////////////////////////////////////////////////
const (
cannedID_Root = fuseops.RootInodeID + iota
cannedID_Foo
cannedID_Bar
cannedID_Next
)
type fsImpl struct {
fuseutil.NotImplementedFileSystem
/////////////////////////
// Mutable state
/////////////////////////
mu syncutil.InvariantMutex
// An index of inode by ID, for all IDs we have issued.
//
// INVARIANT: For each v in inodes, v.lookupCount >= 0
//
// 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
}
type inode struct {
// The current lookup count.
lookupCount int
}
// LOCKS_REQUIRED(fs.mu)
func (fs *fsImpl) checkInvariants() {
// INVARIANT: For each v in inodes, v.lookupCount >= 0
for _, v := range fs.inodes {
if !(v.lookupCount >= 0) {
panic("Negative lookup count")
}
}
// INVARIANT: For each k in inodes, k < nextInodeID
for k, _ := range fs.inodes {
if !(k < fs.nextInodeID) {
panic("Unexpectedly large inode ID")
}
}
}
// 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))
}
}
}
func (fs *fsImpl) Init(
op *fuseops.InitOp) {
var err error
defer fuseutil.RespondToOp(op, &err)
return
}