libgocryptfs/internal/fusefrontend_reverse/rpath.go
Jakob Unterwurzacher 778c955eea fusefrontend_reverse: switch to stable inode numbers
The volatile inode numbers that we used before cause "find" to complain and error out.
Virtual inode numbers are derived from their parent file inode number by adding 10^19,
which is hopefully large enough no never cause problems in practice.

If the backing directory contains inode numbers higher than that, stat() on these files
will return EOVERFLOW.

Example directory lising after this change:

  $ ls -i
               926473 gocryptfs.conf
  1000000000000926466 gocryptfs.diriv
               944878 gocryptfs.longname.hmZojMqC6ns47eyVxLlH2ailKjN9bxfosi3C-FR8mjA
  1000000000000944878 gocryptfs.longname.hmZojMqC6ns47eyVxLlH2ailKjN9bxfosi3C-FR8mjA.name
               934408 Tdfbf02CKsTaGVYnAsSypA
2017-04-01 17:19:15 +02:00

118 lines
3.6 KiB
Go

package fusefrontend_reverse
import (
"crypto/sha256"
"encoding/base64"
"path/filepath"
"strings"
"syscall"
"github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/tlog"
)
// saneDir is like filepath.Dir but returns "" instead of "."
func saneDir(path string) string {
d := filepath.Dir(path)
if d == "." {
return ""
}
return d
}
type ivPurposeType string
const (
ivPurposeDirIV ivPurposeType = "DIRIV"
ivPurposeFileID ivPurposeType = "FILEID"
ivPurposeSymlinkIV ivPurposeType = "SYMLINKIV"
ivPurposeBlock0IV ivPurposeType = "BLOCK0IV"
)
// derivePathIV derives an IV from an encrypted path by hashing it with sha256
func derivePathIV(path string, purpose ivPurposeType) []byte {
// Use null byte as separator as it cannot occur in the path
extended := []byte(path + "\000" + string(purpose))
hash := sha256.Sum256(extended)
return hash[:nametransform.DirIVLen]
}
// abs basically returns storage dir + "/" + relPath.
// It takes an error parameter so it can directly wrap decryptPath like this:
// a, err := rfs.abs(rfs.decryptPath(relPath))
// abs never generates an error on its own. In other words, abs(p, nil) never
// fails.
func (rfs *ReverseFS) abs(relPath string, err error) (string, error) {
if err != nil {
return "", err
}
return filepath.Join(rfs.args.Cipherdir, relPath), nil
}
func (rfs *ReverseFS) rDecryptName(cName string, dirIV []byte, pDir string) (pName string, err error) {
nameType := nametransform.NameType(cName)
if nameType == nametransform.LongNameNone {
pName, err = rfs.nameTransform.DecryptName(cName, dirIV)
if err != nil {
// We get lots of decrypt requests for names like ".Trash" that
// are invalid base64. Convert them to ENOENT so the correct
// error gets returned to the user.
if _, ok := err.(base64.CorruptInputError); ok {
return "", syscall.ENOENT
}
// Stat attempts on the link target of encrypted symlinks.
// These are always valid base64 but the length is not a
// multiple of 16.
if err == syscall.EINVAL {
return "", syscall.ENOENT
}
return "", err
}
} else if nameType == nametransform.LongNameContent {
pName, err = rfs.findLongnameParent(pDir, dirIV, cName)
if err != nil {
return "", err
}
} else {
// It makes no sense to decrypt a ".name" file. This is a virtual file
// that has no representation in the plaintext filesystem. ".name"
// files should have already been handled in virtualfile.go.
tlog.Warn.Printf("rDecryptName: cannot decrypt virtual file %q", cName)
return "", syscall.EINVAL
}
return pName, nil
}
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)
transformedPart, err := rfs.rDecryptName(parts[i], dirIV, currentPlainDir)
if err != nil {
return "", err
}
transformedParts = append(transformedParts, transformedPart)
}
pRelPath := filepath.Join(transformedParts...)
rPathCache.store(cDir, dirIV, saneDir(pRelPath))
return pRelPath, nil
}