// OpenDir - FUSE readdir call func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { relPath, err := rfs.decryptPath(cipherPath) if err != nil { return nil, fuse.ToStatus(err) } // Read plaintext dir entries, status := rfs.loopbackfs.OpenDir(relPath, context) if entries == nil { return nil, status } if rfs.args.PlaintextNames { return rfs.openDirPlaintextnames(cipherPath, entries) } // 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 virtualFiles[0] = fuse.DirEntry{ Mode: syscall.S_IFREG | 0400, Name: nametransform.DirIVFilename, } // Actually used entries nVirtual := 1 // Encrypt names dirIV := derivePathIV(cipherPath, ivPurposeDirIV) for i := range entries { var cName string // ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf" if cipherPath == "" && entries[i].Name == configfile.ConfReverseName { cName = configfile.ConfDefaultName } else { cName = rfs.nameTransform.EncryptName(entries[i].Name, dirIV) if len(cName) > syscall.NAME_MAX { cName = nametransform.HashLongName(cName) dotNameFile := fuse.DirEntry{ Mode: syscall.S_IFREG | 0600, Name: cName + nametransform.LongNameSuffix, } virtualFiles[nVirtual] = dotNameFile nVirtual++ } } entries[i].Name = cName } entries = append(entries, virtualFiles[:nVirtual]...) return entries, fuse.OK }
// EncryptPath implements ctlsock.Backend. // This is actually not used inside reverse mode, but we implement it because // third-party tools want to encrypt paths through the control socket. func (rfs *ReverseFS) EncryptPath(plainPath string) (string, error) { if rfs.args.PlaintextNames || plainPath == "" { return plainPath, nil } cipherPath := "" parts := strings.Split(plainPath, "/") for _, part := range parts { dirIV := derivePathIV(cipherPath, ivPurposeDirIV) encryptedPart := rfs.nameTransform.EncryptName(part, dirIV) if rfs.args.LongNames && len(encryptedPart) > syscall.NAME_MAX { encryptedPart = nametransform.HashLongName(encryptedPart) } cipherPath = filepath.Join(cipherPath, encryptedPart) } return cipherPath, nil }
// 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 { log.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 }