Commit 94429a4b authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Split up pathfilesystem.go and pathops.go

parent bad4ee66
......@@ -11,9 +11,12 @@ MANUAL_GOFILES=api.go \
defaultraw.go \
direntry.go\
files.go \
fsmount.go \
fsinode.go \
fsconnector.go \
fuse.go \
handle.go \
inode.go \
latencymap.go \
lockingfs.go \
loggingfs.go \
......@@ -23,8 +26,7 @@ MANUAL_GOFILES=api.go \
mountstate.go \
opcode.go \
pathdebug.go \
pathfilesystem.go \
pathops.go \
fsops.go \
readonlyfs.go \
request.go \
switchfs.go \
......
package fuse
/*
FilesystemConnector is a lowlevel FUSE filesystem that translates
from inode numbers (as delivered by the kernel) to traditional path
names. The paths are then used as arguments for methods of
FileSystem instances.
FileSystemConnector supports mounts of different FileSystems
on top of each other's directories.
General todos:
- We are doing lookups (incurring GetAttr() costs) for internal
lookups (eg. after doing a symlink). We could probably do without
the GetAttr calls.
*/
// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface are in
// fsops.go
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
"unsafe"
)
// openedFile stores either an open dir or an open file.
type openedFile struct {
Handled
*fileSystemMount
*inode
// O_CREAT, O_TRUNC, etc.
OpenFlags uint32
// FOPEN_KEEP_CACHE and friends.
FuseFlags uint32
dir rawDir
file File
}
type fileSystemMount struct {
// If non-nil the file system mounted here.
fs FileSystem
// Node that we were mounted on.
mountInode *inode
options *FileSystemOptions
// Protects parent/child relations within the mount.
// treeLock should be acquired before openFilesLock
treeLock sync.RWMutex
// Manage filehandles of open files.
openFiles HandleMap
}
func (me *fileSystemMount) fileInfoToEntry(fi *os.FileInfo, out *EntryOut) {
SplitNs(me.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec)
SplitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
if !fi.IsDirectory() {
fi.Nlink = 1
}
CopyFileInfo(fi, &out.Attr)
me.setOwner(&out.Attr)
}
func (me *fileSystemMount) fileInfoToAttr(fi *os.FileInfo, out *AttrOut) {
CopyFileInfo(fi, &out.Attr)
SplitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
me.setOwner(&out.Attr)
}
func (me *FileSystemConnector) getOpenedFile(h uint64) *openedFile {
b := (*openedFile)(unsafe.Pointer(DecodeHandle(h)))
return b
}
func (me *fileSystemMount) unregisterFileHandle(handle uint64) *openedFile {
obj := me.openFiles.Forget(handle)
opened := (*openedFile)(unsafe.Pointer(obj))
node := opened.inode
node.OpenFilesMutex.Lock()
defer node.OpenFilesMutex.Unlock()
idx := -1
for i, v := range node.OpenFiles {
if v == opened {
idx = i
break
}
}
l := len(node.OpenFiles)
node.OpenFiles[idx] = node.OpenFiles[l-1]
node.OpenFiles = node.OpenFiles[:l-1]
return opened
}
func (me *fileSystemMount) registerFileHandle(node *inode, dir rawDir, f File, flags uint32) (uint64, *openedFile) {
node.OpenFilesMutex.Lock()
defer node.OpenFilesMutex.Unlock()
b := &openedFile{
dir: dir,
file: f,
inode: node,
fileSystemMount: me,
OpenFlags: flags,
}
withFlags, ok := f.(*WithFlags)
if ok {
b.FuseFlags = withFlags.Flags
f = withFlags.File
}
node.OpenFiles = append(node.OpenFiles, b)
handle := me.openFiles.Register(&b.Handled)
return handle, b
}
////////////////
// Tests should set to true.
var paranoia = false
// The inode reflects the kernel's idea of the inode.
type inode struct {
Handled
// Constant during lifetime.
NodeId uint64
// Number of open files and its protection.
OpenFilesMutex sync.Mutex
OpenFiles []*openedFile
// treeLock is a pointer to me.mount.treeLock; we need store
// this mutex separately, since unmount may set me.mount = nil
// during Unmount(). Constant during lifetime.
//
// If multiple treeLocks must be acquired, the treeLocks
// closer to the root must be acquired first.
treeLock *sync.RWMutex
// All data below is protected by treeLock.
fsInode *fsInode
Children map[string]*inode
// Contains directories that function as mounts. The entries
// are duplicated in Children.
Mounts map[string]*fileSystemMount
LookupCount int
// Non-nil if this is a mountpoint.
mountPoint *fileSystemMount
// The file system to which this node belongs. Is constant
// during the lifetime, except upon Unmount() when it is set
// to nil.
mount *fileSystemMount
}
// Must be called with treeLock for the mount held.
func (me *inode) addChild(name string, child *inode) {
if paranoia {
ch := me.Children[name]
if ch != nil {
panic(fmt.Sprintf("Already have an inode with same name: %v: %v", name, ch))
}
}
me.Children[name] = child
me.fsInode.addChild(name, child.fsInode)
}
// Must be called with treeLock for the mount held.
func (me *inode) rmChild(name string) (ch *inode) {
ch = me.Children[name]
if ch != nil {
me.Children[name] = nil, false
me.fsInode.rmChild(name, ch.fsInode)
}
return ch
}
// Can only be called on untouched inodes.
func (me *inode) mountFs(fs FileSystem, opts *FileSystemOptions) {
me.mountPoint = &fileSystemMount{
fs: fs,
openFiles: NewHandleMap(true),
mountInode: me,
options: opts,
}
me.mount = me.mountPoint
me.treeLock = &me.mountPoint.treeLock
}
// Must be called with treeLock held.
func (me *inode) canUnmount() bool {
for _, v := range me.Children {
if v.mountPoint != nil {
// This access may be out of date, but it is no
// problem to err on the safe side.
return false
}
if !v.canUnmount() {
return false
}
}
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
return len(me.OpenFiles) == 0
}
// Must be called with treeLock held
func (me *inode) recursiveUnmount() {
for _, v := range me.Children {
v.recursiveUnmount()
}
me.mount = nil
}
func (me *inode) IsDir() bool {
return me.Children != nil
}
func (me *inode) getMountDirEntries() (out []DirEntry) {
me.treeLock.RLock()
defer me.treeLock.RUnlock()
for k, _ := range me.Mounts {
out = append(out, DirEntry{
Name: k,
Mode: S_IFDIR,
})
}
return out
}
// Returns any open file, preferably a r/w one.
func (me *inode) getAnyFile() (file File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
if file == nil || f.OpenFlags & O_ANYWRITE != 0 {
file = f.file
}
}
return file
}
// Returns an open writable file for the given inode.
func (me *inode) getWritableFiles() (files []File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
if f.OpenFlags & O_ANYWRITE != 0 {
files = append(files, f.file)
}
}
return files
}
const initDirSize = 20
func (me *inode) verify(cur *fileSystemMount) {
if me.mountPoint != nil {
if me != me.mountPoint.mountInode {
panic("mountpoint mismatch")
}
cur = me.mountPoint
}
if me.mount != cur {
panic(fmt.Sprintf("me.mount not set correctly %v %v", me.mount, cur))
}
for name, m := range me.Mounts {
if m.mountInode != me.Children[name] {
panic(fmt.Sprintf("mountpoint parent mismatch: node:%v name:%v ch:%v",
me.mountPoint, name, me.Children))
}
}
for _, ch := range me.Children {
if ch == nil {
panic("Found nil child.")
}
ch.verify(cur)
}
}
func NewFileSystemOptions() *FileSystemOptions {
return &FileSystemOptions{
NegativeTimeout: 0.0,
......@@ -312,6 +25,7 @@ func NewFileSystemOptions() *FileSystemOptions {
}
}
// FilesystemConnector is a raw FUSE filesystem that manages in-process mounts and inodes.
type FileSystemConnector struct {
DefaultRawFileSystem
......@@ -322,12 +36,17 @@ type FileSystemConnector struct {
rootNode *inode
}
func (me *FileSystemConnector) Init(fsInit *RawFsInit) {
me.fsInit = *fsInit
}
func (me *FileSystemConnector) Statistics() string {
return fmt.Sprintf("Inodes %20d\n", me.inodeMap.Count())
func NewFileSystemConnector(fs FileSystem, opts *FileSystemOptions) (me *FileSystemConnector) {
me = new(FileSystemConnector)
if opts == nil {
opts = NewFileSystemOptions()
}
me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
me.rootNode = me.newInode(true)
me.rootNode.NodeId = FUSE_ROOT_ID
me.verify()
me.mountRoot(fs, opts)
return me
}
func (me *FileSystemConnector) verify() {
......@@ -596,7 +315,6 @@ func (me *FileSystemConnector) Unmount(path string) Status {
return EBUSY
}
mountInode.recursiveUnmount()
mount.mountInode = nil
mountInode.mountPoint = nil
......
package fuse
import (
"log"
"os"
......
package fuse
import (
"os"
"sync"
"unsafe"
)
// openedFile stores either an open dir or an open file.
type openedFile struct {
Handled
// O_CREAT, O_TRUNC, etc.
OpenFlags uint32
// FOPEN_KEEP_CACHE and friends.
FuseFlags uint32
dir rawDir
file File
}
type fileSystemMount struct {
// The file system we mounted here.
fs FileSystem
// Node that we were mounted on.
mountInode *inode
// Options for the mount.
options *FileSystemOptions
// Protects Children hashmaps within the mount. treeLock
// should be acquired before openFilesLock
treeLock sync.RWMutex
// Manage filehandles of open files.
openFiles HandleMap
}
func (me *fileSystemMount) setOwner(attr *Attr) {
if me.options.Owner != nil {
attr.Owner = *me.options.Owner
}
}
func (me *fileSystemMount) fileInfoToEntry(fi *os.FileInfo, out *EntryOut) {
SplitNs(me.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec)
SplitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
if !fi.IsDirectory() {
fi.Nlink = 1
}
CopyFileInfo(fi, &out.Attr)
me.setOwner(&out.Attr)
}
func (me *fileSystemMount) fileInfoToAttr(fi *os.FileInfo, out *AttrOut) {
CopyFileInfo(fi, &out.Attr)
SplitNs(me.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
me.setOwner(&out.Attr)
}
func (me *FileSystemConnector) getOpenedFile(h uint64) *openedFile {
b := (*openedFile)(unsafe.Pointer(DecodeHandle(h)))
return b
}
func (me *fileSystemMount) unregisterFileHandle(handle uint64, node *inode) *openedFile {
obj := me.openFiles.Forget(handle)
opened := (*openedFile)(unsafe.Pointer(obj))
node.OpenFilesMutex.Lock()
defer node.OpenFilesMutex.Unlock()
idx := -1
for i, v := range node.OpenFiles {
if v == opened {
idx = i
break
}
}
l := len(node.OpenFiles)
node.OpenFiles[idx] = node.OpenFiles[l-1]
node.OpenFiles = node.OpenFiles[:l-1]
return opened
}
func (me *fileSystemMount) registerFileHandle(node *inode, dir rawDir, f File, flags uint32) (uint64, *openedFile) {
node.OpenFilesMutex.Lock()
defer node.OpenFilesMutex.Unlock()
b := &openedFile{
dir: dir,
file: f,
OpenFlags: flags,
}
withFlags, ok := f.(*WithFlags)
if ok {
b.FuseFlags = withFlags.Flags
f = withFlags.File
}
node.OpenFiles = append(node.OpenFiles, b)
handle := me.openFiles.Register(&b.Handled)
return handle, b
}
// Translation of raw operation to path based operations.
// FileSystemConnector's implementation of RawFileSystem
package fuse
......@@ -11,39 +11,14 @@ import (
var _ = log.Println
func NewFileSystemConnector(fs FileSystem, opts *FileSystemOptions) (me *FileSystemConnector) {
me = new(FileSystemConnector)
if opts == nil {
opts = NewFileSystemOptions()
}
me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
me.rootNode = me.newInode(true)
me.rootNode.NodeId = FUSE_ROOT_ID
me.verify()
me.mountRoot(fs, opts)
return me
}
func (me *FileSystemConnector) GetPath(nodeid uint64) (path string, mount *fileSystemMount, node *inode) {
n := me.getInodeData(nodeid)
p := n.fsInode.GetPath()
return p, n.mount, n
}
func (me *fileSystemMount) setOwner(attr *Attr) {
if me.options.Owner != nil {
attr.Owner = *me.options.Owner
}
func (me *FileSystemConnector) Init(fsInit *RawFsInit) {
me.fsInit = *fsInit
}
func (me *FileSystemConnector) Lookup(header *InHeader, name string) (out *EntryOut, status Status) {
parent := me.getInodeData(header.NodeId)
return me.internalLookup(parent, name, 1, &header.Context)
}
func (me *FileSystemConnector) internalLookup(parent *inode, name string, lookupCount int, context *Context) (out *EntryOut, status Status) {
out, status, _ = me.internalLookupWithNode(parent, name, lookupCount, context)
out, status, _ = me.internalLookup(parent, name, 1, &header.Context)
return out, status
}
......@@ -66,7 +41,7 @@ func (me *FileSystemConnector) internalMountLookup(mount *fileSystemMount, looku
return out, OK, mount.mountInode
}
func (me *FileSystemConnector) internalLookupWithNode(parent *inode, name string, lookupCount int, context *Context) (out *EntryOut, status Status, node *inode) {
func (me *FileSystemConnector) internalLookup(parent *inode, name string, lookupCount int, context *Context) (out *EntryOut, status Status, node *inode) {
if mount := me.lookupMount(parent, name, lookupCount); mount != nil {
return me.internalMountLookup(mount, lookupCount)
}
......@@ -206,9 +181,9 @@ func (me *FileSystemConnector) Mknod(header *InHeader, input *MknodIn, name stri
n := me.getInodeData(header.NodeId)
code = n.fsInode.Mknod(name, input.Mode, uint32(input.Rdev), &header.Context)
if code.Ok() {
return me.internalLookup(n, name, 1, &header.Context)
out, code, _ = me.internalLookup(n, name, 1, &header.Context)
}
return nil, code
return out, code
}
func (me *FileSystemConnector) Mkdir(header *InHeader, input *MkdirIn, name string) (out *EntryOut, code Status) {
......@@ -216,7 +191,7 @@ func (me *FileSystemConnector) Mkdir(header *InHeader, input *MkdirIn, name stri
code = parent.fsInode.Mkdir(name, input.Mode, &header.Context)
if code.Ok() {
out, code = me.internalLookup(parent, name, 1, &header.Context)
out, code, _ = me.internalLookup(parent, name, 1, &header.Context)
}
return out, code
}
......@@ -245,9 +220,9 @@ func (me *FileSystemConnector) Symlink(header *InHeader, pointedTo string, linkN
parent := me.getInodeData(header.NodeId)
code = parent.fsInode.Symlink(linkName, pointedTo, &header.Context)
if code.Ok() {
return me.internalLookup(parent, linkName, 1, &header.Context)
out, code, _ = me.internalLookup(parent, linkName, 1, &header.Context)
}
return nil, code
return out, code
}
......@@ -283,7 +258,8 @@ func (me *FileSystemConnector) Link(header *InHeader, input *LinkIn, filename st
return nil, code
}
// TODO - revise this for real hardlinks?
return me.internalLookup(parent, filename, 1, &header.Context)
out, code, _ = me.internalLookup(parent, filename, 1, &header.Context)
return out, code
}
func (me *FileSystemConnector) Access(header *InHeader, input *AccessIn) (code Status) {
......@@ -298,7 +274,7 @@ func (me *FileSystemConnector) Create(header *InHeader, input *CreateIn, name st
return 0, 0, nil, code
}
out, code, inode := me.internalLookupWithNode(parent, name, 1, &header.Context)
out, code, inode := me.internalLookup(parent, name, 1, &header.Context)
if inode == nil {
msg := fmt.Sprintf("Create succeded, but GetAttr returned no entry %v, %q. code %v", header.NodeId, name, code)
panic(msg)
......@@ -308,18 +284,19 @@ func (me *FileSystemConnector) Create(header *InHeader, input *CreateIn, name st
}
func (me *FileSystemConnector) Release(header *InHeader, input *ReleaseIn) {
opened := me.getOpenedFile(input.Fh)
opened.inode.mount.unregisterFileHandle(input.Fh)
node := me.getInodeData(header.NodeId)
node.mount.unregisterFileHandle(input.Fh, node)
}
func (me *FileSystemConnector) Flush(header *InHeader, input *FlushIn) Status {
node := me.getInodeData(header.NodeId)
opened := me.getOpenedFile(input.Fh)
return opened.inode.fsInode.Flush(opened.file, opened.OpenFlags, &header.Context)
return node.fsInode.Flush(opened.file, opened.OpenFlags, &header.Context)
}
func (me *FileSystemConnector) ReleaseDir(header *InHeader, input *ReleaseIn) {
node := me.getInodeData(header.NodeId)
opened := node.mount.unregisterFileHandle(input.Fh)
opened := node.mount.unregisterFileHandle(input.Fh, node)
opened.dir.Release()
me.considerDropInode(node)
}
......
package fuse
import (
"fmt"
"sync"
)
// The inode reflects the kernel's idea of the inode.
type inode struct {
Handled
// Constant during lifetime.
NodeId uint64
// Number of open files and its protection.
OpenFilesMutex sync.Mutex
OpenFiles []*openedFile
// treeLock is a pointer to me.mount.treeLock; we need store
// this mutex separately, since unmount may set me.mount = nil
// during Unmount(). Constant during lifetime.
//
// If multiple treeLocks must be acquired, the treeLocks
// closer to the root must be acquired first.
treeLock *sync.RWMutex
// All data below is protected by treeLock.
fsInode *fsInode
Children map[string]*inode
// Contains directories that function as mounts. The entries
// are duplicated in Children.
Mounts map[string]*fileSystemMount
LookupCount int
// Non-nil if this is a mountpoint.
mountPoint *fileSystemMount
// The file system to which this node belongs. Is constant
// during the lifetime, except upon Unmount() when it is set
// to nil.
mount *fileSystemMount
}
// Must be called with treeLock for the mount held.
func (me *inode) addChild(name string, child *inode) {
if paranoia {
ch := me.Children[name]
if ch != nil {
panic(fmt.Sprintf("Already have an inode with same name: %v: %v", name, ch))
}
}
me.Children[name] = child
me.fsInode.addChild(name, child.fsInode)
}
// Must be called with treeLock for the mount held.
func (me *inode) rmChild(name string) (ch *inode) {
ch = me.Children[name]
if ch != nil {
me.Children[name] = nil, false
me.fsInode.rmChild(name, ch.fsInode)
}
return ch
}
// Can only be called on untouched inodes.
func (me *inode) mountFs(fs FileSystem, opts *FileSystemOptions) {
me.mountPoint = &fileSystemMount{
fs: fs,
openFiles: NewHandleMap(true),
mountInode: me,
options: opts,
}
me.mount = me.mountPoint
me.treeLock = &me.mountPoint.treeLock
}
// Must be called with treeLock held.
func (me *inode) canUnmount() bool {
for _, v := range me.Children {
if v.mountPoint != nil {
// This access may be out of date, but it is no
// problem to err on the safe side.
return false
}
if !v.canUnmount() {
return false
}
}
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
return len(me.OpenFiles) == 0
}
func (me *inode) IsDir() bool {
return me.Children != nil
}
func (me *inode) getMountDirEntries() (out []DirEntry) {
me.treeLock.RLock()
defer me.treeLock.RUnlock()
for k, _ := range me.Mounts {
out = append(out, DirEntry{
Name: k,
Mode: S_IFDIR,
})
}
return out
}
// Returns any open file, preferably a r/w one.
func (me *inode) getAnyFile() (file File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
if file == nil || f.OpenFlags & O_ANYWRITE != 0 {
file = f.file
}
}
return file
}
// Returns an open writable file for the given inode.
func (me *inode) getWritableFiles() (files []File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
if f.OpenFlags & O_ANYWRITE != 0 {
files = append(files, f.file)
}
}
return files
}
const initDirSize = 20
func (me *inode) verify(cur *fileSystemMount) {
if me.mountPoint != nil {
if me != me.mountPoint.mountInode {
panic("mountpoint mismatch")
}
cur = me.mountPoint
}
if me.mount != cur {
panic(fmt.Sprintf("me.mount not set correctly %v %v", me.mount, cur))
}
for name, m := range me.Mounts {
if m.mountInode != me.Children[name] {
panic(fmt.Sprintf("mountpoint parent mismatch: node:%v name:%v ch:%v",
me.mountPoint, name, me.Children))
}
}
for _, ch := range me.Children {
if ch == nil {
panic("Found nil child.")
}
ch.verify(cur)
}
}
......@@ -149,11 +149,6 @@ func (me *FileSystemDebug) AddMountState(state *MountState) {
func() []byte { return []byte(state.BufferPoolStats()) })
}
func (me *FileSystemDebug) AddFileSystemConnector(conn *FileSystemConnector) {
me.Add("filesystemconnector-stats",
func() []byte { return []byte(conn.Statistics()) })
}
func hotPaths(timing *TimingFileSystem) []byte {
hot := timing.HotPaths("GetAttr")
unique := len(hot)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment