@@ -67,3 +67,66 @@ int ceph_fscrypt_set_ops(struct super_block *sb)
}
return 0;
}
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+ struct ceph_acl_sec_ctx *as)
+{
+ int ret, ctxsize;
+ size_t name_len;
+ char *name;
+ struct ceph_pagelist *pagelist = as->pagelist;
+ bool encrypted = false;
+
+ ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
+ if (ret)
+ return ret;
+ if (!encrypted)
+ return 0;
+
+ inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED);
+
+ /* No need to set context for dummy encryption */
+ if (fscrypt_get_dummy_context(inode->i_sb))
+ return 0;
+
+ ctxsize = fscrypt_new_context_from_inode((union fscrypt_context *)&as->fscrypt, inode);
+
+ /* marshal it in page array */
+ if (!pagelist) {
+ pagelist = ceph_pagelist_alloc(GFP_KERNEL);
+ if (!pagelist)
+ return -ENOMEM;
+ ret = ceph_pagelist_reserve(pagelist, PAGE_SIZE);
+ if (ret)
+ goto out;
+ ceph_pagelist_encode_32(pagelist, 1);
+ }
+
+ name = CEPH_XATTR_NAME_ENCRYPTION_CONTEXT;
+ name_len = strlen(name);
+ ret = ceph_pagelist_reserve(pagelist, 4 * 2 + name_len + ctxsize);
+ if (ret)
+ goto out;
+
+ if (as->pagelist) {
+ BUG_ON(pagelist->length <= sizeof(__le32));
+ if (list_is_singular(&pagelist->head)) {
+ le32_add_cpu((__le32*)pagelist->mapped_tail, 1);
+ } else {
+ struct page *page = list_first_entry(&pagelist->head,
+ struct page, lru);
+ void *addr = kmap_atomic(page);
+ le32_add_cpu((__le32*)addr, 1);
+ kunmap_atomic(addr);
+ }
+ }
+
+ ceph_pagelist_encode_32(pagelist, name_len);
+ ceph_pagelist_append(pagelist, name, name_len);
+ ceph_pagelist_encode_32(pagelist, ctxsize);
+ ceph_pagelist_append(pagelist, as->fscrypt, ctxsize);
+out:
+ if (pagelist && !as->pagelist)
+ ceph_pagelist_release(pagelist);
+ return ret;
+}
@@ -11,6 +11,8 @@
#define CEPH_XATTR_NAME_ENCRYPTION_CONTEXT "encryption.ctx"
int ceph_fscrypt_set_ops(struct super_block *sb);
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+ struct ceph_acl_sec_ctx *as);
#else /* CONFIG_FS_ENCRYPTION */
@@ -19,6 +21,12 @@ static inline int ceph_fscrypt_set_ops(struct super_block *sb)
return 0;
}
+static inline int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+ struct ceph_acl_sec_ctx *as)
+{
+ return 0;
+}
+
#endif /* CONFIG_FS_ENCRYPTION */
#endif
@@ -18,6 +18,7 @@
#include "super.h"
#include "mds_client.h"
#include "cache.h"
+#include "crypto.h"
#include <linux/ceph/decode.h>
/*
@@ -70,12 +71,17 @@ struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
goto out_err;
}
+ inode->i_state = 0;
+ inode->i_mode = *mode;
+
err = ceph_security_init_secctx(dentry, *mode, as_ctx);
if (err < 0)
goto out_err;
- inode->i_state = 0;
- inode->i_mode = *mode;
+ err = ceph_fscrypt_prepare_context(dir, inode, as_ctx);
+ if (err)
+ goto out_err;
+
return inode;
out_err:
iput(inode);
@@ -995,6 +995,9 @@ struct ceph_acl_sec_ctx {
#ifdef CONFIG_CEPH_FS_SECURITY_LABEL
void *sec_ctx;
u32 sec_ctxlen;
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+ u8 fscrypt[FSCRYPT_SET_CONTEXT_MAX_SIZE];
#endif
struct ceph_pagelist *pagelist;
};
After pre-creating a new inode, do an fscrypt prepare on it, fetch a new encryption context and then marshal that into the security context to be sent along with the RPC. Signed-off-by: Jeff Layton <jlayton@kernel.org> --- fs/ceph/crypto.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/ceph/crypto.h | 8 ++++++ fs/ceph/inode.c | 10 ++++++-- fs/ceph/super.h | 3 +++ 4 files changed, 82 insertions(+), 2 deletions(-)