ceph: fscrypt_auth handling for ceph
authorJeff Layton <jlayton@kernel.org>
Mon, 27 Jul 2020 14:16:09 +0000 (10:16 -0400)
committerIlya Dryomov <idryomov@gmail.com>
Tue, 22 Aug 2023 07:01:48 +0000 (09:01 +0200)
Most fscrypt-enabled filesystems store the crypto context in an xattr,
but that's problematic for ceph as xatts are governed by the XATTR cap,
but we really want the crypto context as part of the AUTH cap.

Because of this, the MDS has added two new inode metadata fields:
fscrypt_auth and fscrypt_file. The former is used to hold the crypto
context, and the latter is used to track the real file size.

Parse new fscrypt_auth and fscrypt_file fields in inode traces. For now,
we don't use fscrypt_file, but fscrypt_auth is used to hold the fscrypt
context.

Allow the client to use a setattr request for setting the fscrypt_auth
field. Since this is not a standard setattr request from the VFS, we add
a new field to __ceph_setattr that carries ceph-specific inode attrs.

Have the set_context op do a setattr that sets the fscrypt_auth value,
and get_context just return the contents of that field (since it should
always be available).

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>
fs/ceph/Makefile
fs/ceph/acl.c
fs/ceph/caps.c
fs/ceph/crypto.c [new file with mode: 0644]
fs/ceph/crypto.h [new file with mode: 0644]
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/super.c
fs/ceph/super.h
include/linux/ceph/ceph_fs.h

index 50c635dc7f711c4446ac537cc44d8a08a467ba39..1f77ca04c426fdb9be333b891ebbdee504e64d30 100644 (file)
@@ -12,3 +12,4 @@ ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
 
 ceph-$(CONFIG_CEPH_FSCACHE) += cache.o
 ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o
+ceph-$(CONFIG_FS_ENCRYPTION) += crypto.o
index 6945a938d396959cb554c37df3651311d8f3010e..8a56f979c7cbaa4d4be117ad104e582399e118a5 100644 (file)
@@ -140,7 +140,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
                newattrs.ia_ctime = current_time(inode);
                newattrs.ia_mode = new_mode;
                newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-               ret = __ceph_setattr(inode, &newattrs);
+               ret = __ceph_setattr(inode, &newattrs, NULL);
                if (ret)
                        goto out_free;
        }
@@ -151,7 +151,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
                        newattrs.ia_ctime = old_ctime;
                        newattrs.ia_mode = old_mode;
                        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-                       __ceph_setattr(inode, &newattrs);
+                       __ceph_setattr(inode, &newattrs, NULL);
                }
                goto out_free;
        }
index e2bb0d0072da5adc215d199765ee21a46d478e1f..1c62ef339bc68d79a370feec999f3317917e43be 100644 (file)
@@ -14,6 +14,7 @@
 #include "super.h"
 #include "mds_client.h"
 #include "cache.h"
+#include "crypto.h"
 #include <linux/ceph/decode.h>
 #include <linux/ceph/messenger.h>
 
@@ -1216,15 +1217,12 @@ struct cap_msg_args {
        umode_t                 mode;
        bool                    inline_data;
        bool                    wake;
+       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
 };
 
-/*
- * cap struct size + flock buffer size + inline version + inline data size +
- * osd_epoch_barrier + oldest_flush_tid
- */
-#define CAP_MSG_SIZE (sizeof(struct ceph_mds_caps) + \
-                     4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4)
-
 /* Marshal up the cap msg to the MDS */
 static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
 {
@@ -1240,7 +1238,7 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
             arg->size, arg->max_size, arg->xattr_version,
             arg->xattr_buf ? (int)arg->xattr_buf->vec.iov_len : 0);
 
-       msg->hdr.version = cpu_to_le16(10);
+       msg->hdr.version = cpu_to_le16(12);
        msg->hdr.tid = cpu_to_le64(arg->flush_tid);
 
        fc = msg->front.iov_base;
@@ -1311,6 +1309,21 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
 
        /* Advisory flags (version 10) */
        ceph_encode_32(&p, arg->flags);
+
+       /* dirstats (version 11) - these are r/o on the client */
+       ceph_encode_64(&p, 0);
+       ceph_encode_64(&p, 0);
+
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+       /* fscrypt_auth and fscrypt_file (version 12) */
+       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);
+#else /* CONFIG_FS_ENCRYPTION */
+       ceph_encode_32(&p, 0);
+       ceph_encode_32(&p, 0);
+#endif /* CONFIG_FS_ENCRYPTION */
 }
 
 /*
@@ -1432,7 +1445,37 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
                }
        }
        arg->flags = flags;
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+       if (ci->fscrypt_auth_len &&
+           WARN_ON_ONCE(ci->fscrypt_auth_len > sizeof(struct ceph_fscrypt_auth))) {
+               /* Don't set this if it's too big */
+               arg->fscrypt_auth_len = 0;
+       } else {
+               arg->fscrypt_auth_len = ci->fscrypt_auth_len;
+               memcpy(arg->fscrypt_auth, ci->fscrypt_auth,
+                      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 */
+}
+
+#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)
+
+#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;
 }
+#else
+static inline int cap_msg_size(struct cap_msg_args *arg)
+{
+       return CAP_MSG_FIXED_FIELDS;
+}
+#endif /* CONFIG_FS_ENCRYPTION */
 
 /*
  * Send a cap msg on the given inode.
@@ -1444,7 +1487,8 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
        struct ceph_msg *msg;
        struct inode *inode = &ci->netfs.inode;
 
-       msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
+       msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(arg), GFP_NOFS,
+                          false);
        if (!msg) {
                pr_err("error allocating cap msg: ino (%llx.%llx) flushing %s tid %llu, requeuing cap.\n",
                       ceph_vinop(inode), ceph_cap_string(arg->dirty),
@@ -1470,10 +1514,6 @@ static inline int __send_flush_snap(struct inode *inode,
        struct cap_msg_args     arg;
        struct ceph_msg         *msg;
 
-       msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
-       if (!msg)
-               return -ENOMEM;
-
        arg.session = session;
        arg.ino = ceph_vino(inode).ino;
        arg.cid = 0;
@@ -1511,6 +1551,18 @@ static inline int __send_flush_snap(struct inode *inode,
        arg.flags = 0;
        arg.wake = false;
 
+       /*
+        * No fscrypt_auth changes from a capsnap. It will need
+        * to update fscrypt_file on size changes (TODO).
+        */
+       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);
+       if (!msg)
+               return -ENOMEM;
+
        encode_cap_msg(msg, &arg);
        ceph_con_send(&arg.session->s_con, msg);
        return 0;
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
new file mode 100644 (file)
index 0000000..b17a6ee
--- /dev/null
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/ceph/ceph_debug.h>
+#include <linux/xattr.h>
+#include <linux/fscrypt.h>
+
+#include "super.h"
+#include "crypto.h"
+
+static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth;
+       u32 ctxlen;
+
+       /* Non existent or too short? */
+       if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1)))
+               return -ENOBUFS;
+
+       /* Some format we don't recognize? */
+       if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION)
+               return -ENOBUFS;
+
+       ctxlen = le32_to_cpu(cfa->cfa_blob_len);
+       if (len < ctxlen)
+               return -ERANGE;
+
+       memcpy(ctx, cfa->cfa_blob, ctxlen);
+       return ctxlen;
+}
+
+static int ceph_crypt_set_context(struct inode *inode, const void *ctx,
+                                 size_t len, void *fs_data)
+{
+       int ret;
+       struct iattr attr = { };
+       struct ceph_iattr cia = { };
+       struct ceph_fscrypt_auth *cfa;
+
+       WARN_ON_ONCE(fs_data);
+
+       if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE)
+               return -EINVAL;
+
+       cfa = kzalloc(sizeof(*cfa), GFP_KERNEL);
+       if (!cfa)
+               return -ENOMEM;
+
+       cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
+       cfa->cfa_blob_len = cpu_to_le32(len);
+       memcpy(cfa->cfa_blob, ctx, len);
+
+       cia.fscrypt_auth = cfa;
+
+       ret = __ceph_setattr(inode, &attr, &cia);
+       if (ret == 0)
+               inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED);
+       kfree(cia.fscrypt_auth);
+       return ret;
+}
+
+static bool ceph_crypt_empty_dir(struct inode *inode)
+{
+       struct ceph_inode_info *ci = ceph_inode(inode);
+
+       return ci->i_rsubdirs + ci->i_rfiles == 1;
+}
+
+static struct fscrypt_operations ceph_fscrypt_ops = {
+       .get_context            = ceph_crypt_get_context,
+       .set_context            = ceph_crypt_set_context,
+       .empty_dir              = ceph_crypt_empty_dir,
+};
+
+void ceph_fscrypt_set_ops(struct super_block *sb)
+{
+       fscrypt_set_ops(sb, &ceph_fscrypt_ops);
+}
diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
new file mode 100644 (file)
index 0000000..6dca674
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Ceph fscrypt functionality
+ */
+
+#ifndef _CEPH_CRYPTO_H
+#define _CEPH_CRYPTO_H
+
+#include <linux/fscrypt.h>
+
+struct ceph_fscrypt_auth {
+       __le32  cfa_version;
+       __le32  cfa_blob_len;
+       u8      cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
+} __packed;
+
+#define CEPH_FSCRYPT_AUTH_VERSION      1
+static inline u32 ceph_fscrypt_auth_len(struct ceph_fscrypt_auth *fa)
+{
+       u32 ctxsize = le32_to_cpu(fa->cfa_blob_len);
+
+       return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
+}
+
+#ifdef CONFIG_FS_ENCRYPTION
+void ceph_fscrypt_set_ops(struct super_block *sb);
+
+#else /* CONFIG_FS_ENCRYPTION */
+
+static inline void ceph_fscrypt_set_ops(struct super_block *sb)
+{
+}
+
+#endif /* CONFIG_FS_ENCRYPTION */
+
+#endif
index e2317202470776cb3b84fd7abbc3ab7da3eb0ce4..a3aa7870a6a29b3657e5bcddbb439c9eb08318c4 100644 (file)
 #include <linux/random.h>
 #include <linux/sort.h>
 #include <linux/iversion.h>
+#include <linux/fscrypt.h>
 
 #include "super.h"
 #include "mds_client.h"
 #include "cache.h"
+#include "crypto.h"
 #include <linux/ceph/decode.h>
 
 /*
@@ -617,6 +619,10 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        INIT_WORK(&ci->i_work, ceph_inode_work);
        ci->i_work_mask = 0;
        memset(&ci->i_btime, '\0', sizeof(ci->i_btime));
+#ifdef CONFIG_FS_ENCRYPTION
+       ci->fscrypt_auth = NULL;
+       ci->fscrypt_auth_len = 0;
+#endif
        return &ci->netfs.inode;
 }
 
@@ -625,6 +631,9 @@ void ceph_free_inode(struct inode *inode)
        struct ceph_inode_info *ci = ceph_inode(inode);
 
        kfree(ci->i_symlink);
+#ifdef CONFIG_FS_ENCRYPTION
+       kfree(ci->fscrypt_auth);
+#endif
        kmem_cache_free(ceph_inode_cachep, ci);
 }
 
@@ -645,6 +654,7 @@ void ceph_evict_inode(struct inode *inode)
        clear_inode(inode);
 
        ceph_fscache_unregister_inode_cookie(ci);
+       fscrypt_put_encryption_info(inode);
 
        __ceph_remove_caps(ci);
 
@@ -935,6 +945,17 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 
        __ceph_update_quota(ci, iinfo->max_bytes, iinfo->max_files);
 
+#ifdef CONFIG_FS_ENCRYPTION
+       if (iinfo->fscrypt_auth_len && (inode->i_state & I_NEW)) {
+               kfree(ci->fscrypt_auth);
+               ci->fscrypt_auth_len = iinfo->fscrypt_auth_len;
+               ci->fscrypt_auth = iinfo->fscrypt_auth;
+               iinfo->fscrypt_auth = NULL;
+               iinfo->fscrypt_auth_len = 0;
+               inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED);
+       }
+#endif
+
        if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
            (issued & CEPH_CAP_AUTH_EXCL) == 0) {
                inode->i_mode = mode;
@@ -2079,7 +2100,8 @@ static const struct inode_operations ceph_symlink_iops = {
        .listxattr = ceph_listxattr,
 };
 
-int __ceph_setattr(struct inode *inode, struct iattr *attr)
+int __ceph_setattr(struct inode *inode, struct iattr *attr,
+                  struct ceph_iattr *cia)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
        unsigned int ia_valid = attr->ia_valid;
@@ -2119,6 +2141,43 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
        }
 
        dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+       if (cia && cia->fscrypt_auth) {
+               u32 len = ceph_fscrypt_auth_len(cia->fscrypt_auth);
+
+               if (len > sizeof(*cia->fscrypt_auth)) {
+                       err = -EINVAL;
+                       spin_unlock(&ci->i_ceph_lock);
+                       goto out;
+               }
+
+               dout("setattr %llx:%llx fscrypt_auth len %u to %u)\n",
+                       ceph_vinop(inode), ci->fscrypt_auth_len, len);
+
+               /* It should never be re-set once set */
+               WARN_ON_ONCE(ci->fscrypt_auth);
+
+               if (issued & CEPH_CAP_AUTH_EXCL) {
+                       dirtied |= CEPH_CAP_AUTH_EXCL;
+                       kfree(ci->fscrypt_auth);
+                       ci->fscrypt_auth = (u8 *)cia->fscrypt_auth;
+                       ci->fscrypt_auth_len = len;
+               } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 ||
+                          ci->fscrypt_auth_len != len ||
+                          memcmp(ci->fscrypt_auth, cia->fscrypt_auth, len)) {
+                       req->r_fscrypt_auth = cia->fscrypt_auth;
+                       mask |= CEPH_SETATTR_FSCRYPT_AUTH;
+                       release |= CEPH_CAP_AUTH_SHARED;
+               }
+               cia->fscrypt_auth = NULL;
+       }
+#else
+       if (cia && cia->fscrypt_auth) {
+               err = -EINVAL;
+               spin_unlock(&ci->i_ceph_lock);
+               goto out;
+       }
+#endif /* CONFIG_FS_ENCRYPTION */
 
        if (ia_valid & ATTR_UID) {
                dout("setattr %p uid %d -> %d\n", inode,
@@ -2282,6 +2341,7 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
                req->r_stamp = attr->ia_ctime;
                err = ceph_mdsc_do_request(mdsc, NULL, req);
        }
+out:
        dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err,
             ceph_cap_string(dirtied), mask);
 
@@ -2322,7 +2382,7 @@ int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
            ceph_quota_is_max_bytes_exceeded(inode, attr->ia_size))
                return -EDQUOT;
 
-       err = __ceph_setattr(inode, attr);
+       err = __ceph_setattr(inode, attr, NULL);
 
        if (err >= 0 && (attr->ia_valid & ATTR_MODE))
                err = posix_acl_chmod(&nop_mnt_idmap, dentry, attr->ia_mode);
index 77b5e7b2e5ddfff8bf06561327cfc26d74fe17fa..c3927dab5a3bee1965de3b89afb9e1982e80c096 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "super.h"
 #include "mds_client.h"
+#include "crypto.h"
 
 #include <linux/ceph/ceph_features.h>
 #include <linux/ceph/messenger.h>
@@ -184,8 +185,54 @@ static int parse_reply_info_in(void **p, void *end,
                        info->rsnaps = 0;
                }
 
+               if (struct_v >= 5) {
+                       u32 alen;
+
+                       ceph_decode_32_safe(p, end, alen, bad);
+
+                       while (alen--) {
+                               u32 len;
+
+                               /* key */
+                               ceph_decode_32_safe(p, end, len, bad);
+                               ceph_decode_skip_n(p, end, len, bad);
+                               /* value */
+                               ceph_decode_32_safe(p, end, len, bad);
+                               ceph_decode_skip_n(p, end, len, bad);
+                       }
+               }
+
+               /* fscrypt flag -- ignore */
+               if (struct_v >= 6)
+                       ceph_decode_skip_8(p, end, bad);
+
+               info->fscrypt_auth = NULL;
+               info->fscrypt_auth_len = 0;
+               info->fscrypt_file = NULL;
+               info->fscrypt_file_len = 0;
+               if (struct_v >= 7) {
+                       ceph_decode_32_safe(p, end, info->fscrypt_auth_len, bad);
+                       if (info->fscrypt_auth_len) {
+                               info->fscrypt_auth = kmalloc(info->fscrypt_auth_len,
+                                                            GFP_KERNEL);
+                               if (!info->fscrypt_auth)
+                                       return -ENOMEM;
+                               ceph_decode_copy_safe(p, end, info->fscrypt_auth,
+                                                     info->fscrypt_auth_len, bad);
+                       }
+                       ceph_decode_32_safe(p, end, info->fscrypt_file_len, bad);
+                       if (info->fscrypt_file_len) {
+                               info->fscrypt_file = kmalloc(info->fscrypt_file_len,
+                                                            GFP_KERNEL);
+                               if (!info->fscrypt_file)
+                                       return -ENOMEM;
+                               ceph_decode_copy_safe(p, end, info->fscrypt_file,
+                                                     info->fscrypt_file_len, bad);
+                       }
+               }
                *p = end;
        } else {
+               /* legacy (unversioned) struct */
                if (features & CEPH_FEATURE_MDS_INLINE_DATA) {
                        ceph_decode_64_safe(p, end, info->inline_version, bad);
                        ceph_decode_32_safe(p, end, info->inline_len, bad);
@@ -651,8 +698,21 @@ out_bad:
 
 static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
 {
+       int i;
+
+       kfree(info->diri.fscrypt_auth);
+       kfree(info->diri.fscrypt_file);
+       kfree(info->targeti.fscrypt_auth);
+       kfree(info->targeti.fscrypt_file);
        if (!info->dir_entries)
                return;
+
+       for (i = 0; i < info->dir_nr; i++) {
+               struct ceph_mds_reply_dir_entry *rde = info->dir_entries + i;
+
+               kfree(rde->inode.fscrypt_auth);
+               kfree(rde->inode.fscrypt_file);
+       }
        free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
 }
 
@@ -966,6 +1026,7 @@ void ceph_mdsc_release_request(struct kref *kref)
        put_cred(req->r_cred);
        if (req->r_pagelist)
                ceph_pagelist_release(req->r_pagelist);
+       kfree(req->r_fscrypt_auth);
        put_request_session(req);
        ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation);
        WARN_ON_ONCE(!list_empty(&req->r_wait));
@@ -2543,8 +2604,8 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
        return r;
 }
 
-static void encode_timestamp_and_gids(void **p,
-                                     const struct ceph_mds_request *req)
+static void encode_mclientrequest_tail(void **p,
+                                      const struct ceph_mds_request *req)
 {
        struct ceph_timespec ts;
        int i;
@@ -2557,6 +2618,20 @@ static void encode_timestamp_and_gids(void **p,
        for (i = 0; i < req->r_cred->group_info->ngroups; i++)
                ceph_encode_64(p, from_kgid(&init_user_ns,
                                            req->r_cred->group_info->gid[i]));
+
+       /* v5: altname (TODO: skip for now) */
+       ceph_encode_32(p, 0);
+
+       /* v6: fscrypt_auth and fscrypt_file */
+       if (req->r_fscrypt_auth) {
+               u32 authlen = ceph_fscrypt_auth_len(req->r_fscrypt_auth);
+
+               ceph_encode_32(p, authlen);
+               ceph_encode_copy(p, req->r_fscrypt_auth, authlen);
+       } else {
+               ceph_encode_32(p, 0);
+       }
+       ceph_encode_32(p, 0); // fscrypt_file for now
 }
 
 /*
@@ -2605,12 +2680,14 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
                goto out_free1;
        }
 
+       /* head */
        len = legacy ? sizeof(*head) : sizeof(struct ceph_mds_request_head);
-       len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
-               sizeof(struct ceph_timespec);
-       len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
 
-       /* calculate (max) length for cap releases */
+       /* filepaths */
+       len += 2 * (1 + sizeof(u32) + sizeof(u64));
+       len += pathlen1 + pathlen2;
+
+       /* cap releases */
        len += sizeof(struct ceph_mds_request_release) *
                (!!req->r_inode_drop + !!req->r_dentry_drop +
                 !!req->r_old_inode_drop + !!req->r_old_dentry_drop);
@@ -2620,6 +2697,25 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
        if (req->r_old_dentry_drop)
                len += pathlen2;
 
+       /* MClientRequest tail */
+
+       /* req->r_stamp */
+       len += sizeof(struct ceph_timespec);
+
+       /* gid list */
+       len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
+
+       /* alternate name */
+       len += sizeof(u32);     // TODO
+
+       /* fscrypt_auth */
+       len += sizeof(u32); // fscrypt_auth
+       if (req->r_fscrypt_auth)
+               len += ceph_fscrypt_auth_len(req->r_fscrypt_auth);
+
+       /* fscrypt_file */
+       len += sizeof(u32);
+
        msg = ceph_msg_new2(CEPH_MSG_CLIENT_REQUEST, len, 1, GFP_NOFS, false);
        if (!msg) {
                msg = ERR_PTR(-ENOMEM);
@@ -2639,7 +2735,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
        } else {
                struct ceph_mds_request_head *new_head = msg->front.iov_base;
 
-               msg->hdr.version = cpu_to_le16(4);
+               msg->hdr.version = cpu_to_le16(6);
                new_head->version = cpu_to_le16(CEPH_MDS_REQUEST_HEAD_VERSION);
                head = (struct ceph_mds_request_head_old *)&new_head->oldest_client_tid;
                p = msg->front.iov_base + sizeof(*new_head);
@@ -2690,7 +2786,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
 
        head->num_releases = cpu_to_le16(releases);
 
-       encode_timestamp_and_gids(&p, req);
+       encode_mclientrequest_tail(&p, req);
 
        if (WARN_ON_ONCE(p > end)) {
                ceph_msg_put(msg);
@@ -2820,7 +2916,7 @@ static int __prepare_send_request(struct ceph_mds_session *session,
                rhead->num_releases = 0;
 
                p = msg->front.iov_base + req->r_request_release_offset;
-               encode_timestamp_and_gids(&p, req);
+               encode_mclientrequest_tail(&p, req);
 
                msg->front.iov_len = p - msg->front.iov_base;
                msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
index 3c1aa99c1876bd254cd0abe23b7220c9b5d97b68..a2e85fb5aab199270166b620bec82e192de3b591 100644 (file)
@@ -86,6 +86,10 @@ struct ceph_mds_reply_info_in {
        s32 dir_pin;
        struct ceph_timespec btime;
        struct ceph_timespec snap_btime;
+       u8 *fscrypt_auth;
+       u8 *fscrypt_file;
+       u32 fscrypt_auth_len;
+       u32 fscrypt_file_len;
        u64 rsnaps;
        u64 change_attr;
 };
@@ -278,6 +282,9 @@ struct ceph_mds_request {
        struct mutex r_fill_mutex;
 
        union ceph_mds_request_args r_args;
+
+       struct ceph_fscrypt_auth *r_fscrypt_auth;
+
        int r_fmode;        /* file mode, if expecting cap */
        int r_request_release_offset;
        const struct cred *r_cred;
index d6a1790f69233cca2d96547cf0fee5c5803b5713..c4ab2db85ef00374c457b7c81b45297c797d4aab 100644 (file)
@@ -20,6 +20,7 @@
 #include "super.h"
 #include "mds_client.h"
 #include "cache.h"
+#include "crypto.h"
 
 #include <linux/ceph/ceph_features.h>
 #include <linux/ceph/decode.h>
@@ -1135,6 +1136,8 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
        s->s_time_max = U32_MAX;
        s->s_flags |= SB_NODIRATIME | SB_NOATIME;
 
+       ceph_fscrypt_set_ops(s);
+
        ret = set_anon_super_fc(s, fc);
        if (ret != 0)
                fsc->sb = NULL;
index eb621bb276bd4f0b7a4bda190db9c185531d16e3..3a39a9b3bc334891939bcc612728a01bd54ba661 100644 (file)
@@ -450,6 +450,13 @@ struct ceph_inode_info {
 
        struct work_struct i_work;
        unsigned long  i_work_mask;
+
+#ifdef CONFIG_FS_ENCRYPTION
+       u32 fscrypt_auth_len;
+       u32 fscrypt_file_len;
+       u8 *fscrypt_auth;
+       u8 *fscrypt_file;
+#endif
 };
 
 struct ceph_netfs_request_data {
@@ -1073,7 +1080,13 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
 }
 extern int ceph_permission(struct mnt_idmap *idmap,
                           struct inode *inode, int mask);
-extern int __ceph_setattr(struct inode *inode, struct iattr *attr);
+
+struct ceph_iattr {
+       struct ceph_fscrypt_auth        *fscrypt_auth;
+};
+
+extern int __ceph_setattr(struct inode *inode, struct iattr *attr,
+                         struct ceph_iattr *cia);
 extern int ceph_setattr(struct mnt_idmap *idmap,
                        struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct mnt_idmap *idmap,
index 49586ff2615204e91761bf93690f4be379f443c8..45f8ce61e10348e3be1de177b7bfb118f0f0c167 100644 (file)
@@ -359,14 +359,19 @@ enum {
 
 extern const char *ceph_mds_op_name(int op);
 
-
-#define CEPH_SETATTR_MODE   1
-#define CEPH_SETATTR_UID    2
-#define CEPH_SETATTR_GID    4
-#define CEPH_SETATTR_MTIME  8
-#define CEPH_SETATTR_ATIME 16
-#define CEPH_SETATTR_SIZE  32
-#define CEPH_SETATTR_CTIME 64
+#define CEPH_SETATTR_MODE              (1 << 0)
+#define CEPH_SETATTR_UID               (1 << 1)
+#define CEPH_SETATTR_GID               (1 << 2)
+#define CEPH_SETATTR_MTIME             (1 << 3)
+#define CEPH_SETATTR_ATIME             (1 << 4)
+#define CEPH_SETATTR_SIZE              (1 << 5)
+#define CEPH_SETATTR_CTIME             (1 << 6)
+#define CEPH_SETATTR_MTIME_NOW         (1 << 7)
+#define CEPH_SETATTR_ATIME_NOW         (1 << 8)
+#define CEPH_SETATTR_BTIME             (1 << 9)
+#define CEPH_SETATTR_KILL_SGUID        (1 << 10)
+#define CEPH_SETATTR_FSCRYPT_AUTH      (1 << 11)
+#define CEPH_SETATTR_FSCRYPT_FILE      (1 << 12)
 
 /*
  * Ceph setxattr request flags.