diff options
author | Jakob Unterwurzacher | 2021-08-16 18:40:48 +0200 |
---|---|---|
committer | Jakob Unterwurzacher | 2021-08-16 19:23:58 +0200 |
commit | b2724070d95234a8cd281f275211e0f827a8bbe1 (patch) | |
tree | 0c4efc4714d0826ac99ec40c905a111c9f9c7015 /internal | |
parent | ad4b99170b9ad438909f5cba8c32109a18697a7a (diff) |
reverse mode: implement -one-file-system
Fixes https://github.com/rfjakob/gocryptfs/issues/475
Diffstat (limited to 'internal')
-rw-r--r-- | internal/fusefrontend/args.go | 4 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node.go | 13 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node_dir_ops.go | 23 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node_helpers.go | 5 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/root_node.go | 14 |
5 files changed, 50 insertions, 9 deletions
diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index ae1c30c..d92c3ff 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -49,4 +49,8 @@ type Args struct { // SharedStorage disables caching & hard link tracking, // enabled via cli flag "-sharedstorage" SharedStorage bool + // OneFileSystem disables crossing filesystem boundaries, + // like rsync's `--one-file-system` does. + // Only applicable to reverse mode. + OneFileSystem bool } diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go index 787b99b..1b2fd67 100644 --- a/internal/fusefrontend_reverse/node.go +++ b/internal/fusefrontend_reverse/node.go @@ -22,6 +22,10 @@ import ( // in a `gocryptfs -reverse` mount. type Node struct { fs.Inode + // isOtherFilesystem is used for --one-filesystem. + // It is set when the device number of this file or directory + // is different from n.rootNode().rootDev. + isOtherFilesystem bool } // Lookup - FUSE call for discovering a file. @@ -31,7 +35,14 @@ func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch if t == typeDiriv { // gocryptfs.diriv return n.lookupDiriv(ctx, out) - } else if t == typeName { + } + rn := n.rootNode() + if rn.args.OneFileSystem && n.isOtherFilesystem { + // With --one-file-system, we present mountpoints as empty. That is, + // it contains only a gocryptfs.diriv file (allowed above). + return nil, syscall.ENOENT + } + if t == typeName { // gocryptfs.longname.*.name return n.lookupLongnameName(ctx, cName, out) } else if t == typeConfig { diff --git a/internal/fusefrontend_reverse/node_dir_ops.go b/internal/fusefrontend_reverse/node_dir_ops.go index c287284..21b9775 100644 --- a/internal/fusefrontend_reverse/node_dir_ops.go +++ b/internal/fusefrontend_reverse/node_dir_ops.go @@ -23,6 +23,22 @@ import ( // This function is symlink-safe through use of openBackingDir() and // ReadDirIVAt(). func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.Errno) { + // Virtual files: at least one gocryptfs.diriv file + virtualFiles := []fuse.DirEntry{ + {Mode: virtualFileMode, Name: nametransform.DirIVFilename}, + } + rn := n.rootNode() + + // This directory is a mountpoint. Present it as empty. + if rn.args.OneFileSystem && n.isOtherFilesystem { + if rn.args.PlaintextNames { + return fs.NewListDirStream(nil), 0 + } else { + // An "empty" directory still has a gocryptfs.diriv file! + return fs.NewListDirStream(virtualFiles), 0 + } + } + d, errno := n.prepareAtSyscall("") if errno != 0 { return @@ -41,8 +57,6 @@ func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall. return nil, fs.ToErrno(err) } - rn := n.rootNode() - // Filter out excluded entries entries = rn.excludeDirEntries(d, entries) @@ -50,11 +64,6 @@ func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall. return n.readdirPlaintextnames(entries) } - // Virtual files: at least one gocryptfs.diriv file - virtualFiles := []fuse.DirEntry{ - {Mode: virtualFileMode, Name: nametransform.DirIVFilename}, - } - dirIV := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) // Encrypt names for i := range entries { diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index 92f6a87..7b286a0 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -91,6 +91,7 @@ func (n *Node) prepareAtSyscall(child string) (d *dirfdPlus, errno syscall.Errno // newChild attaches a new child inode to n. // The passed-in `st` will be modified to get a unique inode number. func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode { + isOtherFilesystem := (uint64(st.Dev) != n.rootNode().rootDev) // Get unique inode number rn := n.rootNode() rn.inoMap.TranslateStat(st) @@ -101,7 +102,9 @@ func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.Entry Gen: 1, Ino: st.Ino, } - node := &Node{} + node := &Node{ + isOtherFilesystem: isOtherFilesystem, + } return n.NewInode(ctx, node, id) } diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go index b072f85..d57e1e6 100644 --- a/internal/fusefrontend_reverse/root_node.go +++ b/internal/fusefrontend_reverse/root_node.go @@ -36,17 +36,31 @@ type RootNode struct { // inoMap translates inode numbers from different devices to unique inode // numbers. inoMap *inomap.InoMap + // rootDev stores the device number of the backing directory. Used for + // --one-file-system. + rootDev uint64 } // NewRootNode returns an encrypted FUSE overlay filesystem. // In this case (reverse mode) the backing directory is plain-text and // ReverseFS provides an encrypted view. func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode { + var rootDev uint64 + if args.OneFileSystem { + var st syscall.Stat_t + err := syscall.Stat(args.Cipherdir, &st) + if err != nil { + log.Panicf("Could not stat backing directory %q: %v", args.Cipherdir, err) + } + rootDev = uint64(st.Dev) + } + rn := &RootNode{ args: args, nameTransform: n, contentEnc: c, inoMap: inomap.New(), + rootDev: rootDev, } if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 { rn.excluder = prepareExcluder(args) |