Add READDIRPLUS support
parent
a307ab844b
commit
efe41d860d
|
@ -151,6 +151,7 @@ func (c *Connection) Init() error {
|
||||||
cacheSymlinks := initOp.Flags&fusekernel.InitCacheSymlinks > 0
|
cacheSymlinks := initOp.Flags&fusekernel.InitCacheSymlinks > 0
|
||||||
noOpenSupport := initOp.Flags&fusekernel.InitNoOpenSupport > 0
|
noOpenSupport := initOp.Flags&fusekernel.InitNoOpenSupport > 0
|
||||||
noOpendirSupport := initOp.Flags&fusekernel.InitNoOpendirSupport > 0
|
noOpendirSupport := initOp.Flags&fusekernel.InitNoOpendirSupport > 0
|
||||||
|
readdirplusSupport := initOp.Flags&fusekernel.InitDoReaddirplus > 0
|
||||||
|
|
||||||
// Respond to the init op.
|
// Respond to the init op.
|
||||||
initOp.Library = c.protocol
|
initOp.Library = c.protocol
|
||||||
|
@ -193,6 +194,11 @@ func (c *Connection) Init() error {
|
||||||
initOp.Flags |= fusekernel.InitNoOpendirSupport
|
initOp.Flags |= fusekernel.InitNoOpendirSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the kernel to do readdirplus (readdir+lookup in one call)
|
||||||
|
if c.cfg.UseReadDirPlus && readdirplusSupport {
|
||||||
|
initOp.Flags |= fusekernel.InitDoReaddirplus
|
||||||
|
}
|
||||||
|
|
||||||
c.Reply(ctx, nil)
|
c.Reply(ctx, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,6 +341,8 @@ func convertInMessage(
|
||||||
}
|
}
|
||||||
o = to
|
o = to
|
||||||
|
|
||||||
|
case fusekernel.OpReaddirplus:
|
||||||
|
fallthrough
|
||||||
case fusekernel.OpReaddir:
|
case fusekernel.OpReaddir:
|
||||||
in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
|
in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
|
||||||
if in == nil {
|
if in == nil {
|
||||||
|
@ -351,6 +353,7 @@ func convertInMessage(
|
||||||
Inode: fuseops.InodeID(inMsg.Header().Nodeid),
|
Inode: fuseops.InodeID(inMsg.Header().Nodeid),
|
||||||
Handle: fuseops.HandleID(in.Fh),
|
Handle: fuseops.HandleID(in.Fh),
|
||||||
Offset: fuseops.DirOffset(in.Offset),
|
Offset: fuseops.DirOffset(in.Offset),
|
||||||
|
Plus: inMsg.Header().Opcode == fusekernel.OpReaddirplus,
|
||||||
OpContext: fuseops.OpContext{Pid: inMsg.Header().Pid},
|
OpContext: fuseops.OpContext{Pid: inMsg.Header().Pid},
|
||||||
}
|
}
|
||||||
o = to
|
o = to
|
||||||
|
|
|
@ -562,12 +562,18 @@ type ReadDirOp struct {
|
||||||
// offset, and return array offsets into that cached listing.
|
// offset, and return array offsets into that cached listing.
|
||||||
Offset DirOffset
|
Offset DirOffset
|
||||||
|
|
||||||
|
// Whether this operation is a READDIRPLUS
|
||||||
|
//
|
||||||
|
// If true, then the FS must return inode attributes and expiration time
|
||||||
|
// along with each directory entry and increment its reference count.
|
||||||
|
Plus bool
|
||||||
|
|
||||||
// The destination buffer, whose length gives the size of the read.
|
// The destination buffer, whose length gives the size of the read.
|
||||||
//
|
//
|
||||||
// The output data should consist of a sequence of FUSE directory entries in
|
// The output data should consist of a sequence of FUSE directory entries in
|
||||||
// the format generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is
|
// the format generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is
|
||||||
// consumed by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.WriteDirent
|
// consumed by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.WriteDirent
|
||||||
// to generate this data.
|
// or fuseutil.WriteDirentPlus to generate this data.
|
||||||
//
|
//
|
||||||
// Each entry returned exposes a directory offset to the user that may later
|
// Each entry returned exposes a directory offset to the user that may later
|
||||||
// show up in ReadDirRequest.Offset. See notes on that field for more
|
// show up in ReadDirRequest.Offset. See notes on that field for more
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/jacobsa/fuse/fuseops"
|
"github.com/jacobsa/fuse/fuseops"
|
||||||
|
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DirentType uint32
|
type DirentType uint32
|
||||||
|
@ -50,10 +51,18 @@ type Dirent struct {
|
||||||
Type DirentType
|
Type DirentType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the supplied directory entry intto the given buffer in the format
|
// Write the supplied directory entry into the given buffer in the format
|
||||||
// expected in fuseops.ReadFileOp.Data, returning the number of bytes written.
|
// expected in fuseops.ReadDirOp.Data, returning the number of bytes written.
|
||||||
// Return zero if the entry would not fit.
|
// Return zero if the entry would not fit.
|
||||||
func WriteDirent(buf []byte, d Dirent) (n int) {
|
func WriteDirent(buf []byte, d Dirent) (n int) {
|
||||||
|
return WriteDirentPlus(buf, nil, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the supplied directory entry and, optionally, inode entry into the
|
||||||
|
// given buffer in the format expected in fuseops.ReadDirOp.Data with enabled
|
||||||
|
// READDIRPLUS capability, returning the number of bytes written.
|
||||||
|
// Returns zero if the entry would not fit.
|
||||||
|
func WriteDirentPlus(buf []byte, e *fuseops.ChildInodeEntry, d Dirent) (n int) {
|
||||||
// We want to write bytes with the layout of fuse_dirent
|
// We want to write bytes with the layout of fuse_dirent
|
||||||
// (http://goo.gl/BmFxob) in host order. The struct must be aligned according
|
// (http://goo.gl/BmFxob) in host order. The struct must be aligned according
|
||||||
// to FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH), which dictates 8-byte
|
// to FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH), which dictates 8-byte
|
||||||
|
@ -78,10 +87,21 @@ func WriteDirent(buf []byte, d Dirent) (n int) {
|
||||||
|
|
||||||
// Do we have enough room?
|
// Do we have enough room?
|
||||||
totalLen := direntSize + len(d.Name) + padLen
|
totalLen := direntSize + len(d.Name) + padLen
|
||||||
|
if e != nil {
|
||||||
|
// READDIRPLUS was added in protocol 7.21, entry attributes were added in 7.9
|
||||||
|
// So here EntryOut is always full-length
|
||||||
|
totalLen += int(unsafe.Sizeof(fusekernel.EntryOut{}))
|
||||||
|
}
|
||||||
if totalLen > len(buf) {
|
if totalLen > len(buf) {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
out := (*fusekernel.EntryOut)(unsafe.Pointer(&buf[n]))
|
||||||
|
fuseops.ConvertChildInodeEntry(e, out)
|
||||||
|
n += int(unsafe.Sizeof(fusekernel.EntryOut{}))
|
||||||
|
}
|
||||||
|
|
||||||
// Write the header.
|
// Write the header.
|
||||||
de := fuse_dirent{
|
de := fuse_dirent{
|
||||||
ino: uint64(d.Inode),
|
ino: uint64(d.Inode),
|
||||||
|
|
|
@ -417,6 +417,7 @@ const (
|
||||||
OpNotifyReply = 41
|
OpNotifyReply = 41
|
||||||
OpBatchForget = 42
|
OpBatchForget = 42
|
||||||
OpFallocate = 43
|
OpFallocate = 43
|
||||||
|
OpReaddirplus = 44
|
||||||
|
|
||||||
// OS X
|
// OS X
|
||||||
OpSetvolname = 61
|
OpSetvolname = 61
|
||||||
|
|
|
@ -151,6 +151,11 @@ type MountConfig struct {
|
||||||
// OpenDir calls at all (Linux >= 5.1):
|
// OpenDir calls at all (Linux >= 5.1):
|
||||||
EnableNoOpendirSupport bool
|
EnableNoOpendirSupport bool
|
||||||
|
|
||||||
|
// Tell the kernel to use READDIRPLUS.
|
||||||
|
// Note that the implementation may still fall back to READDIR if the running
|
||||||
|
// kernel doesn't have support for READDIRPLUS.
|
||||||
|
UseReadDirPlus bool
|
||||||
|
|
||||||
// Disable FUSE default permissions.
|
// Disable FUSE default permissions.
|
||||||
// This is useful for situations where the backing data store (e.g., S3) doesn't
|
// This is useful for situations where the backing data store (e.g., S3) doesn't
|
||||||
// actually utilise any form of qualifiable UNIX permissions.
|
// actually utilise any form of qualifiable UNIX permissions.
|
||||||
|
|
Loading…
Reference in New Issue