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 { ...@@ -218,10 +218,21 @@ type RawFileSystem interface {
Access(cancel <-chan struct{}, input *AccessIn) (code Status) Access(cancel <-chan struct{}, input *AccessIn) (code Status)
// Extended attributes. // 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) // GetXAttr reads an extended attribute, and should return the
ListXAttr(cancel <-chan struct{}, header *InHeader) (attributes []byte, code Status) // 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 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) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) (code Status)
// File handling. // File handling.
......
...@@ -81,20 +81,16 @@ func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name ...@@ -81,20 +81,16 @@ func (fs *defaultRawFileSystem) Link(cancel <-chan struct{}, input *LinkIn, name
return ENOSYS 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 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 { func (fs *defaultRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr string, data []byte) Status {
return ENOSYS return ENOSYS
} }
func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status) { func (fs *defaultRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader, dest []byte) (n uint32, code Status) {
return nil, ENOSYS return 0, ENOSYS
} }
func (fs *defaultRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status { 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 ...@@ -109,19 +109,14 @@ func (fs *lockingRawFileSystem) SetXAttr(cancel <-chan struct{}, input *SetXAttr
return fs.RawFS.SetXAttr(cancel, input, attr, data) 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()() 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()() defer fs.locked()()
return fs.RawFS.GetXAttrSize(cancel, header, attr) return fs.RawFS.ListXAttr(cancel, header, data)
}
func (fs *lockingRawFileSystem) ListXAttr(cancel <-chan struct{}, header *InHeader) (data []byte, code Status) {
defer fs.locked()()
return fs.RawFS.ListXAttr(cancel, header)
} }
func (fs *lockingRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status { func (fs *lockingRawFileSystem) RemoveXAttr(cancel <-chan struct{}, header *InHeader, attr string) Status {
......
...@@ -8,7 +8,6 @@ package nodefs ...@@ -8,7 +8,6 @@ package nodefs
// RawFileSystem // RawFileSystem
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"strings" "strings"
...@@ -377,11 +376,15 @@ func (c *rawBridge) ReleaseDir(input *fuse.ReleaseIn) { ...@@ -377,11 +376,15 @@ func (c *rawBridge) ReleaseDir(input *fuse.ReleaseIn) {
node.mount.unregisterFileHandle(input.Fh, node) node.mount.unregisterFileHandle(input.Fh, node)
} }
} }
func (c *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attribute string, dest []byte) (sz uint32, code fuse.Status) {
func (c *rawBridge) GetXAttrSize(cancel <-chan struct{}, header *fuse.InHeader, attribute string) (sz int, code fuse.Status) {
node := c.toInode(header.NodeId) node := c.toInode(header.NodeId)
data, errno := node.fsInode.GetXAttr(attribute, &fuse.Context{Caller: header.Caller, Cancel: cancel}) 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) { 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 ...@@ -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}) 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) node := c.toInode(header.NodeId)
attrs, code := node.fsInode.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}) attrs, code := node.fsInode.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if code != fuse.OK { 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 { for _, v := range attrs {
b.Write([]byte(v)) dest = append(dest, v...)
b.WriteByte(0) dest = append(dest, 0)
} }
return b.Bytes(), code return sz, code
} }
//////////////// ////////////////
......
...@@ -239,48 +239,29 @@ func doGetXAttr(server *Server, req *request) { ...@@ -239,48 +239,29 @@ func doGetXAttr(server *Server, req *request) {
input := (*GetXAttrIn)(req.inData) input := (*GetXAttrIn)(req.inData)
if input.Size == 0 { req.flatData = server.allocOut(req, input.Size)
out := (*GetXAttrOut)(req.outData()) out := (*GetXAttrOut)(req.outData())
switch req.inHeader.Opcode {
case _OP_GETXATTR: var n uint32
// 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
switch req.inHeader.Opcode { switch req.inHeader.Opcode {
case _OP_GETXATTR: 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: 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: default:
log.Panicf("xattr opcode %v", req.inHeader.Opcode)
req.status = ENOSYS req.status = ENOSYS
} }
if len(data) > int(input.Size) { if input.Size == 0 && req.status == ERANGE {
req.status = ERANGE // For input.size==0, returning ERANGE is an error.
} req.status = OK
out.Size = n
if !req.status.Ok() { } else if req.status.Ok() {
return req.flatData = req.flatData[:n]
out.Size = n
} else {
req.flatData = req.flatData[:0]
} }
req.flatData = data
} }
func doGetAttr(server *Server, req *request) { func doGetAttr(server *Server, req *request) {
......
...@@ -228,16 +228,21 @@ func (fs *wrappingFS) Link(cancel <-chan struct{}, input *LinkIn, name string, o ...@@ -228,16 +228,21 @@ func (fs *wrappingFS) Link(cancel <-chan struct{}, input *LinkIn, name string, o
return ENOSYS 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 { 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 { }); 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 { 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 { }); ok {
return s.GetXAttrSize(cancel, header, attr) return s.GetXAttr(cancel, header, attr, dest)
} }
return 0, ENOSYS return 0, ENOSYS
} }
...@@ -270,18 +275,24 @@ func (fs *wrappingFS) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr s ...@@ -270,18 +275,24 @@ func (fs *wrappingFS) SetXAttr(cancel <-chan struct{}, input *SetXAttrIn, attr s
return ENOSYS 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 { if s, ok := fs.fs.(interface {
// Old signature.
ListXAttr(header *InHeader) (data []byte, code Status) ListXAttr(header *InHeader) (data []byte, code Status)
}); ok { }); 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 { 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 { }); 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 { 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