libgocryptfs/internal/fusefrontend/node_prepare_syscall.go

120 lines
2.9 KiB
Go

package fusefrontend
import (
"sync/atomic"
"syscall"
"github.com/rfjakob/gocryptfs/v2/internal/tlog"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
)
// prepareAtSyscall returns a (dirfd, cName) pair that can be used
// with the "___at" family of system calls (openat, fstatat, unlinkat...) to
// access the backing encrypted child file.
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
if child == "" {
tlog.Warn.Printf("BUG: prepareAtSyscall: child=%q, should have called prepareAtSyscallMyself", child)
return n.prepareAtSyscallMyself()
}
rn := n.rootNode()
// All filesystem operations go through here, so this is a good place
// to reset the idle marker.
atomic.StoreUint32(&rn.IsIdle, 0)
if n.IsRoot() && rn.isFiltered(child) {
return -1, "", syscall.EPERM
}
var encryptName func(int, string, []byte) (string, error)
if !rn.args.PlaintextNames {
encryptName = func(dirfd int, child string, iv []byte) (cName string, err error) {
// Badname allowed, try to determine filenames
if rn.nameTransform.HaveBadnamePatterns() {
return rn.nameTransform.EncryptAndHashBadName(child, iv, dirfd)
}
return rn.nameTransform.EncryptAndHashName(child, iv)
}
}
// Cache lookup
var iv []byte
dirfd, iv = rn.dirCache.Lookup(n)
if dirfd > 0 {
if rn.args.PlaintextNames {
return dirfd, child, 0
}
var err error
cName, err = encryptName(dirfd, child, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
return
}
// Slowpath: Open ourselves & read diriv
parentDirfd, myCName, errno := n.prepareAtSyscallMyself()
if errno != 0 {
return
}
defer syscall.Close(parentDirfd)
dirfd, err := syscallcompat.Openat(parentDirfd, myCName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
if err != nil {
return -1, "", fs.ToErrno(err)
}
// Cache store
if !rn.args.PlaintextNames {
var err error
iv, err = rn.nameTransform.ReadDirIVAt(dirfd)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
}
rn.dirCache.Store(n, dirfd, iv)
if rn.args.PlaintextNames {
return dirfd, child, 0
}
cName, err = encryptName(dirfd, child, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
return
}
func (n *Node) prepareAtSyscallMyself() (dirfd int, cName string, errno syscall.Errno) {
dirfd = -1
// Handle root node
if n.IsRoot() {
var err error
rn := n.rootNode()
// Open cipherdir (following symlinks)
dirfd, err = syscallcompat.Open(rn.args.Cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
if err != nil {
return -1, "", fs.ToErrno(err)
}
return dirfd, ".", 0
}
// Otherwise convert to prepareAtSyscall of parent node
myName, p1 := n.Parent()
if p1 == nil || myName == "" {
errno = syscall.ENOENT
return
}
parent := toNode(p1.Operations())
return parent.prepareAtSyscall(myName)
}