v2api/reverse: implement Readlink

This commit is contained in:
Jakob Unterwurzacher 2020-08-08 18:45:47 +02:00
parent 84ed139cd2
commit 5276092663
3 changed files with 88 additions and 8 deletions

View File

@ -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)
}

View File

@ -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))
*/

View File

@ -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
}