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:
parent
16be62fc8a
commit
0d91f0ad6a
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue