fusefrontend: make OpenDir() symlink-safe

Interestingly, little or no performance impact:

$ ./benchmark.bash
Testing gocryptfs at /tmp/benchmark.bash.39W: gocryptfs v1.6-42-g30c2349-dirty; go-fuse v20170619-66-g6df8ddc; 2018-11-04 go1.11
Downloading linux-3.0.tar.gz
/tmp/linux-3.0.tar.gz                  100%[=========================================================================>]  92.20M  2.93MB/s    in 31s
2018-11-04 21:44:44 URL:https://cdn.kernel.org/pub/linux/kernel/v3.0/linux-3.0.tar.gz [96675825/96675825] -> "/tmp/linux-3.0.tar.gz" [1]
WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 1.1808 s, 222 MB/s
READ:  262144000 bytes (262 MB, 250 MiB) copied, 0.866438 s, 303 MB/s
UNTAR: 24.745
MD5:   12.050
LS:    3.525
RM:    9.544

Note: kernel has been updated:

$ uname -a
Linux brikett 4.18.16-200.fc28.x86_64 #1 SMP Sat Oct 20 23:53:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
This commit is contained in:
Jakob Unterwurzacher 2018-11-04 21:46:51 +01:00
parent de3a2c1895
commit 21f1f858b9
2 changed files with 19 additions and 20 deletions

View File

@ -260,18 +260,21 @@ retry:
return fuse.OK return fuse.OK
} }
// OpenDir implements pathfs.FileSystem // OpenDir - FUSE call
//
// This function is symlink-safe through use of openBackingDir() and
// ReadDirIVAt().
func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
tlog.Debug.Printf("OpenDir(%s)", dirName) tlog.Debug.Printf("OpenDir(%s)", dirName)
cDirName, err := fs.encryptPath(dirName) parentDirFd, cDirName, err := fs.openBackingDir(dirName)
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }
defer syscall.Close(parentDirFd)
// Read ciphertext directory // Read ciphertext directory
cDirAbsPath := filepath.Join(fs.args.Cipherdir, cDirName)
var cipherEntries []fuse.DirEntry var cipherEntries []fuse.DirEntry
var status fuse.Status var status fuse.Status
fd, err := syscall.Open(cDirAbsPath, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0) fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }
@ -283,13 +286,9 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
// Get DirIV (stays nil if PlaintextNames is used) // Get DirIV (stays nil if PlaintextNames is used)
var cachedIV []byte var cachedIV []byte
if !fs.args.PlaintextNames { if !fs.args.PlaintextNames {
cachedIV, _ = fs.nameTransform.DirIVCache.Lookup(dirName) // Read the DirIV from disk
if cachedIV == nil { cachedIV, err = nametransform.ReadDirIVAt(fd)
// Read the DirIV from disk and store it in the cache
fs.dirIVLock.RLock()
cachedIV, err = nametransform.ReadDirIV(cDirAbsPath)
if err != nil { if err != nil {
fs.dirIVLock.RUnlock()
// The directory itself does not exist // The directory itself does not exist
if err == syscall.ENOENT { if err == syscall.ENOENT {
return nil, fuse.ENOENT return nil, fuse.ENOENT
@ -298,9 +297,6 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f
tlog.Warn.Printf("OpenDir %q: could not read gocryptfs.diriv: %v", cDirName, err) tlog.Warn.Printf("OpenDir %q: could not read gocryptfs.diriv: %v", cDirName, err)
return nil, fuse.EIO return nil, fuse.EIO
} }
fs.nameTransform.DirIVCache.Store(dirName, cachedIV, cDirName)
fs.dirIVLock.RUnlock()
}
} }
// Decrypted directory entries // Decrypted directory entries
var plain []fuse.DirEntry var plain []fuse.DirEntry

View File

@ -29,6 +29,9 @@ const (
// This function is exported because it allows for an efficient readdir implementation. // This function is exported because it allows for an efficient readdir implementation.
// If the directory itself cannot be opened, a syscall error will be returned. // If the directory itself cannot be opened, a syscall error will be returned.
// Otherwise, a fmt.Errorf() error value is returned with the details. // Otherwise, a fmt.Errorf() error value is returned with the details.
//
// TODO: this function is not symlink-safe and should be deleted once the only
// remaining user, EncryptPathDirIV(), is gone.
func ReadDirIV(dir string) (iv []byte, err error) { func ReadDirIV(dir string) (iv []byte, err error) {
fd, err := os.Open(filepath.Join(dir, DirIVFilename)) fd, err := os.Open(filepath.Join(dir, DirIVFilename))
if err != nil { if err != nil {