diff options
| -rw-r--r-- | Documentation/MANPAGE.md | 24 | ||||
| -rw-r--r-- | Documentation/duplicate-inodes.txt | 36 | ||||
| -rw-r--r-- | cli_args.go | 4 | ||||
| -rw-r--r-- | mount.go | 25 | 
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") @@ -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{ | 
