aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend
diff options
context:
space:
mode:
authorJakob Unterwurzacher2026-02-02 22:18:25 +0100
committerJakob Unterwurzacher2026-02-03 20:28:10 +0100
commit14045511d3d91f36845a138359718c0fe7dff21c (patch)
tree66bcc155f68e7c284a2179a0ff582e06e001209c /internal/fusefrontend
parent6ac778beb0c962e0c2413c1bd91595a9bbccd25b (diff)
Use user-provided Listxattr buffer sizexattr_user_buffer
This huge buffer showed up big time in ./profiling/ls.bash and caused a 2x LS benchmark slowdown. Instead of the fixed-size buffer, use the buffer size hint the user has provided us. We return inaccurate (larger than actual, which should be safe) numbers when the user has provided no buffer (size probe).
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r--internal/fusefrontend/node_xattr.go15
-rw-r--r--internal/fusefrontend/node_xattr_darwin.go11
-rw-r--r--internal/fusefrontend/node_xattr_linux.go9
3 files changed, 21 insertions, 14 deletions
diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go
index 1470a2a..2ca5ff7 100644
--- a/internal/fusefrontend/node_xattr.go
+++ b/internal/fusefrontend/node_xattr.go
@@ -9,6 +9,7 @@ import (
"github.com/hanwen/go-fuse/v2/fuse"
+ "github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
)
@@ -136,10 +137,22 @@ func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errn
if rn.args.NoXattr {
return 0, 0
}
- cNames, errno := n.listXAttr()
+ // cBuf is the buffer for the encrypted attr names. Double the size of what the user
+ // requested to allow for ciphertext expansion due to padding and base64 encoding.
+ // TODO: double-check max expansion factor
+ cBuf := make([]byte, 2*len(dest))
+ sz, errno := n.listXAttr(cBuf)
if errno != 0 {
return 0, errno
}
+ // A call with empty dest is a size probe as described in man 2 llistxattr.
+ // Should return the size of the full attr list and success.
+ // Because we don't know the size of the decrypted names yet, we return the
+ // size of the encrypted names. This is slightly higher and hence safe.
+ if len(dest) == 0 {
+ return uint32(sz), 0
+ }
+ cNames := syscallcompat.ParseListxattrBlob(cBuf[:sz])
var buf bytes.Buffer
for _, curName := range cNames {
// ACLs are passed through without encryption
diff --git a/internal/fusefrontend/node_xattr_darwin.go b/internal/fusefrontend/node_xattr_darwin.go
index f8f224f..ad0cd9e 100644
--- a/internal/fusefrontend/node_xattr_darwin.go
+++ b/internal/fusefrontend/node_xattr_darwin.go
@@ -88,7 +88,7 @@ func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
return fs.ToErrno(err)
}
-func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
+func (n *Node) listXAttr(buf []byte) (sz int, errno syscall.Errno) {
dirfd, cName, errno := n.prepareAtSyscallMyself()
if errno != 0 {
return
@@ -98,13 +98,10 @@ func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
// O_NONBLOCK to not block on FIFOs.
fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK|syscall.O_NOFOLLOW, 0)
if err != nil {
- return nil, fs.ToErrno(err)
+ return 0, fs.ToErrno(err)
}
defer syscall.Close(fd)
- cNames, err := syscallcompat.Flistxattr(fd)
- if err != nil {
- return nil, fs.ToErrno(err)
- }
- return cNames, 0
+ sz, err = unix.Flistxattr(fd, buf)
+ return sz, fs.ToErrno(err)
}
diff --git a/internal/fusefrontend/node_xattr_linux.go b/internal/fusefrontend/node_xattr_linux.go
index 9964212..d1747e8 100644
--- a/internal/fusefrontend/node_xattr_linux.go
+++ b/internal/fusefrontend/node_xattr_linux.go
@@ -57,7 +57,7 @@ func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) {
return fs.ToErrno(unix.Lremovexattr(procPath, cAttr))
}
-func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
+func (n *Node) listXAttr(buf []byte) (sz int, errno syscall.Errno) {
dirfd, cName, errno := n.prepareAtSyscallMyself()
if errno != 0 {
return
@@ -65,9 +65,6 @@ func (n *Node) listXAttr() (out []string, errno syscall.Errno) {
defer syscall.Close(dirfd)
procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName)
- cNames, err := syscallcompat.Llistxattr(procPath)
- if err != nil {
- return nil, fs.ToErrno(err)
- }
- return cNames, 0
+ sz, err := unix.Llistxattr(procPath, buf)
+ return sz, fs.ToErrno(err)
}