Commit 8acbd4b0 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add inode notify.

This can be used to advise the kernel of updated attributes or data of
existing data; it cannot be used to remove negative entries.
parent bee7c066
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
// Types for users to implement. // Types for users to implement.
// A filesystem API that uses paths rather than inodes. A minimal // A filesystem API that uses paths rather than inodes. A minimal
// file system should have at least a functional GetAttr method. // file system should have at least a functional GetAttr method.
// Typically, each call happens in its own goroutine, so take care to // Typically, each call happens in its own goroutine, so take care to
...@@ -161,7 +160,18 @@ type RawFileSystem interface { ...@@ -161,7 +160,18 @@ type RawFileSystem interface {
// //
Ioctl(header *InHeader, input *IoctlIn) (output *IoctlOut, data []byte, code Status) Ioctl(header *InHeader, input *IoctlIn) (output *IoctlOut, data []byte, code Status)
// Provide callbacks for pushing notifications to the kernel.
Init(params *RawFsInit)
} }
// DefaultRawFileSystem returns ENOSYS for every operation. // DefaultRawFileSystem returns ENOSYS for every operation.
type DefaultRawFileSystem struct{} type DefaultRawFileSystem struct{}
// Talk back to FUSE.
//
// TODO - implement EntryNotify. Currently, EntryNotify causes a
// kernel error.
type RawFsInit struct {
InodeNotify func(*NotifyInvalInodeOut) Status
}
...@@ -9,6 +9,9 @@ import ( ...@@ -9,6 +9,9 @@ import (
var _ = log.Println var _ = log.Println
var _ = fmt.Println var _ = fmt.Println
func (me *DefaultRawFileSystem) Init(init *RawFsInit) {
}
func (me *DefaultRawFileSystem) Lookup(h *InHeader, name string) (out *EntryOut, code Status) { func (me *DefaultRawFileSystem) Lookup(h *InHeader, name string) (out *EntryOut, code Status) {
return nil, ENOSYS return nil, ENOSYS
} }
......
...@@ -14,10 +14,14 @@ import ( ...@@ -14,10 +14,14 @@ import (
"io/ioutil" "io/ioutil"
) )
func (code Status) String() string { func (code Status) String() string {
if code.Ok() { if code <= 0 {
return "OK" return []string{
"OK",
"NOTIFY_POLL",
"NOTIFY_INVAL_INODE",
"NOTIFY_INVAL_ENTRY",
}[-code]
} }
return fmt.Sprintf("%d=%v", int(code), os.Errno(code)) return fmt.Sprintf("%d=%v", int(code), os.Errno(code))
} }
...@@ -82,7 +86,6 @@ func CopyFileInfo(fi *os.FileInfo, attr *Attr) { ...@@ -82,7 +86,6 @@ func CopyFileInfo(fi *os.FileInfo, attr *Attr) {
attr.Blksize = uint32(fi.Blksize) attr.Blksize = uint32(fi.Blksize)
} }
func writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, errno int) { func writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, errno int) {
n1, _, e1 := syscall.Syscall( n1, _, e1 := syscall.Syscall(
syscall.SYS_WRITEV, syscall.SYS_WRITEV,
...@@ -127,7 +130,6 @@ func ModeToType(mode uint32) uint32 { ...@@ -127,7 +130,6 @@ func ModeToType(mode uint32) uint32 {
return (mode & 0170000) >> 12 return (mode & 0170000) >> 12
} }
func CheckSuccess(e os.Error) { func CheckSuccess(e os.Error) {
if e != nil { if e != nil {
panic(fmt.Sprintf("Unexpected error: %v", e)) panic(fmt.Sprintf("Unexpected error: %v", e))
...@@ -174,4 +176,3 @@ func CurrentOwner() *Owner { ...@@ -174,4 +176,3 @@ func CurrentOwner() *Owner {
Gid: uint32(os.Getgid()), Gid: uint32(os.Getgid()),
} }
} }
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"os" "os"
"syscall" "syscall"
"time" "time"
"unsafe"
) )
const ( const (
...@@ -63,6 +64,12 @@ func (me *MountState) Mount(mountPoint string, opts *MountOptions) os.Error { ...@@ -63,6 +64,12 @@ func (me *MountState) Mount(mountPoint string, opts *MountOptions) os.Error {
if err != nil { if err != nil {
return err return err
} }
initParams := RawFsInit{
InodeNotify: func(n *NotifyInvalInodeOut) Status {
return me.writeInodeNotify(n)
},
}
me.fileSystem.Init(&initParams)
me.mountPoint = mp me.mountPoint = mp
me.mountFile = file me.mountFile = file
return nil return nil
...@@ -212,15 +219,19 @@ func (me *MountState) handleRequest(req *request) { ...@@ -212,15 +219,19 @@ func (me *MountState) handleRequest(req *request) {
req.handler.Func(me, req) req.handler.Func(me, req)
} }
me.write(req) errNo := me.write(req)
if errNo != 0 {
log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v",
req.outHeaderBytes, errNo, operationName(req.inHeader.opcode))
}
} }
func (me *MountState) write(req *request) { func (me *MountState) write(req *request) Status {
// If we try to write OK, nil, we will get // If we try to write OK, nil, we will get
// error: writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]] // error: writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
// failed, err: writev: no such file or directory // failed, err: writev: no such file or directory
if req.inHeader.opcode == _OP_FORGET { if req.inHeader.opcode == _OP_FORGET {
return return OK
} }
req.serialize() req.serialize()
...@@ -233,7 +244,7 @@ func (me *MountState) write(req *request) { ...@@ -233,7 +244,7 @@ func (me *MountState) write(req *request) {
} }
if req.outHeaderBytes == nil { if req.outHeaderBytes == nil {
return return OK
} }
var err os.Error var err os.Error
...@@ -244,8 +255,23 @@ func (me *MountState) write(req *request) { ...@@ -244,8 +255,23 @@ func (me *MountState) write(req *request) {
[][]byte{req.outHeaderBytes, req.flatData}) [][]byte{req.outHeaderBytes, req.flatData})
} }
if err != nil { return OsErrorToErrno(err)
log.Printf("writer: Write/Writev %v failed, err: %v. opcode: %v", }
req.outHeaderBytes, err, operationName(req.inHeader.opcode))
func (me *MountState) writeInodeNotify(entry *NotifyInvalInodeOut) Status {
req := request{
inHeader: &InHeader{
opcode: _OP_NOTIFY_INODE,
},
handler: operationHandlers[_OP_NOTIFY_INODE],
status: NOTIFY_INVAL_INODE,
}
req.outData = unsafe.Pointer(entry)
req.serialize()
result := me.write(&req)
if me.Debug {
log.Println("Response: INODE_NOTIFY", result)
} }
return result
} }
package fuse
import (
"log"
"os"
"testing"
)
var _ = log.Println
type NotifyFs struct {
DefaultFileSystem
size int64
exist bool
}
func (me *NotifyFs) GetAttr(name string) (*os.FileInfo, Status) {
if name == "file" || (name == "dir/file" && me.exist) {
return &os.FileInfo{Mode: S_IFREG | 0644, Size: me.size}, OK
}
if name == "dir" {
return &os.FileInfo{Mode: S_IFDIR | 0755}, OK
}
return nil, ENOENT
}
type NotifyTest struct {
fs *NotifyFs
connector *FileSystemConnector
dir string
state *MountState
}
func NewNotifyTest() *NotifyTest {
me := &NotifyTest{}
me.fs = &NotifyFs{}
me.dir = MakeTempDir()
entryTtl := 0.1
opts := &FileSystemOptions{
EntryTimeout: entryTtl,
AttrTimeout: entryTtl,
NegativeTimeout: entryTtl,
}
var err os.Error
me.state, me.connector, err = MountFileSystem(me.dir, me.fs, opts)
CheckSuccess(err)
me.state.Debug = true
go me.state.Loop(false)
return me
}
func (me *NotifyTest) Clean() {
err := me.state.Unmount()
if err == nil {
os.RemoveAll(me.dir)
}
}
func TestInodeNotify(t *testing.T) {
test := NewNotifyTest()
defer test.Clean()
fs := test.fs
dir := test.dir
fs.size = 42
fi, err := os.Lstat(dir + "/file")
CheckSuccess(err)
if !fi.IsRegular() || fi.Size != 42 {
t.Error(fi)
}
fs.size = 666
fi, err = os.Lstat(dir + "/file")
CheckSuccess(err)
if !fi.IsRegular() || fi.Size == 666 {
t.Error(fi)
}
code := test.connector.FileNotify("file", -1, 0)
if !code.Ok() {
t.Error(code)
}
fi, err = os.Lstat(dir + "/file")
CheckSuccess(err)
if !fi.IsRegular() || fi.Size != 666 {
t.Error(fi)
}
}
func TestInodeNotifyRemoval(t *testing.T) {
test := NewNotifyTest()
defer test.Clean()
fs := test.fs
dir := test.dir
fs.exist = true
fi, err := os.Lstat(dir + "/dir/file")
CheckSuccess(err)
if !fi.IsRegular() {
t.Error("IsRegular", fi)
}
fs.exist = false
fi, err = os.Lstat(dir + "/dir/file")
CheckSuccess(err)
code := test.connector.FileNotify("dir/file", -1, 0)
if !code.Ok() {
t.Error(code)
}
fi, err = os.Lstat(dir + "/dir/file")
if fi != nil {
t.Error("should have been removed", fi)
}
}
...@@ -52,9 +52,12 @@ const ( ...@@ -52,9 +52,12 @@ const (
_OP_IOCTL = opcode(39) _OP_IOCTL = opcode(39)
_OP_POLL = opcode(40) _OP_POLL = opcode(40)
_OPCODE_COUNT = opcode(41) // Ugh - what will happen if FUSE introduces a new opcode here?
) _OP_NOTIFY_ENTRY = opcode(51)
_OP_NOTIFY_INODE = opcode(52)
_OPCODE_COUNT = opcode(53)
)
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -121,7 +124,6 @@ func doCreate(state *MountState, req *request) { ...@@ -121,7 +124,6 @@ func doCreate(state *MountState, req *request) {
} }
} }
func doReadDir(state *MountState, req *request) { func doReadDir(state *MountState, req *request) {
entries, code := state.fileSystem.ReadDir(req.inHeader, (*ReadIn)(req.inData)) entries, code := state.fileSystem.ReadDir(req.inHeader, (*ReadIn)(req.inData))
if entries != nil { if entries != nil {
...@@ -130,7 +132,6 @@ func doReadDir(state *MountState, req *request) { ...@@ -130,7 +132,6 @@ func doReadDir(state *MountState, req *request) {
req.status = code req.status = code
} }
func doOpenDir(state *MountState, req *request) { func doOpenDir(state *MountState, req *request) {
flags, handle, status := state.fileSystem.OpenDir(req.inHeader, (*OpenIn)(req.inData)) flags, handle, status := state.fileSystem.OpenDir(req.inHeader, (*OpenIn)(req.inData))
req.status = status req.status = status
...@@ -157,7 +158,6 @@ func doWrite(state *MountState, req *request) { ...@@ -157,7 +158,6 @@ func doWrite(state *MountState, req *request) {
req.status = status req.status = status
} }
func doGetXAttr(state *MountState, req *request) { func doGetXAttr(state *MountState, req *request) {
input := (*GetXAttrIn)(req.inData) input := (*GetXAttrIn)(req.inData)
var data []byte var data []byte
...@@ -361,67 +361,72 @@ func init() { ...@@ -361,67 +361,72 @@ func init() {
} }
for op, sz := range map[opcode]uintptr{ for op, sz := range map[opcode]uintptr{
_OP_LOOKUP: unsafe.Sizeof(EntryOut{}), _OP_LOOKUP: unsafe.Sizeof(EntryOut{}),
_OP_GETATTR: unsafe.Sizeof(AttrOut{}), _OP_GETATTR: unsafe.Sizeof(AttrOut{}),
_OP_SETATTR: unsafe.Sizeof(AttrOut{}), _OP_SETATTR: unsafe.Sizeof(AttrOut{}),
_OP_SYMLINK: unsafe.Sizeof(EntryOut{}), _OP_SYMLINK: unsafe.Sizeof(EntryOut{}),
_OP_MKNOD: unsafe.Sizeof(EntryOut{}), _OP_MKNOD: unsafe.Sizeof(EntryOut{}),
_OP_MKDIR: unsafe.Sizeof(EntryOut{}), _OP_MKDIR: unsafe.Sizeof(EntryOut{}),
_OP_LINK: unsafe.Sizeof(EntryOut{}), _OP_LINK: unsafe.Sizeof(EntryOut{}),
_OP_OPEN: unsafe.Sizeof(OpenOut{}), _OP_OPEN: unsafe.Sizeof(OpenOut{}),
_OP_WRITE: unsafe.Sizeof(WriteOut{}), _OP_WRITE: unsafe.Sizeof(WriteOut{}),
_OP_STATFS: unsafe.Sizeof(StatfsOut{}), _OP_STATFS: unsafe.Sizeof(StatfsOut{}),
_OP_GETXATTR: unsafe.Sizeof(GetXAttrOut{}), _OP_GETXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}), _OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_INIT: unsafe.Sizeof(InitOut{}), _OP_INIT: unsafe.Sizeof(InitOut{}),
_OP_OPENDIR: unsafe.Sizeof(OpenOut{}), _OP_OPENDIR: unsafe.Sizeof(OpenOut{}),
_OP_CREATE: unsafe.Sizeof(CreateOut{}), _OP_CREATE: unsafe.Sizeof(CreateOut{}),
_OP_BMAP: unsafe.Sizeof(BmapOut{}), _OP_BMAP: unsafe.Sizeof(BmapOut{}),
_OP_IOCTL: unsafe.Sizeof(IoctlOut{}), _OP_IOCTL: unsafe.Sizeof(IoctlOut{}),
_OP_POLL: unsafe.Sizeof(PollOut{}), _OP_POLL: unsafe.Sizeof(PollOut{}),
_OP_NOTIFY_ENTRY: unsafe.Sizeof(NotifyInvalEntryOut{}),
_OP_NOTIFY_INODE: unsafe.Sizeof(NotifyInvalInodeOut{}),
} { } {
operationHandlers[op].OutputSize = sz operationHandlers[op].OutputSize = sz
} }
for op, v := range map[opcode]string{ for op, v := range map[opcode]string{
_OP_LOOKUP: "LOOKUP", _OP_LOOKUP: "LOOKUP",
_OP_FORGET: "FORGET", _OP_FORGET: "FORGET",
_OP_GETATTR: "GETATTR", _OP_GETATTR: "GETATTR",
_OP_SETATTR: "SETATTR", _OP_SETATTR: "SETATTR",
_OP_READLINK: "READLINK", _OP_READLINK: "READLINK",
_OP_SYMLINK: "SYMLINK", _OP_SYMLINK: "SYMLINK",
_OP_MKNOD: "MKNOD", _OP_MKNOD: "MKNOD",
_OP_MKDIR: "MKDIR", _OP_MKDIR: "MKDIR",
_OP_UNLINK: "UNLINK", _OP_UNLINK: "UNLINK",
_OP_RMDIR: "RMDIR", _OP_RMDIR: "RMDIR",
_OP_RENAME: "RENAME", _OP_RENAME: "RENAME",
_OP_LINK: "LINK", _OP_LINK: "LINK",
_OP_OPEN: "OPEN", _OP_OPEN: "OPEN",
_OP_READ: "READ", _OP_READ: "READ",
_OP_WRITE: "WRITE", _OP_WRITE: "WRITE",
_OP_STATFS: "STATFS", _OP_STATFS: "STATFS",
_OP_RELEASE: "RELEASE", _OP_RELEASE: "RELEASE",
_OP_FSYNC: "FSYNC", _OP_FSYNC: "FSYNC",
_OP_SETXATTR: "SETXATTR", _OP_SETXATTR: "SETXATTR",
_OP_GETXATTR: "GETXATTR", _OP_GETXATTR: "GETXATTR",
_OP_LISTXATTR: "LISTXATTR", _OP_LISTXATTR: "LISTXATTR",
_OP_REMOVEXATTR: "REMOVEXATTR", _OP_REMOVEXATTR: "REMOVEXATTR",
_OP_FLUSH: "FLUSH", _OP_FLUSH: "FLUSH",
_OP_INIT: "INIT", _OP_INIT: "INIT",
_OP_OPENDIR: "OPENDIR", _OP_OPENDIR: "OPENDIR",
_OP_READDIR: "READDIR", _OP_READDIR: "READDIR",
_OP_RELEASEDIR: "RELEASEDIR", _OP_RELEASEDIR: "RELEASEDIR",
_OP_FSYNCDIR: "FSYNCDIR", _OP_FSYNCDIR: "FSYNCDIR",
_OP_GETLK: "GETLK", _OP_GETLK: "GETLK",
_OP_SETLK: "SETLK", _OP_SETLK: "SETLK",
_OP_SETLKW: "SETLKW", _OP_SETLKW: "SETLKW",
_OP_ACCESS: "ACCESS", _OP_ACCESS: "ACCESS",
_OP_CREATE: "CREATE", _OP_CREATE: "CREATE",
_OP_INTERRUPT: "INTERRUPT", _OP_INTERRUPT: "INTERRUPT",
_OP_BMAP: "BMAP", _OP_BMAP: "BMAP",
_OP_DESTROY: "DESTROY", _OP_DESTROY: "DESTROY",
_OP_IOCTL: "IOCTL", _OP_IOCTL: "IOCTL",
_OP_POLL: "POLL"} { _OP_POLL: "POLL",
_OP_NOTIFY_ENTRY: "NOTIFY_ENTRY",
_OP_NOTIFY_INODE: "NOTIFY_INODE",
} {
operationHandlers[op].Name = v operationHandlers[op].Name = v
} }
...@@ -461,13 +466,15 @@ func init() { ...@@ -461,13 +466,15 @@ func init() {
// Outputs. // Outputs.
for op, f := range map[opcode]castPointerFunc{ for op, f := range map[opcode]castPointerFunc{
_OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) }, _OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) }, _OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) }, _OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) },
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) }, _OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) }, _OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_NOTIFY_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
_OP_NOTIFY_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) },
} { } {
operationHandlers[op].DecodeOut = f operationHandlers[op].DecodeOut = f
} }
......
...@@ -306,10 +306,15 @@ type FileSystemConnector struct { ...@@ -306,10 +306,15 @@ type FileSystemConnector struct {
Debug bool Debug bool
fsInit RawFsInit
inodeMap HandleMap inodeMap HandleMap
rootNode *inode rootNode *inode
} }
func (me *FileSystemConnector) Init(fsInit *RawFsInit) {
me.fsInit = *fsInit
}
func (me *FileSystemConnector) Statistics() string { func (me *FileSystemConnector) Statistics() string {
return fmt.Sprintf("Inodes %20d\n", me.inodeMap.Count()) return fmt.Sprintf("Inodes %20d\n", me.inodeMap.Count())
} }
...@@ -630,3 +635,17 @@ func (me *FileSystemConnector) getOpenFileData(nodeid uint64, fh uint64) (f File ...@@ -630,3 +635,17 @@ func (me *FileSystemConnector) getOpenFileData(nodeid uint64, fh uint64) (f File
} }
return return
} }
func (me *FileSystemConnector) FileNotify(path string, off int64, length int64) Status {
node := me.findInode(path)
if node == nil {
return ENOENT
}
out := NotifyInvalInodeOut{
Length: length,
Off: off,
Ino: node.NodeId,
}
return me.fsInit.InodeNotify(&out)
}
...@@ -125,7 +125,7 @@ func (me *request) parse() { ...@@ -125,7 +125,7 @@ func (me *request) parse() {
func (me *request) serialize() { func (me *request) serialize() {
dataLength := me.handler.OutputSize dataLength := me.handler.OutputSize
if me.outData == nil || me.status != OK { if me.outData == nil || me.status > OK {
dataLength = 0 dataLength = 0
} }
......
...@@ -54,14 +54,13 @@ const ( ...@@ -54,14 +54,13 @@ const (
EXDEV = Status(syscall.EXDEV) EXDEV = Status(syscall.EXDEV)
) )
type NotifyCode int type NotifyCode int
const ( const (
FUSE_NOTIFY_POLL = 1 NOTIFY_POLL = -1
FUSE_NOTIFY_INVAL_INODE = 2 NOTIFY_INVAL_INODE = -2
FUSE_NOTIFY_INVAL_ENTRY = 3 NOTIFY_INVAL_ENTRY = -3
FUSE_NOTIFY_CODE_MAX = 4 NOTIFY_CODE_MAX = -4
) )
type Attr struct { type Attr struct {
...@@ -164,7 +163,6 @@ type LinkIn struct { ...@@ -164,7 +163,6 @@ type LinkIn struct {
Oldnodeid uint64 Oldnodeid uint64
} }
const ( // SetAttrIn.Valid const ( // SetAttrIn.Valid
FATTR_MODE = (1 << 0) FATTR_MODE = (1 << 0)
FATTR_UID = (1 << 1) FATTR_UID = (1 << 1)
...@@ -254,7 +252,6 @@ type ReadIn struct { ...@@ -254,7 +252,6 @@ type ReadIn struct {
Padding uint32 Padding uint32
} }
const ( const (
FUSE_WRITE_CACHE = (1 << 0) FUSE_WRITE_CACHE = (1 << 0)
FUSE_WRITE_LOCKOWNER = (1 << 1) FUSE_WRITE_LOCKOWNER = (1 << 1)
......
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