diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend_reverse/node.go | 59 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/virtualfile.go | 100 | 
2 files changed, 153 insertions, 6 deletions
| diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go index 8af7bed..0b2df12 100644 --- a/internal/fusefrontend_reverse/node.go +++ b/internal/fusefrontend_reverse/node.go @@ -9,6 +9,8 @@ import (  	"github.com/hanwen/go-fuse/v2/fs"  	"github.com/hanwen/go-fuse/v2/fuse" +	"github.com/rfjakob/gocryptfs/internal/configfile" +	"github.com/rfjakob/gocryptfs/internal/pathiv"  	"github.com/rfjakob/gocryptfs/internal/syscallcompat"  ) @@ -19,13 +21,56 @@ type Node struct {  }  // Lookup - FUSE call for discovering a file. -// TODO handle virtual files  func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { -	dirfd, pName, errno := n.prepareAtSyscall(name) -	if errno != 0 { +	dirfd := int(-1) +	pName := "" +	t := n.lookupFileType(name) +	// gocryptfs.conf +	if t == typeConfig { +		var err error +		rn := n.rootNode() +		dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, "") +		if err != nil { +			errno = fs.ToErrno(err) +			return +		} +		defer syscall.Close(dirfd) +		pName = configfile.ConfReverseName +	} else if t == typeDiriv { +		// gocryptfs.diriv +		dirfd, pName, errno = n.prepareAtSyscall("") +		if errno != 0 { +			return +		} +		defer syscall.Close(dirfd) +		st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW) +		if err != nil { +			errno = fs.ToErrno(err) +			return +		} +		content := pathiv.Derive(n.Path(), pathiv.PurposeDirIV) +		var vf *virtualFile +		vf, errno = n.newVirtualFile(content, st, inoTagDirIV) +		if errno != 0 { +			return nil, errno +		} +		out.Attr = vf.attr +		// Create child node +		id := fs.StableAttr{Mode: uint32(vf.attr.Mode), Gen: 1, Ino: vf.attr.Ino} +		ch = n.NewInode(ctx, vf, id)  		return +	} else if t == typeName { +		// gocryptfs.longname.*.name + +		// TODO +	} else if t == typeReal { +		// real file +		dirfd, pName, errno = n.prepareAtSyscall(name) +		if errno != 0 { +			return +		} +		defer syscall.Close(dirfd)  	} -	defer syscall.Close(dirfd)  	// Get device number and inode number into `st`  	st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW) @@ -36,8 +81,10 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  	// Create new inode and fill `out`  	ch = n.newChild(ctx, st, out) -	// Translate ciphertext size in `out.Attr.Size` to plaintext size -	n.translateSize(dirfd, pName, &out.Attr) +	if t == typeReal { +		// Translate ciphertext size in `out.Attr.Size` to plaintext size +		n.translateSize(dirfd, pName, &out.Attr) +	}  	return ch, 0  } diff --git a/internal/fusefrontend_reverse/virtualfile.go b/internal/fusefrontend_reverse/virtualfile.go index a92c127..a7f6913 100644 --- a/internal/fusefrontend_reverse/virtualfile.go +++ b/internal/fusefrontend_reverse/virtualfile.go @@ -1,7 +1,16 @@  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/internal/configfile" +	"github.com/rfjakob/gocryptfs/internal/inomap" +	"github.com/rfjakob/gocryptfs/internal/nametransform"  )  const ( @@ -13,3 +22,94 @@ const (  	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 { +		// 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 +} + +type virtualFile struct { +	fs.Inode + +	// file content +	content []byte +	// attributes for Getattr() +	attr fuse.Attr +} + +// 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" (file descriptor). +// For a "gocryptfs.diriv" file, you would use the parent directory as +// "parentFile". +func (n *Node) newVirtualFile(content []byte, parentStat *syscall.Stat_t, inoTag uint8) (vf *virtualFile, 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 +	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) + +	vf = &virtualFile{content: content, attr: a} +	return +} + +// Open - FUSE call +func (f *virtualFile) 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 *virtualFile) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno { +	out.Attr = f.attr +	return 0 +} + +// Read - FUSE call +func (f *virtualFile) 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 +} | 
