diff options
| author | Jakob Unterwurzacher | 2020-08-02 19:33:12 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-08-02 19:33:12 +0200 | 
| commit | 84ed139cd2cede9b773fe7892a0bc2515fc1f00f (patch) | |
| tree | 9886e8fdce9450fd6232e532d5e8e939871dd2fc | |
| parent | 4674bac8381838718f0defba0f2e0d9eba2a41a3 (diff) | |
v2api/reverse: implement Lookup for longname
| -rw-r--r-- | internal/fusefrontend_reverse/node.go | 46 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/node_helpers.go | 84 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/root_node.go | 41 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rpath.go | 13 | ||||
| -rw-r--r-- | internal/nametransform/names.go | 4 | 
5 files changed, 148 insertions, 40 deletions
| diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go index 0b2df12..713beee 100644 --- a/internal/fusefrontend_reverse/node.go +++ b/internal/fusefrontend_reverse/node.go @@ -10,7 +10,6 @@ import (  	"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"  ) @@ -25,9 +24,16 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  	dirfd := int(-1)  	pName := ""  	t := n.lookupFileType(name) -	// gocryptfs.conf -	if t == typeConfig { +	if t == typeDiriv { +		// gocryptfs.diriv +		return n.lookupDiriv(ctx, out) +	} else if t == typeName { +		// gocryptfs.longname.*.name +		return n.lookupLongnameName(ctx, name, out) +	} else if t == typeConfig { +		// gocryptfs.conf  		var err error +		pName = configfile.ConfReverseName  		rn := n.rootNode()  		dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, "")  		if err != nil { @@ -35,34 +41,6 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  			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) @@ -71,21 +49,17 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  		}  		defer syscall.Close(dirfd)  	} -  	// Get device number and inode number into `st`  	st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW)  	if err != nil {  		return nil, fs.ToErrno(err)  	} -  	// Create new inode and fill `out`  	ch = n.newChild(ctx, st, out) - +	// Translate ciphertext size in `out.Attr.Size` to plaintext size  	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/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index 24cdbd1..df9de9b 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -5,8 +5,24 @@ import (  	"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/pathiv" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat" +) + +const ( +	// File names are padded to 16-byte multiples, encrypted and +	// base64-encoded. We can encode at most 176 bytes to stay below the 255 +	// bytes limit: +	// * base64(176 bytes) = 235 bytes +	// * base64(192 bytes) = 256 bytes (over 255!) +	// But the PKCS#7 padding is at least one byte. This means we can only use +	// 175 bytes for the file name. +	shortNameMax = 175  )  // translateSize translates the ciphertext size in `out` into plaintext size. @@ -36,13 +52,13 @@ func (n *Node) rootNode() *RootNode {  // If you pass a `child` file name, the (dirfd, cName) pair will refer to  // a child of this node.  // If `child` is empty, the (dirfd, cName) pair refers to this node itself. -func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) { +func (n *Node) prepareAtSyscall(child string) (dirfd int, pName string, errno syscall.Errno) {  	p := n.Path()  	if child != "" {  		p = filepath.Join(p, child)  	}  	rn := n.rootNode() -	dirfd, cName, err := rn.openBackingDir(p) +	dirfd, pName, err := rn.openBackingDir(p)  	if err != nil {  		errno = fs.ToErrno(err)  	} @@ -71,3 +87,67 @@ func (n *Node) isRoot() bool {  	rn := n.rootNode()  	return &rn.Node == n  } + +func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { +	dirfd, pName1, errno := n.prepareAtSyscall("") +	if errno != 0 { +		return +	} +	defer syscall.Close(dirfd) + +	// Find the file the gocryptfs.longname.XYZ.name file belongs to in the +	// directory listing +	fd, err := syscallcompat.Openat(dirfd, pName1, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) +	if err != nil { +		errno = fs.ToErrno(err) +		return +	} +	diriv := pathiv.Derive(n.Path(), pathiv.PurposeDirIV) +	rn := n.rootNode() +	pName, cFullname, errno := rn.findLongnameParent(fd, diriv, nameFile) +	if errno != 0 { +		return +	} +	// Get attrs from parent file +	st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW) +	if err != nil { +		errno = fs.ToErrno(err) +		return +	} +	var vf *virtualFile +	vf, errno = n.newVirtualFile([]byte(cFullname), st, inoTagNameFile) +	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 + +} + +// lookupDiriv returns a new Inode for a gocryptfs.diriv file inside `n`. +func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { +	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 +} diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go index 726380f..4297ecf 100644 --- a/internal/fusefrontend_reverse/root_node.go +++ b/internal/fusefrontend_reverse/root_node.go @@ -1,10 +1,19 @@  package fusefrontend_reverse  import ( +	"log" +	"strings" +	"syscall" + +	"golang.org/x/sys/unix" + +	"github.com/hanwen/go-fuse/v2/fs" +  	"github.com/rfjakob/gocryptfs/internal/contentenc"  	"github.com/rfjakob/gocryptfs/internal/fusefrontend"  	"github.com/rfjakob/gocryptfs/internal/inomap"  	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/syscallcompat"  	"github.com/sabhiram/go-gitignore"  ) @@ -37,3 +46,35 @@ func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n nametransfo  		excluder:      prepareExcluder(args),  	}  } + +// You can pass either gocryptfs.longname.XYZ.name or gocryptfs.longname.XYZ. +func (rn *RootNode) findLongnameParent(fd int, diriv []byte, longname string) (pName string, cFullName string, errno syscall.Errno) { +	if strings.HasSuffix(longname, nametransform.LongNameSuffix) { +		longname = nametransform.RemoveLongNameSuffix(longname) +	} +	entries, err := syscallcompat.Getdents(fd) +	if err != nil { +		errno = fs.ToErrno(err) +		return +	} +	for _, entry := range entries { +		if len(entry.Name) <= shortNameMax { +			continue +		} +		cFullName = rn.nameTransform.EncryptName(entry.Name, diriv) +		if len(cFullName) <= unix.NAME_MAX { +			// Entry should have been skipped by the "continue" above +			log.Panic("logic error or wrong shortNameMax constant?") +		} +		hName := rn.nameTransform.HashLongName(cFullName) +		if longname == hName { +			pName = entry.Name +			break +		} +	} +	if pName == "" { +		errno = syscall.ENOENT +		return +	} +	return +} diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index 35b9361..d212dfc 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -47,11 +47,20 @@ func (rfs *RootNode) rDecryptName(cName string, dirIV []byte, pDir string) (pNam  			return "", err  		}  	} else if nameType == nametransform.LongNameContent { -		panic("todo") -		//pName, err = rfs.findLongnameParent(pDir, dirIV, cName) +		dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(pDir))  		if err != nil {  			return "", err  		} +		defer syscall.Close(dirfd) +		fd, err := syscallcompat.Openat(dirfd, filepath.Base(pDir), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) +		if err != nil { +			return "", err +		} +		var errno syscall.Errno +		pName, _, errno = rfs.findLongnameParent(fd, dirIV, cName) +		if errno != 0 { +			return "", errno +		}  	} else {  		// It makes no sense to decrypt a ".name" file. This is a virtual file  		// that has no representation in the plaintext filesystem. ".name" diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go index a659f0a..119d592 100644 --- a/internal/nametransform/names.go +++ b/internal/nametransform/names.go @@ -23,6 +23,10 @@ type NameTransformer interface {  	DecryptName(cipherName string, iv []byte) (string, error)  	EncryptName(plainName string, iv []byte) string  	EncryptAndHashName(name string, iv []byte) (string, error) +	// HashLongName - take the hash of a long string "name" and return +	// "gocryptfs.longname.[sha256]" +	// +	// This function does not do any I/O.  	HashLongName(name string) string  	WriteLongNameAt(dirfd int, hashName string, plainName string) error  	B64EncodeToString(src []byte) string | 
