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/hanwen/go-fuse/v2/fuse"
|
||||||
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/configfile"
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
"github.com/rfjakob/gocryptfs/internal/pathiv"
|
|
||||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
"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)
|
dirfd := int(-1)
|
||||||
pName := ""
|
pName := ""
|
||||||
t := n.lookupFileType(name)
|
t := n.lookupFileType(name)
|
||||||
|
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
|
// gocryptfs.conf
|
||||||
if t == typeConfig {
|
|
||||||
var err error
|
var err error
|
||||||
|
pName = configfile.ConfReverseName
|
||||||
rn := n.rootNode()
|
rn := n.rootNode()
|
||||||
dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, "")
|
dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -35,34 +41,6 @@ func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer syscall.Close(dirfd)
|
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 {
|
} else if t == typeReal {
|
||||||
// real file
|
// real file
|
||||||
dirfd, pName, errno = n.prepareAtSyscall(name)
|
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)
|
defer syscall.Close(dirfd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get device number and inode number into `st`
|
// Get device number and inode number into `st`
|
||||||
st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW)
|
st, err := syscallcompat.Fstatat2(dirfd, pName, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fs.ToErrno(err)
|
return nil, fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new inode and fill `out`
|
// Create new inode and fill `out`
|
||||||
ch = n.newChild(ctx, st, out)
|
ch = n.newChild(ctx, st, out)
|
||||||
|
|
||||||
if t == typeReal {
|
|
||||||
// Translate ciphertext size in `out.Attr.Size` to plaintext size
|
// Translate ciphertext size in `out.Attr.Size` to plaintext size
|
||||||
|
if t == typeReal {
|
||||||
n.translateSize(dirfd, pName, &out.Attr)
|
n.translateSize(dirfd, pName, &out.Attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ch, 0
|
return ch, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,24 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/hanwen/go-fuse/v2/fs"
|
"github.com/hanwen/go-fuse/v2/fs"
|
||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"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.
|
// 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
|
// If you pass a `child` file name, the (dirfd, cName) pair will refer to
|
||||||
// a child of this node.
|
// a child of this node.
|
||||||
// If `child` is empty, the (dirfd, cName) pair refers to this node itself.
|
// 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()
|
p := n.Path()
|
||||||
if child != "" {
|
if child != "" {
|
||||||
p = filepath.Join(p, child)
|
p = filepath.Join(p, child)
|
||||||
}
|
}
|
||||||
rn := n.rootNode()
|
rn := n.rootNode()
|
||||||
dirfd, cName, err := rn.openBackingDir(p)
|
dirfd, pName, err := rn.openBackingDir(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errno = fs.ToErrno(err)
|
errno = fs.ToErrno(err)
|
||||||
}
|
}
|
||||||
@ -71,3 +87,67 @@ func (n *Node) isRoot() bool {
|
|||||||
rn := n.rootNode()
|
rn := n.rootNode()
|
||||||
return &rn.Node == n
|
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
|
package fusefrontend_reverse
|
||||||
|
|
||||||
import (
|
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/contentenc"
|
||||||
"github.com/rfjakob/gocryptfs/internal/fusefrontend"
|
"github.com/rfjakob/gocryptfs/internal/fusefrontend"
|
||||||
"github.com/rfjakob/gocryptfs/internal/inomap"
|
"github.com/rfjakob/gocryptfs/internal/inomap"
|
||||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
|
|
||||||
"github.com/sabhiram/go-gitignore"
|
"github.com/sabhiram/go-gitignore"
|
||||||
)
|
)
|
||||||
@ -37,3 +46,35 @@ func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n nametransfo
|
|||||||
excluder: prepareExcluder(args),
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
} else if nameType == nametransform.LongNameContent {
|
} else if nameType == nametransform.LongNameContent {
|
||||||
panic("todo")
|
dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(pDir))
|
||||||
//pName, err = rfs.findLongnameParent(pDir, dirIV, cName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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 {
|
} else {
|
||||||
// It makes no sense to decrypt a ".name" file. This is a virtual file
|
// It makes no sense to decrypt a ".name" file. This is a virtual file
|
||||||
// that has no representation in the plaintext filesystem. ".name"
|
// that has no representation in the plaintext filesystem. ".name"
|
||||||
|
@ -23,6 +23,10 @@ type NameTransformer interface {
|
|||||||
DecryptName(cipherName string, iv []byte) (string, error)
|
DecryptName(cipherName string, iv []byte) (string, error)
|
||||||
EncryptName(plainName string, iv []byte) string
|
EncryptName(plainName string, iv []byte) string
|
||||||
EncryptAndHashName(name string, iv []byte) (string, error)
|
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
|
HashLongName(name string) string
|
||||||
WriteLongNameAt(dirfd int, hashName string, plainName string) error
|
WriteLongNameAt(dirfd int, hashName string, plainName string) error
|
||||||
B64EncodeToString(src []byte) string
|
B64EncodeToString(src []byte) string
|
||||||
|
Loading…
Reference in New Issue
Block a user