56888d83dd
This makes sure writing to a file fails early if the underlying filesystem does not support fallocate. It also prevents partial header write due to ENOSPC.
139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
package cryptfs
|
|
|
|
import (
|
|
"fmt"
|
|
"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
|
|
}
|
|
|
|
// lookup - fetch entry for "dir" from the cache
|
|
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, ""
|
|
}
|
|
|
|
// store - write entry for "dir" into the caches
|
|
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)
|
|
Debug.Printf("readDirIV: reading %s\n", ivfile)
|
|
iv, err = ioutil.ReadFile(ivfile)
|
|
if err != nil {
|
|
Warn.Printf("readDirIV: %v\n", err)
|
|
return nil, err
|
|
}
|
|
if len(iv) != DIRIV_LEN {
|
|
return nil, fmt.Errorf("readDirIV: Invalid length %d\n", len(iv))
|
|
}
|
|
return iv, nil
|
|
}
|
|
|
|
// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path)
|
|
// This function is exported because it is used from pathfs_frontend, main,
|
|
// and also the automated tests.
|
|
func WriteDirIV(dir string) error {
|
|
iv := RandBytes(DIRIV_LEN)
|
|
file := filepath.Join(dir, DIRIV_FILENAME)
|
|
// 0444 permissions: the file is not secret but should not be written to
|
|
return ioutil.WriteFile(file, iv, 0444)
|
|
}
|
|
|
|
// EncryptPathDirIV - encrypt path using CBC with DirIV
|
|
func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string) (string, error) {
|
|
if be.plaintextNames {
|
|
return plainPath, nil
|
|
}
|
|
// Empty string means root directory
|
|
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)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
encryptedName := be.encryptName(plainName, iv)
|
|
encryptedNames = append(encryptedNames, encryptedName)
|
|
wd = filepath.Join(wd, encryptedName)
|
|
}
|
|
// 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
|
|
func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) {
|
|
if be.plaintextNames {
|
|
return encryptedPath, nil
|
|
}
|
|
var wd = rootDir
|
|
var plainNames []string
|
|
encryptedNames := strings.Split(encryptedPath, "/")
|
|
Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames)
|
|
for _, encryptedName := range encryptedNames {
|
|
iv, err := be.ReadDirIV(wd)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plainName, err := be.DecryptName(encryptedName, iv)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plainNames = append(plainNames, plainName)
|
|
wd = filepath.Join(wd, encryptedName)
|
|
}
|
|
return filepath.Join(plainNames...), nil
|
|
}
|