From 6c26cda53122df871befd0c1e08b892e0d56d31e Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 1 Aug 2020 22:28:25 +0200 Subject: [PATCH] v2api/reverse: implement Readdir --- internal/fusefrontend_reverse/node_dir_ops.go | 81 +++++++++++++++++++ internal/fusefrontend_reverse/node_helpers.go | 6 ++ internal/fusefrontend_reverse/virtualfile.go | 15 ++++ 3 files changed, 102 insertions(+) create mode 100644 internal/fusefrontend_reverse/node_dir_ops.go create mode 100644 internal/fusefrontend_reverse/virtualfile.go diff --git a/internal/fusefrontend_reverse/node_dir_ops.go b/internal/fusefrontend_reverse/node_dir_ops.go new file mode 100644 index 0000000..c48d884 --- /dev/null +++ b/internal/fusefrontend_reverse/node_dir_ops.go @@ -0,0 +1,81 @@ +package fusefrontend_reverse + +import ( + "context" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" + + "github.com/rfjakob/gocryptfs/internal/configfile" + "github.com/rfjakob/gocryptfs/internal/nametransform" + "github.com/rfjakob/gocryptfs/internal/pathiv" + "github.com/rfjakob/gocryptfs/internal/syscallcompat" +) + +// Readdir - FUSE call. +// +// This function is symlink-safe through use of openBackingDir() and +// ReadDirIVAt(). +func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.Errno) { + dirfd, cName, errno := n.prepareAtSyscall("") + if errno != 0 { + return + } + defer syscall.Close(dirfd) + + // Read plaintext directory + var entries []fuse.DirEntry + fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + return nil, fs.ToErrno(err) + } + defer syscall.Close(fd) + entries, err = syscallcompat.Getdents(fd) + if err != nil { + return nil, fs.ToErrno(err) + } + + rn := n.rootNode() + if rn.args.PlaintextNames { + panic("todo") + } + + // Filter out excluded entries + //TODO + //entries = rfs.excludeDirEntries(relPath, entries) + + // Virtual files: at least one gocryptfs.diriv file + virtualFiles := []fuse.DirEntry{ + {Mode: virtualFileMode, Name: nametransform.DirIVFilename}, + } + + cipherPath := n.Path() + dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV) + // Encrypt names + for i := range entries { + var cName string + // ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf" + if n.isRoot() && entries[i].Name == configfile.ConfReverseName && + !rn.args.ConfigCustom { + cName = configfile.ConfDefaultName + } else { + cName = rn.nameTransform.EncryptName(entries[i].Name, dirIV) + if len(cName) > unix.NAME_MAX { + cName = rn.nameTransform.HashLongName(cName) + dotNameFile := fuse.DirEntry{ + Mode: virtualFileMode, + Name: cName + nametransform.LongNameSuffix, + } + virtualFiles = append(virtualFiles, dotNameFile) + } + } + entries[i].Name = cName + } + + // Add virtual files + entries = append(entries, virtualFiles...) + return fs.NewListDirStream(entries), 0 +} diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index a26ee81..24cdbd1 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -65,3 +65,9 @@ func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.Entry node := &Node{} return n.NewInode(ctx, node, id) } + +// isRoot returns true if this node is the root node +func (n *Node) isRoot() bool { + rn := n.rootNode() + return &rn.Node == n +} diff --git a/internal/fusefrontend_reverse/virtualfile.go b/internal/fusefrontend_reverse/virtualfile.go new file mode 100644 index 0000000..a92c127 --- /dev/null +++ b/internal/fusefrontend_reverse/virtualfile.go @@ -0,0 +1,15 @@ +package fusefrontend_reverse + +import ( + "syscall" +) + +const ( + // virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and + // *.name). They are always readable, as stated in func Access + virtualFileMode = syscall.S_IFREG | 0444 + // We use inomap's `Tag` feature to generate unique inode numbers for + // virtual files. These are the tags we use. + inoTagDirIV = 1 + inoTagNameFile = 2 +)