aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarek Kowalski2026-01-18 10:48:51 -0800
committerGitHub2026-01-18 19:48:51 +0100
commit6013d56f0c09100190107613ce8c5c4c929216dd (patch)
tree33eff2f7c85aeb50be96652773fd76512d9ab386
parentc9cf6f1f8a5b90c9cb70ed19f8c8426dc2655c9d (diff)
added -noxattr flag which ignores all xattr operations (#987)HEADmaster
* added -noxattr flag which ignores all xattr operations
-rw-r--r--cli_args.go3
-rw-r--r--internal/fusefrontend/args.go2
-rw-r--r--internal/fusefrontend/node_xattr.go18
-rw-r--r--internal/fusefrontend/node_xattr_darwin.go3
-rw-r--r--internal/fusefrontend/node_xattr_linux.go3
-rw-r--r--internal/fusefrontend_reverse/node_xattr.go10
-rw-r--r--internal/fusefrontend_reverse/node_xattr_darwin.go3
-rw-r--r--internal/fusefrontend_reverse/node_xattr_linux.go3
-rw-r--r--mount.go1
9 files changed, 43 insertions, 3 deletions
diff --git a/cli_args.go b/cli_args.go
index e690e15..707b453 100644
--- a/cli_args.go
+++ b/cli_args.go
@@ -31,7 +31,7 @@ type argContainer struct {
longnames, allow_other, reverse, aessiv, nonempty, raw64,
noprealloc, speed, hkdf, serialize_reads, hh, info,
sharedstorage, fsck, one_file_system, deterministic_names,
- xchacha bool
+ xchacha, noxattr bool
// Mount options with opposites
dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool
masterkey, mountpoint, cipherdir, cpuprofile,
@@ -188,6 +188,7 @@ func parseCliOpts(osArgs []string) (args argContainer) {
flagSet.BoolVar(&args.one_file_system, "one-file-system", false, "Don't cross filesystem boundaries")
flagSet.BoolVar(&args.deterministic_names, "deterministic-names", false, "Disable diriv file name randomisation")
flagSet.BoolVar(&args.xchacha, "xchacha", false, "Use XChaCha20-Poly1305 file content encryption")
+ flagSet.BoolVar(&args.noxattr, "noxattr", false, "Disable extended attribute operations")
// Mount options with opposites
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")
diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go
index 64a5923..ec3d1c2 100644
--- a/internal/fusefrontend/args.go
+++ b/internal/fusefrontend/args.go
@@ -51,4 +51,6 @@ type Args struct {
OneFileSystem bool
// DeterministicNames disables gocryptfs.diriv files
DeterministicNames bool
+ // NoXattr disables extended attribute operations
+ NoXattr bool
}
diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go
index 8ff7bab..1470a2a 100644
--- a/internal/fusefrontend/node_xattr.go
+++ b/internal/fusefrontend/node_xattr.go
@@ -32,6 +32,10 @@ func isAcl(attr string) bool {
// This function is symlink-safe through Fgetxattr.
func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
rn := n.rootNode()
+ // If -noxattr is enabled, return ENOATTR for all getxattr calls
+ if rn.args.NoXattr {
+ return 0, noSuchAttributeError
+ }
// If we are not mounted with -suid, reading the capability xattr does not
// make a lot of sense, so reject the request and gain a massive speedup.
// See https://github.com/rfjakob/gocryptfs/issues/515 .
@@ -77,6 +81,10 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
// This function is symlink-safe through Fsetxattr.
func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
rn := n.rootNode()
+ // If -noxattr is enabled, fail all setxattr calls
+ if rn.args.NoXattr {
+ return syscall.EOPNOTSUPP
+ }
flags = uint32(filterXattrSetFlags(int(flags)))
// ACLs are passed through without encryption
@@ -102,6 +110,10 @@ func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uin
// This function is symlink-safe through Fremovexattr.
func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno {
rn := n.rootNode()
+ // If -noxattr is enabled, fail all removexattr calls
+ if rn.args.NoXattr {
+ return syscall.EOPNOTSUPP
+ }
// ACLs are passed through without encryption
if isAcl(attr) {
@@ -119,11 +131,15 @@ func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno {
//
// This function is symlink-safe through Flistxattr.
func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
+ rn := n.rootNode()
+ // If -noxattr is enabled, return zero results for listxattr
+ if rn.args.NoXattr {
+ return 0, 0
+ }
cNames, errno := n.listXAttr()
if errno != 0 {
return 0, errno
}
- rn := n.rootNode()
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 a539847..f8f224f 100644
--- a/internal/fusefrontend/node_xattr_darwin.go
+++ b/internal/fusefrontend/node_xattr_darwin.go
@@ -11,6 +11,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Darwin, ENOATTR is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENOATTR
+
// On Darwin we have to unset XATTR_NOSECURITY 0x0008
func filterXattrSetFlags(flags int) int {
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
diff --git a/internal/fusefrontend/node_xattr_linux.go b/internal/fusefrontend/node_xattr_linux.go
index 4a356a5..9964212 100644
--- a/internal/fusefrontend/node_xattr_linux.go
+++ b/internal/fusefrontend/node_xattr_linux.go
@@ -12,6 +12,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Linux, ENODATA is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENODATA
+
func filterXattrSetFlags(flags int) int {
return flags
}
diff --git a/internal/fusefrontend_reverse/node_xattr.go b/internal/fusefrontend_reverse/node_xattr.go
index f4e3bda..f22764a 100644
--- a/internal/fusefrontend_reverse/node_xattr.go
+++ b/internal/fusefrontend_reverse/node_xattr.go
@@ -25,6 +25,10 @@ func isAcl(attr string) bool {
// This function is symlink-safe through Fgetxattr.
func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
rn := n.rootNode()
+ // If -noxattr is enabled, return ENOATTR for all getxattr calls
+ if rn.args.NoXattr {
+ return 0, noSuchAttributeError
+ }
var data []byte
// ACLs are passed through without encryption
if isAcl(attr) {
@@ -56,11 +60,15 @@ func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32,
//
// This function is symlink-safe through Flistxattr.
func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
+ rn := n.rootNode()
+ // If -noxattr is enabled, return zero results for listxattr
+ if rn.args.NoXattr {
+ return 0, 0
+ }
pNames, errno := n.listXAttr()
if errno != 0 {
return 0, errno
}
- rn := n.rootNode()
var buf bytes.Buffer
for _, pName := range pNames {
// ACLs are passed through without encryption
diff --git a/internal/fusefrontend_reverse/node_xattr_darwin.go b/internal/fusefrontend_reverse/node_xattr_darwin.go
index f5b58d9..6816a18 100644
--- a/internal/fusefrontend_reverse/node_xattr_darwin.go
+++ b/internal/fusefrontend_reverse/node_xattr_darwin.go
@@ -8,6 +8,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Darwin, ENOATTR is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENOATTR
+
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
d, errno := n.prepareAtSyscall("")
if errno != 0 {
diff --git a/internal/fusefrontend_reverse/node_xattr_linux.go b/internal/fusefrontend_reverse/node_xattr_linux.go
index f6d04a5..3c574f5 100644
--- a/internal/fusefrontend_reverse/node_xattr_linux.go
+++ b/internal/fusefrontend_reverse/node_xattr_linux.go
@@ -9,6 +9,9 @@ import (
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
+// On Linux, ENODATA is returned when an attribute is not found.
+const noSuchAttributeError = syscall.ENODATA
+
func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) {
d, errno := n.prepareAtSyscall("")
if errno != 0 {
diff --git a/mount.go b/mount.go
index 7f72773..f7378aa 100644
--- a/mount.go
+++ b/mount.go
@@ -284,6 +284,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
SharedStorage: args.sharedstorage,
OneFileSystem: args.one_file_system,
DeterministicNames: args.deterministic_names,
+ NoXattr: args.noxattr,
}
// confFile is nil when "-zerokey" or "-masterkey" was used
if confFile != nil {