diff --git a/internal/fusefrontend_reverse/ctlsock_interface.go b/internal/fusefrontend_reverse/ctlsock_interface.go index c8ac379..a1e782d 100644 --- a/internal/fusefrontend_reverse/ctlsock_interface.go +++ b/internal/fusefrontend_reverse/ctlsock_interface.go @@ -13,8 +13,7 @@ import ( var _ ctlsock.Interface = &ReverseFS{} // Verify that interface is implemented. // EncryptPath implements ctlsock.Backend. -// This is actually not used inside reverse mode, but we implement it because -// third-party tools want to encrypt paths through the control socket. +// This is used for the control socket and for the "-exclude" logic. func (rfs *ReverseFS) EncryptPath(plainPath string) (string, error) { if rfs.args.PlaintextNames || plainPath == "" { return plainPath, nil diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 9ef6a94..50d995c 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -39,6 +39,7 @@ type ReverseFS struct { // Content encryption helper contentEnc *contentenc.ContentEnc // Relative ciphertext paths to exclude (hide) from the user. Used by -exclude. + // With -plaintextnames, these are relative *plaintext* paths. cExclude []string } @@ -288,7 +289,7 @@ func (rfs *ReverseFS) openDirPlaintextnames(relPath string, entries []fuse.DirEn if dupe >= 0 { // Warn the user loudly: The gocryptfs.conf_NAME_COLLISION file will // throw ENOENT errors that are hard to miss. - tlog.Warn.Printf("The file %s is mapped to %s and shadows another file. Please rename %s in %s .", + tlog.Warn.Printf("The file %q is mapped to %q and shadows another file. Please rename %q in directory %q.", configfile.ConfReverseName, configfile.ConfDefaultName, configfile.ConfDefaultName, rfs.args.Cipherdir) entries[dupe].Name = "gocryptfs.conf_NAME_COLLISION_" + fmt.Sprintf("%d", cryptocore.RandUint64()) } @@ -320,7 +321,12 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse. return nil, fuse.ToStatus(err) } if rfs.args.PlaintextNames { - return rfs.openDirPlaintextnames(cipherPath, entries) + entries, status := rfs.openDirPlaintextnames(cipherPath, entries) + if !status.Ok() { + return nil, status + } + entries = rfs.excludeDirEntries(cipherPath, entries) + return entries, fuse.OK } // Allocate maximum possible number of virtual files. // If all files have long names we need a virtual ".name" file for each, @@ -356,24 +362,33 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse. entries[i].Name = cName } // Filter out excluded entries - if rfs.cExclude != nil { - filtered := make([]fuse.DirEntry, 0, len(entries)) - for _, entry := range entries { - // filepath.Join handles the case of cipherPath="" correctly: - // Join("", "foo") -> "foo". This does not: cipherPath + "/" + name" - p := filepath.Join(cipherPath, entry.Name) - if rfs.isExcluded(p) { - // Skip file - continue - } - filtered = append(filtered, entry) - } - entries = filtered - } + entries = rfs.excludeDirEntries(cipherPath, entries) + // Add virtual files entries = append(entries, virtualFiles[:nVirtual]...) return entries, fuse.OK } +// excludeDirEntries filters out directory entries that are "-exclude"d. +// cDir is the relative ciphertext path to the directory these entries are +// from. +func (rfs *ReverseFS) excludeDirEntries(cDir string, entries []fuse.DirEntry) (filtered []fuse.DirEntry) { + if rfs.cExclude == nil { + return entries + } + filtered = make([]fuse.DirEntry, 0, len(entries)) + for _, entry := range entries { + // filepath.Join handles the case of cipherPath="" correctly: + // Join("", "foo") -> "foo". This does not: cipherPath + "/" + name" + p := filepath.Join(cDir, entry.Name) + if rfs.isExcluded(p) { + // Skip file + continue + } + filtered = append(filtered, entry) + } + return filtered +} + // StatFs - FUSE call. Returns information about the filesystem (free space // etc). // Securing statfs against symlink races seems to be more trouble than