Commit 7b281480 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

By default, return ENODATA for GetXAttr.

Any node that returns ENOSYS for Getxattr will cause the kernel to
stop issuing Getxattr calls altogether. In file systems that must
support GetXAttr and that has multiple node types, tracking down all
node types and overriding GetXAttr for them individually is annoying.

For filesystems that need the performance benefit of skipping GETXATTR
calls, provide a DisableXAttrs option.

Change-Id: I98327aa959b3b26192e15bc3be96b7a6c0ada5f6
parent 9161846d
...@@ -61,6 +61,10 @@ type MountOptions struct { ...@@ -61,6 +61,10 @@ type MountOptions struct {
// If set, wrap the file system in a single-threaded locking wrapper. // If set, wrap the file system in a single-threaded locking wrapper.
SingleThreaded bool SingleThreaded bool
// If set, return ENOSYS for Getxattr calls, so the kernel does not issue any
// Xattr operations at all.
DisableXAttrs bool
} }
// RawFileSystem is an interface close to the FUSE wire protocol. // RawFileSystem is an interface close to the FUSE wire protocol.
......
...@@ -82,7 +82,7 @@ func (fs *defaultRawFileSystem) GetXAttrSize(header *InHeader, attr string) (siz ...@@ -82,7 +82,7 @@ func (fs *defaultRawFileSystem) GetXAttrSize(header *InHeader, attr string) (siz
} }
func (fs *defaultRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) { func (fs *defaultRawFileSystem) GetXAttrData(header *InHeader, attr string) (data []byte, code Status) {
return nil, ENOSYS return nil, ENODATA
} }
func (fs *defaultRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status { func (fs *defaultRawFileSystem) SetXAttr(input *SetXAttrIn, attr string, data []byte) Status {
......
...@@ -106,7 +106,7 @@ func (n *defaultNode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Stat ...@@ -106,7 +106,7 @@ func (n *defaultNode) OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Stat
} }
func (n *defaultNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) { func (n *defaultNode) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
return nil, fuse.ENOSYS return nil, fuse.ENODATA
} }
func (n *defaultNode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status { func (n *defaultNode) RemoveXAttr(attr string, context *fuse.Context) fuse.Status {
......
...@@ -169,6 +169,11 @@ const _SECURITY_ACL = "system.posix_acl_access" ...@@ -169,6 +169,11 @@ const _SECURITY_ACL = "system.posix_acl_access"
const _SECURITY_ACL_DEFAULT = "system.posix_acl_default" const _SECURITY_ACL_DEFAULT = "system.posix_acl_default"
func doGetXAttr(server *Server, req *request) { func doGetXAttr(server *Server, req *request) {
if server.opts.DisableXAttrs {
req.status = ENOSYS
return
}
if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR { if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR {
fn := req.filenames[0] fn := req.filenames[0]
if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT || if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT ||
...@@ -184,6 +189,9 @@ func doGetXAttr(server *Server, req *request) { ...@@ -184,6 +189,9 @@ func doGetXAttr(server *Server, req *request) {
out := (*GetXAttrOut)(req.outData) out := (*GetXAttrOut)(req.outData)
switch req.inHeader.Opcode { switch req.inHeader.Opcode {
case _OP_GETXATTR: 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.inHeader, req.filenames[0]) sz, code := server.fileSystem.GetXAttrSize(req.inHeader, req.filenames[0])
if code.Ok() { if code.Ok() {
out.Size = uint32(sz) out.Size = uint32(sz)
......
package test
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
)
type xattrNode struct {
nodefs.Node
}
func (n *xattrNode) OnMount(fsConn *nodefs.FileSystemConnector) {
n.Inode().NewChild("child", false, &xattrChildNode{nodefs.NewDefaultNode()})
}
type xattrChildNode struct {
nodefs.Node
}
func (n *xattrChildNode) GetXAttr(attr string, context *fuse.Context) ([]byte, fuse.Status) {
return []byte("value"), fuse.OK
}
func TestDefaultXAttr(t *testing.T) {
dir, err := ioutil.TempDir("", "go-fuse")
if err != nil {
t.Fatalf("TempDir: %v", err)
}
defer os.RemoveAll(dir)
root := &xattrNode{
Node: nodefs.NewDefaultNode(),
}
s, _, err := nodefs.MountRoot(dir, root, nil)
if err != nil {
t.Fatalf("MountRoot: %v", err)
}
s.SetDebug(VerboseTest())
go s.Serve()
defer s.Unmount()
var data [1024]byte
sz, err := syscall.Getxattr(filepath.Join(dir, "child"), "attr", data[:])
if err != nil {
t.Fatalf("Getxattr: %v", err)
} else if val := string(data[:sz]); val != "value" {
t.Fatalf("got %v, want 'value'", val)
}
}
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