aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/MANPAGE.md26
-rw-r--r--cli_args.go3
-rw-r--r--cli_args_test.go12
-rw-r--r--mount.go1
-rw-r--r--tests/cli/cli_test.go19
-rw-r--r--tests/matrix/main_test.go1
6 files changed, 61 insertions, 1 deletions
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index 3ef6500..e306cf3 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -386,6 +386,32 @@ Only applicable to reverse mode.
Limitation: Mounted single files (yes this is possible) are NOT hidden.
+#### -readdirplus
+Enable FUSE `READDIRPLUS` on Linux. It is disabled by default, so
+attributes are fetched only when an application requests them.
+
+`READDIRPLUS` requests attributes for every entry during a directory
+listing. This can improve metadata-heavy workloads such as `ls -l`,
+but adds unnecessary work to names-only listings. With the default
+cache timeouts (without `-sharedstorage`), enabling `READDIRPLUS` made
+metadata-heavy listings 1.36x faster in a local 100,000-file directory
+and 1.82x faster in a single run over a 1,000,000-file NFS directory.
+It made names-only listings 6.5x and 18.2x slower respectively.
+
+The default also applies to reverse mode. Backup and synchronization
+tools that inspect metadata for most entries may benefit from
+`-readdirplus`.
+
+This option can also be combined with `-sharedstorage`. Because that
+mode disables kernel attribute caching, whether `READDIRPLUS` helps
+depends on the workload and backing storage.
+
+On platforms other than Linux, this option is accepted but has no
+effect.
+
+For benchmarks and more details, see
+https://github.com/rfjakob/gocryptfs/issues/1026 .
+
#### -rw, -ro
Mount the filesystem read-write (`-rw`, default) or read-only (`-ro`).
If both are specified, `-ro` takes precedence.
diff --git a/cli_args.go b/cli_args.go
index 707b453..52061e3 100644
--- a/cli_args.go
+++ b/cli_args.go
@@ -29,7 +29,7 @@ type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, reverse, aessiv, nonempty, raw64,
- noprealloc, speed, hkdf, serialize_reads, hh, info,
+ noprealloc, readdirplus, speed, hkdf, serialize_reads, hh, info,
sharedstorage, fsck, one_file_system, deterministic_names,
xchacha, noxattr bool
// Mount options with opposites
@@ -178,6 +178,7 @@ func parseCliOpts(osArgs []string) (args argContainer) {
flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories")
flagSet.BoolVar(&args.raw64, "raw64", true, "Use unpadded base64 for file names")
flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing")
+ flagSet.BoolVar(&args.readdirplus, "readdirplus", false, "Enable FUSE READDIRPLUS (Linux only)")
flagSet.BoolVar(&args.speed, "speed", false, "Run crypto speed test")
flagSet.BoolVar(&args.hkdf, "hkdf", true, "Use HKDF as an additional key derivation step")
flagSet.BoolVar(&args.serialize_reads, "serialize_reads", false, "Try to serialize read operations")
diff --git a/cli_args_test.go b/cli_args_test.go
index 4fb01ac..d7e28ab 100644
--- a/cli_args_test.go
+++ b/cli_args_test.go
@@ -157,6 +157,18 @@ func TestParseCliOpts(t *testing.T) {
}...)
o = defaultArgs
+ o.readdirplus = true
+ testcases = append(testcases, []testcaseContainer{
+ {
+ i: []string{"gocryptfs", "-readdirplus"},
+ o: o,
+ }, {
+ i: []string{"gocryptfs", "-o", "readdirplus"},
+ o: o,
+ },
+ }...)
+
+ o = defaultArgs
o.exclude = []string{"foo", "bar", "baz,boe"}
testcases = append(testcases, []testcaseContainer{
{
diff --git a/mount.go b/mount.go
index 4b78be7..32a7dd7 100644
--- a/mount.go
+++ b/mount.go
@@ -390,6 +390,7 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
// Enable go-fuse warnings
fuseOpts.Logger = log.New(os.Stderr, "go-fuse: ", log.Lmicroseconds)
fuseOpts.MountOptions = fuse.MountOptions{
+ DisableReadDirPlus: !args.readdirplus,
// Writes and reads are usually capped at 128kiB on Linux through
// the FUSE_MAX_PAGES_PER_REQ kernel constant in fuse_i.h. Our
// sync.Pool buffer pools are sized acc. to the default. Users may set
diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go
index 01cc3b7..472941d 100644
--- a/tests/cli/cli_test.go
+++ b/tests/cli/cli_test.go
@@ -991,6 +991,25 @@ func TestInitNotEmpty(t *testing.T) {
}
}
+// TestReaddirplus checks that mounting and listing work with -readdirplus.
+func TestReaddirplus(t *testing.T) {
+ dir := test_helpers.InitFS(t)
+ mnt := dir + ".mnt"
+ test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-readdirplus")
+ defer test_helpers.UnmountPanic(mnt)
+
+ if err := os.WriteFile(mnt+"/file", nil, 0600); err != nil {
+ t.Fatal(err)
+ }
+ entries, err := os.ReadDir(mnt)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(entries) != 1 || entries[0].Name() != "file" {
+ t.Fatalf("unexpected directory entries: %v", entries)
+ }
+}
+
// TestSharedstorage checks that `-sharedstorage` shows stable inode numbers to
// userspace despite having hard link tracking disabled
func TestSharedstorage(t *testing.T) {
diff --git a/tests/matrix/main_test.go b/tests/matrix/main_test.go
index cf0b6c4..126adaf 100644
--- a/tests/matrix/main_test.go
+++ b/tests/matrix/main_test.go
@@ -59,6 +59,7 @@ func TestMain(m *testing.M) {
{false, "auto", false, true, nil},
// -serialize_reads
{false, "auto", false, false, []string{"-serialize_reads"}},
+ {false, "auto", false, false, []string{"-readdirplus"}},
{false, "auto", false, false, []string{"-sharedstorage"}},
{false, "auto", false, false, []string{"-deterministic-names"}},
// Test xchacha with and without openssl