diff options
Diffstat (limited to 'internal/fusefrontend_reverse_v1api/virtualfile.go')
-rw-r--r-- | internal/fusefrontend_reverse_v1api/virtualfile.go | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/internal/fusefrontend_reverse_v1api/virtualfile.go b/internal/fusefrontend_reverse_v1api/virtualfile.go new file mode 100644 index 0000000..f258e7f --- /dev/null +++ b/internal/fusefrontend_reverse_v1api/virtualfile.go @@ -0,0 +1,110 @@ +package fusefrontend_reverse + +import ( + "log" + "path/filepath" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/hanwen/go-fuse/v2/fuse" + "github.com/hanwen/go-fuse/v2/fuse/nodefs" + + "github.com/rfjakob/gocryptfs/internal/inomap" + "github.com/rfjakob/gocryptfs/internal/nametransform" + "github.com/rfjakob/gocryptfs/internal/pathiv" + "github.com/rfjakob/gocryptfs/internal/syscallcompat" + "github.com/rfjakob/gocryptfs/internal/tlog" +) + +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 +) + +func (rfs *ReverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) { + cDir := nametransform.Dir(cRelPath) + dir, err := rfs.decryptPath(cDir) + if err != nil { + return nil, fuse.ToStatus(err) + } + iv := pathiv.Derive(cDir, pathiv.PurposeDirIV) + return rfs.newVirtualFile(iv, rfs.args.Cipherdir, dir, inoTagDirIV) +} + +type virtualFile struct { + // Embed nodefs.defaultFile for a ENOSYS implementation of all methods + nodefs.File + // pointer to parent filesystem + rfs *ReverseFS + // file content + content []byte + // backing directory + cipherdir string + // path to a parent file (relative to cipherdir) + parentFile string + // inomap `Tag`. + // Depending on the file type, either `inoTagDirIV` or `inoTagNameFile`. + inoTag uint8 +} + +// newVirtualFile 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" (plaintext path relative to "cipherdir"). +// For a "gocryptfs.diriv" file, you would use the parent directory as +// "parentFile". +func (rfs *ReverseFS) newVirtualFile(content []byte, cipherdir string, parentFile string, inoTag uint8) (nodefs.File, fuse.Status) { + if inoTag == 0 { + log.Panicf("BUG: inoTag for virtual file is zero - this will cause ino collisions!") + } + return &virtualFile{ + File: nodefs.NewDefaultFile(), + rfs: rfs, + content: content, + cipherdir: cipherdir, + parentFile: parentFile, + inoTag: inoTag, + }, fuse.OK +} + +// Read - FUSE call +func (f *virtualFile) Read(buf []byte, off int64) (resultData fuse.ReadResult, status fuse.Status) { + if off >= int64(len(f.content)) { + return nil, fuse.OK + } + end := int(off) + len(buf) + if end > len(f.content) { + end = len(f.content) + } + return fuse.ReadResultData(f.content[off:end]), fuse.OK +} + +// GetAttr - FUSE call +func (f *virtualFile) GetAttr(a *fuse.Attr) fuse.Status { + dir := filepath.Dir(f.parentFile) + dirfd, err := syscallcompat.OpenDirNofollow(f.cipherdir, dir) + if err != nil { + return fuse.ToStatus(err) + } + defer syscall.Close(dirfd) + name := filepath.Base(f.parentFile) + var st2 unix.Stat_t + err = syscallcompat.Fstatat(dirfd, name, &st2, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + tlog.Debug.Printf("GetAttr: Fstatat %q: %v\n", f.parentFile, err) + return fuse.ToStatus(err) + } + st := syscallcompat.Unix2syscall(st2) + q := inomap.NewQIno(uint64(st.Dev), f.inoTag, uint64(st.Ino)) + st.Ino = f.rfs.inoMap.Translate(q) + st.Size = int64(len(f.content)) + st.Mode = virtualFileMode + st.Nlink = 1 + a.FromStat(&st) + return fuse.OK +} |