main: add "-sharedstorage" flag

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.

Mitigates https://github.com/rfjakob/gocryptfs/issues/156
This commit is contained in:
Jakob Unterwurzacher 2017-11-12 20:06:13 +01:00
parent 9ab6cdb9b9
commit e36a0ebf18
4 changed files with 82 additions and 7 deletions

View File

@ -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"

View File

@ -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

View File

@ -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")

View 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{