diff options
| author | Jakob Unterwurzacher | 2020-06-11 23:39:27 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-06-21 12:01:34 +0200 | 
| commit | 6aa9f5636f03392b5da5fc19dc4ea908e2e55e26 (patch) | |
| tree | d173bfb7f5d03e9a46026af5c34701a2b06a557d /internal/fusefrontend | |
| parent | 9b8ce55383fb6a0a146d0551a63d62b59be6eeb7 (diff) | |
v2api: implement Lookup()
Compiles, but untested otherwise. No caching.
Diffstat (limited to 'internal/fusefrontend')
| -rw-r--r-- | internal/fusefrontend/node.go | 54 | ||||
| -rw-r--r-- | internal/fusefrontend/node_openbackingdir.go | 68 | 
2 files changed, 118 insertions, 4 deletions
| diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index fde6a1a..f246408 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -2,13 +2,18 @@ package fusefrontend  import (  	"context" +	"path/filepath"  	"syscall" +	"golang.org/x/sys/unix" +  	"github.com/hanwen/go-fuse/v2/fs"  	"github.com/hanwen/go-fuse/v2/fuse"  	"github.com/rfjakob/gocryptfs/internal/contentenc" +	"github.com/rfjakob/gocryptfs/internal/inomap"  	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat"  )  // Node is a file or directory in the filesystem tree @@ -20,25 +25,66 @@ type Node struct {  // RootNode is the root of the filesystem tree of Nodes.  type RootNode struct {  	Node - -	// This flag is set to zero each time fs.isFiltered() is called +	// args stores configuration arguments +	args Args +	// Filename encryption helper +	nameTransform nametransform.NameTransformer +	// Content encryption helper +	contentEnc *contentenc.ContentEnc +	// IsIdle flag is set to zero each time fs.isFiltered() is called  	// (uint32 so that it can be reset with CompareAndSwapUint32).  	// When -idle was used when mounting, idleMonitor() sets it to 1  	// periodically.  	IsIdle uint32 + +	// inoMap translates inode numbers from different devices to unique inode +	// numbers. +	inoMap *inomap.InoMap  }  func NewRootNode(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *RootNode {  	// TODO -	return &RootNode{} +	return &RootNode{ +		args:          args, +		nameTransform: n, +		contentEnc:    c, +		inoMap:        inomap.New(), +	}  } +// path returns the relative plaintext path of this node  func (n *Node) path() string {  	return n.Path(n.Root())  } +func (n *Node) rootNode() *RootNode { +	return n.Root().Operations().(*RootNode) +} +  func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { -	return nil, 1 +	rn := n.rootNode() +	p := filepath.Join(n.path(), name) +	dirfd, cName, err := rn.openBackingDir(p) +	if err != nil { +		return nil, fs.ToErrno(err) +	} +	// Get device number and inode number into `st` +	st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) +	if err != nil { +		return nil, fs.ToErrno(err) +	} +	// Get unique inode number +	rn.inoMap.TranslateStat(st) +	out.Attr.FromStat(st) +	// Create child node +	id := fs.StableAttr{ +		Mode: uint32(st.Mode), +		Gen:  1, +		Ino:  st.Ino, +	} +	node := &Node{} +	ch := n.NewInode(ctx, node, id) +	return ch, 0  }  func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno { diff --git a/internal/fusefrontend/node_openbackingdir.go b/internal/fusefrontend/node_openbackingdir.go new file mode 100644 index 0000000..733c239 --- /dev/null +++ b/internal/fusefrontend/node_openbackingdir.go @@ -0,0 +1,68 @@ +package fusefrontend + +import ( +	"path/filepath" +	"strings" +	"syscall" + +	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat" +) + +// openBackingDir opens the parent ciphertext directory of plaintext path +// "relPath". It returns the dirfd (opened with O_PATH) and the encrypted +// basename. +// +// The caller should then use Openat(dirfd, cName, ...) and friends. +// For convenience, if relPath is "", cName is going to be ".". +// +// openBackingDir is secure against symlink races by using Openat and +// ReadDirIVAt. +func (rn *RootNode) openBackingDir(relPath string) (dirfd int, cName string, err error) { +	dirRelPath := nametransform.Dir(relPath) +	// With PlaintextNames, we don't need to read DirIVs. Easy. +	if rn.args.PlaintextNames { +		dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, dirRelPath) +		if err != nil { +			return -1, "", err +		} +		// If relPath is empty, cName is ".". +		cName = filepath.Base(relPath) +		return dirfd, cName, nil +	} +	// Open cipherdir (following symlinks) +	dirfd, err = syscall.Open(rn.args.Cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) +	if err != nil { +		return -1, "", err +	} +	// If relPath is empty, cName is ".". +	if relPath == "" { +		return dirfd, ".", nil +	} +	// Walk the directory tree +	parts := strings.Split(relPath, "/") +	for i, name := range parts { +		iv, err := nametransform.ReadDirIVAt(dirfd) +		if err != nil { +			syscall.Close(dirfd) +			return -1, "", err +		} +		cName, err = rn.nameTransform.EncryptAndHashName(name, iv) +		if err != nil { +			syscall.Close(dirfd) +			return -1, "", err +		} +		// Last part? We are done. +		if i == len(parts)-1 { +			break +		} +		// Not the last part? Descend into next directory. +		dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) +		syscall.Close(dirfd) +		if err != nil { +			return -1, "", err +		} +		dirfd = dirfd2 +	} +	return dirfd, cName, nil +} | 
