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

Add MemNodeFs, an in-memory writable file system.

parent 24d412fa
......@@ -21,6 +21,7 @@ MANUAL_GOFILES=api.go \
lockingfs.go \
loopback.go \
misc.go \
memnode.go \
mount.go \
mountstate.go \
opcode.go \
......
package fuse
import (
"log"
"os"
)
var _ = log.Println
type DefaultNodeFileSystem struct {
root DefaultFsNode
}
func (me *DefaultNodeFileSystem) OnUnmount() {
......@@ -19,7 +21,7 @@ func (me *DefaultNodeFileSystem) StatFs() *StatfsOut {
return nil
}
func (me *DefaultNodeFileSystem) Root() FsNode {
return &me.root
return new(DefaultFsNode)
}
////////////////////////////////////////////////////////////////
......@@ -44,7 +46,7 @@ func (me *DefaultFsNode) OnForget() {
}
func (me *DefaultFsNode) Lookup(name string, context *Context) (fi *os.FileInfo, node FsNode, code Status) {
return nil, nil, ENOSYS
return nil, nil, ENOENT
}
func (me *DefaultFsNode) Access(mode uint32, context *Context) (code Status) {
......@@ -92,7 +94,17 @@ func (me *DefaultFsNode) Flush(file File, openFlags uint32, context *Context) (c
}
func (me *DefaultFsNode) OpenDir(context *Context) (chan DirEntry, Status) {
return nil, ENOSYS
ch := me.Inode().Children()
s := make(chan DirEntry, len(ch))
for name, child := range ch {
fi, code := child.FsNode().GetAttr(nil, context)
if code.Ok() {
log.Printf("mode %o", fi.Mode)
s <- DirEntry{Name: name, Mode: fi.Mode}
}
}
close(s)
return s, OK
}
func (me *DefaultFsNode) GetXAttr(attribute string, context *Context) (data []byte, code Status) {
......
package fuse
import (
"fmt"
"log"
"os"
"sync"
"time"
)
var _ = log.Println
type MemNodeFs struct {
DefaultNodeFileSystem
backingStore string
root *memNode
mutex sync.Mutex
nextFree int
}
func (me *MemNodeFs) Root() FsNode {
return me.root
}
func (me *MemNodeFs) newNode() *memNode {
me.mutex.Lock()
defer me.mutex.Unlock()
n := &memNode{
fs: me,
id: me.nextFree,
}
now := time.Nanoseconds()
n.info.Mtime_ns = now
n.info.Atime_ns = now
n.info.Ctime_ns = now
me.nextFree++
return n
}
func NewMemNodeFs(backingStore string) *MemNodeFs {
me := &MemNodeFs{}
me.backingStore = backingStore
me.root = me.newNode()
return me
}
func (me *MemNodeFs) Filename(n *Inode) string {
mn := n.FsNode().(*memNode)
return mn.filename()
}
type memNode struct {
DefaultFsNode
fs *MemNodeFs
id int
regular string
link string
info os.FileInfo
}
func (me *memNode) newNode(isdir bool) *memNode {
n := me.fs.newNode()
me.Inode().NewSynthetic(isdir, n)
return n
}
func (me *memNode) filename() string {
return fmt.Sprintf("%s/%d", me.fs.backingStore, me.id)
}
func (me *memNode) Readlink(c *Context) ([]byte, Status) {
return []byte(me.link), OK
}
func (me *memNode) Mkdir(name string, mode uint32, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
n := me.newNode(true)
n.info.Mode = mode | S_IFDIR
me.Inode().AddChild(name, n.Inode())
return &n.info, n, OK
}
func (me *memNode) Unlink(name string, context *Context) (code Status) {
ch := me.Inode().RmChild(name)
if ch == nil {
return ENOENT
}
return OK
}
func (me *memNode) Rmdir(name string, context *Context) (code Status) {
return me.Unlink(name, context)
}
func (me *memNode) Symlink(name string, content string, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
n := me.newNode(false)
n.info.Mode = S_IFLNK | 0777
n.link = content
me.Inode().AddChild(name, n.Inode())
return &n.info, n, OK
}
func (me *memNode) Rename(oldName string, newParent FsNode, newName string, context *Context) (code Status) {
ch := me.Inode().RmChild(oldName)
newParent.Inode().RmChild(newName)
newParent.Inode().AddChild(newName, ch)
return OK
}
func (me *memNode) Link(name string, existing FsNode, context *Context) (fi *os.FileInfo, newNode FsNode, code Status) {
me.Inode().AddChild(name, existing.Inode())
fi, code = existing.GetAttr(nil, context)
return fi, existing, code
}
func (me *memNode) Create(name string, flags uint32, mode uint32, context *Context) (file File, fi *os.FileInfo, newNode FsNode, code Status) {
n := me.newNode(false)
n.info.Mode = mode | S_IFREG
f, err := os.Create(n.filename())
if err != nil {
return nil, nil, nil, OsErrorToErrno(err)
}
me.Inode().AddChild(name, n.Inode())
return &LoopbackFile{File: f}, &n.info, n, OK
}
func (me *memNode) Open(flags uint32, context *Context) (file File, code Status) {
f, err := os.OpenFile(me.filename(), int(flags), 0666)
if err != nil {
return nil, OsErrorToErrno(err)
}
return &LoopbackFile{File: f}, OK
}
func (me *memNode) GetAttr(file File, context *Context) (fi *os.FileInfo, code Status) {
if me.info.Mode & S_IFREG != 0 {
fi, err := os.Lstat(me.filename())
return fi, OsErrorToErrno(err)
}
return &me.info, OK
}
func (me *memNode) Truncate(file File, size uint64, context *Context) (code Status) {
if file != nil {
return file.Truncate(size)
}
err := os.Truncate(me.filename(), int64(size))
return OsErrorToErrno(err)
}
func (me *memNode) Utimens(file File, atime uint64, mtime uint64, context *Context) (code Status) {
if file != nil {
return file.Utimens(atime, mtime)
}
if me.info.Mode & S_IFREG != 0 {
err := os.Chtimes(me.filename(), int64(atime), int64(mtime))
return OsErrorToErrno(err)
}
me.info.Atime_ns = int64(atime)
me.info.Mtime_ns = int64(mtime)
return OK
}
package fuse
import (
"io/ioutil"
"log"
"os"
"testing"
)
var _ = log.Println
func setupMemNodeTest(t *testing.T) (wd string, fs *MemNodeFs, clean func()) {
tmp, err := ioutil.TempDir("", "go-fuse")
CheckSuccess(err)
back := tmp + "/backing"
os.Mkdir(back, 0700)
fs = NewMemNodeFs(back)
mnt := tmp + "/mnt"
os.Mkdir(mnt, 0700)
connector := NewFileSystemConnector(fs,
&FileSystemOptions{
EntryTimeout: testTtl,
AttrTimeout: testTtl,
NegativeTimeout: 0.0,
})
connector.Debug = true
state := NewMountState(connector)
state.Mount(mnt, nil)
//me.state.Debug = false
state.Debug = true
// Unthreaded, but in background.
go state.Loop()
return mnt, fs, func() {
state.Unmount()
os.RemoveAll(tmp)
}
}
func TestMemNodeFs(t *testing.T) {
wd, _, clean := setupMemNodeTest(t)
defer clean()
err := ioutil.WriteFile(wd + "/test", []byte{42}, 0644)
CheckSuccess(err)
entries, err := ioutil.ReadDir(wd)
log.Println(entries)
if len(entries) != 1 || entries[0].Name != "test" {
t.Fatalf("Readdir got %v, expected 1 file named 'test'", entries)
}
}
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