From 195d9d18a90d88ff2cb0530d832c59d98934fd1f Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Fri, 20 Aug 2021 10:57:26 +0200 Subject: Implement -deterministic-names: extended -zerodiriv -deterministc-names uses all-zero dirivs but does not write them to disk anymore. --- internal/fusefrontend/args.go | 4 +- internal/fusefrontend/ctlsock_interface.go | 4 +- internal/fusefrontend/node_dir_ops.go | 111 +++++++++++++++----------- internal/fusefrontend/node_prepare_syscall.go | 3 +- internal/fusefrontend/xattr_unit_test.go | 2 +- 5 files changed, 71 insertions(+), 53 deletions(-) (limited to 'internal/fusefrontend') diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index 02ffddb..e20987b 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -53,6 +53,6 @@ type Args struct { // like rsync's `--one-file-system` does. // Only applicable to reverse mode. OneFileSystem bool - // ZeroDirIV creates diriv files as all-zero files - ZeroDirIV bool + // DeterministicNames disables gocryptfs.diriv files + DeterministicNames bool } diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go index 9e8cffc..87f0dc3 100644 --- a/internal/fusefrontend/ctlsock_interface.go +++ b/internal/fusefrontend/ctlsock_interface.go @@ -32,7 +32,7 @@ func (rn *RootNode) EncryptPath(plainPath string) (cipherPath string, err error) parts := strings.Split(plainPath, "/") wd := dirfd for i, part := range parts { - dirIV, err := nametransform.ReadDirIVAt(wd) + dirIV, err := rn.nameTransform.ReadDirIVAt(wd) if err != nil { return "", err } @@ -78,7 +78,7 @@ func (rn *RootNode) DecryptPath(cipherPath string) (plainPath string, err error) parts := strings.Split(cipherPath, "/") wd := dirfd for i, part := range parts { - dirIV, err := nametransform.ReadDirIVAt(wd) + dirIV, err := rn.nameTransform.ReadDirIVAt(wd) if err != nil { return "", err } diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go index b43a4e4..c4ab861 100644 --- a/internal/fusefrontend/node_dir_ops.go +++ b/internal/fusefrontend/node_dir_ops.go @@ -34,9 +34,14 @@ func haveDsstore(entries []fuse.DirEntry) bool { // mkdirWithIv - create a new directory and corresponding diriv file. dirfd // should be a handle to the parent directory, cName is the name of the new // directory and mode specifies the access permissions to use. +// If DeterministicNames is set, the diriv file is NOT created. func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error { - rn := n.rootNode() + + if rn.args.DeterministicNames { + return syscallcompat.MkdiratUser(dirfd, cName, mode, context) + } + // Between the creation of the directory and the creation of gocryptfs.diriv // the directory is inconsistent. Take the lock to prevent other readers // from seeing it. @@ -49,7 +54,7 @@ func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.C dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0) if err == nil { // Create gocryptfs.diriv - err = nametransform.WriteDirIVAt(dirfd2, !rn.args.ZeroDirIV) + err = nametransform.WriteDirIVAt(dirfd2) syscall.Close(dirfd2) } if err != nil { @@ -90,62 +95,67 @@ func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.En return nil, fs.ToErrno(err) } st = syscallcompat.Unix2syscall(ust) - } else { - // We need write and execute permissions to create gocryptfs.diriv. - // Also, we need read permissions to open the directory (to avoid - // race-conditions between getting and setting the mode). - origMode := mode - mode = mode | 0700 - // Handle long file name - if nametransform.IsLongContent(cName) { - // Create ".name" - err := rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - return nil, fs.ToErrno(err) - } + // Create child node & return + ch := n.newChild(ctx, &st, out) + return ch, 0 - // Create directory - err = rn.mkdirWithIv(dirfd, cName, mode, context) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - return nil, fs.ToErrno(err) - } - } else { - err := rn.mkdirWithIv(dirfd, cName, mode, context) - if err != nil { - return nil, fs.ToErrno(err) - } - } + } + + // We need write and execute permissions to create gocryptfs.diriv. + // Also, we need read permissions to open the directory (to avoid + // race-conditions between getting and setting the mode). + origMode := mode + mode = mode | 0700 - fd, err := syscallcompat.Openat(dirfd, cName, - syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + // Handle long file name + if nametransform.IsLongContent(cName) { + // Create ".name" + err := rn.nameTransform.WriteLongNameAt(dirfd, cName, name) if err != nil { - tlog.Warn.Printf("Mkdir %q: Openat failed: %v", cName, err) return nil, fs.ToErrno(err) } - defer syscall.Close(fd) - - err = syscall.Fstat(fd, &st) + // Create directory & rollback .name file on error + err = rn.mkdirWithIv(dirfd, cName, mode, context) + if err != nil { + nametransform.DeleteLongNameAt(dirfd, cName) + return nil, fs.ToErrno(err) + } + } else { + err := rn.mkdirWithIv(dirfd, cName, mode, context) if err != nil { - tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err) return nil, fs.ToErrno(err) } + } - // Fix permissions - if origMode != mode { - // Preserve SGID bit if it was set due to inheritance. - origMode = uint32(st.Mode&^0777) | origMode - err = syscall.Fchmod(fd, origMode) - if err != nil { - tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err) - } + // Fill `st` + fd, err := syscallcompat.Openat(dirfd, cName, + syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + tlog.Warn.Printf("Mkdir %q: Openat failed: %v", cName, err) + return nil, fs.ToErrno(err) + } + defer syscall.Close(fd) + + err = syscall.Fstat(fd, &st) + if err != nil { + tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err) + return nil, fs.ToErrno(err) + } + + // Fix permissions + if origMode != mode { + // Preserve SGID bit if it was set due to inheritance. + origMode = uint32(st.Mode&^0777) | origMode + err = syscall.Fchmod(fd, origMode) + if err != nil { + tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err) } + } - // Create child node + // Create child node & return ch := n.newChild(ctx, &st, out) - return ch, 0 } @@ -175,7 +185,7 @@ func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { rn := n.rootNode() if !rn.args.PlaintextNames { // Read the DirIV from disk - cachedIV, err = nametransform.ReadDirIVAt(fd) + cachedIV, err = rn.nameTransform.ReadDirIVAt(fd) if err != nil { tlog.Warn.Printf("OpenDir %q: could not read %s: %v", cDirName, nametransform.DirIVFilename, err) return nil, syscall.EIO @@ -196,7 +206,7 @@ func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { plain = append(plain, cipherEntries[i]) continue } - if cName == nametransform.DirIVFilename { + if !rn.args.DeterministicNames && cName == nametransform.DirIVFilename { // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled continue } @@ -249,6 +259,15 @@ func (n *Node) Rmdir(ctx context.Context, name string) (code syscall.Errno) { err := unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) return fs.ToErrno(err) } + if rn.args.DeterministicNames { + if err := unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR); err != nil { + return fs.ToErrno(err) + } + if nametransform.IsLongContent(cName) { + nametransform.DeleteLongNameAt(parentDirFd, cName) + } + return 0 + } // Unless we are running as root, we need read, write and execute permissions // to handle gocryptfs.diriv. permWorkaround := false diff --git a/internal/fusefrontend/node_prepare_syscall.go b/internal/fusefrontend/node_prepare_syscall.go index 0894379..8a0e75c 100644 --- a/internal/fusefrontend/node_prepare_syscall.go +++ b/internal/fusefrontend/node_prepare_syscall.go @@ -8,7 +8,6 @@ import ( "github.com/hanwen/go-fuse/v2/fs" - "github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/syscallcompat" ) @@ -73,7 +72,7 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy // Cache store if !rn.args.PlaintextNames { var err error - iv, err = nametransform.ReadDirIVAt(dirfd) + iv, err = rn.nameTransform.ReadDirIVAt(dirfd) if err != nil { syscall.Close(dirfd) return -1, "", fs.ToErrno(err) diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go index c48781c..397e3ef 100644 --- a/internal/fusefrontend/xattr_unit_test.go +++ b/internal/fusefrontend/xattr_unit_test.go @@ -19,7 +19,7 @@ func newTestFS(args Args) *RootNode { key := make([]byte, cryptocore.KeyLen) cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true, false) cEnc := contentenc.New(cCore, contentenc.DefaultBS, false) - n := nametransform.New(cCore.EMECipher, true, true, nil) + n := nametransform.New(cCore.EMECipher, true, true, nil, false) rn := NewRootNode(args, cEnc, n) oneSec := time.Second options := &fs.Options{ -- cgit v1.2.3