diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index 63febc3..df04973 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -26,6 +26,8 @@ type CryptFS struct { // Stores an all-zero block of size cipherBS allZeroBlock []byte plaintextNames bool + // DirIV cache for filename encryption + DirIVCacheEnc DirIVCache } func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool) *CryptFS { diff --git a/cryptfs/kdf.go b/cryptfs/kdf.go index 73be0bb..db08500 100644 --- a/cryptfs/kdf.go +++ b/cryptfs/kdf.go @@ -1,10 +1,10 @@ package cryptfs import ( - "os" - "math" "fmt" "golang.org/x/crypto/scrypt" + "math" + "os" ) const ( @@ -50,5 +50,5 @@ func (s *scryptKdf) DeriveKey(pw string) []byte { // LogN - N is saved as 2^LogN, but LogN is much easier to work with. // This function gives you LogN = Log2(N). func (s *scryptKdf) LogN() int { - return int(math.Log2(float64(s.N))+0.5) + return int(math.Log2(float64(s.N)) + 0.5) } diff --git a/cryptfs/names_diriv.go b/cryptfs/names_diriv.go index ae8545b..9012e48 100644 --- a/cryptfs/names_diriv.go +++ b/cryptfs/names_diriv.go @@ -5,8 +5,47 @@ import ( "io/ioutil" "path/filepath" "strings" + "sync" ) +// A simple one-entry DirIV cache +type DirIVCache struct { + // Invalidated? + cleared bool + // The DirIV + iv []byte + // Directory the DirIV belongs to + dir string + // Ecrypted version of "dir" + translatedDir string + // Synchronisation + lock sync.RWMutex +} + +func (c *DirIVCache) lookup(dir string) (bool, []byte, string) { + c.lock.RLock() + defer c.lock.RUnlock() + if !c.cleared && c.dir == dir { + return true, c.iv, c.translatedDir + } + return false, nil, "" +} + +func (c *DirIVCache) store(dir string, iv []byte, translatedDir string) { + c.lock.Lock() + defer c.lock.Unlock() + c.cleared = false + c.iv = iv + c.dir = dir + c.translatedDir = translatedDir +} + +func (c *DirIVCache) Clear() { + c.lock.Lock() + defer c.lock.Unlock() + c.cleared = true +} + // readDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) func (be *CryptFS) ReadDirIV(dir string) (iv []byte, err error) { ivfile := filepath.Join(dir, DIRIV_FILENAME) @@ -41,11 +80,23 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, e if plainPath == "" { return plainPath, nil } + // Check if the DirIV is cached + parentDir := filepath.Dir(plainPath) + found, iv, cParentDir := be.DirIVCacheEnc.lookup(parentDir) + if found { + //fmt.Print("h") + baseName := filepath.Base(plainPath) + cBaseName := be.encryptName(baseName, iv) + cPath := cParentDir + "/" + cBaseName + return cPath, nil + } + // Walk the directory tree var wd = rootDir var encryptedNames []string + var err error plainNames := strings.Split(plainPath, "/") for _, plainName := range plainNames { - iv, err := be.ReadDirIV(wd) + iv, err = be.ReadDirIV(wd) if err != nil { return "", err } @@ -53,7 +104,11 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, e encryptedNames = append(encryptedNames, encryptedName) wd = filepath.Join(wd, encryptedName) } - return filepath.Join(encryptedNames...), nil + // Cache the final DirIV + cPath := strings.Join(encryptedNames, "/") + cParentDir = filepath.Dir(cPath) + be.DirIVCacheEnc.store(parentDir, iv, cParentDir) + return cPath, nil } // DecryptPathDirIV - encrypt path using CBC with DirIV diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go index fe9a8b0..736e5dc 100644 --- a/pathfs_frontend/fs.go +++ b/pathfs_frontend/fs.go @@ -251,6 +251,8 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu if err != nil { return fuse.ToStatus(err) } + // The new directory may take the place of an older one that is still in the cache + fs.CryptFS.DirIVCacheEnc.Clear() // Create directory fs.dirIVLock.Lock() defer fs.dirIVLock.Unlock() @@ -315,7 +317,9 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) { tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", st.Ino) tmpDirivPath := filepath.Join(parentDir, tmpName) cryptfs.Debug.Printf("Rmdir: Renaming %s to %s\n", cryptfs.DIRIV_FILENAME, tmpDirivPath) - fs.dirIVLock.Lock() // directory will be in an inconsistent state after the rename + // The directory is in an inconsistent state between rename and rmdir. Protect against + // concurrent readers. + fs.dirIVLock.Lock() defer fs.dirIVLock.Unlock() err = os.Rename(dirivPath, tmpDirivPath) if err != nil { @@ -338,7 +342,8 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) { if err != nil { cryptfs.Warn.Printf("Rmdir: Could not clean up %s: %v\n", tmpName, err) } - + // The now-deleted directory may have been in the DirIV cache. Clear it. + fs.CryptFS.DirIVCacheEnc.Clear() return fuse.OK } @@ -382,6 +387,10 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod if err != nil { return fuse.ToStatus(err) } + // The Rename may cause a directory to take the place of another directory. + // That directory may still be in the DirIV cache, clear it. + fs.CryptFS.DirIVCacheEnc.Clear() + return fs.FileSystem.Rename(cOldPath, cNewPath, context) } diff --git a/performance.txt b/performance.txt index 55678c3..2cd53a7 100644 --- a/performance.txt +++ b/performance.txt @@ -8,3 +8,4 @@ VERSION UNTAR LS RM v0.4 48 1.5 5 v0.5-rc1 56 7 19 v0.5-rc1-1 54 4.1 9 +v0.5-rc1-2 45 1.7 3.4