diff options
Diffstat (limited to 'internal/fusefrontend/node_dir_ops.go')
-rw-r--r-- | internal/fusefrontend/node_dir_ops.go | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go new file mode 100644 index 0000000..38d0940 --- /dev/null +++ b/internal/fusefrontend/node_dir_ops.go @@ -0,0 +1,97 @@ +package fusefrontend + +import ( + "context" + "path/filepath" + "syscall" + + "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/syscallcompat" + "github.com/rfjakob/gocryptfs/internal/tlog" +) + +func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { + rn := n.rootNode() + p := n.path() + dirName := filepath.Base(p) + parentDirFd, cDirName, err := rn.openBackingDir(p) + if err != nil { + return nil, fs.ToErrno(err) + } + defer syscall.Close(parentDirFd) + + // Read ciphertext directory + var cipherEntries []fuse.DirEntry + fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + return nil, fs.ToErrno(err) + } + defer syscall.Close(fd) + cipherEntries, err = syscallcompat.Getdents(fd) + if err != nil { + return nil, fs.ToErrno(err) + } + // Get DirIV (stays nil if PlaintextNames is used) + var cachedIV []byte + if !rn.args.PlaintextNames { + // Read the DirIV from disk + cachedIV, err = nametransform.ReadDirIVAt(fd) + if err != nil { + tlog.Warn.Printf("OpenDir %q: could not read %s: %v", cDirName, nametransform.DirIVFilename, err) + return nil, syscall.EIO + } + } + // Decrypted directory entries + var plain []fuse.DirEntry + // Filter and decrypt filenames + for i := range cipherEntries { + cName := cipherEntries[i].Name + if dirName == "." && cName == configfile.ConfDefaultName { + // silently ignore "gocryptfs.conf" in the top level dir + continue + } + if rn.args.PlaintextNames { + plain = append(plain, cipherEntries[i]) + continue + } + if cName == nametransform.DirIVFilename { + // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled + continue + } + // Handle long file name + isLong := nametransform.LongNameNone + if rn.args.LongNames { + isLong = nametransform.NameType(cName) + } + if isLong == nametransform.LongNameContent { + cNameLong, err := nametransform.ReadLongNameAt(fd, cName) + if err != nil { + tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v", + cDirName, cName, err) + rn.reportMitigatedCorruption(cName) + continue + } + cName = cNameLong + } else if isLong == nametransform.LongNameFilename { + // ignore "gocryptfs.longname.*.name" + continue + } + name, err := rn.nameTransform.DecryptName(cName, cachedIV) + if err != nil { + tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v", + cDirName, cName, err) + rn.reportMitigatedCorruption(cName) + continue + } + // Override the ciphertext name with the plaintext name but reuse the rest + // of the structure + cipherEntries[i].Name = name + plain = append(plain, cipherEntries[i]) + } + + return fs.NewListDirStream(plain), 0 +} |