aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/performance.txt2
-rw-r--r--internal/syscallcompat/sys_common.go66
2 files changed, 36 insertions, 32 deletions
diff --git a/Documentation/performance.txt b/Documentation/performance.txt
index 60ec66b..7e964e0 100644
--- a/Documentation/performance.txt
+++ b/Documentation/performance.txt
@@ -75,6 +75,8 @@ v2.0 420 1000 8.5 4.5 1.8 2.3 go1.16.5, Linux 5.
v2.0.1-28-g49507ea 471 991 8.6 4.5 1.7 2.2
v2.0.1-28-g49507ea 335 951 10.2 5.4 4.1 2.0 go1.25.4, Linux 6.18.6
v2.6.1-22-gbc94538 432 950 10.0 5.4 3.8 2.0
+v2.6.1-24-gb239d51 426 941 9.9 5.5 3.7 2.0 go-fuse v2.9.0
+v2.6.1-26-g700432e 461 962 9.8 5.4 2.0 2.0
Results for EncFS for comparison (benchmark.bash -encfs):
diff --git a/internal/syscallcompat/sys_common.go b/internal/syscallcompat/sys_common.go
index 1aa6a6e..70ee633 100644
--- a/internal/syscallcompat/sys_common.go
+++ b/internal/syscallcompat/sys_common.go
@@ -112,10 +112,10 @@ const XATTR_SIZE_MAX = 65536
// Make the buffer 1kB bigger so we can detect overflows. Unfortunately,
// slices larger than 64kB are always allocated on the heap.
-const XATTR_BUFSZ = XATTR_SIZE_MAX + 1024
+const GETXATTR_BUFSZ_BIG = XATTR_SIZE_MAX + 1024
// We try with a small buffer first - this one can be allocated on the stack.
-const XATTR_BUFSZ_SMALL = 500
+const GETXATTR_BUFSZ_SMALL = 500
// Fgetxattr is a wrapper around unix.Fgetxattr that handles the buffer sizing.
func Fgetxattr(fd int, attr string) (val []byte, err error) {
@@ -135,7 +135,7 @@ func Lgetxattr(path string, attr string) (val []byte, err error) {
func getxattrSmartBuf(fn func(buf []byte) (int, error)) ([]byte, error) {
// Fastpaths. Important for security.capabilities, which gets queried a lot.
- buf := make([]byte, XATTR_BUFSZ_SMALL)
+ buf := make([]byte, GETXATTR_BUFSZ_SMALL)
sz, err := fn(buf)
// Non-existing xattr
if err == unix.ENODATA {
@@ -159,7 +159,7 @@ func getxattrSmartBuf(fn func(buf []byte) (int, error)) ([]byte, error) {
// We choose the simple approach of buffer that is bigger than the limit on
// Linux, and return an error for everything that is bigger (which can
// only happen on MacOS).
- buf = make([]byte, XATTR_BUFSZ)
+ buf = make([]byte, GETXATTR_BUFSZ_BIG)
sz, err = fn(buf)
if err == syscall.ERANGE {
// Do NOT return ERANGE - the user might retry ad inifinitum!
@@ -182,42 +182,44 @@ out:
// Flistxattr is a wrapper for unix.Flistxattr that handles buffer sizing and
// parsing the returned blob to a string slice.
func Flistxattr(fd int) (attrs []string, err error) {
- // See the buffer sizing comments in getxattrSmartBuf.
- // TODO: smarter buffer sizing?
- buf := make([]byte, XATTR_BUFSZ)
- sz, err := unix.Flistxattr(fd, buf)
- if err == syscall.ERANGE {
- // Do NOT return ERANGE - the user might retry ad inifinitum!
- return nil, syscall.EOVERFLOW
+ listxattrSyscall := func(buf []byte) (int, error) {
+ return unix.Flistxattr(fd, buf)
}
- if err != nil {
- return nil, err
- }
- if sz >= XATTR_SIZE_MAX {
- return nil, syscall.EOVERFLOW
- }
- attrs = parseListxattrBlob(buf[:sz])
- return attrs, nil
+ return listxattrSmartBuf(listxattrSyscall)
}
// Llistxattr is a wrapper for unix.Llistxattr that handles buffer sizing and
// parsing the returned blob to a string slice.
func Llistxattr(path string) (attrs []string, err error) {
- // TODO: smarter buffer sizing?
- buf := make([]byte, XATTR_BUFSZ)
- sz, err := unix.Llistxattr(path, buf)
- if err == syscall.ERANGE {
- // Do NOT return ERANGE - the user might retry ad inifinitum!
- return nil, syscall.EOVERFLOW
+ listxattrSyscall := func(buf []byte) (int, error) {
+ return unix.Llistxattr(path, buf)
}
- if err != nil {
- return nil, err
- }
- if sz >= XATTR_SIZE_MAX {
- return nil, syscall.EOVERFLOW
+ return listxattrSmartBuf(listxattrSyscall)
+}
+
+// listxattrSmartBuf handles smart buffer sizing for Flistxattr and Llistxattr
+func listxattrSmartBuf(listxattrSyscall func([]byte) (int, error)) ([]string, error) {
+ const LISTXATTR_BUFSZ_SMALL = 100
+
+ // Blindly try with the small buffer first
+ buf := make([]byte, LISTXATTR_BUFSZ_SMALL)
+ sz, err := listxattrSyscall(buf)
+ if err == syscall.ERANGE {
+ // Did not fit. Find the actual size
+ sz, err = listxattrSyscall(nil)
+ if err != nil {
+ return nil, err
+ }
+ // ...and allocate the buffer to fit
+ buf = make([]byte, sz)
+ sz, err = listxattrSyscall(buf)
+ if err != nil {
+ // When an xattr got added between the size probe and here,
+ // we could fail with ERANGE. This is ok as the caller will retry.
+ return nil, err
+ }
}
- attrs = parseListxattrBlob(buf[:sz])
- return attrs, nil
+ return parseListxattrBlob(buf[:sz]), nil
}
func parseListxattrBlob(buf []byte) (attrs []string) {