diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index 00d059f..e74592a 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -90,7 +90,8 @@ func WriteDirIV(dir string) error { return nil } -// EncryptPathDirIV - encrypt relative plaintext path using EME with DirIV. +// EncryptPathDirIV - encrypt relative plaintext path "plainPath" using EME with +// DirIV. "rootDir" is the backing storage root directory. // Components that are longer than 255 bytes are hashed if be.longnames == true. func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) { // Empty string means root directory @@ -103,7 +104,8 @@ func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cip if len(baseName) > syscall.NAME_MAX { return "", syscall.ENAMETOOLONG } - // Check if the DirIV is cached + // Check if the DirIV is cached. This catches the case of the user iterating + // over files in a directory pretty well. parentDir := filepath.Dir(plainPath) iv, cParentDir := be.DirIVCache.lookup(parentDir) if iv != nil { @@ -114,10 +116,27 @@ func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cip cipherPath = filepath.Join(cParentDir, cBaseName) return cipherPath, nil } - // Not cached - walk the directory tree - var wd = rootDir - var encryptedNames []string + // We have to walk the directory tree, in the worst case starting at the root + // directory. + wd := rootDir plainNames := strings.Split(plainPath, "/") + // So the DirIV we need is not cached. But maybe one level higher is + // cached. Then we can skip a few items in the directory walk. + // The catches the case of walking directories recursively. + parentDir2 := filepath.Dir(parentDir) + iv, cParentDir = be.DirIVCache.lookup(parentDir2) + if iv != nil { + parentDirBase := filepath.Base(parentDir) + cBaseName := be.EncryptName(parentDirBase, iv) + if be.longNames && len(cBaseName) > syscall.NAME_MAX { + cBaseName = be.HashLongName(cBaseName) + } + wd = filepath.Join(wd, cParentDir, cBaseName) + cipherPath = filepath.Join(cParentDir, cBaseName) + skip := len(strings.Split(cipherPath, "/")) + plainNames = plainNames[skip:] + } + // Walk the directory tree starting at "wd" for _, plainName := range plainNames { iv, err = ReadDirIV(wd) if err != nil { @@ -127,10 +146,9 @@ func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cip if be.longNames && len(encryptedName) > syscall.NAME_MAX { encryptedName = be.HashLongName(encryptedName) } - encryptedNames = append(encryptedNames, encryptedName) + cipherPath = filepath.Join(cipherPath, encryptedName) wd = filepath.Join(wd, encryptedName) } - cipherPath = filepath.Join(encryptedNames...) // Cache the final DirIV cParentDir = filepath.Dir(cipherPath) be.DirIVCache.store(parentDir, iv, cParentDir)