Commit 0f169dda authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fuse: change raw API for ListXAttr/GetXAttr

The protocol is:

  request: read (with buffer 1024)
  response: data (if buffer is big enough), or (data size, ERANGE)

Before, the raw API provided

* GetXAttrSize: called if the kernel sends a 0 sized buffer (which
  never happens

* GetXAttrData: called in other cases. The return value is []byte
  forcing allocation of potentially large buffers

* ListXAttr: return value []byte, so also problematic for allocation.

Now, the raw API mirrors the kernel API more closely, and use recycled
buffers for more efficiency.
parent f8840792
......@@ -218,10 +218,21 @@ type RawFileSystem interface {
Access(cancel <-chan struct{}, input *AccessIn) (code Status)
// Extended attributes.
GetXAttrSize(cancel <-chan struct{}, header *InHeader, attr string) (sz int, code Status)
GetXAttrData(cancel <-chan struct{}, header *InHeader, attr string) (data []byte, code Status)
ListXAttr(cancel <-chan struct{}, header *InHeader) (attributes []byte, code Status)
// GetXAttr reads an extended attribute, and should return the
// number of bytes. If the buffer is too small, return ERANGE,
// with the required buffer size.
GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (sz uint32, code Status)
// ListXAttr lists extended attributes as '\0' delimited byte
// slice, and return the number of bytes. If the buffer is too
// small, return ERANGE, with the required buffer size.
ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (uint32, Status)
// SetAttr writes an extended attribute.
SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status
// RemoveXAttr removes an extended attribute.
RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) (code Status)
// File handling.
......
......@@ -81,20 +81,16 @@ func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name
return ENOSYS
}
func (fs *defaultRawFileSystem) GetXAttrSize(cancel <-chan struct{}, header *InHeader, attr string) (size int, code Status) {
func (fs *defaultRawFileSystem) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) GetXAttrData(cancel <-chan struct{}, header *InHeader, attr string) (data []byte, code Status) {
return nil, ENOATTR
}
func (fs *defaultRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status {
return ENOSYS
}
func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status) {
return nil, ENOSYS
func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
return 0, ENOSYS
}
func (fs *defaultRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
......
......@@ -109,19 +109,14 @@ func (fs *lockingRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttr
return fs.RawFS.SetXAttr(cancel, input, attr, data)
}
func (fs *lockingRawFileSystem) GetXAttrData(cancel <-chan struct{}, header *InHeader, attr string) (data []byte, code Status) {
func (fs *lockingRawFileSystem) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (uint32, Status) {
defer fs.locked()()
return fs.RawFS.GetXAttrData(cancel, header, attr)
return fs.RawFS.GetXAttr(cancel, header, attr, dest)
}
func (fs *lockingRawFileSystem) GetXAttrSize(cancel <-chan struct{}, header *InHeader, attr string) (sz int, code Status) {
func (fs *lockingRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, data []byte) (uint32, Status) {
defer fs.locked()()
return fs.RawFS.GetXAttrSize(cancel, header, attr)
}
func (fs *lockingRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status) {
defer fs.locked()()
return fs.RawFS.ListXAttr(cancel, header)
return fs.RawFS.ListXAttr(cancel, header, data)
}
func (fs *lockingRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
......
......@@ -8,7 +8,6 @@ package nodefs
// RawFileSystem
import (
"bytes"
"fmt"
"log"
"strings"
......@@ -377,11 +376,15 @@ func (c *rawBridge) ReleaseDir(input *fuse.ReleaseIn) {
node.mount.unregisterFileHandle(input.Fh, node)
}
}
func (c *rawBridge) GetXAttrSize(cancel <-chan struct{}, header *fuse.InHeader, attribute string) (sz int, code fuse.Status) {
func (c *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attribute string, dest []byte) (sz uint32, code fuse.Status) {
node := c.toInode(header.NodeId)
data, errno := node.fsInode.GetXAttr(attribute, &fuse.Context{Caller: header.Caller, Cancel: cancel})
return len(data), errno
if len(data) > len(dest) {
return uint32(len(data)), fuse.ERANGE
}
copy(dest, data)
return uint32(len(data)), errno
}
func (c *rawBridge) GetXAttrData(cancel <-chan struct{}, header *fuse.InHeader, attribute string) (data []byte, code fuse.Status) {
......@@ -399,20 +402,29 @@ func (c *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, att
return node.fsInode.SetXAttr(attr, data, int(input.Flags), &fuse.Context{Caller: input.Caller, Cancel: cancel})
}
func (c *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader) (data []byte, code fuse.Status) {
func (c *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (uint32, fuse.Status) {
node := c.toInode(header.NodeId)
attrs, code := node.fsInode.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if code != fuse.OK {
return nil, code
return 0, code
}
var sz uint32
for _, v := range attrs {
sz += uint32(len(v)) + 1
}
if int(sz) > len(dest) {
return sz, fuse.ERANGE
}
b := bytes.NewBuffer([]byte{})
dest = dest[:0]
for _, v := range attrs {
b.Write([]byte(v))
b.WriteByte(0)
dest = append(dest, v...)
dest = append(dest, 0)
}
return b.Bytes(), code
return sz, code
}
////////////////
......
......@@ -239,48 +239,29 @@ func doGetXAttr(server *Server, req *request) {
input := (*GetXAttrIn)(req.inData)
if input.Size == 0 {
out := (*GetXAttrOut)(req.outData())
switch req.inHeader.Opcode {
case _OP_GETXATTR:
// TODO(hanwen): double check this. For getxattr, input.Size
// field refers to the size of the attribute, so it usually
// is not 0.
sz, code := server.fileSystem.GetXAttrSize(req.cancel, req.inHeader, req.filenames[0])
if code.Ok() {
out.Size = uint32(sz)
}
req.status = code
return
case _OP_LISTXATTR:
data, code := server.fileSystem.ListXAttr(req.cancel, req.inHeader)
if code.Ok() {
out.Size = uint32(len(data))
}
req.status = code
return
}
}
var data []byte
req.flatData = server.allocOut(req, input.Size)
out := (*GetXAttrOut)(req.outData())
var n uint32
switch req.inHeader.Opcode {
case _OP_GETXATTR:
data, req.status = server.fileSystem.GetXAttrData(req.cancel, req.inHeader, req.filenames[0])
n, req.status = server.fileSystem.GetXAttr(req.cancel, req.inHeader, req.filenames[0], req.flatData)
case _OP_LISTXATTR:
data, req.status = server.fileSystem.ListXAttr(req.cancel, req.inHeader)
n, req.status = server.fileSystem.ListXAttr(req.cancel, req.inHeader, req.flatData)
default:
log.Panicf("xattr opcode %v", req.inHeader.Opcode)
req.status = ENOSYS
}
if len(data) > int(input.Size) {
req.status = ERANGE
}
if !req.status.Ok() {
return
if input.Size == 0 && req.status == ERANGE {
// For input.size==0, returning ERANGE is an error.
req.status = OK
out.Size = n
} else if req.status.Ok() {
req.flatData = req.flatData[:n]
out.Size = n
} else {
req.flatData = req.flatData[:0]
}
req.flatData = data
}
func doGetAttr(server *Server, req *request) {
......
......@@ -228,16 +228,21 @@ func (fs *wrappingFS) Link(cancel <-chan struct{}, input *LinkIn, name string, o
return ENOSYS
}
func (fs *wrappingFS) GetXAttrSize(cancel <-chan struct{}, header *InHeader, attr string) (size int, code Status) {
func (fs *wrappingFS) GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status) {
if s, ok := fs.fs.(interface {
GetXAttrSize(header *InHeader, attr string) (size int, code Status)
// Old signature.
GetXAttrData(header *InHeader, attr string) ([]byte, Status)
}); ok {
return s.GetXAttrSize(header, attr)
d, s := s.GetXAttrData(header, attr)
if len(d) > len(dest) {
return uint32(len(d)), ERANGE
}
return uint32(copy(dest, d)), s
}
if s, ok := fs.fs.(interface {
GetXAttrSize(cancel <-chan struct{}, header *InHeader, attr string) (size int, code Status)
GetXAttr(cancel <-chan struct{}, header *InHeader, attr string, dest []byte) (size uint32, code Status)
}); ok {
return s.GetXAttrSize(cancel, header, attr)
return s.GetXAttr(cancel, header, attr, dest)
}
return 0, ENOSYS
}
......@@ -270,18 +275,24 @@ func (fs *wrappingFS) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr s
return ENOSYS
}
func (fs *wrappingFS) ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status) {
func (fs *wrappingFS) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
if s, ok := fs.fs.(interface {
// Old signature.
ListXAttr(header *InHeader) (data []byte, code Status)
}); ok {
return s.ListXAttr(header)
d, s := s.ListXAttr(header)
if len(d) > len(dest) {
return uint32(len(d)), ERANGE
}
return uint32(copy(dest, d)), s
}
if s, ok := fs.fs.(interface {
ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status)
ListXAttr(cancel <-chan struct{}, header *InHeader, data []byte) (uint32, Status)
}); ok {
return s.ListXAttr(cancel, header)
return s.ListXAttr(cancel, header, dest)
}
return nil, ENOSYS
return 0, ENOSYS
}
func (fs *wrappingFS) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
......
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