summaryrefslogtreecommitdiffstats
path: root/fs/ceph
diff options
context:
space:
mode:
authorJeff Layton <jlayton@kernel.org>2022-08-25 09:31:06 -0400
committerIlya Dryomov <idryomov@gmail.com>2023-08-24 11:24:35 +0200
commit16be62fc8a53482529201b4be6bbcd0de3a058cb (patch)
tree08888b72ce3d9a13ae2a3f250fe3ead3a8a080e4 /fs/ceph
parent14e034a61c908d4479be1a7ee9fe5b8d3d1f09b8 (diff)
downloadlinux-stable-16be62fc8a53482529201b4be6bbcd0de3a058cb.tar.gz
linux-stable-16be62fc8a53482529201b4be6bbcd0de3a058cb.tar.bz2
linux-stable-16be62fc8a53482529201b4be6bbcd0de3a058cb.zip
ceph: size handling in MClientRequest, cap updates and inode traces
For encrypted inodes, transmit a rounded-up size to the MDS as the normal file size and send the real inode size in fscrypt_file field. Also, fix up creates and truncates to also transmit fscrypt_file. When we get an inode trace from the MDS, grab the fscrypt_file field if the inode is encrypted, and use it to populate the i_size field instead of the regular inode size field. 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>
Diffstat (limited to 'fs/ceph')
-rw-r--r--fs/ceph/caps.c44
-rw-r--r--fs/ceph/dir.c3
-rw-r--r--fs/ceph/file.c1
-rw-r--r--fs/ceph/inode.c33
-rw-r--r--fs/ceph/mds_client.c9
-rw-r--r--fs/ceph/mds_client.h2
6 files changed, 70 insertions, 22 deletions
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 4738be54dc73..aff256274415 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -1217,10 +1217,9 @@ struct cap_msg_args {
umode_t mode;
bool inline_data;
bool wake;
+ bool encrypted;
u32 fscrypt_auth_len;
- u32 fscrypt_file_len;
u8 fscrypt_auth[sizeof(struct ceph_fscrypt_auth)]; // for context
- u8 fscrypt_file[sizeof(u64)]; // for size
};
/* Marshal up the cap msg to the MDS */
@@ -1255,7 +1254,13 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
fc->ino = cpu_to_le64(arg->ino);
fc->snap_follows = cpu_to_le64(arg->follows);
- fc->size = cpu_to_le64(arg->size);
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+ if (arg->encrypted)
+ fc->size = cpu_to_le64(round_up(arg->size,
+ CEPH_FSCRYPT_BLOCK_SIZE));
+ else
+#endif
+ fc->size = cpu_to_le64(arg->size);
fc->max_size = cpu_to_le64(arg->max_size);
ceph_encode_timespec64(&fc->mtime, &arg->mtime);
ceph_encode_timespec64(&fc->atime, &arg->atime);
@@ -1315,11 +1320,17 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
ceph_encode_64(&p, 0);
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
- /* fscrypt_auth and fscrypt_file (version 12) */
+ /*
+ * fscrypt_auth and fscrypt_file (version 12)
+ *
+ * fscrypt_auth holds the crypto context (if any). fscrypt_file
+ * tracks the real i_size as an __le64 field (and we use a rounded-up
+ * i_size in the traditional size field).
+ */
ceph_encode_32(&p, arg->fscrypt_auth_len);
ceph_encode_copy(&p, arg->fscrypt_auth, arg->fscrypt_auth_len);
- ceph_encode_32(&p, arg->fscrypt_file_len);
- ceph_encode_copy(&p, arg->fscrypt_file, arg->fscrypt_file_len);
+ ceph_encode_32(&p, sizeof(__le64));
+ ceph_encode_64(&p, arg->size);
#else /* CONFIG_FS_ENCRYPTION */
ceph_encode_32(&p, 0);
ceph_encode_32(&p, 0);
@@ -1391,7 +1402,6 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
arg->follows = flushing ? ci->i_head_snapc->seq : 0;
arg->flush_tid = flush_tid;
arg->oldest_flush_tid = oldest_flush_tid;
-
arg->size = i_size_read(inode);
ci->i_reported_size = arg->size;
arg->max_size = ci->i_wanted_max_size;
@@ -1445,6 +1455,7 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
}
}
arg->flags = flags;
+ arg->encrypted = IS_ENCRYPTED(inode);
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
if (ci->fscrypt_auth_len &&
WARN_ON_ONCE(ci->fscrypt_auth_len > sizeof(struct ceph_fscrypt_auth))) {
@@ -1456,21 +1467,21 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
min_t(size_t, ci->fscrypt_auth_len,
sizeof(arg->fscrypt_auth)));
}
- /* FIXME: use this to track "real" size */
- arg->fscrypt_file_len = 0;
#endif /* CONFIG_FS_ENCRYPTION */
}
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \
- 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4)
+ 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4 + 8)
-#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
static inline int cap_msg_size(struct cap_msg_args *arg)
{
- return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len +
- arg->fscrypt_file_len;
+ return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len;
}
#else
+#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \
+ 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4)
+
static inline int cap_msg_size(struct cap_msg_args *arg)
{
return CAP_MSG_FIXED_FIELDS;
@@ -1550,13 +1561,10 @@ static inline int __send_flush_snap(struct inode *inode,
arg.inline_data = capsnap->inline_data;
arg.flags = 0;
arg.wake = false;
+ arg.encrypted = IS_ENCRYPTED(inode);
- /*
- * No fscrypt_auth changes from a capsnap. It will need
- * to update fscrypt_file on size changes (TODO).
- */
+ /* No fscrypt_auth changes from a capsnap.*/
arg.fscrypt_auth_len = 0;
- arg.fscrypt_file_len = 0;
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(&arg),
GFP_NOFS, false);
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 08504afbe242..4990886a366c 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -915,6 +915,9 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir,
goto out_req;
}
+ if (S_ISREG(mode) && IS_ENCRYPTED(dir))
+ set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags);
+
req->r_dentry = dget(dentry);
req->r_num_caps = 2;
req->r_parent = dir;
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 6e9ae398ef2c..c3ce224db032 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -790,6 +790,7 @@ retry:
req->r_parent = dir;
ihold(dir);
if (IS_ENCRYPTED(dir)) {
+ set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags);
if (!fscrypt_has_encryption_key(dir)) {
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_NOKEY_NAME;
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 9ec4ebe5ccaf..841f60e8de6a 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1028,6 +1028,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
if (new_version ||
(new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
+ u64 size = le64_to_cpu(info->size);
s64 old_pool = ci->i_layout.pool_id;
struct ceph_string *old_ns;
@@ -1041,10 +1042,22 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
pool_ns = old_ns;
+ if (IS_ENCRYPTED(inode) && size &&
+ iinfo->fscrypt_file_len == sizeof(__le64)) {
+ u64 fsize = __le64_to_cpu(*(__le64 *)iinfo->fscrypt_file);
+
+ if (size == round_up(fsize, CEPH_FSCRYPT_BLOCK_SIZE)) {
+ size = fsize;
+ } else {
+ pr_warn("fscrypt size mismatch: size=%llu fscrypt_file=%llu, discarding fscrypt_file size.\n",
+ info->size, size);
+ }
+ }
+
queue_trunc = ceph_fill_file_size(inode, issued,
le32_to_cpu(info->truncate_seq),
le64_to_cpu(info->truncate_size),
- le64_to_cpu(info->size));
+ size);
/* only update max_size on auth cap */
if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
ci->i_max_size != le64_to_cpu(info->max_size)) {
@@ -2388,11 +2401,25 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr,
}
} else if ((issued & CEPH_CAP_FILE_SHARED) == 0 ||
attr->ia_size != isize) {
- req->r_args.setattr.size = cpu_to_le64(attr->ia_size);
- req->r_args.setattr.old_size = cpu_to_le64(isize);
mask |= CEPH_SETATTR_SIZE;
release |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL |
CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR;
+ if (IS_ENCRYPTED(inode) && attr->ia_size) {
+ set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags);
+ mask |= CEPH_SETATTR_FSCRYPT_FILE;
+ req->r_args.setattr.size =
+ cpu_to_le64(round_up(attr->ia_size,
+ CEPH_FSCRYPT_BLOCK_SIZE));
+ req->r_args.setattr.old_size =
+ cpu_to_le64(round_up(isize,
+ CEPH_FSCRYPT_BLOCK_SIZE));
+ req->r_fscrypt_file = attr->ia_size;
+ /* FIXME: client must zero out any partial blocks! */
+ } else {
+ req->r_args.setattr.size = cpu_to_le64(attr->ia_size);
+ req->r_args.setattr.old_size = cpu_to_le64(isize);
+ req->r_fscrypt_file = 0;
+ }
}
}
if (ia_valid & ATTR_MTIME) {
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 7de22052ee22..c257d75c5757 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -2832,7 +2832,12 @@ static void encode_mclientrequest_tail(void **p,
} else {
ceph_encode_32(p, 0);
}
- ceph_encode_32(p, 0); // fscrypt_file for now
+ if (test_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags)) {
+ ceph_encode_32(p, sizeof(__le64));
+ ceph_encode_64(p, req->r_fscrypt_file);
+ } else {
+ ceph_encode_32(p, 0);
+ }
}
/*
@@ -2922,6 +2927,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
/* fscrypt_file */
len += sizeof(u32);
+ if (test_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags))
+ len += sizeof(__le64);
msg = ceph_msg_new2(CEPH_MSG_CLIENT_REQUEST, len, 1, GFP_NOFS, false);
if (!msg) {
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index a8f622439f38..0477388a0d1c 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -282,6 +282,7 @@ struct ceph_mds_request {
#define CEPH_MDS_R_DID_PREPOPULATE (6) /* prepopulated readdir */
#define CEPH_MDS_R_PARENT_LOCKED (7) /* is r_parent->i_rwsem wlocked? */
#define CEPH_MDS_R_ASYNC (8) /* async request */
+#define CEPH_MDS_R_FSCRYPT_FILE (9) /* must marshal fscrypt_file field */
unsigned long r_req_flags;
struct mutex r_fill_mutex;
@@ -289,6 +290,7 @@ struct ceph_mds_request {
union ceph_mds_request_args r_args;
struct ceph_fscrypt_auth *r_fscrypt_auth;
+ u64 r_fscrypt_file;
u8 *r_altname; /* fscrypt binary crypttext for long filenames */
u32 r_altname_len; /* length of r_altname */