Commit 7e41a9d5 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fuse: support readdirplus, combines readdir and lookup/getattr.

This adds ReadDirPlus to RawFileSystem. Node and path filesystems take
advantage of ReadDirPlus automatically.
parent 39b429e7
...@@ -61,7 +61,7 @@ type MountOptions struct { ...@@ -61,7 +61,7 @@ type MountOptions struct {
// small. // small.
Name string Name string
// If set, wrap the file system in a single-threaded wrapper. // If set, wrap the file system in a single-threaded locking wrapper.
SingleThreaded bool SingleThreaded bool
} }
...@@ -119,6 +119,7 @@ type RawFileSystem interface { ...@@ -119,6 +119,7 @@ type RawFileSystem interface {
// Directory handling // Directory handling
OpenDir(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status Status) OpenDir(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status Status)
ReadDir(out *DirEntryList, context *Context, input *raw.ReadIn) Status ReadDir(out *DirEntryList, context *Context, input *raw.ReadIn) Status
ReadDirPlus(out *DirEntryList, context *Context, input *raw.ReadIn) Status
ReleaseDir(context *Context, input *raw.ReleaseIn) ReleaseDir(context *Context, input *raw.ReleaseIn)
FsyncDir(context *Context, input *raw.FsyncIn) (code Status) FsyncDir(context *Context, input *raw.FsyncIn) (code Status)
......
...@@ -134,6 +134,10 @@ func (fs *defaultRawFileSystem) ReadDir(l *DirEntryList, context *Context, input ...@@ -134,6 +134,10 @@ func (fs *defaultRawFileSystem) ReadDir(l *DirEntryList, context *Context, input
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ReadDirPlus(l *DirEntryList, context *Context, input *raw.ReadIn) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ReleaseDir(context *Context, input *raw.ReleaseIn) { func (fs *defaultRawFileSystem) ReleaseDir(context *Context, input *raw.ReleaseIn) {
} }
......
...@@ -3,13 +3,12 @@ package fuse ...@@ -3,13 +3,12 @@ package fuse
// all of the code for DirEntryList. // all of the code for DirEntryList.
import ( import (
"log" "fmt"
"unsafe" "unsafe"
"github.com/hanwen/go-fuse/raw" "github.com/hanwen/go-fuse/raw"
) )
var _ = log.Print
var eightPadding [8]byte var eightPadding [8]byte
const direntSize = int(unsafe.Sizeof(raw.Dirent{})) const direntSize = int(unsafe.Sizeof(raw.Dirent{}))
...@@ -21,6 +20,10 @@ type DirEntry struct { ...@@ -21,6 +20,10 @@ type DirEntry struct {
Name string Name string
} }
func (d DirEntry) String() string {
return fmt.Sprintf("%o: %q", d.Mode, d.Name)
}
type DirEntryList struct { type DirEntryList struct {
buf []byte buf []byte
size int size int
...@@ -39,12 +42,12 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList { ...@@ -39,12 +42,12 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList {
// AddDirEntry tries to add an entry. // AddDirEntry tries to add an entry.
func (l *DirEntryList) AddDirEntry(e DirEntry) bool { func (l *DirEntryList) AddDirEntry(e DirEntry) bool {
return l.Add(e.Name, uint64(raw.FUSE_UNKNOWN_INO), e.Mode) return l.Add(nil, e.Name, uint64(raw.FUSE_UNKNOWN_INO), e.Mode)
} }
func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool { func (l *DirEntryList) Add(prefix []byte, name string, inode uint64, mode uint32) bool {
padding := (8 - len(name)&7) & 7 padding := (8 - len(name)&7) & 7
delta := padding + direntSize + len(name) delta := padding + direntSize + len(name) + len(prefix)
oldLen := len(l.buf) oldLen := len(l.buf)
newLen := delta + oldLen newLen := delta + oldLen
...@@ -52,6 +55,8 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool { ...@@ -52,6 +55,8 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool {
return false return false
} }
l.buf = l.buf[:newLen] l.buf = l.buf[:newLen]
copy(l.buf[oldLen:], prefix)
oldLen += len(prefix)
dirent := (*raw.Dirent)(unsafe.Pointer(&l.buf[oldLen])) dirent := (*raw.Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Off = l.Offset + 1 dirent.Off = l.Offset + 1
dirent.Ino = inode dirent.Ino = inode
...@@ -69,6 +74,12 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool { ...@@ -69,6 +74,12 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool {
return true return true
} }
func (l *DirEntryList) AddDirLookupEntry(e DirEntry, entryOut *raw.EntryOut) bool {
var lookup []byte
toSlice(&lookup, unsafe.Pointer(entryOut), unsafe.Sizeof(raw.EntryOut{}))
return l.Add(lookup, e.Name, uint64(raw.FUSE_UNKNOWN_INO), e.Mode)
}
func (l *DirEntryList) Bytes() []byte { func (l *DirEntryList) Bytes() []byte {
return l.buf return l.buf
} }
......
...@@ -179,6 +179,11 @@ func (fs *lockingRawFileSystem) ReadDir(out *DirEntryList, header *Context, inpu ...@@ -179,6 +179,11 @@ func (fs *lockingRawFileSystem) ReadDir(out *DirEntryList, header *Context, inpu
return fs.RawFS.ReadDir(out, header, input) return fs.RawFS.ReadDir(out, header, input)
} }
func (fs *lockingRawFileSystem) ReadDirPlus(out *DirEntryList, header *Context, input *raw.ReadIn) Status {
defer fs.locked()()
return fs.RawFS.ReadDirPlus(out, header, input)
}
func (fs *lockingRawFileSystem) FsyncDir(header *Context, input *raw.FsyncIn) (code Status) { func (fs *lockingRawFileSystem) FsyncDir(header *Context, input *raw.FsyncIn) (code Status) {
defer fs.locked()() defer fs.locked()()
return fs.RawFS.FsyncDir(header, input) return fs.RawFS.FsyncDir(header, input)
......
...@@ -11,16 +11,18 @@ type connectorDir struct { ...@@ -11,16 +11,18 @@ type connectorDir struct {
node Node node Node
stream []fuse.DirEntry stream []fuse.DirEntry
lastOffset uint64 lastOffset uint64
rawFS fuse.RawFileSystem
lookups []raw.EntryOut
} }
func (d *connectorDir) ReadDir(list *fuse.DirEntryList, input *raw.ReadIn) (code fuse.Status) { func (d *connectorDir) ReadDir(list *fuse.DirEntryList, input *raw.ReadIn, context *fuse.Context) (code fuse.Status) {
if d.stream == nil { if d.stream == nil {
return fuse.OK return fuse.OK
} }
// rewinddir() should be as if reopening directory. // rewinddir() should be as if reopening directory.
// TODO - test this. // TODO - test this.
if d.lastOffset > 0 && input.Offset == 0 { if d.lastOffset > 0 && input.Offset == 0 {
d.stream, code = d.node.OpenDir(nil) d.stream, code = d.node.OpenDir(context)
if !code.Ok() { if !code.Ok() {
return code return code
} }
...@@ -40,11 +42,55 @@ func (d *connectorDir) ReadDir(list *fuse.DirEntryList, input *raw.ReadIn) (code ...@@ -40,11 +42,55 @@ func (d *connectorDir) ReadDir(list *fuse.DirEntryList, input *raw.ReadIn) (code
return fuse.OK return fuse.OK
} }
func (d *connectorDir) ReadDirPlus(list *fuse.DirEntryList, input *raw.ReadIn, context *fuse.Context) (code fuse.Status) {
if d.stream == nil {
return fuse.OK
}
// rewinddir() should be as if reopening directory.
if d.lastOffset > 0 && input.Offset == 0 {
d.stream, code = d.node.OpenDir(context)
if !code.Ok() {
return code
}
d.lookups = nil
}
if d.lookups == nil {
d.lookups = make([]raw.EntryOut, len(d.stream))
for i, n := range d.stream {
if n.Name == "." || n.Name == ".." {
continue
}
// We ignore the return value
code := d.rawFS.Lookup(&d.lookups[i], context, n.Name)
if !code.Ok() {
d.lookups[i] = raw.EntryOut{}
}
}
}
todo := d.stream[input.Offset:]
for i, e := range todo {
if e.Name == "" {
log.Printf("got empty directory entry, mode %o.", e.Mode)
continue
}
if !list.AddDirLookupEntry(e, &d.lookups[i]) {
break
}
}
d.lastOffset = list.Offset
return fuse.OK
}
// Read everything so we make goroutines exit. // Read everything so we make goroutines exit.
func (d *connectorDir) Release() { func (d *connectorDir) Release() {
} }
type rawDir interface { type rawDir interface {
ReadDir(out *fuse.DirEntryList, input *raw.ReadIn) fuse.Status ReadDir(out *fuse.DirEntryList, input *raw.ReadIn, c *fuse.Context) fuse.Status
ReadDirPlus(out *fuse.DirEntryList, input *raw.ReadIn, c *fuse.Context) fuse.Status
Release() Release()
} }
...@@ -157,6 +157,7 @@ func (c *rawBridge) OpenDir(out *raw.OpenOut, context *fuse.Context, input *raw. ...@@ -157,6 +157,7 @@ func (c *rawBridge) OpenDir(out *raw.OpenOut, context *fuse.Context, input *raw.
stream: append(stream, stream: append(stream,
fuse.DirEntry{fuse.S_IFDIR, "."}, fuse.DirEntry{fuse.S_IFDIR, "."},
fuse.DirEntry{fuse.S_IFDIR, ".."}), fuse.DirEntry{fuse.S_IFDIR, ".."}),
rawFS: c,
} }
h, opened := node.mount.registerFileHandle(node, de, nil, input.Flags) h, opened := node.mount.registerFileHandle(node, de, nil, input.Flags)
out.OpenFlags = opened.FuseFlags out.OpenFlags = opened.FuseFlags
...@@ -167,7 +168,13 @@ func (c *rawBridge) OpenDir(out *raw.OpenOut, context *fuse.Context, input *raw. ...@@ -167,7 +168,13 @@ func (c *rawBridge) OpenDir(out *raw.OpenOut, context *fuse.Context, input *raw.
func (c *rawBridge) ReadDir(l *fuse.DirEntryList, context *fuse.Context, input *raw.ReadIn) fuse.Status { func (c *rawBridge) ReadDir(l *fuse.DirEntryList, context *fuse.Context, input *raw.ReadIn) fuse.Status {
node := c.toInode(context.NodeId) node := c.toInode(context.NodeId)
opened := node.mount.getOpenedFile(input.Fh) opened := node.mount.getOpenedFile(input.Fh)
return opened.dir.ReadDir(l, input) return opened.dir.ReadDir(l, input, context)
}
func (c *rawBridge) ReadDirPlus(l *fuse.DirEntryList, context *fuse.Context, input *raw.ReadIn) fuse.Status {
node := c.toInode(context.NodeId)
opened := node.mount.getOpenedFile(input.Fh)
return opened.dir.ReadDirPlus(l, input, context)
} }
func (c *rawBridge) Open(out *raw.OpenOut, context *fuse.Context, input *raw.OpenIn) (status fuse.Status) { func (c *rawBridge) Open(out *raw.OpenOut, context *fuse.Context, input *raw.OpenIn) (status fuse.Status) {
......
...@@ -82,7 +82,9 @@ func doInit(state *Server, req *request) { ...@@ -82,7 +82,9 @@ func doInit(state *Server, req *request) {
state.reqMu.Lock() state.reqMu.Lock()
state.kernelSettings = *input state.kernelSettings = *input
state.kernelSettings.Flags = input.Flags & (raw.CAP_ASYNC_READ | raw.CAP_BIG_WRITES | raw.CAP_FILE_OPS | raw.CAP_AUTO_INVAL_DATA) state.kernelSettings.Flags = input.Flags & (raw.CAP_ASYNC_READ | raw.CAP_BIG_WRITES | raw.CAP_FILE_OPS |
raw.CAP_AUTO_INVAL_DATA | raw.CAP_READDIRPLUS)
if input.Minor >= 13 { if input.Minor >= 13 {
state.setSplice() state.setSplice()
} }
...@@ -130,6 +132,16 @@ func doReadDir(state *Server, req *request) { ...@@ -130,6 +132,16 @@ func doReadDir(state *Server, req *request) {
req.status = code req.status = code
} }
func doReadDirPlus(server *Server, req *request) {
in := (*raw.ReadIn)(req.inData)
buf := server.allocOut(req, in.Size)
entries := NewDirEntryList(buf, uint64(in.Offset))
code := server.fileSystem.ReadDirPlus(entries, &req.context, in)
req.flatData = entries.Bytes()
req.status = code
}
func doOpenDir(state *Server, req *request) { func doOpenDir(state *Server, req *request) {
out := (*raw.OpenOut)(req.outData) out := (*raw.OpenOut)(req.outData)
status := state.fileSystem.OpenDir(out, &req.context, (*raw.OpenIn)(req.inData)) status := state.fileSystem.OpenDir(out, &req.context, (*raw.OpenIn)(req.inData))
...@@ -417,6 +429,7 @@ func init() { ...@@ -417,6 +429,7 @@ func init() {
_OP_IOCTL: unsafe.Sizeof(raw.IoctlIn{}), _OP_IOCTL: unsafe.Sizeof(raw.IoctlIn{}),
_OP_POLL: unsafe.Sizeof(raw.PollIn{}), _OP_POLL: unsafe.Sizeof(raw.PollIn{}),
_OP_FALLOCATE: unsafe.Sizeof(raw.FallocateIn{}), _OP_FALLOCATE: unsafe.Sizeof(raw.FallocateIn{}),
_OP_READDIRPLUS: unsafe.Sizeof(raw.ReadIn{}),
} { } {
operationHandlers[op].InputSize = sz operationHandlers[op].InputSize = sz
} }
...@@ -531,6 +544,7 @@ func init() { ...@@ -531,6 +544,7 @@ func init() {
_OP_IOCTL: doIoctl, _OP_IOCTL: doIoctl,
_OP_DESTROY: doDestroy, _OP_DESTROY: doDestroy,
_OP_FALLOCATE: doFallocate, _OP_FALLOCATE: doFallocate,
_OP_READDIRPLUS: doReadDirPlus,
} { } {
operationHandlers[op].Func = v operationHandlers[op].Func = v
} }
...@@ -576,6 +590,7 @@ func init() { ...@@ -576,6 +590,7 @@ func init() {
_OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) }, _OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) },
_OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) }, _OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*raw.ReleaseIn)(ptr) },
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*raw.FallocateIn)(ptr) }, _OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*raw.FallocateIn)(ptr) },
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*raw.ReadIn)(ptr) },
} { } {
operationHandlers[op].DecodeIn = f operationHandlers[op].DecodeIn = f
} }
......
...@@ -5,5 +5,5 @@ const outputHeaderSize = 160 ...@@ -5,5 +5,5 @@ const outputHeaderSize = 160
const ( const (
_FUSE_KERNEL_VERSION = 7 _FUSE_KERNEL_VERSION = 7
_MINIMUM_MINOR_VERSION = 12 _MINIMUM_MINOR_VERSION = 12
_OUR_MINOR_VERSION = 20 _OUR_MINOR_VERSION = 21
) )
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