aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend/node_helpers.go
diff options
context:
space:
mode:
authorJakob Unterwurzacher2021-04-07 07:15:14 +0200
committerJakob Unterwurzacher2021-04-07 07:18:35 +0200
commit4a07d6598cc2633634f7ff8485b506f2a8bd3eaf (patch)
tree1f1d1e6c6ac90bd2a38dc70344e02fdb55b63c63 /internal/fusefrontend/node_helpers.go
parent770c4deb7182b17a5a2f35a7ebfc0f5ef0cae158 (diff)
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.
Diffstat (limited to 'internal/fusefrontend/node_helpers.go')
-rw-r--r--internal/fusefrontend/node_helpers.go42
1 files changed, 34 insertions, 8 deletions
diff --git a/internal/fusefrontend/node_helpers.go b/internal/fusefrontend/node_helpers.go
index c44a559..6c30523 100644
--- a/internal/fusefrontend/node_helpers.go
+++ b/internal/fusefrontend/node_helpers.go
@@ -2,7 +2,9 @@ package fusefrontend
import (
"context"
+ "log"
"path/filepath"
+ "sync/atomic"
"syscall"
"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
// 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) {
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
- // TODO: also handle caching for root node & plaintextnames
- cacheable := (child != "" && !rn.args.PlaintextNames)
+ // TODO make it work for plaintextnames as well?
+ cacheable := (!rn.args.PlaintextNames)
if cacheable {
var iv []byte
dirfd, iv = rn.dirCache.Lookup(n)
@@ -102,10 +128,10 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
}
// Slowpath
- p := n.Path()
- if child != "" {
- p = filepath.Join(p, child)
+ if child == "" {
+ log.Panicf("BUG: child name is empty - this cannot happen")
}
+ p := filepath.Join(n.Path(), child)
if rn.isFiltered(p) {
errno = syscall.EPERM
return
@@ -113,12 +139,12 @@ func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno sy
dirfd, cName, err := rn.openBackingDir(p)
if err != nil {
errno = fs.ToErrno(err)
+ return
}
// Cache store
- // TODO: also handle caching for root node & plaintextnames
if cacheable {
- // TODO: openBackingDir already calls ReadDirIVAt(). Get the data out.
+ // TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work?
iv, err := nametransform.ReadDirIVAt(dirfd)
if err != nil {
syscall.Close(dirfd)