libgocryptfs/internal/fusefrontend_reverse/reverse_longnames.go
Jakob Unterwurzacher d3764b7753 reverse: fix longname decoding bug
This could have caused spurious ENOENT errors.

That it did not cause these errors all the time is interesting
and probably because an earlier readdir would place the entry
in the cache. This masks the bug.
2016-11-10 23:30:30 +01:00

101 lines
2.5 KiB
Go

package fusefrontend_reverse
import (
"os"
"path/filepath"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/tlog"
)
const (
shortNameMax = 176
)
var longnameParentCache map[string]string
var longnameCacheLock sync.Mutex
// Very simple cache cleaner: Nuke it every hour
func longnameCacheCleaner() {
for {
time.Sleep(time.Hour)
longnameCacheLock.Lock()
longnameParentCache = map[string]string{}
longnameCacheLock.Unlock()
}
}
func initLongnameCache() {
if longnameParentCache != nil {
return
}
longnameParentCache = map[string]string{}
go longnameCacheCleaner()
}
// findLongnameParent converts "gocryptfs.longname.XYZ" to the plaintext name
func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname string) (plaintextName string, err error) {
longnameCacheLock.Lock()
hit := longnameParentCache[longname]
longnameCacheLock.Unlock()
if hit != "" {
return hit, nil
}
absDir := filepath.Join(rfs.args.Cipherdir, dir)
dirfd, err := os.Open(absDir)
if err != nil {
tlog.Warn.Printf("findLongnameParent: opendir failed: %v\n", err)
return "", err
}
dirEntries, err := dirfd.Readdirnames(-1)
if err != nil {
return "", err
}
longnameCacheLock.Lock()
defer longnameCacheLock.Unlock()
for _, plaintextName = range dirEntries {
if len(plaintextName) <= shortNameMax {
continue
}
cName := rfs.nameTransform.EncryptName(plaintextName, dirIV)
if len(cName) <= syscall.NAME_MAX {
panic("logic error or wrong shortNameMax constant?")
}
hName := nametransform.HashLongName(cName)
longnameParentCache[hName] = plaintextName
if longname == hName {
hit = plaintextName
}
}
if hit == "" {
return "", syscall.ENOENT
}
return hit, nil
}
func (rfs *ReverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
dotName := filepath.Base(relPath) // gocryptfs.longname.XYZ.name
longname := dotName[:len(dotName)-len(nametransform.LongNameSuffix)] // gocryptfs.longname.XYZ
cDir := saneDir(relPath)
pDir, err := rfs.decryptPath(cDir)
if err != nil {
return nil, fuse.ToStatus(err)
}
dirIV := derivePathIV(cDir, ivPurposeDirIV)
e, err := rfs.findLongnameParent(pDir, dirIV, longname)
if err != nil {
return nil, fuse.ToStatus(err)
}
content := []byte(rfs.nameTransform.EncryptName(e, dirIV))
parentFile := filepath.Join(rfs.args.Cipherdir, pDir)
return rfs.newVirtualFile(content, parentFile)
}