fs: add initial dirfd caching
dirfd caching was temporarily removed when moving to the v2api. Add it back to gain back some lost speed.
This commit is contained in:
parent
6aae2aad97
commit
24d5d39300
@ -21,13 +21,11 @@ const (
|
|||||||
enableDebugMessages = false
|
enableDebugMessages = false
|
||||||
// Enable hit rate statistics printing
|
// Enable hit rate statistics printing
|
||||||
enableStats = false
|
enableStats = false
|
||||||
|
|
||||||
pathFmt = "%-40q"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type dirCacheEntryStruct struct {
|
type dirCacheEntryStruct struct {
|
||||||
// relative plaintext path to the directory
|
// pointer to the Node this entry belongs to
|
||||||
dirRelPath string
|
node *Node
|
||||||
// fd to the directory (opened with O_PATH!)
|
// fd to the directory (opened with O_PATH!)
|
||||||
fd int
|
fd int
|
||||||
// content of gocryptfs.diriv in this directory
|
// content of gocryptfs.diriv in this directory
|
||||||
@ -46,7 +44,7 @@ func (e *dirCacheEntryStruct) Clear() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.fd = -1
|
e.fd = -1
|
||||||
e.dirRelPath = ""
|
e.node = nil
|
||||||
e.iv = nil
|
e.iv = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +74,7 @@ func (d *dirCacheStruct) Clear() {
|
|||||||
|
|
||||||
// Store the entry in the cache. The passed "fd" will be Dup()ed, and the caller
|
// Store the entry in the cache. The passed "fd" will be Dup()ed, and the caller
|
||||||
// can close their copy at will.
|
// can close their copy at will.
|
||||||
func (d *dirCacheStruct) Store(dirRelPath string, fd int, iv []byte) {
|
func (d *dirCacheStruct) Store(node *Node, fd int, iv []byte) {
|
||||||
// Note: package ensurefds012, imported from main, guarantees that dirCache
|
// Note: package ensurefds012, imported from main, guarantees that dirCache
|
||||||
// can never get fds 0,1,2.
|
// can never get fds 0,1,2.
|
||||||
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
||||||
@ -94,9 +92,9 @@ func (d *dirCacheStruct) Store(dirRelPath string, fd int, iv []byte) {
|
|||||||
tlog.Warn.Printf("dirCache.Store: Dup failed: %v", err)
|
tlog.Warn.Printf("dirCache.Store: Dup failed: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.dbg("Store "+pathFmt+" fd=%d iv=%x\n", dirRelPath, fd2, iv)
|
d.dbg("dirCache.Store %p fd=%d iv=%x\n", node, fd2, iv)
|
||||||
e.fd = fd2
|
e.fd = fd2
|
||||||
e.dirRelPath = dirRelPath
|
e.node = node
|
||||||
e.iv = iv
|
e.iv = iv
|
||||||
// expireThread is started on the first Lookup()
|
// expireThread is started on the first Lookup()
|
||||||
if !d.expireThreadRunning {
|
if !d.expireThreadRunning {
|
||||||
@ -108,7 +106,7 @@ func (d *dirCacheStruct) Store(dirRelPath string, fd int, iv []byte) {
|
|||||||
// Lookup checks if relPath is in the cache, and returns an (fd, iv) pair.
|
// Lookup checks if relPath is in the cache, and returns an (fd, iv) pair.
|
||||||
// It returns (-1, nil) if not found. The fd is internally Dup()ed and the
|
// It returns (-1, nil) if not found. The fd is internally Dup()ed and the
|
||||||
// caller must close it when done.
|
// caller must close it when done.
|
||||||
func (d *dirCacheStruct) Lookup(dirRelPath string) (fd int, iv []byte) {
|
func (d *dirCacheStruct) Lookup(node *Node) (fd int, iv []byte) {
|
||||||
d.Lock()
|
d.Lock()
|
||||||
defer d.Unlock()
|
defer d.Unlock()
|
||||||
if enableStats {
|
if enableStats {
|
||||||
@ -121,7 +119,7 @@ func (d *dirCacheStruct) Lookup(dirRelPath string) (fd int, iv []byte) {
|
|||||||
// Cache slot is empty
|
// Cache slot is empty
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if dirRelPath != e.dirRelPath {
|
if node != e.node {
|
||||||
// Not the right path
|
// Not the right path
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -135,7 +133,7 @@ func (d *dirCacheStruct) Lookup(dirRelPath string) (fd int, iv []byte) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if fd == 0 {
|
if fd == 0 {
|
||||||
d.dbg("Lookup "+pathFmt+" miss\n", dirRelPath)
|
d.dbg("dirCache.Lookup %p miss\n", node)
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
if enableStats {
|
if enableStats {
|
||||||
@ -144,7 +142,7 @@ func (d *dirCacheStruct) Lookup(dirRelPath string) (fd int, iv []byte) {
|
|||||||
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
if fd <= 0 || len(iv) != nametransform.DirIVLen {
|
||||||
log.Panicf("Lookup sanity check failed: fd=%d len=%d", fd, len(iv))
|
log.Panicf("Lookup sanity check failed: fd=%d len=%d", fd, len(iv))
|
||||||
}
|
}
|
||||||
d.dbg("Lookup "+pathFmt+" hit fd=%d dup=%d iv=%x\n", dirRelPath, e.fd, fd, iv)
|
d.dbg("dirCache.Lookup %p hit fd=%d dup=%d iv=%x\n", node, e.fd, fd, iv)
|
||||||
return fd, iv
|
return fd, iv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
@ -83,11 +84,28 @@ func (n *Node) rootNode() *RootNode {
|
|||||||
// a child of this node.
|
// a child of this node.
|
||||||
// If `child` is empty, the (dirfd, cName) pair refers to this node itself.
|
// If `child` is empty, the (dirfd, cName) pair refers to this node itself.
|
||||||
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
|
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
|
||||||
|
rn := n.rootNode()
|
||||||
|
|
||||||
|
// Cache lookup
|
||||||
|
// TODO: also handle caching for root node & plaintextnames
|
||||||
|
cacheable := (child != "" && !rn.args.PlaintextNames)
|
||||||
|
if cacheable {
|
||||||
|
var iv []byte
|
||||||
|
dirfd, iv = rn.dirCache.Lookup(n)
|
||||||
|
if dirfd > 0 {
|
||||||
|
cName, err := rn.nameTransform.EncryptAndHashName(child, iv)
|
||||||
|
if err != nil {
|
||||||
|
return -1, "", fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
return dirfd, cName, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slowpath
|
||||||
p := n.Path()
|
p := n.Path()
|
||||||
if child != "" {
|
if child != "" {
|
||||||
p = filepath.Join(p, child)
|
p = filepath.Join(p, child)
|
||||||
}
|
}
|
||||||
rn := n.rootNode()
|
|
||||||
if rn.isFiltered(p) {
|
if rn.isFiltered(p) {
|
||||||
errno = syscall.EPERM
|
errno = syscall.EPERM
|
||||||
return
|
return
|
||||||
@ -96,6 +114,18 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
errno = fs.ToErrno(err)
|
errno = fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache store
|
||||||
|
// TODO: also handle caching for root node & plaintextnames
|
||||||
|
if cacheable {
|
||||||
|
// TODO: openBackingDir already calls ReadDirIVAt(). Get the data out.
|
||||||
|
iv, err := nametransform.ReadDirIVAt(dirfd)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(dirfd)
|
||||||
|
return -1, "", fs.ToErrno(err)
|
||||||
|
}
|
||||||
|
rn.dirCache.Store(n, dirfd, iv)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ type RootNode struct {
|
|||||||
// When -idle was used when mounting, idleMonitor() sets it to 1
|
// When -idle was used when mounting, idleMonitor() sets it to 1
|
||||||
// periodically.
|
// periodically.
|
||||||
IsIdle uint32
|
IsIdle uint32
|
||||||
|
// dirCache caches directory fds
|
||||||
|
dirCache dirCacheStruct
|
||||||
// inoMap translates inode numbers from different devices to unique inode
|
// inoMap translates inode numbers from different devices to unique inode
|
||||||
// numbers.
|
// numbers.
|
||||||
inoMap inomap.TranslateStater
|
inoMap inomap.TranslateStater
|
||||||
|
Loading…
x
Reference in New Issue
Block a user