diff options
| author | Jakob Unterwurzacher | 2020-08-08 18:45:47 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-08-08 18:45:47 +0200 | 
| commit | 527609266386890fe1657c225c643ca448bcd71d (patch) | |
| tree | 8cb9261b827118311c195cbb6324ffaf87052970 /internal | |
| parent | 84ed139cd2cede9b773fe7892a0bc2515fc1f00f (diff) | |
v2api/reverse: implement Readlink
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend_reverse/node.go | 28 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/node_api_check.go | 37 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/node_helpers.go | 31 | 
3 files changed, 88 insertions, 8 deletions
| diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go index 713beee..884a97e 100644 --- a/internal/fusefrontend_reverse/node.go +++ b/internal/fusefrontend_reverse/node.go @@ -2,6 +2,7 @@ package fusefrontend_reverse  import (  	"context" +	"path/filepath"  	"syscall"  	"golang.org/x/sys/unix" @@ -20,16 +21,16 @@ type Node struct {  }  // Lookup - FUSE call for discovering a file. -func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { +func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) {  	dirfd := int(-1)  	pName := "" -	t := n.lookupFileType(name) +	t := n.lookupFileType(cName)  	if t == typeDiriv {  		// gocryptfs.diriv  		return n.lookupDiriv(ctx, out)  	} else if t == typeName {  		// gocryptfs.longname.*.name -		return n.lookupLongnameName(ctx, name, out) +		return n.lookupLongnameName(ctx, cName, out)  	} else if t == typeConfig {  		// gocryptfs.conf  		var err error @@ -43,7 +44,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  		defer syscall.Close(dirfd)  	} else if t == typeReal {  		// real file -		dirfd, pName, errno = n.prepareAtSyscall(name) +		dirfd, pName, errno = n.prepareAtSyscall(cName)  		if errno != 0 {  			return  		} @@ -58,7 +59,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch  	ch = n.newChild(ctx, st, out)  	// Translate ciphertext size in `out.Attr.Size` to plaintext size  	if t == typeReal { -		n.translateSize(dirfd, pName, &out.Attr) +		n.translateSize(dirfd, cName, pName, &out.Attr)  	}  	return ch, 0  } @@ -89,10 +90,25 @@ func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut)  	out.Attr.FromStat(st)  	// Translate ciphertext size in `out.Attr.Size` to plaintext size -	n.translateSize(dirfd, pName, &out.Attr) +	cName := filepath.Base(n.Path()) +	n.translateSize(dirfd, cName, pName, &out.Attr)  	if rn.args.ForceOwner != nil {  		out.Owner = *rn.args.ForceOwner  	}  	return 0  } + +// Readlink - FUSE call. +// +// Symlink-safe through openBackingDir() + Readlinkat(). +func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) { +	dirfd, pName, errno := n.prepareAtSyscall("") +	if errno != 0 { +		return +	} +	defer syscall.Close(dirfd) + +	cName := filepath.Base(n.Path()) +	return n.readlink(dirfd, cName, pName) +} diff --git a/internal/fusefrontend_reverse/node_api_check.go b/internal/fusefrontend_reverse/node_api_check.go new file mode 100644 index 0000000..e926fc3 --- /dev/null +++ b/internal/fusefrontend_reverse/node_api_check.go @@ -0,0 +1,37 @@ +package fusefrontend_reverse + +import ( +	"github.com/hanwen/go-fuse/v2/fs" +) + +// Check that we have implemented the fs.Node* interfaces +var _ = (fs.NodeGetattrer)((*Node)(nil)) +var _ = (fs.NodeLookuper)((*Node)(nil)) +var _ = (fs.NodeReaddirer)((*Node)(nil)) +var _ = (fs.NodeReadlinker)((*Node)(nil)) + +/* TODO +var _ = (fs.NodeOpener)((*Node)(nil)) +var _ = (fs.NodeStatfser)((*Node)(nil)) +var _ = (fs.NodeMknoder)((*Node)(nil)) +var _ = (fs.NodeGetxattrer)((*Node)(nil)) +var _ = (fs.NodeListxattrer)((*Node)(nil)) +*/ + +/* Not needed +var _ = (fs.NodeOpendirer)((*Node)(nil)) +*/ + +/* Will not implement these - reverse mode is read-only! +var _ = (fs.NodeCreater)((*Node)(nil)) +var _ = (fs.NodeMkdirer)((*Node)(nil)) +var _ = (fs.NodeRmdirer)((*Node)(nil)) +var _ = (fs.NodeUnlinker)((*Node)(nil)) +var _ = (fs.NodeSetattrer)((*Node)(nil)) +var _ = (fs.NodeLinker)((*Node)(nil)) +var _ = (fs.NodeSymlinker)((*Node)(nil)) +var _ = (fs.NodeRenamer)((*Node)(nil)) +var _ = (fs.NodeSetxattrer)((*Node)(nil)) +var _ = (fs.NodeRemovexattrer)((*Node)(nil)) +var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) +*/ diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go index df9de9b..7669a17 100644 --- a/internal/fusefrontend_reverse/node_helpers.go +++ b/internal/fusefrontend_reverse/node_helpers.go @@ -26,12 +26,13 @@ const (  )  // translateSize translates the ciphertext size in `out` into plaintext size. -func (n *Node) translateSize(dirfd int, cName string, out *fuse.Attr) { +func (n *Node) translateSize(dirfd int, cName string, pName string, out *fuse.Attr) {  	if out.IsRegular() {  		rn := n.rootNode()  		out.Size = rn.contentEnc.PlainSizeToCipherSize(out.Size)  	} else if out.IsSymlink() { -		panic("todo: call readlink once it is implemented") +		cLink, _ := n.readlink(dirfd, cName, pName) +		out.Size = uint64(len(cLink))  	}  } @@ -151,3 +152,29 @@ func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inod  	ch = n.NewInode(ctx, vf, id)  	return  } + +// readlink reads and encrypts a symlink. Used by Readlink, Getattr, Lookup. +func (n *Node) readlink(dirfd int, cName string, pName string) (out []byte, errno syscall.Errno) { +	plainTarget, err := syscallcompat.Readlinkat(dirfd, pName) +	if err != nil { +		errno = fs.ToErrno(err) +		return +	} +	rn := n.rootNode() +	if rn.args.PlaintextNames { +		return []byte(plainTarget), 0 +	} +	// Nonce is derived from the relative *ciphertext* path +	p := filepath.Join(n.Path(), cName) +	nonce := pathiv.Derive(p, pathiv.PurposeSymlinkIV) +	// Symlinks are encrypted like file contents and base64-encoded +	cBinTarget := rn.contentEnc.EncryptBlockNonce([]byte(plainTarget), 0, nil, nonce) +	cTarget := rn.nameTransform.B64EncodeToString(cBinTarget) +	// The kernel will reject a symlink target above 4096 chars and return +	// and I/O error to the user. Better emit the proper error ourselves. +	if len(cTarget) > syscallcompat.PATH_MAX { +		errno = syscall.ENAMETOOLONG +		return +	} +	return []byte(cTarget), 0 +} | 
