v2api/reverse: implement Lookup for longname
This commit is contained in:
parent
4674bac838
commit
84ed139cd2
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user