diff options
-rw-r--r-- | internal/fusefrontend_reverse/reverse_longnames.go | 51 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 15 |
2 files changed, 56 insertions, 10 deletions
diff --git a/internal/fusefrontend_reverse/reverse_longnames.go b/internal/fusefrontend_reverse/reverse_longnames.go index 46e1791..487802f 100644 --- a/internal/fusefrontend_reverse/reverse_longnames.go +++ b/internal/fusefrontend_reverse/reverse_longnames.go @@ -3,7 +3,9 @@ package fusefrontend_reverse import ( "os" "path/filepath" + "sync" "syscall" + "time" "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" @@ -15,7 +17,37 @@ const ( shortNameMax = 176 ) -func (rfs *reverseFS) findLongnameParent(dir string, dirIV []byte, longname string) (string, error) { +type dirIVNameStruct struct { + dirIV [nametransform.DirIVLen]byte + name string +} + +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 init() { + 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 { @@ -25,20 +57,27 @@ func (rfs *reverseFS) findLongnameParent(dir string, dirIV []byte, longname stri if err != nil { return "", err } - for _, e := range dirEntries { - if len(e) <= shortNameMax { + longnameCacheLock.Lock() + defer longnameCacheLock.Unlock() + for _, plaintextName = range dirEntries { + if len(plaintextName) <= shortNameMax { continue } - cName := rfs.nameTransform.EncryptName(e, dirIV) + 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 { - return e, nil + hit = plaintextName } } - return "", syscall.ENOENT + if hit == "" { + return "", syscall.ENOENT + } else { + return hit, nil + } } func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) { diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 7305687..9ebf91c 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -248,9 +248,15 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse. if entries == nil { return nil, status } + // Allocate maximum possible number of virtual files. + // If all files have long names we need a virtual ".name" file for each, + // plus one for gocryptfs.diriv. + virtualFiles := make([]fuse.DirEntry, len(entries)+1) // Virtual gocryptfs.diriv file - dirIVEntry := fuse.DirEntry{syscall.S_IFREG | 0400, nametransform.DirIVFilename} - virtualFiles := []fuse.DirEntry{dirIVEntry} + virtualFiles[0] = fuse.DirEntry{syscall.S_IFREG | 0400, nametransform.DirIVFilename} + // Actually used entries + nVirtual := 1 + // Encrypt names dirIV := deriveDirIV(cipherPath) for i := range entries { @@ -263,12 +269,13 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse. if len(cName) > syscall.NAME_MAX { cName = nametransform.HashLongName(cName) dotNameFile := fuse.DirEntry{syscall.S_IFREG | 0600, cName + nametransform.LongNameSuffix} - virtualFiles = append(virtualFiles, dotNameFile) + virtualFiles[nVirtual] = dotNameFile + nVirtual++ } } entries[i].Name = cName } - entries = append(entries, virtualFiles...) + entries = append(entries, virtualFiles[:nVirtual]...) return entries, fuse.OK } |