fusefrontend: make dirCache work for "node itself"

"node itself" can be converted to node + child by
ascending one level.

Performance gains are spectacular, as will be seen
in the next commit.
This commit is contained in:
Jakob Unterwurzacher 2021-04-07 07:15:14 +02:00
parent 770c4deb71
commit 4a07d6598c
2 changed files with 34 additions and 11 deletions

View File

@ -2,7 +2,9 @@ package fusefrontend
import ( import (
"context" "context"
"log"
"path/filepath" "path/filepath"
"sync/atomic"
"syscall" "syscall"
"github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fs"
@ -82,13 +84,37 @@ func (n *Node) rootNode() *RootNode {
// //
// If you pass a `child` file name, the (dirfd, cName) pair will refer to // If you pass a `child` file name, the (dirfd, cName) pair will refer to
// 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. For
// the root node, that means (dirfd, ".").
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() rn := n.rootNode()
// all filesystem operations go through prepareAtSyscall(), so this is a
// good place to reset the idle marker.
atomic.StoreUint32(&rn.IsIdle, 0)
// root node itself is special
if child == "" && n.IsRoot() {
var err error
dirfd, cName, err = rn.openBackingDir("")
if err != nil {
errno = fs.ToErrno(err)
}
return
}
// normal node itself can be converted to child of parent node
if child == "" {
name, p1 := n.Parent()
if p1 == nil || name == "" {
return -1, "", syscall.ENOENT
}
p2 := toNode(p1.Operations())
return p2.prepareAtSyscall(name)
}
// Cache lookup // Cache lookup
// TODO: also handle caching for root node & plaintextnames // TODO make it work for plaintextnames as well?
cacheable := (child != "" && !rn.args.PlaintextNames) cacheable := (!rn.args.PlaintextNames)
if cacheable { if cacheable {
var iv []byte var iv []byte
dirfd, iv = rn.dirCache.Lookup(n) dirfd, iv = rn.dirCache.Lookup(n)
@ -102,10 +128,10 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
} }
// Slowpath // Slowpath
p := n.Path() if child == "" {
if child != "" { log.Panicf("BUG: child name is empty - this cannot happen")
p = filepath.Join(p, child)
} }
p := filepath.Join(n.Path(), child)
if rn.isFiltered(p) { if rn.isFiltered(p) {
errno = syscall.EPERM errno = syscall.EPERM
return return
@ -113,12 +139,12 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
dirfd, cName, err := rn.openBackingDir(p) dirfd, cName, err := rn.openBackingDir(p)
if err != nil { if err != nil {
errno = fs.ToErrno(err) errno = fs.ToErrno(err)
return
} }
// Cache store // Cache store
// TODO: also handle caching for root node & plaintextnames
if cacheable { if cacheable {
// TODO: openBackingDir already calls ReadDirIVAt(). Get the data out. // TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work?
iv, err := nametransform.ReadDirIVAt(dirfd) iv, err := nametransform.ReadDirIVAt(dirfd)
if err != nil { if err != nil {
syscall.Close(dirfd) syscall.Close(dirfd)

View File

@ -5,7 +5,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"syscall" "syscall"
"time" "time"
@ -127,8 +126,6 @@ func (rn *RootNode) reportMitigatedCorruption(item string) {
// //
// Prevents name clashes with internal files when file names are not encrypted // Prevents name clashes with internal files when file names are not encrypted
func (rn *RootNode) isFiltered(path string) bool { func (rn *RootNode) isFiltered(path string) bool {
atomic.StoreUint32(&rn.IsIdle, 0)
if !rn.args.PlaintextNames { if !rn.args.PlaintextNames {
return false return false
} }