ceph: handle fscrypt fields in cap messages from MDS

Handle the new fscrypt_file and fscrypt_auth fields in cap messages. Use
them to populate new fields in cap_extra_info and update the inode with
those values.

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>
This commit is contained in:
Jeff Layton 2022-08-25 09:31:09 -04:00 committed by Ilya Dryomov
parent 16be62fc8a
commit 0d91f0ad6a

View File

@ -3383,6 +3383,9 @@ struct cap_extra_info {
/* currently issued */
int issued;
struct timespec64 btime;
u8 *fscrypt_auth;
u32 fscrypt_auth_len;
u64 fscrypt_file_size;
};
/*
@ -3415,6 +3418,14 @@ static void handle_cap_grant(struct inode *inode,
bool deleted_inode = false;
bool fill_inline = false;
/*
* If there is at least one crypto block then we'll trust
* fscrypt_file_size. If the real length of the file is 0, then
* ignore it (it has probably been truncated down to 0 by the MDS).
*/
if (IS_ENCRYPTED(inode) && size)
size = extra_info->fscrypt_file_size;
dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
inode, cap, session->s_mds, seq, ceph_cap_string(newcaps));
dout(" size %llu max_size %llu, i_size %llu\n", size, max_size,
@ -3481,6 +3492,14 @@ static void handle_cap_grant(struct inode *inode,
dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode,
from_kuid(&init_user_ns, inode->i_uid),
from_kgid(&init_user_ns, inode->i_gid));
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
if (ci->fscrypt_auth_len != extra_info->fscrypt_auth_len ||
memcmp(ci->fscrypt_auth, extra_info->fscrypt_auth,
ci->fscrypt_auth_len))
pr_warn_ratelimited("%s: cap grant attempt to change fscrypt_auth on non-I_NEW inode (old len %d new len %d)\n",
__func__, ci->fscrypt_auth_len,
extra_info->fscrypt_auth_len);
#endif
}
if ((newcaps & CEPH_CAP_LINK_SHARED) &&
@ -3897,7 +3916,8 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid,
*/
static bool handle_cap_trunc(struct inode *inode,
struct ceph_mds_caps *trunc,
struct ceph_mds_session *session)
struct ceph_mds_session *session,
struct cap_extra_info *extra_info)
{
struct ceph_inode_info *ci = ceph_inode(inode);
int mds = session->s_mds;
@ -3914,6 +3934,14 @@ static bool handle_cap_trunc(struct inode *inode,
issued |= implemented | dirty;
/*
* If there is at least one crypto block then we'll trust
* fscrypt_file_size. If the real length of the file is 0, then
* ignore it (it has probably been truncated down to 0 by the MDS).
*/
if (IS_ENCRYPTED(inode) && size)
size = extra_info->fscrypt_file_size;
dout("handle_cap_trunc inode %p mds%d seq %d to %lld seq %d\n",
inode, mds, seq, truncate_size, truncate_seq);
queue_trunc = ceph_fill_file_size(inode, issued,
@ -4135,6 +4163,52 @@ retry:
*target_cap = cap;
}
#ifdef CONFIG_FS_ENCRYPTION
static int parse_fscrypt_fields(void **p, void *end,
struct cap_extra_info *extra)
{
u32 len;
ceph_decode_32_safe(p, end, extra->fscrypt_auth_len, bad);
if (extra->fscrypt_auth_len) {
ceph_decode_need(p, end, extra->fscrypt_auth_len, bad);
extra->fscrypt_auth = kmalloc(extra->fscrypt_auth_len,
GFP_KERNEL);
if (!extra->fscrypt_auth)
return -ENOMEM;
ceph_decode_copy_safe(p, end, extra->fscrypt_auth,
extra->fscrypt_auth_len, bad);
}
ceph_decode_32_safe(p, end, len, bad);
if (len >= sizeof(u64)) {
ceph_decode_64_safe(p, end, extra->fscrypt_file_size, bad);
len -= sizeof(u64);
}
ceph_decode_skip_n(p, end, len, bad);
return 0;
bad:
return -EIO;
}
#else
static int parse_fscrypt_fields(void **p, void *end,
struct cap_extra_info *extra)
{
u32 len;
/* Don't care about these fields unless we're encryption-capable */
ceph_decode_32_safe(p, end, len, bad);
if (len)
ceph_decode_skip_n(p, end, len, bad);
ceph_decode_32_safe(p, end, len, bad);
if (len)
ceph_decode_skip_n(p, end, len, bad);
return 0;
bad:
return -EIO;
}
#endif
/*
* Handle a caps message from the MDS.
*
@ -4255,6 +4329,11 @@ void ceph_handle_caps(struct ceph_mds_session *session,
ceph_decode_64_safe(&p, end, extra_info.nsubdirs, bad);
}
if (msg_version >= 12) {
if (parse_fscrypt_fields(&p, end, &extra_info))
goto bad;
}
/* lookup ino */
inode = ceph_find_inode(mdsc->fsc->sb, vino);
dout(" op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), vino.ino,
@ -4352,7 +4431,8 @@ void ceph_handle_caps(struct ceph_mds_session *session,
break;
case CEPH_CAP_OP_TRUNC:
queue_trunc = handle_cap_trunc(inode, h, session);
queue_trunc = handle_cap_trunc(inode, h, session,
&extra_info);
spin_unlock(&ci->i_ceph_lock);
if (queue_trunc)
ceph_queue_vmtruncate(inode);
@ -4375,6 +4455,7 @@ out:
if (close_sessions)
ceph_mdsc_close_sessions(mdsc);
kfree(extra_info.fscrypt_auth);
return;
flush_cap_releases: