Commit d0d764dd authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Move all inode -> path conversions into fsInode.

This further prepares fsInode as an intermediate layer between raw and
path filesystems.
parent 06c1effd
......@@ -171,7 +171,7 @@ type RawFileSystem interface {
Release(header *InHeader, input *ReleaseIn)
Write(*WriteIn, []byte) (written uint32, code Status)
Flush(*FlushIn) Status
Flush(header *InHeader, input *FlushIn) Status
Fsync(*FsyncIn) (code Status)
// Directory handling
......
......@@ -105,7 +105,7 @@ func (me *DefaultRawFileSystem) Write(input *WriteIn, data []byte) (written uint
return 0, ENOSYS
}
func (me *DefaultRawFileSystem) Flush(input *FlushIn) Status {
func (me *DefaultRawFileSystem) Flush(header *InHeader, input *FlushIn) Status {
return OK
}
......
......@@ -120,18 +120,32 @@ func NewFile() *MutableDataFile {
return &MutableDataFile{}
}
func TestFSetAttr(t *testing.T) {
fs := &FSetAttrFs{}
dir := MakeTempDir()
defer os.RemoveAll(dir)
func setupFAttrTest(fs FileSystem) (dir string, clean func()) {
dir = MakeTempDir()
state, _, err := MountFileSystem(dir, fs, nil)
CheckSuccess(err)
state.Debug = true
defer state.Unmount()
go state.Loop(false)
// Trigger INIT.
os.Lstat(dir)
if state.KernelSettings().Flags&CAP_FILE_OPS == 0 {
log.Println("Mount does not support file operations")
}
return dir, func() {
if state.Unmount() == nil {
os.RemoveAll(dir)
}
}
}
func TestFSetAttr(t *testing.T) {
fs := &FSetAttrFs{}
dir, clean := setupFAttrTest(fs)
defer clean()
fn := dir + "/file"
f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
CheckSuccess(err)
......@@ -149,17 +163,6 @@ func TestFSetAttr(t *testing.T) {
t.Error("truncate")
}
if state.KernelSettings().Flags&CAP_FILE_OPS == 0 {
log.Println("Mount does not support file operations")
}
_, err = f.Stat()
CheckSuccess(err)
if !fs.file.GetAttrCalled {
t.Error("Should have called File.GetAttr")
}
err = f.Chmod(024)
CheckSuccess(err)
if fs.file.FileInfo.Mode&07777 != 024 {
......@@ -169,7 +172,8 @@ func TestFSetAttr(t *testing.T) {
err = os.Chtimes(fn, 100e3, 101e3)
CheckSuccess(err)
if fs.file.FileInfo.Atime_ns != 100e3 || fs.file.FileInfo.Mtime_ns != 101e3 {
t.Error("Utimens", fs.file.FileInfo)
t.Errorf("Utimens: atime %d != 100e3 mtime %d != 101e3",
fs.file.FileInfo.Atime_ns, fs.file.FileInfo.Mtime_ns)
}
// TODO - test chown if run as root.
......
package fuse
import (
"log"
"os"
"path/filepath"
)
var _ = log.Println
......@@ -21,13 +23,13 @@ type fsInode struct {
// inode. It returns nil for mount if the file was deleted or the
// filesystem unmounted. This will take the treeLock for the mount,
// so it can not be used in internal methods.
func (me *fsInode) GetPath() (path string, mount *fileSystemMount) {
func (me *fsInode) GetPath() (path string) {
me.inode.treeLock.RLock()
defer me.inode.treeLock.RUnlock()
if me.inode.mount == nil {
// Node from unmounted file system.
return ".deleted", nil
return ".deleted"
}
rev_components := make([]string, 0, 10)
......@@ -36,9 +38,9 @@ func (me *fsInode) GetPath() (path string, mount *fileSystemMount) {
rev_components = append(rev_components, n.Name)
}
if n.mountPoint == nil {
return ".deleted", nil
return ".deleted"
}
return ReverseJoin(rev_components, "/"), n.mountPoint
return ReverseJoin(rev_components, "/")
}
func (me *fsInode) addChild(name string, ch *fsInode) {
......@@ -52,3 +54,171 @@ func (me *fsInode) rmChild(name string, ch *fsInode) {
ch.Name = ".deleted"
ch.Parent = nil
}
////////////////////////////////////////////////////////////////
func (me *fsInode) Readlink(c *Context) ([]byte, Status) {
path := me.GetPath()
val, err := me.inode.mount.fs.Readlink(path, c)
return []byte(val), err
}
func (me *fsInode) Access(mode uint32, context *Context) (code Status) {
p := me.GetPath()
return me.inode.mount.fs.Access(p, mode, context)
}
func (me *fsInode) GetXAttr(attribute string, context *Context) (data []byte, code Status) {
return me.inode.mount.fs.GetXAttr(me.GetPath(), attribute, context)
}
func (me *fsInode) RemoveXAttr(attr string, context *Context) Status {
p := me.GetPath()
return me.inode.mount.fs.RemoveXAttr(p, attr, context)
}
func (me *fsInode) SetXAttr(attr string, data []byte, flags int, context *Context) Status {
return me.inode.mount.fs.SetXAttr(me.GetPath(), attr, data, flags, context)
}
func (me *fsInode) ListXAttr(context *Context) (attrs []string, code Status) {
return me.inode.mount.fs.ListXAttr(me.GetPath(), context)
}
func (me *fsInode) Flush(file File, openFlags uint32, context *Context) (code Status) {
code = file.Flush()
if code.Ok() && openFlags&O_ANYWRITE != 0 {
// We only signal releases to the FS if the
// open could have changed things.
path := me.GetPath()
code = me.inode.mount.fs.Flush(path)
}
return code
}
func (me *fsInode) OpenDir(context *Context) (chan DirEntry, Status) {
return me.inode.mount.fs.OpenDir(me.GetPath(), context)
}
func (me *fsInode) Mknod(name string, mode uint32, dev uint32, context *Context) Status {
p := me.GetPath()
return me.inode.mount.fs.Mknod(filepath.Join(p, name), mode, dev, context)
}
func (me *fsInode) Mkdir(name string, mode uint32, context *Context) (code Status) {
return me.inode.mount.fs.Mkdir(filepath.Join(me.GetPath(), name), mode, context)
}
func (me *fsInode) Unlink(name string, context *Context) (code Status) {
return me.inode.mount.fs.Unlink(filepath.Join(me.GetPath(), name), context)
}
func (me *fsInode) Rmdir(name string, context *Context) (code Status) {
return me.inode.mount.fs.Rmdir(filepath.Join(me.GetPath(), name), context)
}
func (me *fsInode) Symlink(name string, content string, context *Context) (code Status) {
return me.inode.mount.fs.Symlink(content, filepath.Join(me.GetPath(), name), context)
}
func (me *fsInode) Rename(oldName string, newParent *fsInode, newName string, context *Context) (code Status) {
oldPath := filepath.Join(me.GetPath(), oldName)
newPath := filepath.Join(newParent.GetPath(), newName)
return me.inode.mount.fs.Rename(oldPath, newPath, context)
}
func (me *fsInode) Link(name string, existing *fsInode, context *Context) (code Status) {
return me.inode.mount.fs.Link(existing.GetPath(), filepath.Join(me.GetPath(), name), context)
}
func (me *fsInode) Create(name string, flags uint32, mode uint32, context *Context) (file File, code Status) {
fullPath := filepath.Join(me.GetPath(), name)
return me.inode.mount.fs.Create(fullPath, flags, mode, context)
}
func (me *fsInode) Open(flags uint32, context *Context) (file File, code Status) {
return me.inode.mount.fs.Open(me.GetPath(), flags, context)
}
// TODO: should return fsInode.
func (me *fsInode) Lookup(name string) (fi *os.FileInfo, code Status) {
fullPath := filepath.Join(me.GetPath(), name)
return me.inode.mount.fs.GetAttr(fullPath, nil)
}
func (me *fsInode) GetAttr(file File, context *Context) (fi *os.FileInfo, code Status) {
if file == nil && me.Parent == nil && me.inode.mountPoint == nil {
// called on a deleted files.
file = me.inode.getAnyFile()
}
if file != nil {
fi, code = file.GetAttr()
}
if file == nil || code == ENOSYS {
fi, code = me.inode.mount.fs.GetAttr(me.GetPath(), context)
}
if fi != nil && !fi.IsDirectory() {
fi.Nlink = 1
}
return fi, code
}
func (me *fsInode) Chmod(file File, perms uint32, context *Context) (code Status) {
if file == nil {
file = me.inode.getWritableFile()
}
if file != nil {
// TODO - pass context
code = file.Chmod(perms)
}
if file == nil || code == ENOSYS {
code = me.inode.mount.fs.Chmod(me.GetPath(), perms, context)
}
return code
}
func (me *fsInode) Chown(file File, uid uint32, gid uint32, context *Context) (code Status) {
if file != nil {
// TODO - pass context
code = file.Chown(uid, gid)
}
if file == nil || code == ENOSYS {
// TODO - can we get just FATTR_GID but not FATTR_UID ?
code = me.inode.mount.fs.Chown(me.GetPath(), uid, gid, context)
}
return code
}
func (me *fsInode) Truncate(file File, size uint64, context *Context) (code Status) {
if file == nil {
file = me.inode.getWritableFile()
}
if file != nil {
code = file.Truncate(size)
}
if file == nil || code == ENOSYS {
code = me.inode.mount.fs.Truncate(me.GetPath(), size, context)
}
return code
}
func (me *fsInode) Utimens(file File, atime uint64, mtime uint64, context *Context) (code Status) {
if file == nil {
file = me.inode.getWritableFile()
}
if file != nil {
code = file.Utimens(atime, mtime)
}
if file == nil || code == ENOSYS {
code = me.inode.mount.fs.Utimens(me.GetPath(), atime, mtime, context)
}
return code
}
......@@ -279,9 +279,9 @@ func (me *LockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint
return me.RawFileSystem.Write(input, data)
}
func (me *LockingRawFileSystem) Flush(input *FlushIn) Status {
func (me *LockingRawFileSystem) Flush(header *InHeader, input *FlushIn) Status {
defer me.locked()()
return me.RawFileSystem.Flush(input)
return me.RawFileSystem.Flush(header, input)
}
func (me *LockingRawFileSystem) Fsync(input *FsyncIn) (code Status) {
......
......@@ -315,6 +315,7 @@ func TestRename(t *testing.T) {
}
}
// Flaky test, due to rename race condition.
func TestDelRename(t *testing.T) {
me := NewTestCase(t)
defer me.Cleanup()
......@@ -342,7 +343,6 @@ func TestDelRename(t *testing.T) {
err = os.Rename(s, d)
CheckSuccess(err)
}
func TestOverwriteRename(t *testing.T) {
......
......@@ -237,7 +237,7 @@ func doRead(state *MountState, req *request) {
}
func doFlush(state *MountState, req *request) {
req.status = state.fileSystem.Flush((*FlushIn)(req.inData))
req.status = state.fileSystem.Flush(req.inHeader, (*FlushIn)(req.inData))
}
func doRelease(state *MountState, req *request) {
......
......@@ -21,6 +21,7 @@ package fuse
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
......@@ -60,6 +61,24 @@ type fileSystemMount struct {
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
......@@ -230,6 +249,29 @@ func (me *inode) getMountDirEntries() (out []DirEntry) {
return out
}
func (me *inode) getAnyFile() (file File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
return f.file
}
return nil
}
// Returns an open writable file for the given inode.
func (me *inode) getWritableFile() (file File) {
me.OpenFilesMutex.Lock()
defer me.OpenFilesMutex.Unlock()
for _, f := range me.OpenFiles {
if f.OpenFlags & O_ANYWRITE != 0 {
return f.file
}
}
return nil
}
const initDirSize = 20
func (me *inode) verify(cur *fileSystemMount) {
......
This diff is collapsed.
......@@ -146,9 +146,9 @@ func (me *TimingRawFileSystem) Write(input *WriteIn, data []byte) (written uint3
return me.RawFileSystem.Write(input, data)
}
func (me *TimingRawFileSystem) Flush(input *FlushIn) Status {
func (me *TimingRawFileSystem) Flush(header *InHeader, input *FlushIn) Status {
defer me.startTimer("Flush")()
return me.RawFileSystem.Flush(input)
return me.RawFileSystem.Flush(header, input)
}
func (me *TimingRawFileSystem) Fsync(input *FsyncIn) (code Status) {
......
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