Rejiggered the ReadFile and ReadDir API.
The new API lends itself toward reading directly into the output buffer, saving a memmove and allowing for easy reuse of output buffers.geesefs-0-30-9
commit
4fd46371a2
|
@ -252,7 +252,7 @@ func convertInMessage(
|
||||||
Inode: fuseops.InodeID(m.Header().Nodeid),
|
Inode: fuseops.InodeID(m.Header().Nodeid),
|
||||||
Handle: fuseops.HandleID(in.Fh),
|
Handle: fuseops.HandleID(in.Fh),
|
||||||
Offset: int64(in.Offset),
|
Offset: int64(in.Offset),
|
||||||
Size: int(in.Size),
|
Dst: make([]byte, in.Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
case fusekernel.OpReaddir:
|
case fusekernel.OpReaddir:
|
||||||
|
@ -266,7 +266,7 @@ func convertInMessage(
|
||||||
Inode: fuseops.InodeID(m.Header().Nodeid),
|
Inode: fuseops.InodeID(m.Header().Nodeid),
|
||||||
Handle: fuseops.HandleID(in.Fh),
|
Handle: fuseops.HandleID(in.Fh),
|
||||||
Offset: fuseops.DirOffset(in.Offset),
|
Offset: fuseops.DirOffset(in.Offset),
|
||||||
Size: int(in.Size),
|
Dst: make([]byte, in.Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
case fusekernel.OpRelease:
|
case fusekernel.OpRelease:
|
||||||
|
@ -486,7 +486,7 @@ func (c *Connection) kernelResponseForOp(
|
||||||
|
|
||||||
case *fuseops.ReadDirOp:
|
case *fuseops.ReadDirOp:
|
||||||
m = c.getOutMessage()
|
m = c.getOutMessage()
|
||||||
m.Append(o.Data)
|
m.Append(o.Dst[:o.BytesRead])
|
||||||
|
|
||||||
case *fuseops.ReleaseDirHandleOp:
|
case *fuseops.ReleaseDirHandleOp:
|
||||||
m = c.getOutMessage()
|
m = c.getOutMessage()
|
||||||
|
@ -498,7 +498,7 @@ func (c *Connection) kernelResponseForOp(
|
||||||
|
|
||||||
case *fuseops.ReadFileOp:
|
case *fuseops.ReadFileOp:
|
||||||
m = c.getOutMessage()
|
m = c.getOutMessage()
|
||||||
m.Append(o.Data)
|
m.Append(o.Dst[:o.BytesRead])
|
||||||
|
|
||||||
case *fuseops.WriteFileOp:
|
case *fuseops.WriteFileOp:
|
||||||
m = c.getOutMessage()
|
m = c.getOutMessage()
|
||||||
|
|
|
@ -384,25 +384,29 @@ 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
|
||||||
|
|
||||||
// The maximum number of bytes to return in ReadDirResponse.Data. A smaller
|
// The destination buffer, whose length gives the size of the read.
|
||||||
// number is acceptable.
|
|
||||||
Size int
|
|
||||||
|
|
||||||
// Set by the file system: a buffer consisting of a sequence of FUSE
|
|
||||||
// directory entries in the format generated by fuse_add_direntry
|
|
||||||
// (http://goo.gl/qCcHCV), which is consumed by parse_dirfile
|
|
||||||
// (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to generate this data.
|
|
||||||
//
|
//
|
||||||
// The buffer must not exceed the length specified in ReadDirRequest.Size. It
|
// The output data should consist of a sequence of FUSE directory entries in
|
||||||
// is okay for the final entry to be truncated; parse_dirfile copes with this
|
// the format generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is
|
||||||
// by ignoring the partial record.
|
// consumed by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.WriteDirent
|
||||||
|
// 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
|
||||||
// information.
|
// information.
|
||||||
|
Dst []byte
|
||||||
|
|
||||||
|
// Set by the file system: the number of bytes read into Dst.
|
||||||
//
|
//
|
||||||
// An empty buffer indicates the end of the directory has been reached.
|
// It is okay for this to be less than len(Dst) if there are not enough
|
||||||
Data []byte
|
// entries available or the final entry would not fit.
|
||||||
|
//
|
||||||
|
// Zero means that the end of the directory has been reached. This is
|
||||||
|
// unambiguous because NAME_MAX (https://goo.gl/ZxzKaE) plus the size of
|
||||||
|
// fuse_dirent (https://goo.gl/WO8s3F) plus the 8-byte alignment of
|
||||||
|
// FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH) is less than the read size of
|
||||||
|
// PAGE_SIZE used by fuse_readdir (cf. https://goo.gl/VajtS2).
|
||||||
|
BytesRead int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release a previously-minted directory handle. The kernel sends this when
|
// Release a previously-minted directory handle. The kernel sends this when
|
||||||
|
@ -455,20 +459,21 @@ type ReadFileOp struct {
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
Handle HandleID
|
Handle HandleID
|
||||||
|
|
||||||
// The range of the file to read.
|
// The offset within the file at which to read.
|
||||||
|
Offset int64
|
||||||
|
|
||||||
|
// The destination buffer, whose length gives the size of the read.
|
||||||
|
Dst []byte
|
||||||
|
|
||||||
|
// Set by the file system: the number of bytes read.
|
||||||
//
|
//
|
||||||
// The FUSE documentation requires that exactly the number of bytes be
|
// The FUSE documentation requires that exactly the requested number of bytes
|
||||||
// returned, except in the case of EOF or error (http://goo.gl/ZgfBkF). This
|
// be returned, except in the case of EOF or error (http://goo.gl/ZgfBkF).
|
||||||
// appears to be because it uses file mmapping machinery
|
// This appears to be because it uses file mmapping machinery
|
||||||
// (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand
|
// (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand
|
||||||
// where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned
|
// where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned
|
||||||
// by a previous call to LookUpInode, GetInodeAttributes, etc.
|
// by a previous call to LookUpInode, GetInodeAttributes, etc.
|
||||||
Offset int64
|
BytesRead int
|
||||||
Size int
|
|
||||||
|
|
||||||
// Set by the file system: the data read. If this is less than the requested
|
|
||||||
// size, it indicates EOF. An error should not be returned in this case.
|
|
||||||
Data []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data to a file previously opened with CreateFile or OpenFile.
|
// Write data to a file previously opened with CreateFile or OpenFile.
|
||||||
|
|
|
@ -35,7 +35,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// A struct representing an entry within a directory file, describing a child.
|
// A struct representing an entry within a directory file, describing a child.
|
||||||
// See notes on fuseops.ReadDirOp and on AppendDirent for details.
|
// See notes on fuseops.ReadDirOp and on WriteDirent for details.
|
||||||
type Dirent struct {
|
type Dirent struct {
|
||||||
// The (opaque) offset within the directory file of the entry following this
|
// The (opaque) offset within the directory file of the entry following this
|
||||||
// one. See notes on fuseops.ReadDirOp.Offset for details.
|
// one. See notes on fuseops.ReadDirOp.Offset for details.
|
||||||
|
@ -50,10 +50,11 @@ type Dirent struct {
|
||||||
Type DirentType
|
Type DirentType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the supplied directory entry to the given buffer in the format
|
// Write the supplied directory entry intto the given buffer in the format
|
||||||
// expected in fuseops.ReadFileOp.Data, returning the resulting buffer.
|
// expected in fuseops.ReadFileOp.Data, returning the number of bytes written.
|
||||||
func AppendDirent(input []byte, d Dirent) (output []byte) {
|
// Return zero if the entry would not fit.
|
||||||
// We want to append bytes with the layout of fuse_dirent
|
func WriteDirent(buf []byte, d Dirent) (n int) {
|
||||||
|
// 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
|
||||||
// alignment.
|
// alignment.
|
||||||
|
@ -65,10 +66,23 @@ func AppendDirent(input []byte, d Dirent) (output []byte) {
|
||||||
name [0]byte
|
name [0]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
const alignment = 8
|
const direntAlignment = 8
|
||||||
const nameOffset = 8 + 8 + 4 + 4
|
const direntSize = 8 + 8 + 4 + 4
|
||||||
|
|
||||||
// Write the header into the buffer.
|
// Compute the number of bytes of padding we'll need to maintain alignment
|
||||||
|
// for the next entry.
|
||||||
|
var padLen int
|
||||||
|
if len(d.Name)%direntAlignment != 0 {
|
||||||
|
padLen = direntAlignment - (len(d.Name) % direntAlignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have enough room?
|
||||||
|
totalLen := direntSize + len(d.Name) + padLen
|
||||||
|
if totalLen > len(buf) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the header.
|
||||||
de := fuse_dirent{
|
de := fuse_dirent{
|
||||||
ino: uint64(d.Inode),
|
ino: uint64(d.Inode),
|
||||||
off: uint64(d.Offset),
|
off: uint64(d.Offset),
|
||||||
|
@ -76,17 +90,15 @@ func AppendDirent(input []byte, d Dirent) (output []byte) {
|
||||||
type_: uint32(d.Type),
|
type_: uint32(d.Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
output = append(input, (*[nameOffset]byte)(unsafe.Pointer(&de))[:]...)
|
n += copy(buf[n:], (*[direntSize]byte)(unsafe.Pointer(&de))[:])
|
||||||
|
|
||||||
// Write the name afterward.
|
// Write the name afterward.
|
||||||
output = append(output, d.Name...)
|
n += copy(buf[n:], d.Name)
|
||||||
|
|
||||||
// Add any necessary padding.
|
// Add any necessary padding.
|
||||||
if len(d.Name)%alignment != 0 {
|
if padLen != 0 {
|
||||||
padLen := alignment - (len(d.Name) % alignment)
|
var padding [direntAlignment]byte
|
||||||
|
n += copy(buf[n:], padding[:padLen])
|
||||||
var padding [alignment]byte
|
|
||||||
output = append(output, padding[:padLen]...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -180,8 +180,7 @@ func (fs *flushFS) ReadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read what we can.
|
// Read what we can.
|
||||||
op.Data = make([]byte, op.Size)
|
op.BytesRead = copy(op.Dst, fs.fooContents[op.Offset:])
|
||||||
copy(op.Data, fs.fooContents[op.Offset:])
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -298,14 +297,16 @@ func (fs *flushFS) ReadDir(
|
||||||
|
|
||||||
// Fill in the listing.
|
// Fill in the listing.
|
||||||
for _, de := range dirents {
|
for _, de := range dirents {
|
||||||
op.Data = fuseutil.AppendDirent(op.Data, de)
|
n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], de)
|
||||||
}
|
|
||||||
|
|
||||||
// We don't support doing this in anything more than one shot.
|
// We don't support doing this in anything more than one shot.
|
||||||
if len(op.Data) > op.Size {
|
if n == 0 {
|
||||||
err = fmt.Errorf("Couldn't fit listing in %v bytes", op.Size)
|
err = fmt.Errorf("Couldn't fit listing in %v bytes", len(op.Dst))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
op.BytesRead += n
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,11 +228,12 @@ func (fs *helloFS) ReadDir(
|
||||||
|
|
||||||
// Resume at the specified offset into the array.
|
// Resume at the specified offset into the array.
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
op.Data = fuseutil.AppendDirent(op.Data, e)
|
n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
|
||||||
if len(op.Data) > op.Size {
|
if n == 0 {
|
||||||
op.Data = op.Data[:op.Size]
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
op.BytesRead += n
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -251,9 +252,7 @@ func (fs *helloFS) ReadFile(
|
||||||
// Let io.ReaderAt deal with the semantics.
|
// Let io.ReaderAt deal with the semantics.
|
||||||
reader := strings.NewReader("Hello, world!")
|
reader := strings.NewReader("Hello, world!")
|
||||||
|
|
||||||
op.Data = make([]byte, op.Size)
|
op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
|
||||||
n, err := reader.ReadAt(op.Data, op.Offset)
|
|
||||||
op.Data = op.Data[:n]
|
|
||||||
|
|
||||||
// Special case: FUSE doesn't expect us to return io.EOF.
|
// Special case: FUSE doesn't expect us to return io.EOF.
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
|
|
@ -278,7 +278,7 @@ func (in *inode) RemoveChild(name string) {
|
||||||
// Serve a ReadDir request.
|
// Serve a ReadDir request.
|
||||||
//
|
//
|
||||||
// REQUIRES: in.isDir()
|
// REQUIRES: in.isDir()
|
||||||
func (in *inode) ReadDir(offset int, size int) (data []byte) {
|
func (in *inode) ReadDir(p []byte, offset int) (n int) {
|
||||||
if !in.isDir() {
|
if !in.isDir() {
|
||||||
panic("ReadDir called on non-directory.")
|
panic("ReadDir called on non-directory.")
|
||||||
}
|
}
|
||||||
|
@ -291,13 +291,12 @@ func (in *inode) ReadDir(offset int, size int) (data []byte) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
data = fuseutil.AppendDirent(data, in.entries[i])
|
tmp := fuseutil.WriteDirent(p[n:], in.entries[i])
|
||||||
|
if tmp == 0 {
|
||||||
// Trim and stop early if we've exceeded the requested size.
|
|
||||||
if len(data) > size {
|
|
||||||
data = data[:size]
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n += tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -428,7 +428,9 @@ func (fs *memFS) Rename(
|
||||||
existingID, _, ok := newParent.LookUpChild(op.NewName)
|
existingID, _, ok := newParent.LookUpChild(op.NewName)
|
||||||
if ok {
|
if ok {
|
||||||
existing := fs.getInodeOrDie(existingID)
|
existing := fs.getInodeOrDie(existingID)
|
||||||
if existing.isDir() && len(existing.ReadDir(0, 1024)) > 0 {
|
|
||||||
|
var buf [4096]byte
|
||||||
|
if existing.isDir() && existing.ReadDir(buf[:], 0) > 0 {
|
||||||
err = fuse.ENOTEMPTY
|
err = fuse.ENOTEMPTY
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -538,7 +540,7 @@ func (fs *memFS) ReadDir(
|
||||||
inode := fs.getInodeOrDie(op.Inode)
|
inode := fs.getInodeOrDie(op.Inode)
|
||||||
|
|
||||||
// Serve the request.
|
// Serve the request.
|
||||||
op.Data = inode.ReadDir(int(op.Offset), op.Size)
|
op.BytesRead = inode.ReadDir(op.Dst, int(op.Offset))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -571,9 +573,7 @@ func (fs *memFS) ReadFile(
|
||||||
inode := fs.getInodeOrDie(op.Inode)
|
inode := fs.getInodeOrDie(op.Inode)
|
||||||
|
|
||||||
// Serve the request.
|
// Serve the request.
|
||||||
op.Data = make([]byte, op.Size)
|
op.BytesRead, err = inode.ReadAt(op.Dst, op.Offset)
|
||||||
n, err := inode.ReadAt(op.Data, op.Offset)
|
|
||||||
op.Data = op.Data[:n]
|
|
||||||
|
|
||||||
// Don't return EOF errors; we just indicate EOF to fuse using a short read.
|
// Don't return EOF errors; we just indicate EOF to fuse using a short read.
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
|
Loading…
Reference in New Issue