aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend_reverse/node_helpers.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/fusefrontend_reverse/node_helpers.go')
-rw-r--r--internal/fusefrontend_reverse/node_helpers.go31
1 files changed, 29 insertions, 2 deletions
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
+}