package fusefrontend_reverse import ( "context" "log" "syscall" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/rfjakob/gocryptfs/v2/internal/configfile" "github.com/rfjakob/gocryptfs/v2/internal/inomap" "github.com/rfjakob/gocryptfs/v2/internal/nametransform" ) const ( // virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and // *.name). They are always readable, as stated in func Access virtualFileMode = syscall.S_IFREG | 0444 // We use inomap's `Tag` feature to generate unique inode numbers for // virtual files. These are the tags we use. inoTagDirIV = 1 inoTagNameFile = 2 ) type fileType int // Values returned by lookupFileType const ( // A real file/directory/symlink in the backing plaintext directory typeReal fileType = iota // A DirIV (gocryptfs.diriv) file typeDiriv // A gocryptfs.longname.*.name file for a file with a long name typeName // The config file gocryptfs.conf typeConfig ) // lookupFileType returns the type of child file name // (one of the fileType constants above). Called from Lookup(). func (n *Node) lookupFileType(cName string) fileType { rn := n.rootNode() // In -plaintextname mode, neither diriv nor longname files exist. if !rn.args.PlaintextNames { if !rn.args.DeterministicNames { // Is it a gocryptfs.diriv file? if cName == nametransform.DirIVFilename { return typeDiriv } } // Is it a gocryptfs.longname.*.name file? if t := nametransform.NameType(cName); t == nametransform.LongNameFilename { return typeName } } // gocryptfs.conf in the root directory. This is passed through to // .gocryptfs.reverse.conf in the backing plaintext directory. if n.isRoot() && !rn.args.ConfigCustom && cName == configfile.ConfDefaultName { return typeConfig } return typeReal } // VirtualMemNode is an in-memory node that does not have a representation // on disk. type VirtualMemNode struct { fs.Inode // file content content []byte // attributes for Getattr() attr fuse.Attr } // newVirtualMemNode creates a new in-memory file that does not have a representation // on disk. "content" is the file content. Timestamps and file owner are copied // from "parentFile" (file descriptor). // For a "gocryptfs.diriv" file, you would use the parent directory as // "parentFile". func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, inoTag uint8) (vf *VirtualMemNode, errno syscall.Errno) { if inoTag == 0 { log.Panicf("BUG: inoTag for virtual file is zero - this will cause ino collisions!") } // Adjust inode number and size rn := n.rootNode() st := parentStat if inoTag == inoTagNameFile { // No stable mapping for gocryptfs.longname.*.name files, instead use an // incrementing counter. We don't want two of those files to ever have the // same inode number, even for hard-linked files. st.Ino = rn.inoMap.NextSpillIno() } else { q := inomap.NewQIno(uint64(st.Dev), inoTag, uint64(st.Ino)) st.Ino = rn.inoMap.Translate(q) } st.Size = int64(len(content)) st.Mode = virtualFileMode st.Nlink = 1 var a fuse.Attr a.FromStat(st) if rn.args.ForceOwner != nil { a.Owner = *rn.args.ForceOwner } vf = &VirtualMemNode{content: content, attr: a} return } // Open - FUSE call func (f *VirtualMemNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { return nil, fuse.FOPEN_KEEP_CACHE, 0 } // GetAttr - FUSE call func (f *VirtualMemNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno { out.Attr = f.attr return 0 } // Read - FUSE call func (f *VirtualMemNode) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { end := int(off) + len(dest) if end > len(f.content) { end = len(f.content) } return fuse.ReadResultData(f.content[off:end]), 0 }