fusego/samples/forgetfs/forget_fs.go

270 lines
6.2 KiB
Go
Raw 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 (
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"
2015-03-30 08:26:52 +03:00
"github.com/jacobsa/gcloud/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 non-zero reference counts remaining, after
// unmounting.
2015-03-30 08:16:25 +03:00
func NewFileSystem() (fs *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{
lookupCount: 1,
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,
}
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.
2015-03-30 08:20:02 +03:00
fs = &ForgetFS{
impl: impl,
server: fuseutil.NewFileSystemServer(impl),
}
2015-03-30 08:09:25 +03:00
return
}
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.
//
2015-03-30 08:28:02 +03:00
// INVARIANT: For each v in inodes, v.lookupCount >= 0
2015-03-30 08:26:52 +03:00
//
// 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.
lookupCount int
2015-03-31 01:48:50 +03:00
// true if lookupCount has ever been positive.
lookedUp bool
}
func (in *inode) Forgotten() bool {
return in.lookedUp && in.lookupCount <= 0
}
func (in *inode) IncrementLookupCount() {
in.lookupCount++
in.lookedUp = true
2015-03-30 08:26:52 +03:00
}
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 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")
}
}
}
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) (in *inode) {
in = fs.inodes[id]
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
}
////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////
2015-03-30 08:20:42 +03:00
func (fs *fsImpl) Init(
op *fuseops.InitOp) {
var err error
defer fuseutil.RespondToOp(op, &err)
return
}
2015-03-31 01:39:27 +03:00
2015-03-31 01:46:01 +03:00
func (fs *fsImpl) LookUpInode(
op *fuseops.LookUpInodeOp) {
2015-03-31 01:39:27 +03:00
var err error
defer fuseutil.RespondToOp(op, &err)
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:
err = fuse.ENOENT
return
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
}
2015-03-31 01:46:01 +03:00
return
}
func (fs *fsImpl) GetInodeAttributes(
op *fuseops.GetInodeAttributesOp) {
var err error
defer fuseutil.RespondToOp(op, &err)
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
}