Commit 0d91f0ad authored by Jeff Layton's avatar Jeff Layton Committed by Ilya Dryomov
Browse files

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: default avatarJeff Layton <jlayton@kernel.org>
Reviewed-by: default avatarXiubo Li <xiubli@redhat.com>
Reviewed-and-tested-by: default avatarLuís Henriques <lhenriques@suse.de>
Reviewed-by: default avatarMilind Changire <mchangir@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent 16be62fc
Loading
Loading
Loading
Loading
+83 −2
Original line number Diff line number Diff line
@@ -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 @@ static void handle_cap_import(struct ceph_mds_client *mdsc,
	*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 @@ void ceph_handle_caps(struct ceph_mds_session *session,
	if (close_sessions)
		ceph_mdsc_close_sessions(mdsc);

	kfree(extra_info.fscrypt_auth);
	return;

flush_cap_releases: