summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/MANPAGE.md24
-rw-r--r--Documentation/duplicate-inodes.txt36
-rw-r--r--cli_args.go4
-rw-r--r--mount.go25
4 files changed, 82 insertions, 7 deletions
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index cfe27a5..efb8565 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -248,6 +248,30 @@ These factors will limit throughput to below 70MB/s.
For more details visit https://github.com/rfjakob/gocryptfs/issues/92 .
+#### -sharedstorage
+Enable work-arounds so gocryptfs works better when the backing
+storage directory is concurrently accessed by multiple gocryptfs
+instances.
+
+At the moment, it does two things:
+
+1. Disable stat() caching so changes to the backing storage show up
+ immediately.
+2. Disable hard link tracking, as the inode numbers on the backing
+ storage are not stable when files are deleted and re-created behind
+ our back. This would otherwise produce strange "file does not exist"
+ and other errors.
+
+When "-sharedstorage" is active, performance is reduced and hard
+links cannot be created.
+
+Even with this flag set, you may hit occasional problems. Running
+gocryptfs on shared storage does not receive as much testing as the
+usual (exclusive) use-case. Please test your workload in advance
+and report any problems you may hit.
+
+More info: https://github.com/rfjakob/gocryptfs/issues/156
+
#### -speed
Run crypto speed test. Benchmark Go's built-in GCM against OpenSSL
(if available). The library that will be selected on "-openssl=auto"
diff --git a/Documentation/duplicate-inodes.txt b/Documentation/duplicate-inodes.txt
new file mode 100644
index 0000000..36fb89c
--- /dev/null
+++ b/Documentation/duplicate-inodes.txt
@@ -0,0 +1,36 @@
+ls: cannot access foo: No such file or directory
+ls: cannot access foo: No such file or directory
+ls: cannot access foo: No such file or directory
+ls: cannot access foo: No such file or directory
+36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo
+36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo
+36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo
+36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo
+
+
+u1026@d8min:/mnt/synology/public/tmp/g1$ strace -e lstat -p 8899 -f
+Process 8899 attached with 10 threads
+2017/11/11 18:12:21 Dispatch 238: LOOKUP, NodeId: 1. names: [foo] 4 bytes
+[pid 10539] lstat("/mnt/synology/public/tmp/g1/a/4DZNVle_txclugO7n_FRIg", 0xc4241adbe8) = -1 ENOENT (No such file or directory)
+2017/11/11 18:12:21 Serialize 238: LOOKUP code: OK value: {NodeId: 0 Generation=0 EntryValid=1.000 AttrValid=0.000 Attr={M00 SZ=0 L=0 0:0 B0*0 i0:0 A 0.000000000 M 0.000000000 C 0.000000000}}
+2017/11/11 18:12:22 Dispatch 239: LOOKUP, NodeId: 1. names: [foo] 4 bytes
+[pid 8903] lstat("/mnt/synology/public/tmp/g1/a/Xsy8mhdcIh0u9aiI7-iLiw", {st_mode=S_IFREG|0777, st_size=0, ...}) = 0
+2017/11/11 18:12:22 Serialize 239: LOOKUP code: OK value: {NodeId: 3 Generation=4 EntryValid=1.000 AttrValid=1.000 Attr={M0100777 SZ=0 L=1 1026:100 B0*16384 i0:36962337 A 1510419642.457639700 M 1510419642.457639700 C 1510419702.353712800}}
+
+
+Call Trace:
+
+nodefs/fsops.go (c *rawBridge) Lookup
+ nodefs/fsops.go (c *FileSystemConnector) internalLookup
+ nodefs/inode.go (n *Inode) GetChild
+ pathfs/pathfs.go (n *pathInode) GetAttr
+ pathfs/pathfs.go (n *pathInode) GetPath
+ nodefs/inode.go (n *Inode) Parent()
+ pathfs/loopback.go (fs *loopbackFileSystem) GetAttr
+
+Call Trace 2 (new child):
+
+nodefs/fsops.go (c *rawBridge) Lookup
+ nodefs/fsops.go (c *FileSystemConnector) internalLookup
+ pathfs/pathfs.go (n *pathInode) Lookup
+ pathfs/pathfs.go (n *pathInode) findChild
diff --git a/cli_args.go b/cli_args.go
index 836d29c..71ad6bd 100644
--- a/cli_args.go
+++ b/cli_args.go
@@ -21,7 +21,8 @@ type argContainer struct {
debug, init, zerokey, fusedebug, openssl, passwd, fg, version,
plaintextnames, quiet, nosyslog, wpanic,
longnames, allow_other, ro, reverse, aessiv, nonempty, raw64,
- noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info bool
+ noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info,
+ sharedstorage bool
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
memprofile, ko, passfile, ctlsock, fsname, force_owner, trace string
// Configuration file name override
@@ -130,6 +131,7 @@ func parseCliOpts() (args argContainer) {
" Requires gocryptfs to be compiled with openssl support and implies -openssl true")
flagSet.BoolVar(&args.hh, "hh", false, "Show this long help text")
flagSet.BoolVar(&args.info, "info", false, "Display information about CIPHERDIR")
+ flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer")
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
diff --git a/mount.go b/mount.go
index 007cc46..c3fda80 100644
--- a/mount.go
+++ b/mount.go
@@ -230,6 +230,12 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile
var ctlSockBackend ctlsock.Interface
// pathFsOpts are passed into go-fuse/pathfs
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
+ if args.sharedstorage {
+ // shared storage mode disables hard link tracking as the backing inode
+ // numbers may change behind our back:
+ // https://github.com/rfjakob/gocryptfs/issues/156
+ pathFsOpts.ClientInodes = false
+ }
if args.reverse {
// The dance with the intermediate variables is because we need to
// cast the FS into pathfs.FileSystem *and* ctlsock.Interface. This
@@ -257,12 +263,19 @@ func initFuseFrontend(masterkey []byte, args *argContainer, confFile *configfile
go ctlsock.Serve(args._ctlsockFd, ctlSockBackend)
}
pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
- fuseOpts := &nodefs.Options{
- // These options are to be compatible with libfuse defaults,
- // making benchmarking easier.
- NegativeTimeout: time.Second,
- AttrTimeout: time.Second,
- EntryTimeout: time.Second,
+ var fuseOpts *nodefs.Options
+ if args.sharedstorage {
+ // sharedstorage mode sets all cache timeouts to zero so changes to the
+ // backing shared storage show up immediately.
+ fuseOpts = &nodefs.Options{}
+ } else {
+ fuseOpts = &nodefs.Options{
+ // These options are to be compatible with libfuse defaults,
+ // making benchmarking easier.
+ NegativeTimeout: time.Second,
+ AttrTimeout: time.Second,
+ EntryTimeout: time.Second,
+ }
}
conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
mOpts := fuse.MountOptions{