]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph: handle fscrypt fields in cap messages from MDS
authorJeff Layton <jlayton@kernel.org>
Thu, 25 Aug 2022 13:31:09 +0000 (09:31 -0400)
committerXiubo Li <xiubli@redhat.com>
Fri, 26 Aug 2022 23:58:54 +0000 (07:58 +0800)
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.

Reviewed-by: Xiubo Li <xiubli@redhat.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
fs/ceph/caps.c

index e36dadb6e572a7b2e48c6fe4fc34b187eb57684e..448125edd76b47c63c9ffea0b0498dc89f36ae49 100644 (file)
@@ -3394,6 +3394,9 @@ struct cap_extra_info {
        /* currently issued */
        int issued;
        struct timespec64 btime;
+       u8 *fscrypt_auth;
+       u32 fscrypt_auth_len;
+       u64 fscrypt_file_size;
 };
 
 /*
@@ -3426,6 +3429,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,
@@ -3492,6 +3503,10 @@ 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));
+
+               WARN_ON_ONCE(ci->fscrypt_auth_len != extra_info->fscrypt_auth_len ||
+                            memcmp(ci->fscrypt_auth, extra_info->fscrypt_auth,
+                                    ci->fscrypt_auth_len));
        }
 
        if ((newcaps & CEPH_CAP_LINK_SHARED) &&
@@ -3904,7 +3919,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;
@@ -3921,6 +3937,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,
@@ -4143,6 +4167,49 @@ 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.
  *
@@ -4261,6 +4328,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,
@@ -4357,7 +4429,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 +4448,7 @@ done_unlocked:
        iput(inode);
 out:
        ceph_put_string(extra_info.pool_ns);
+       kfree(extra_info.fscrypt_auth);
        return;
 
 flush_cap_releases: