linux-stable/fs/ceph/crypto.c
Jeff Layton 2d332d5bc4 ceph: fscrypt_auth handling for ceph
Most fscrypt-enabled filesystems store the crypto context in an xattr,
but that's problematic for ceph as xatts are governed by the XATTR cap,
but we really want the crypto context as part of the AUTH cap.

Because of this, the MDS has added two new inode metadata fields:
fscrypt_auth and fscrypt_file. The former is used to hold the crypto
context, and the latter is used to track the real file size.

Parse new fscrypt_auth and fscrypt_file fields in inode traces. For now,
we don't use fscrypt_file, but fscrypt_auth is used to hold the fscrypt
context.

Allow the client to use a setattr request for setting the fscrypt_auth
field. Since this is not a standard setattr request from the VFS, we add
a new field to __ceph_setattr that carries ceph-specific inode attrs.

Have the set_context op do a setattr that sets the fscrypt_auth value,
and get_context just return the contents of that field (since it should
always be available).

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
Reviewed-and-tested-by: Luís Henriques <lhenriques@suse.de>
Reviewed-by: Milind Changire <mchangir@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
2023-08-22 09:01:48 +02:00

77 lines
1.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/ceph/ceph_debug.h>
#include <linux/xattr.h>
#include <linux/fscrypt.h>
#include "super.h"
#include "crypto.h"
static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
{
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth;
u32 ctxlen;
/* Non existent or too short? */
if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1)))
return -ENOBUFS;
/* Some format we don't recognize? */
if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION)
return -ENOBUFS;
ctxlen = le32_to_cpu(cfa->cfa_blob_len);
if (len < ctxlen)
return -ERANGE;
memcpy(ctx, cfa->cfa_blob, ctxlen);
return ctxlen;
}
static int ceph_crypt_set_context(struct inode *inode, const void *ctx,
size_t len, void *fs_data)
{
int ret;
struct iattr attr = { };
struct ceph_iattr cia = { };
struct ceph_fscrypt_auth *cfa;
WARN_ON_ONCE(fs_data);
if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE)
return -EINVAL;
cfa = kzalloc(sizeof(*cfa), GFP_KERNEL);
if (!cfa)
return -ENOMEM;
cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
cfa->cfa_blob_len = cpu_to_le32(len);
memcpy(cfa->cfa_blob, ctx, len);
cia.fscrypt_auth = cfa;
ret = __ceph_setattr(inode, &attr, &cia);
if (ret == 0)
inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED);
kfree(cia.fscrypt_auth);
return ret;
}
static bool ceph_crypt_empty_dir(struct inode *inode)
{
struct ceph_inode_info *ci = ceph_inode(inode);
return ci->i_rsubdirs + ci->i_rfiles == 1;
}
static struct fscrypt_operations ceph_fscrypt_ops = {
.get_context = ceph_crypt_get_context,
.set_context = ceph_crypt_set_context,
.empty_dir = ceph_crypt_empty_dir,
};
void ceph_fscrypt_set_ops(struct super_block *sb)
{
fscrypt_set_ops(sb, &ceph_fscrypt_ops);
}