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 {
// small.
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
}
......@@ -119,6 +119,7 @@ type RawFileSystem interface {
// Directory handling
OpenDir(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status 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)
FsyncDir(context *Context, input *raw.FsyncIn) (code Status)
......
......@@ -134,6 +134,10 @@ func (fs *defaultRawFileSystem) ReadDir(l *DirEntryList, context *Context, input
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) {
}
......
......@@ -3,13 +3,12 @@ package fuse
// all of the code for DirEntryList.
import (
"log"
"fmt"
"unsafe"
"github.com/hanwen/go-fuse/raw"
)
var _ = log.Print
var eightPadding [8]byte
const direntSize = int(unsafe.Sizeof(raw.Dirent{}))
......@@ -21,6 +20,10 @@ type DirEntry struct {
Name string
}
func (d DirEntry) String() string {
return fmt.Sprintf("%o: %q", d.Mode, d.Name)
}
type DirEntryList struct {
buf []byte
size int
......@@ -39,12 +42,12 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList {
// AddDirEntry tries to add an entry.
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
delta := padding + direntSize + len(name)
delta := padding + direntSize + len(name) + len(prefix)
oldLen := len(l.buf)
newLen := delta + oldLen
......@@ -52,6 +55,8 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool {
return false
}
l.buf = l.buf[:newLen]
copy(l.buf[oldLen:], prefix)
oldLen += len(prefix)
dirent := (*raw.Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Off = l.Offset + 1
dirent.Ino = inode
......@@ -69,6 +74,12 @@ func (l *DirEntryList) Add(name string, inode uint64, mode uint32) bool {
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 {
return l.buf
}
......
......@@ -179,6 +179,11 @@ func (fs *lockingRawFileSystem) ReadDir(out *DirEntryList, header *Context, inpu
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) {
defer fs.locked()()
return fs.RawFS.FsyncDir(header, input)
......
......@@ -11,16 +11,18 @@ type connectorDir struct {
node Node
stream []fuse.DirEntry
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 {
return fuse.OK
}
// rewinddir() should be as if reopening directory.
// TODO - test this.
if d.lastOffset > 0 && input.Offset == 0 {
d.stream, code = d.node.OpenDir(nil)
d.stream, code = d.node.OpenDir(context)
if !code.Ok() {
return code
}
......@@ -40,11 +42,55 @@ func (d *connectorDir) ReadDir(list *fuse.DirEntryList, input *raw.ReadIn) (code
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.
func (d *connectorDir) Release() {
}
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()
}
......@@ -157,6 +157,7 @@ func (c *rawBridge) OpenDir(out *raw.OpenOut, context *fuse.Context, input *raw.
stream: append(stream,
fuse.DirEntry{fuse.S_IFDIR, "."},
fuse.DirEntry{fuse.S_IFDIR, ".."}),
rawFS: c,
}
h, opened := node.mount.registerFileHandle(node, de, nil, input.Flags)
out.OpenFlags = opened.FuseFlags
......@@ -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 {
node := c.toInode(context.NodeId)
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) {
......
......@@ -82,7 +82,9 @@ func doInit(state *Server, req *request) {
state.reqMu.Lock()
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 {
state.setSplice()
}
......@@ -130,6 +132,16 @@ func doReadDir(state *Server, req *request) {
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) {
out := (*raw.OpenOut)(req.outData)
status := state.fileSystem.OpenDir(out, &req.context, (*raw.OpenIn)(req.inData))
......@@ -417,6 +429,7 @@ func init() {
_OP_IOCTL: unsafe.Sizeof(raw.IoctlIn{}),
_OP_POLL: unsafe.Sizeof(raw.PollIn{}),
_OP_FALLOCATE: unsafe.Sizeof(raw.FallocateIn{}),
_OP_READDIRPLUS: unsafe.Sizeof(raw.ReadIn{}),
} {
operationHandlers[op].InputSize = sz
}
......@@ -531,6 +544,7 @@ func init() {
_OP_IOCTL: doIoctl,
_OP_DESTROY: doDestroy,
_OP_FALLOCATE: doFallocate,
_OP_READDIRPLUS: doReadDirPlus,
} {
operationHandlers[op].Func = v
}
......@@ -576,6 +590,7 @@ func init() {
_OP_RELEASE: 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_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*raw.ReadIn)(ptr) },
} {
operationHandlers[op].DecodeIn = f
}
......
......@@ -5,5 +5,5 @@ const outputHeaderSize = 160
const (
_FUSE_KERNEL_VERSION = 7
_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