v2api/reverse: implement Readlink
This commit is contained in:
parent
84ed139cd2
commit
5276092663
@ -2,6 +2,7 @@ package fusefrontend_reverse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@ -20,16 +21,16 @@ type Node struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lookup - FUSE call for discovering a file.
|
// 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)
|
dirfd := int(-1)
|
||||||
pName := ""
|
pName := ""
|
||||||
t := n.lookupFileType(name)
|
t := n.lookupFileType(cName)
|
||||||
if t == typeDiriv {
|
if t == typeDiriv {
|
||||||
// gocryptfs.diriv
|
// gocryptfs.diriv
|
||||||
return n.lookupDiriv(ctx, out)
|
return n.lookupDiriv(ctx, out)
|
||||||
} else if t == typeName {
|
} else if t == typeName {
|
||||||
// gocryptfs.longname.*.name
|
// gocryptfs.longname.*.name
|
||||||
return n.lookupLongnameName(ctx, name, out)
|
return n.lookupLongnameName(ctx, cName, out)
|
||||||
} else if t == typeConfig {
|
} else if t == typeConfig {
|
||||||
// gocryptfs.conf
|
// gocryptfs.conf
|
||||||
var err error
|
var err error
|
||||||
@ -43,7 +44,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch
|
|||||||
defer syscall.Close(dirfd)
|
defer syscall.Close(dirfd)
|
||||||
} else if t == typeReal {
|
} else if t == typeReal {
|
||||||
// real file
|
// real file
|
||||||
dirfd, pName, errno = n.prepareAtSyscall(name)
|
dirfd, pName, errno = n.prepareAtSyscall(cName)
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -58,7 +59,7 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch
|
|||||||
ch = n.newChild(ctx, st, out)
|
ch = n.newChild(ctx, st, out)
|
||||||
// Translate ciphertext size in `out.Attr.Size` to plaintext size
|
// Translate ciphertext size in `out.Attr.Size` to plaintext size
|
||||||
if t == typeReal {
|
if t == typeReal {
|
||||||
n.translateSize(dirfd, pName, &out.Attr)
|
n.translateSize(dirfd, cName, pName, &out.Attr)
|
||||||
}
|
}
|
||||||
return ch, 0
|
return ch, 0
|
||||||
}
|
}
|
||||||
@ -89,10 +90,25 @@ func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut)
|
|||||||
out.Attr.FromStat(st)
|
out.Attr.FromStat(st)
|
||||||
|
|
||||||
// Translate ciphertext size in `out.Attr.Size` to plaintext size
|
// 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 {
|
if rn.args.ForceOwner != nil {
|
||||||
out.Owner = *rn.args.ForceOwner
|
out.Owner = *rn.args.ForceOwner
|
||||||
}
|
}
|
||||||
return 0
|
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)
|
||||||
|
}
|
||||||
|
37
internal/fusefrontend_reverse/node_api_check.go
Normal file
37
internal/fusefrontend_reverse/node_api_check.go
Normal 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))
|
||||||
|
*/
|
@ -26,12 +26,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// translateSize translates the ciphertext size in `out` into plaintext size.
|
// 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() {
|
if out.IsRegular() {
|
||||||
rn := n.rootNode()
|
rn := n.rootNode()
|
||||||
out.Size = rn.contentEnc.PlainSizeToCipherSize(out.Size)
|
out.Size = rn.contentEnc.PlainSizeToCipherSize(out.Size)
|
||||||
} else if out.IsSymlink() {
|
} 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)
|
ch = n.NewInode(ctx, vf, id)
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user