Skip to content

Commit 0d91f0a

Browse files
jtlaytonidryomov
authored andcommitted
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>
1 parent 16be62f commit 0d91f0a

1 file changed

Lines changed: 83 additions & 2 deletions

File tree

fs/ceph/caps.c

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,9 @@ struct cap_extra_info {
33833383
/* currently issued */
33843384
int issued;
33853385
struct timespec64 btime;
3386+
u8 *fscrypt_auth;
3387+
u32 fscrypt_auth_len;
3388+
u64 fscrypt_file_size;
33863389
};
33873390

33883391
/*
@@ -3415,6 +3418,14 @@ static void handle_cap_grant(struct inode *inode,
34153418
bool deleted_inode = false;
34163419
bool fill_inline = false;
34173420

3421+
/*
3422+
* If there is at least one crypto block then we'll trust
3423+
* fscrypt_file_size. If the real length of the file is 0, then
3424+
* ignore it (it has probably been truncated down to 0 by the MDS).
3425+
*/
3426+
if (IS_ENCRYPTED(inode) && size)
3427+
size = extra_info->fscrypt_file_size;
3428+
34183429
dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
34193430
inode, cap, session->s_mds, seq, ceph_cap_string(newcaps));
34203431
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,
34813492
dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode,
34823493
from_kuid(&init_user_ns, inode->i_uid),
34833494
from_kgid(&init_user_ns, inode->i_gid));
3495+
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
3496+
if (ci->fscrypt_auth_len != extra_info->fscrypt_auth_len ||
3497+
memcmp(ci->fscrypt_auth, extra_info->fscrypt_auth,
3498+
ci->fscrypt_auth_len))
3499+
pr_warn_ratelimited("%s: cap grant attempt to change fscrypt_auth on non-I_NEW inode (old len %d new len %d)\n",
3500+
__func__, ci->fscrypt_auth_len,
3501+
extra_info->fscrypt_auth_len);
3502+
#endif
34843503
}
34853504

34863505
if ((newcaps & CEPH_CAP_LINK_SHARED) &&
@@ -3897,7 +3916,8 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid,
38973916
*/
38983917
static bool handle_cap_trunc(struct inode *inode,
38993918
struct ceph_mds_caps *trunc,
3900-
struct ceph_mds_session *session)
3919+
struct ceph_mds_session *session,
3920+
struct cap_extra_info *extra_info)
39013921
{
39023922
struct ceph_inode_info *ci = ceph_inode(inode);
39033923
int mds = session->s_mds;
@@ -3914,6 +3934,14 @@ static bool handle_cap_trunc(struct inode *inode,
39143934

39153935
issued |= implemented | dirty;
39163936

3937+
/*
3938+
* If there is at least one crypto block then we'll trust
3939+
* fscrypt_file_size. If the real length of the file is 0, then
3940+
* ignore it (it has probably been truncated down to 0 by the MDS).
3941+
*/
3942+
if (IS_ENCRYPTED(inode) && size)
3943+
size = extra_info->fscrypt_file_size;
3944+
39173945
dout("handle_cap_trunc inode %p mds%d seq %d to %lld seq %d\n",
39183946
inode, mds, seq, truncate_size, truncate_seq);
39193947
queue_trunc = ceph_fill_file_size(inode, issued,
@@ -4135,6 +4163,52 @@ static void handle_cap_import(struct ceph_mds_client *mdsc,
41354163
*target_cap = cap;
41364164
}
41374165

4166+
#ifdef CONFIG_FS_ENCRYPTION
4167+
static int parse_fscrypt_fields(void **p, void *end,
4168+
struct cap_extra_info *extra)
4169+
{
4170+
u32 len;
4171+
4172+
ceph_decode_32_safe(p, end, extra->fscrypt_auth_len, bad);
4173+
if (extra->fscrypt_auth_len) {
4174+
ceph_decode_need(p, end, extra->fscrypt_auth_len, bad);
4175+
extra->fscrypt_auth = kmalloc(extra->fscrypt_auth_len,
4176+
GFP_KERNEL);
4177+
if (!extra->fscrypt_auth)
4178+
return -ENOMEM;
4179+
ceph_decode_copy_safe(p, end, extra->fscrypt_auth,
4180+
extra->fscrypt_auth_len, bad);
4181+
}
4182+
4183+
ceph_decode_32_safe(p, end, len, bad);
4184+
if (len >= sizeof(u64)) {
4185+
ceph_decode_64_safe(p, end, extra->fscrypt_file_size, bad);
4186+
len -= sizeof(u64);
4187+
}
4188+
ceph_decode_skip_n(p, end, len, bad);
4189+
return 0;
4190+
bad:
4191+
return -EIO;
4192+
}
4193+
#else
4194+
static int parse_fscrypt_fields(void **p, void *end,
4195+
struct cap_extra_info *extra)
4196+
{
4197+
u32 len;
4198+
4199+
/* Don't care about these fields unless we're encryption-capable */
4200+
ceph_decode_32_safe(p, end, len, bad);
4201+
if (len)
4202+
ceph_decode_skip_n(p, end, len, bad);
4203+
ceph_decode_32_safe(p, end, len, bad);
4204+
if (len)
4205+
ceph_decode_skip_n(p, end, len, bad);
4206+
return 0;
4207+
bad:
4208+
return -EIO;
4209+
}
4210+
#endif
4211+
41384212
/*
41394213
* Handle a caps message from the MDS.
41404214
*
@@ -4255,6 +4329,11 @@ void ceph_handle_caps(struct ceph_mds_session *session,
42554329
ceph_decode_64_safe(&p, end, extra_info.nsubdirs, bad);
42564330
}
42574331

4332+
if (msg_version >= 12) {
4333+
if (parse_fscrypt_fields(&p, end, &extra_info))
4334+
goto bad;
4335+
}
4336+
42584337
/* lookup ino */
42594338
inode = ceph_find_inode(mdsc->fsc->sb, vino);
42604339
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,
43524431
break;
43534432

43544433
case CEPH_CAP_OP_TRUNC:
4355-
queue_trunc = handle_cap_trunc(inode, h, session);
4434+
queue_trunc = handle_cap_trunc(inode, h, session,
4435+
&extra_info);
43564436
spin_unlock(&ci->i_ceph_lock);
43574437
if (queue_trunc)
43584438
ceph_queue_vmtruncate(inode);
@@ -4375,6 +4455,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
43754455
if (close_sessions)
43764456
ceph_mdsc_close_sessions(mdsc);
43774457

4458+
kfree(extra_info.fscrypt_auth);
43784459
return;
43794460

43804461
flush_cap_releases:

0 commit comments

Comments
 (0)