diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index af49776..9f52b28 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -82,18 +82,31 @@ func (rfs *ReverseFS) decryptPath(relPath string) (string, error) { if rfs.args.PlaintextNames || relPath == "" { return relPath, nil } + // Check if the parent dir is in the cache + cDir := saneDir(relPath) + dirIV, pDir := rPathCache.lookup(cDir) + if dirIV != nil { + cName := filepath.Base(relPath) + pName, err := rfs.rDecryptName(cName, dirIV, pDir) + if err != nil { + return "", err + } + return filepath.Join(pDir, pName), nil + } parts := strings.Split(relPath, "/") var transformedParts []string for i := range parts { // Start at the top and recurse currentCipherDir := filepath.Join(parts[:i]...) currentPlainDir := filepath.Join(transformedParts[:i]...) - dirIV := derivePathIV(currentCipherDir, ivPurposeDirIV) + dirIV = derivePathIV(currentCipherDir, ivPurposeDirIV) transformedPart, err := rfs.rDecryptName(parts[i], dirIV, currentPlainDir) if err != nil { return "", err } transformedParts = append(transformedParts, transformedPart) } - return filepath.Join(transformedParts...), nil + pRelPath := filepath.Join(transformedParts...) + rPathCache.store(cDir, dirIV, saneDir(pRelPath)) + return pRelPath, nil } diff --git a/internal/fusefrontend_reverse/rpath_cache.go b/internal/fusefrontend_reverse/rpath_cache.go new file mode 100644 index 0000000..81e945b --- /dev/null +++ b/internal/fusefrontend_reverse/rpath_cache.go @@ -0,0 +1,41 @@ +package fusefrontend_reverse + +import ( + "sync" +) + +// rPathCacheContainer is a simple one entry path cache. Because the dirIV +// is generated deterministically from the directory path, there is no need +// to ever invalidate entries. +type rPathCacheContainer struct { + sync.Mutex + // Relative ciphertext path to the directory + cPath string + // Relative plaintext path + pPath string + // Directory IV of the directory + dirIV []byte +} + +func (c *rPathCacheContainer) lookup(cPath string) ([]byte, string) { + c.Lock() + defer c.Unlock() + if cPath == c.cPath { + //fmt.Printf("HIT %q\n", cPath) + return c.dirIV, c.pPath + } + //fmt.Printf("MISS %q\n", cPath) + return nil, "" +} + +// store - write entry for "cPath" into the cache +func (c *rPathCacheContainer) store(cPath string, dirIV []byte, pPath string) { + //fmt.Printf("STORE %q\n", cPath) + c.Lock() + defer c.Unlock() + c.cPath = cPath + c.dirIV = dirIV + c.pPath = pPath +} + +var rPathCache rPathCacheContainer