linux-fscrypt.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support
@ 2021-06-25 13:58 Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 01/24] vfs: export new_inode_pseudo Jeff Layton
                   ` (23 more replies)
  0 siblings, 24 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

This patchset is a fairly substantial rework since the last set. Rather
than storing the crypto context in an xattr, this one stores it in a new
field associated with AUTH caps. That ensures that it's always
available whenever an inode is instantiated, and should prevent the
deadlock that Luis reported. This means that we need to do a setattr
request to set the context now, so support for that is also added.

The required MDS patches are currently here:

    https://github.com/ceph/ceph/pull/41284

...but it's marked as draft for now. I don't want to merge this until
the content encryption piece is more clearly defined. These patches are
also available in my git tree under the ceph-fscrypt-fnames-v7 tag:

    https://git.kernel.org/pub/scm/linux/kernel/git/jlayton/linux.git/

This seems to be reasonably robust in testing. The next step is content
encryption. I have a start on some of those patches, but it's still very
much a WIP.

Jeff Layton (24):
  vfs: export new_inode_pseudo
  fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size
  fscrypt: add fscrypt_context_for_new_inode
  ceph: preallocate inode for ops that may create one
  ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  ceph: add fscrypt_* handling to caps.c
  ceph: add ability to set fscrypt_auth via setattr
  ceph: crypto context handling for ceph
  ceph: implement -o test_dummy_encryption mount option
  ceph: add routine to create fscrypt context prior to RPC
  ceph: add fscrypt ioctls
  ceph: decode alternate_name in lease info
  ceph: make ceph_msdc_build_path use ref-walk
  ceph: add encrypted fname handling to ceph_mdsc_build_path
  ceph: send altname in MClientRequest
  ceph: properly set DCACHE_NOKEY_NAME flag in lookup
  ceph: make d_revalidate call fscrypt revalidator for encrypted
    dentries
  ceph: add helpers for converting names for userland presentation
  ceph: add fscrypt support to ceph_fill_trace
  ceph: add support to readdir for encrypted filenames
  ceph: create symlinks with encrypted and base64-encoded targets
  ceph: make ceph_get_name decrypt filenames
  ceph: add a new ceph.fscrypt.auth vxattr

 fs/ceph/Makefile             |   1 +
 fs/ceph/acl.c                |   4 +-
 fs/ceph/caps.c               |  62 +++++--
 fs/ceph/crypto.c             | 206 +++++++++++++++++++++
 fs/ceph/crypto.h             | 119 ++++++++++++
 fs/ceph/dir.c                | 198 +++++++++++++++-----
 fs/ceph/export.c             |  44 +++--
 fs/ceph/file.c               |  64 ++++---
 fs/ceph/inode.c              | 302 +++++++++++++++++++++++++++---
 fs/ceph/ioctl.c              |  83 +++++++++
 fs/ceph/mds_client.c         | 345 ++++++++++++++++++++++++++++++-----
 fs/ceph/mds_client.h         |  22 ++-
 fs/ceph/super.c              |  80 +++++++-
 fs/ceph/super.h              |  32 +++-
 fs/ceph/xattr.c              |  20 ++
 fs/crypto/fname.c            |  53 ++++--
 fs/crypto/fscrypt_private.h  |   9 +-
 fs/crypto/hooks.c            |   6 +-
 fs/crypto/policy.c           |  34 +++-
 fs/inode.c                   |   1 +
 include/linux/ceph/ceph_fs.h |  21 ++-
 include/linux/fscrypt.h      |  10 +
 22 files changed, 1498 insertions(+), 218 deletions(-)
 create mode 100644 fs/ceph/crypto.c
 create mode 100644 fs/ceph/crypto.h

-- 
2.31.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 01/24] vfs: export new_inode_pseudo
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode Jeff Layton
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel
  Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells, Al Viro

Ceph needs to be able to allocate inodes ahead of a create that might
involve a fscrypt-encrypted inode. new_inode() almost fits the bill,
but it puts the inode on the sb->s_inodes list and when we go to hash
it, that might be done again.

We could work around that by setting I_CREATING on the new inode, but
that causes ilookup5 to return -ESTALE if something tries to find it
before I_NEW is cleared. This is desirable behavior for most
filesystems, but doesn't work for ceph.

To work around all of this, just use new_inode_pseudo which doesn't add
it to the sb->s_inodes list.

Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/inode.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/inode.c b/fs/inode.c
index c93500d84264..cf9ea4b260b0 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -941,6 +941,7 @@ struct inode *new_inode_pseudo(struct super_block *sb)
 	}
 	return inode;
 }
+EXPORT_SYMBOL(new_inode_pseudo);
 
 /**
  *	new_inode 	- obtain an inode
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 01/24] vfs: export new_inode_pseudo Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-11 17:40   ` Eric Biggers
  2021-06-25 13:58 ` [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size Jeff Layton
                   ` (21 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Ceph is going to add fscrypt support, but we still want encrypted
filenames to be composed of printable characters, so we can maintain
compatibility with clients that don't support fscrypt.

We could just adopt fscrypt's current nokey name format, but that is
subject to change in the future, and it also contains dirhash fields
that we don't need for cephfs. Because of this, we're going to concoct
our own scheme for encoding encrypted filenames. It's very similar to
fscrypt's current scheme, but doesn't bother with the dirhash fields.

The ceph encoding scheme will use base64 encoding as well, and we also
want it to avoid characters that are illegal in filenames. Export the
fscrypt base64 encoding/decoding routines so we can use them in ceph's
fscrypt implementation.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/crypto/fname.c       | 34 ++++++++++++++++++++++++----------
 include/linux/fscrypt.h |  5 +++++
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 6ca7d16593ff..32b1f50433ba 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -178,10 +178,8 @@ static int fname_decrypt(const struct inode *inode,
 static const char lookup_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
 
-#define BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
-
 /**
- * base64_encode() - base64-encode some bytes
+ * fscrypt_base64_encode() - base64-encode some bytes
  * @src: the bytes to encode
  * @len: number of bytes to encode
  * @dst: (output) the base64-encoded string.  Not NUL-terminated.
@@ -191,7 +189,7 @@ static const char lookup_table[65] =
  *
  * Return: length of the encoded string
  */
-static int base64_encode(const u8 *src, int len, char *dst)
+int fscrypt_base64_encode(const u8 *src, int len, char *dst)
 {
 	int i, bits = 0, ac = 0;
 	char *cp = dst;
@@ -209,8 +207,20 @@ static int base64_encode(const u8 *src, int len, char *dst)
 		*cp++ = lookup_table[ac & 0x3f];
 	return cp - dst;
 }
+EXPORT_SYMBOL(fscrypt_base64_encode);
 
-static int base64_decode(const char *src, int len, u8 *dst)
+/**
+ * fscrypt_base64_decode() - base64-decode some bytes
+ * @src: the bytes to decode
+ * @len: number of bytes to decode
+ * @dst: (output) decoded binary data
+ *
+ * Decode an input string that was previously encoded using
+ * fscrypt_base64_encode.
+ *
+ * Return: length of the decoded binary data
+ */
+int fscrypt_base64_decode(const char *src, int len, u8 *dst)
 {
 	int i, bits = 0, ac = 0;
 	const char *p;
@@ -232,6 +242,7 @@ static int base64_decode(const char *src, int len, u8 *dst)
 		return -1;
 	return cp - dst;
 }
+EXPORT_SYMBOL(fscrypt_base64_decode);
 
 bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
 				  u32 orig_len, u32 max_len,
@@ -263,8 +274,9 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
 int fscrypt_fname_alloc_buffer(u32 max_encrypted_len,
 			       struct fscrypt_str *crypto_str)
 {
-	const u32 max_encoded_len = BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX);
 	u32 max_presented_len;
+	const u32 max_encoded_len =
+		FSCRYPT_BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX);
 
 	max_presented_len = max(max_encoded_len, max_encrypted_len);
 
@@ -342,7 +354,7 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode,
 		     offsetof(struct fscrypt_nokey_name, bytes));
 	BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, bytes) !=
 		     offsetof(struct fscrypt_nokey_name, sha256));
-	BUILD_BUG_ON(BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX) > NAME_MAX);
+	BUILD_BUG_ON(FSCRYPT_BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX) > NAME_MAX);
 
 	if (hash) {
 		nokey_name.dirhash[0] = hash;
@@ -362,7 +374,8 @@ int fscrypt_fname_disk_to_usr(const struct inode *inode,
 		       nokey_name.sha256);
 		size = FSCRYPT_NOKEY_NAME_MAX;
 	}
-	oname->len = base64_encode((const u8 *)&nokey_name, size, oname->name);
+	oname->len = fscrypt_base64_encode((const u8 *)&nokey_name, size,
+					   oname->name);
 	return 0;
 }
 EXPORT_SYMBOL(fscrypt_fname_disk_to_usr);
@@ -436,14 +449,15 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
 	 * user-supplied name
 	 */
 
-	if (iname->len > BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX))
+	if (iname->len > FSCRYPT_BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX))
 		return -ENOENT;
 
 	fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL);
 	if (fname->crypto_buf.name == NULL)
 		return -ENOMEM;
 
-	ret = base64_decode(iname->name, iname->len, fname->crypto_buf.name);
+	ret = fscrypt_base64_decode(iname->name, iname->len,
+				    fname->crypto_buf.name);
 	if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) ||
 	    (ret > offsetof(struct fscrypt_nokey_name, sha256) &&
 	     ret != FSCRYPT_NOKEY_NAME_MAX)) {
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 2ea1387bb497..e300f6145ddc 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -46,6 +46,9 @@ struct fscrypt_name {
 /* Maximum value for the third parameter of fscrypt_operations.set_context(). */
 #define FSCRYPT_SET_CONTEXT_MAX_SIZE	40
 
+/* Calculate worst-case base64 encoding inflation */
+#define FSCRYPT_BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
+
 #ifdef CONFIG_FS_ENCRYPTION
 /*
  * fscrypt superblock flags
@@ -207,6 +210,8 @@ void fscrypt_free_inode(struct inode *inode);
 int fscrypt_drop_inode(struct inode *inode);
 
 /* fname.c */
+int fscrypt_base64_encode(const u8 *src, int len, char *dst);
+int fscrypt_base64_decode(const char *src, int len, u8 *dst);
 int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname,
 			   int lookup, struct fscrypt_name *fname);
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 01/24] vfs: export new_inode_pseudo Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-11 17:43   ` Eric Biggers
  2021-06-25 13:58 ` [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode Jeff Layton
                   ` (20 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

For ceph, we want to use our own scheme for handling filenames that are
are longer than NAME_MAX after encryption and base64 encoding. This
allows us to have a consistent view of the encrypted filenames for
clients that don't support fscrypt and clients that do but that don't
have the key.

Currently, fs/crypto only supports encrypting filenames using
fscrypt_setup_filename, but that also handles encoding nokey names. Ceph
can't use that because it handles nokey names in a different way.

Export fscrypt_fname_encrypt. Rename fscrypt_fname_encrypted_size to
__fscrypt_fname_encrypted_size and add a new wrapper called
fscrypt_fname_encrypted_size that takes an inode argument rather than a
pointer to a fscrypt_policy union.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/crypto/fname.c           | 19 ++++++++++++++-----
 fs/crypto/fscrypt_private.h |  9 +++------
 fs/crypto/hooks.c           |  6 +++---
 include/linux/fscrypt.h     |  4 ++++
 4 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 32b1f50433ba..5a794de7f61d 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -126,6 +126,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 
 	return 0;
 }
+EXPORT_SYMBOL(fscrypt_fname_encrypt);
 
 /**
  * fname_decrypt() - decrypt a filename
@@ -244,9 +245,9 @@ int fscrypt_base64_decode(const char *src, int len, u8 *dst)
 }
 EXPORT_SYMBOL(fscrypt_base64_decode);
 
-bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
-				  u32 orig_len, u32 max_len,
-				  u32 *encrypted_len_ret)
+bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
+				    u32 orig_len, u32 max_len,
+				    u32 *encrypted_len_ret)
 {
 	int padding = 4 << (fscrypt_policy_flags(policy) &
 			    FSCRYPT_POLICY_FLAGS_PAD_MASK);
@@ -260,6 +261,15 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
 	return true;
 }
 
+bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+				  u32 max_len, u32 *encrypted_len_ret)
+{
+	return __fscrypt_fname_encrypted_size(&inode->i_crypt_info->ci_policy,
+					      orig_len, max_len,
+					      encrypted_len_ret);
+}
+EXPORT_SYMBOL(fscrypt_fname_encrypted_size);
+
 /**
  * fscrypt_fname_alloc_buffer() - allocate a buffer for presented filenames
  * @max_encrypted_len: maximum length of encrypted filenames the buffer will be
@@ -422,8 +432,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
 		return ret;
 
 	if (fscrypt_has_encryption_key(dir)) {
-		if (!fscrypt_fname_encrypted_size(&dir->i_crypt_info->ci_policy,
-						  iname->len,
+		if (!fscrypt_fname_encrypted_size(dir, iname->len,
 						  dir->i_sb->s_cop->max_namelen,
 						  &fname->crypto_buf.len))
 			return -ENAMETOOLONG;
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 3fa965eb3336..195de6d0db40 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -292,14 +292,11 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
 			 const struct fscrypt_info *ci);
 
 /* fname.c */
-int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
-			  u8 *out, unsigned int olen);
-bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
-				  u32 orig_len, u32 max_len,
-				  u32 *encrypted_len_ret);
+bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
+				    u32 orig_len, u32 max_len,
+                                    u32 *encrypted_len_ret);
 
 /* hkdf.c */
-
 struct fscrypt_hkdf {
 	struct crypto_shash *hmac_tfm;
 };
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
index a73b0376e6f3..e65c19aae041 100644
--- a/fs/crypto/hooks.c
+++ b/fs/crypto/hooks.c
@@ -228,9 +228,9 @@ int fscrypt_prepare_symlink(struct inode *dir, const char *target,
 	 * counting it (even though it is meaningless for ciphertext) is simpler
 	 * for now since filesystems will assume it is there and subtract it.
 	 */
-	if (!fscrypt_fname_encrypted_size(policy, len,
-					  max_len - sizeof(struct fscrypt_symlink_data),
-					  &disk_link->len))
+	if (!__fscrypt_fname_encrypted_size(policy, len,
+					    max_len - sizeof(struct fscrypt_symlink_data),
+					    &disk_link->len))
 		return -ENAMETOOLONG;
 	disk_link->len += sizeof(struct fscrypt_symlink_data);
 
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index e300f6145ddc..b5c31baaa8bf 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -212,6 +212,10 @@ int fscrypt_drop_inode(struct inode *inode);
 /* fname.c */
 int fscrypt_base64_encode(const u8 *src, int len, char *dst);
 int fscrypt_base64_decode(const char *src, int len, u8 *dst);
+bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+				  u32 max_len, u32 *encrypted_len_ret);
+int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
+			  u8 *out, unsigned int olen);
 int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname,
 			   int lookup, struct fscrypt_name *fname);
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (2 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-11 17:44   ` Eric Biggers
  2021-06-25 13:58 ` [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one Jeff Layton
                   ` (19 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Most filesystems just call fscrypt_set_context on new inodes, which
usually causes a setxattr. That's a bit late for ceph, which can send
along a full set of attributes with the create request.

Doing so allows it to avoid race windows that where the new inode could
be seen by other clients without the crypto context attached. It also
avoids the separate round trip to the server.

Refactor the fscrypt code a bit to allow us to create a new crypto
context, attach it to the inode, and write it to the buffer, but without
calling set_context on it. ceph can later use this to marshal the
context into the attributes we send along with the create request.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/crypto/policy.c      | 34 ++++++++++++++++++++++++++++------
 include/linux/fscrypt.h |  1 +
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index ed3d623724cd..6a895a31560f 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -664,6 +664,31 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir)
 	return fscrypt_get_dummy_policy(dir->i_sb);
 }
 
+/**
+ * fscrypt_context_for_new_inode() - create an encryption context for a new inode
+ * @ctx: where context should be written
+ * @inode: inode from which to fetch policy and nonce
+ *
+ * Given an in-core "prepared" (via fscrypt_prepare_new_inode) inode,
+ * generate a new context and write it to ctx. ctx _must_ be at least
+ * FSCRYPT_SET_CONTEXT_MAX_SIZE bytes.
+ *
+ * Returns size of the resulting context or a negative error code.
+ */
+int fscrypt_context_for_new_inode(void *ctx, struct inode *inode)
+{
+	struct fscrypt_info *ci = inode->i_crypt_info;
+
+	BUILD_BUG_ON(sizeof(union fscrypt_context) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
+
+	/* fscrypt_prepare_new_inode() should have set up the key already. */
+	if (WARN_ON_ONCE(!ci))
+		return -ENOKEY;
+
+	return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce);
+}
+EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode);
+
 /**
  * fscrypt_set_context() - Set the fscrypt context of a new inode
  * @inode: a new inode
@@ -680,12 +705,9 @@ int fscrypt_set_context(struct inode *inode, void *fs_data)
 	union fscrypt_context ctx;
 	int ctxsize;
 
-	/* fscrypt_prepare_new_inode() should have set up the key already. */
-	if (WARN_ON_ONCE(!ci))
-		return -ENOKEY;
-
-	BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
-	ctxsize = fscrypt_new_context(&ctx, &ci->ci_policy, ci->ci_nonce);
+	ctxsize = fscrypt_context_for_new_inode(&ctx, inode);
+	if (ctxsize < 0)
+		return ctxsize;
 
 	/*
 	 * This may be the first time the inode number is available, so do any
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index b5c31baaa8bf..087fa87bca0b 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -178,6 +178,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg);
 int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg);
 int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
 int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
+int fscrypt_context_for_new_inode(void *ctx, struct inode *inode);
 int fscrypt_set_context(struct inode *inode, void *fs_data);
 
 struct fscrypt_dummy_policy {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (3 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-07  3:37   ` Xiubo Li
  2021-06-25 13:58 ` [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces Jeff Layton
                   ` (18 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

When creating a new inode, we need to determine the crypto context
before we can transmit the RPC. The fscrypt API has a routine for getting
a crypto context before a create occurs, but it requires an inode.

Change the ceph code to preallocate an inode in advance of a create of
any sort (open(), mknod(), symlink(), etc). Move the existing code that
generates the ACL and SELinux blobs into this routine since that's
mostly common across all the different codepaths.

In most cases, we just want to allow ceph_fill_trace to use that inode
after the reply comes in, so add a new field to the MDS request for it
(r_new_inode).

The async create codepath is a bit different though. In that case, we
want to hash the inode in advance of the RPC so that it can be used
before the reply comes in. If the call subsequently fails with
-EJUKEBOX, then just put the references and clean up the as_ctx. Note
that with this change, we now need to regenerate the as_ctx when this
occurs, but it's quite rare for it to happen.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/dir.c        | 70 ++++++++++++++++++++-----------------
 fs/ceph/file.c       | 62 ++++++++++++++++++++-------------
 fs/ceph/inode.c      | 82 ++++++++++++++++++++++++++++++++++++++++----
 fs/ceph/mds_client.c |  3 +-
 fs/ceph/mds_client.h |  1 +
 fs/ceph/super.h      |  7 +++-
 6 files changed, 160 insertions(+), 65 deletions(-)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 0c97ec1e4d8c..8b6a1c960afa 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -852,13 +852,6 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 		goto out;
 	}
 
-	err = ceph_pre_init_acls(dir, &mode, &as_ctx);
-	if (err < 0)
-		goto out;
-	err = ceph_security_init_secctx(dentry, mode, &as_ctx);
-	if (err < 0)
-		goto out;
-
 	dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n",
 	     dir, dentry, mode, rdev);
 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS);
@@ -866,6 +859,14 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 		err = PTR_ERR(req);
 		goto out;
 	}
+
+	req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+	if (IS_ERR(req->r_new_inode)) {
+		err = PTR_ERR(req->r_new_inode);
+		req->r_new_inode = NULL;
+		goto out_req;
+	}
+
 	req->r_dentry = dget(dentry);
 	req->r_num_caps = 2;
 	req->r_parent = dir;
@@ -875,13 +876,13 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 	req->r_args.mknod.rdev = cpu_to_le32(rdev);
 	req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL;
 	req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-	if (as_ctx.pagelist) {
-		req->r_pagelist = as_ctx.pagelist;
-		as_ctx.pagelist = NULL;
-	}
+
+	ceph_as_ctx_to_req(req, &as_ctx);
+
 	err = ceph_mdsc_do_request(mdsc, dir, req);
 	if (!err && !req->r_reply_info.head->is_dentry)
 		err = ceph_handle_notrace_create(dir, dentry);
+out_req:
 	ceph_mdsc_put_request(req);
 out:
 	if (!err)
@@ -904,6 +905,7 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
 	struct ceph_mds_request *req;
 	struct ceph_acl_sec_ctx as_ctx = {};
+	umode_t mode = S_IFLNK | 0777;
 	int err;
 
 	if (ceph_snap(dir) != CEPH_NOSNAP)
@@ -914,21 +916,24 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 		goto out;
 	}
 
-	err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx);
-	if (err < 0)
-		goto out;
-
 	dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
 	if (IS_ERR(req)) {
 		err = PTR_ERR(req);
 		goto out;
 	}
+
+	req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+	if (IS_ERR(req->r_new_inode)) {
+		err = PTR_ERR(req->r_new_inode);
+		req->r_new_inode = NULL;
+		goto out_req;
+	}
+
 	req->r_path2 = kstrdup(dest, GFP_KERNEL);
 	if (!req->r_path2) {
 		err = -ENOMEM;
-		ceph_mdsc_put_request(req);
-		goto out;
+		goto out_req;
 	}
 	req->r_parent = dir;
 	ihold(dir);
@@ -938,13 +943,13 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 	req->r_num_caps = 2;
 	req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL;
 	req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-	if (as_ctx.pagelist) {
-		req->r_pagelist = as_ctx.pagelist;
-		as_ctx.pagelist = NULL;
-	}
+
+	ceph_as_ctx_to_req(req, &as_ctx);
+
 	err = ceph_mdsc_do_request(mdsc, dir, req);
 	if (!err && !req->r_reply_info.head->is_dentry)
 		err = ceph_handle_notrace_create(dir, dentry);
+out_req:
 	ceph_mdsc_put_request(req);
 out:
 	if (err)
@@ -980,13 +985,6 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
 		goto out;
 	}
 
-	mode |= S_IFDIR;
-	err = ceph_pre_init_acls(dir, &mode, &as_ctx);
-	if (err < 0)
-		goto out;
-	err = ceph_security_init_secctx(dentry, mode, &as_ctx);
-	if (err < 0)
-		goto out;
 
 	req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
 	if (IS_ERR(req)) {
@@ -994,6 +992,14 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
 		goto out;
 	}
 
+	mode |= S_IFDIR;
+	req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+	if (IS_ERR(req->r_new_inode)) {
+		err = PTR_ERR(req->r_new_inode);
+		req->r_new_inode = NULL;
+		goto out_req;
+	}
+
 	req->r_dentry = dget(dentry);
 	req->r_num_caps = 2;
 	req->r_parent = dir;
@@ -1002,15 +1008,15 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
 	req->r_args.mkdir.mode = cpu_to_le32(mode);
 	req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL;
 	req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-	if (as_ctx.pagelist) {
-		req->r_pagelist = as_ctx.pagelist;
-		as_ctx.pagelist = NULL;
-	}
+
+	ceph_as_ctx_to_req(req, &as_ctx);
+
 	err = ceph_mdsc_do_request(mdsc, dir, req);
 	if (!err &&
 	    !req->r_reply_info.head->is_target &&
 	    !req->r_reply_info.head->is_dentry)
 		err = ceph_handle_notrace_create(dir, dentry);
+out_req:
 	ceph_mdsc_put_request(req);
 out:
 	if (!err)
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index d1755ac1d964..2cda398ba64d 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -565,7 +565,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
 	ceph_mdsc_release_dir_caps(req);
 }
 
-static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry,
+static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
+				    struct dentry *dentry,
 				    struct file *file, umode_t mode,
 				    struct ceph_mds_request *req,
 				    struct ceph_acl_sec_ctx *as_ctx,
@@ -576,7 +577,6 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry,
 	struct ceph_mds_reply_inode in = { };
 	struct ceph_mds_reply_info_in iinfo = { .in = &in };
 	struct ceph_inode_info *ci = ceph_inode(dir);
-	struct inode *inode;
 	struct timespec64 now;
 	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
 	struct ceph_vino vino = { .ino = req->r_deleg_ino,
@@ -584,10 +584,6 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry,
 
 	ktime_get_real_ts64(&now);
 
-	inode = ceph_get_inode(dentry->d_sb, vino);
-	if (IS_ERR(inode))
-		return PTR_ERR(inode);
-
 	iinfo.inline_version = CEPH_INLINE_NONE;
 	iinfo.change_attr = 1;
 	ceph_encode_timespec64(&iinfo.btime, &now);
@@ -625,8 +621,7 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry,
 		ceph_dir_clear_complete(dir);
 		if (!d_unhashed(dentry))
 			d_drop(dentry);
-		if (inode->i_state & I_NEW)
-			discard_new_inode(inode);
+		discard_new_inode(inode);
 	} else {
 		struct dentry *dn;
 
@@ -666,6 +661,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 	struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
 	struct ceph_mds_client *mdsc = fsc->mdsc;
 	struct ceph_mds_request *req;
+	struct inode *new_inode = NULL;
 	struct dentry *dn;
 	struct ceph_acl_sec_ctx as_ctx = {};
 	bool try_async = ceph_test_mount_opt(fsc, ASYNC_DIROPS);
@@ -678,21 +674,21 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 
 	if (dentry->d_name.len > NAME_MAX)
 		return -ENAMETOOLONG;
-
+retry:
 	if (flags & O_CREAT) {
 		if (ceph_quota_is_max_files_exceeded(dir))
 			return -EDQUOT;
-		err = ceph_pre_init_acls(dir, &mode, &as_ctx);
-		if (err < 0)
-			return err;
-		err = ceph_security_init_secctx(dentry, mode, &as_ctx);
-		if (err < 0)
+
+		new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx);
+		if (IS_ERR(new_inode)) {
+			err = PTR_ERR(new_inode);
 			goto out_ctx;
+		}
 	} else if (!d_in_lookup(dentry)) {
 		/* If it's not being looked up, it's negative */
 		return -ENOENT;
 	}
-retry:
+
 	/* do the open */
 	req = prepare_open_request(dir->i_sb, flags, mode);
 	if (IS_ERR(req)) {
@@ -713,25 +709,40 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 
 		req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL;
 		req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
-		if (as_ctx.pagelist) {
-			req->r_pagelist = as_ctx.pagelist;
-			as_ctx.pagelist = NULL;
-		}
-		if (try_async &&
-		    (req->r_dir_caps =
-		      try_prep_async_create(dir, dentry, &lo,
-					    &req->r_deleg_ino))) {
+
+		ceph_as_ctx_to_req(req, &as_ctx);
+
+		if (try_async && (req->r_dir_caps =
+				  try_prep_async_create(dir, dentry, &lo, &req->r_deleg_ino))) {
+			struct ceph_vino vino = { .ino = req->r_deleg_ino,
+						  .snap = CEPH_NOSNAP };
+
 			set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags);
 			req->r_args.open.flags |= cpu_to_le32(CEPH_O_EXCL);
 			req->r_callback = ceph_async_create_cb;
+
+			/* Hash inode before RPC */
+			new_inode = ceph_get_inode(dir->i_sb, vino, new_inode);
+			if (IS_ERR(new_inode)) {
+				err = PTR_ERR(new_inode);
+				new_inode = NULL;
+				goto out_req;
+			}
+			WARN_ON_ONCE(!(new_inode->i_state & I_NEW));
+
 			err = ceph_mdsc_submit_request(mdsc, dir, req);
 			if (!err) {
-				err = ceph_finish_async_create(dir, dentry,
+				err = ceph_finish_async_create(dir, new_inode, dentry,
 							file, mode, req,
 							&as_ctx, &lo);
+				new_inode = NULL;
 			} else if (err == -EJUKEBOX) {
 				restore_deleg_ino(dir, req->r_deleg_ino);
 				ceph_mdsc_put_request(req);
+				discard_new_inode(new_inode);
+				ceph_release_acl_sec_ctx(&as_ctx);
+				memset(&as_ctx, 0, sizeof(as_ctx));
+				new_inode = NULL;
 				try_async = false;
 				goto retry;
 			}
@@ -740,6 +751,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 	}
 
 	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
+	req->r_new_inode = new_inode;
+	new_inode = NULL;
 	err = ceph_mdsc_do_request(mdsc,
 				   (flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
 				   req);
@@ -782,6 +795,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 	}
 out_req:
 	ceph_mdsc_put_request(req);
+	iput(new_inode);
 out_ctx:
 	ceph_release_acl_sec_ctx(&as_ctx);
 	dout("atomic_open result=%d\n", err);
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index eb562e259347..f62785e4dbcb 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -52,17 +52,85 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
 	return 0;
 }
 
-struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino)
+/**
+ * ceph_new_inode - allocate a new inode in advance of an expected create
+ * @dir: parent directory for new inode
+ * @dentry: dentry that may eventually point to new inode
+ * @mode: mode of new inode
+ * @as_ctx: pointer to inherited security context
+ *
+ * Allocate a new inode in advance of an operation to create a new inode.
+ * This allocates the inode and sets up the acl_sec_ctx with appropriate
+ * info for the new inode.
+ *
+ * Returns a pointer to the new inode or an ERR_PTR.
+ */
+struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
+			     umode_t *mode, struct ceph_acl_sec_ctx *as_ctx)
+{
+	int err;
+	struct inode *inode;
+
+	inode = new_inode_pseudo(dir->i_sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	if (!S_ISLNK(*mode)) {
+		err = ceph_pre_init_acls(dir, mode, as_ctx);
+		if (err < 0)
+			goto out_err;
+	}
+
+	err = ceph_security_init_secctx(dentry, *mode, as_ctx);
+	if (err < 0)
+		goto out_err;
+
+	inode->i_state = 0;
+	inode->i_mode = *mode;
+	return inode;
+out_err:
+	iput(inode);
+	return ERR_PTR(err);
+}
+
+void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx)
+{
+	if (as_ctx->pagelist) {
+		req->r_pagelist = as_ctx->pagelist;
+		as_ctx->pagelist = NULL;
+	}
+}
+
+/**
+ * ceph_get_inode - find or create/hash a new inode
+ * @sb: superblock to search and allocate in
+ * @vino: vino to search for
+ * @newino: optional new inode to insert if one isn't found (may be NULL)
+ *
+ * Search for or insert a new inode into the hash for the given vino, and return a
+ * reference to it. If new is non-NULL, its reference is consumed.
+ */
+struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, struct inode *newino)
 {
 	struct inode *inode;
 
 	if (ceph_vino_is_reserved(vino))
 		return ERR_PTR(-EREMOTEIO);
 
-	inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
-			     ceph_set_ino_cb, &vino);
-	if (!inode)
+	if (newino) {
+		inode = inode_insert5(newino, (unsigned long)vino.ino, ceph_ino_compare,
+					ceph_set_ino_cb, &vino);
+		if (inode != newino)
+			iput(newino);
+	} else {
+		inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
+				     ceph_set_ino_cb, &vino);
+	}
+
+	if (!inode) {
+		dout("No inode found for %llx.%llx\n", vino.ino, vino.snap);
 		return ERR_PTR(-ENOMEM);
+	}
 
 	dout("get_inode on %llu=%llx.%llx got %p new %d\n", ceph_present_inode(inode),
 	     ceph_vinop(inode), inode, !!(inode->i_state & I_NEW));
@@ -78,7 +146,7 @@ struct inode *ceph_get_snapdir(struct inode *parent)
 		.ino = ceph_ino(parent),
 		.snap = CEPH_SNAPDIR,
 	};
-	struct inode *inode = ceph_get_inode(parent->i_sb, vino);
+	struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL);
 	struct ceph_inode_info *ci = ceph_inode(inode);
 
 	if (IS_ERR(inode))
@@ -1546,7 +1614,7 @@ static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req,
 		vino.ino = le64_to_cpu(rde->inode.in->ino);
 		vino.snap = le64_to_cpu(rde->inode.in->snapid);
 
-		in = ceph_get_inode(req->r_dentry->d_sb, vino);
+		in = ceph_get_inode(req->r_dentry->d_sb, vino, NULL);
 		if (IS_ERR(in)) {
 			err = PTR_ERR(in);
 			dout("new_inode badness got %d\n", err);
@@ -1748,7 +1816,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 		if (d_really_is_positive(dn)) {
 			in = d_inode(dn);
 		} else {
-			in = ceph_get_inode(parent->d_sb, tvino);
+			in = ceph_get_inode(parent->d_sb, tvino, NULL);
 			if (IS_ERR(in)) {
 				dout("new_inode badness\n");
 				d_drop(dn);
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 52ae5373437d..3b3a14024ca0 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -831,6 +831,7 @@ void ceph_mdsc_release_request(struct kref *kref)
 		iput(req->r_parent);
 	}
 	iput(req->r_target_inode);
+	iput(req->r_new_inode);
 	if (req->r_dentry)
 		dput(req->r_dentry);
 	if (req->r_old_dentry)
@@ -3237,7 +3238,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
 			.snap = le64_to_cpu(rinfo->targeti.in->snapid)
 		};
 
-		in = ceph_get_inode(mdsc->fsc->sb, tvino);
+		in = ceph_get_inode(mdsc->fsc->sb, tvino, xchg(&req->r_new_inode, NULL));
 		if (IS_ERR(in)) {
 			err = PTR_ERR(in);
 			mutex_lock(&session->s_mutex);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 20e42d8b66c6..64ea9d853b8d 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -259,6 +259,7 @@ struct ceph_mds_request {
 
 	struct inode *r_parent;		    /* parent dir inode */
 	struct inode *r_target_inode;       /* resulting inode */
+	struct inode *r_new_inode;	    /* new inode (for creates) */
 
 #define CEPH_MDS_R_DIRECT_IS_HASH	(1) /* r_direct_hash is valid */
 #define CEPH_MDS_R_ABORTED		(2) /* call was aborted */
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 3bdd41d70e9f..0cd94b296f5f 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -958,6 +958,7 @@ static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci)
 /* inode.c */
 struct ceph_mds_reply_info_in;
 struct ceph_mds_reply_dirfrag;
+struct ceph_acl_sec_ctx;
 
 extern const struct inode_operations ceph_file_iops;
 
@@ -965,8 +966,12 @@ extern struct inode *ceph_alloc_inode(struct super_block *sb);
 extern void ceph_evict_inode(struct inode *inode);
 extern void ceph_free_inode(struct inode *inode);
 
+struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
+			     umode_t *mode, struct ceph_acl_sec_ctx *as_ctx);
+void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx);
+
 extern struct inode *ceph_get_inode(struct super_block *sb,
-				    struct ceph_vino vino);
+				    struct ceph_vino vino, struct inode *newino);
 extern struct inode *ceph_get_snapdir(struct inode *parent);
 extern int ceph_fill_file_size(struct inode *inode, int issued,
 			       u32 truncate_seq, u64 truncate_size, u64 size);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (4 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-07  3:53   ` Xiubo Li
  2021-07-07 10:47   ` Luis Henriques
  2021-06-25 13:58 ` [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c Jeff Layton
                   ` (17 subsequent siblings)
  23 siblings, 2 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

...and store them in the ceph_inode_info.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/file.c       |  2 ++
 fs/ceph/inode.c      | 18 ++++++++++++++++++
 fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 fs/ceph/mds_client.h |  4 ++++
 fs/ceph/super.h      |  6 ++++++
 5 files changed, 74 insertions(+)

diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 2cda398ba64d..ea0e85075b7b 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
 	iinfo.xattr_data = xattr_buf;
 	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
 
+	/* FIXME: set fscrypt_auth and fscrypt_file */
+
 	in.ino = cpu_to_le64(vino.ino);
 	in.snapid = cpu_to_le64(CEPH_NOSNAP);
 	in.version = cpu_to_le64(1);	// ???
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index f62785e4dbcb..b620281ea65b 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
 
 	ci->i_meta_err = 0;
 
+#ifdef CONFIG_FS_ENCRYPTION
+	ci->fscrypt_auth = NULL;
+	ci->fscrypt_auth_len = 0;
+	ci->fscrypt_file = NULL;
+	ci->fscrypt_file_len = 0;
+#endif
+
 	return &ci->vfs_inode;
 }
 
@@ -619,6 +626,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);
 }
 
@@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 		xattr_blob = NULL;
 	}
 
+	if (iinfo->fscrypt_auth_len && !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);
+	}
+
 	/* finally update i_version */
 	if (le64_to_cpu(info->version) > ci->i_version)
 		ci->i_version = le64_to_cpu(info->version);
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 3b3a14024ca0..9c994effc51d 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -183,8 +183,48 @@ 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);
+
+		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);
@@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
 
 static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
 {
+	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;
 	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 64ea9d853b8d..0c3cc61fd038 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -88,6 +88,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;
 };
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 0cd94b296f5f..e032737fe472 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -429,6 +429,12 @@ struct ceph_inode_info {
 
 #ifdef CONFIG_CEPH_FSCACHE
 	struct fscache_cookie *fscache;
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+	u32 fscrypt_auth_len;
+	u32 fscrypt_file_len;
+	u8 *fscrypt_auth;
+	u8 *fscrypt_file;
 #endif
 	errseq_t i_meta_err;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (5 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-07  7:20   ` Xiubo Li
  2021-07-11 23:00   ` Eric Biggers
  2021-06-25 13:58 ` [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr Jeff Layton
                   ` (16 subsequent siblings)
  23 siblings, 2 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 13 deletions(-)

diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 038f59cc4250..1be6c5148700 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -13,6 +13,7 @@
 #include "super.h"
 #include "mds_client.h"
 #include "cache.h"
+#include "crypto.h"
 #include <linux/ceph/decode.h>
 #include <linux/ceph/messenger.h>
 
@@ -1229,15 +1230,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)
 {
@@ -1253,7 +1251,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;
@@ -1324,6 +1322,16 @@ 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);
+
+	/* 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);
 }
 
 /*
@@ -1445,6 +1453,26 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
 		}
 	}
 	arg->flags = flags;
+	if (ci->fscrypt_auth_len &&
+	    WARN_ON_ONCE(ci->fscrypt_auth_len != sizeof(struct ceph_fscrypt_auth))) {
+		/* Don't set this if it isn't right size */
+		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;
+}
+
+#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 + arg->fscrypt_auth_len +
+			arg->fscrypt_file_len;
 }
 
 /*
@@ -1457,7 +1485,7 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
 	struct ceph_msg *msg;
 	struct inode *inode = &ci->vfs_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),
@@ -1483,10 +1511,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;
@@ -1524,6 +1548,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;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (6 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-07  8:11   ` Xiubo Li
  2021-07-07 10:47   ` Luis Henriques
  2021-06-25 13:58 ` [RFC PATCH v7 09/24] ceph: crypto context handling for ceph Jeff Layton
                   ` (15 subsequent siblings)
  23 siblings, 2 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/acl.c                |  4 ++--
 fs/ceph/inode.c              | 30 ++++++++++++++++++++++++++++--
 fs/ceph/mds_client.c         | 31 ++++++++++++++++++++++++++-----
 fs/ceph/mds_client.h         |  3 +++
 fs/ceph/super.h              |  7 ++++++-
 include/linux/ceph/ceph_fs.h | 21 +++++++++++++--------
 6 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 529af59d9fd3..6e716f142022 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -136,7 +136,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
 		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;
 	}
@@ -147,7 +147,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
 			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;
 	}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index b620281ea65b..7821ba04eef3 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -2086,7 +2086,7 @@ 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;
@@ -2127,6 +2127,32 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
 
 	dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
 
+	if (cia && cia->fscrypt_auth) {
+		u32 len = offsetof(struct ceph_fscrypt_auth, cfa_blob) +
+			  le32_to_cpu(cia->fscrypt_auth->cfa_blob_len);
+
+		if (len > sizeof(*cia->fscrypt_auth))
+			return -EINVAL;
+
+		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) {
+			req->r_fscrypt_auth = cia->fscrypt_auth;
+			mask |= CEPH_SETATTR_FSCRYPT_AUTH;
+			release |= CEPH_CAP_AUTH_SHARED;
+		}
+		cia->fscrypt_auth = NULL;
+	}
+
 	if (ia_valid & ATTR_UID) {
 		dout("setattr %p uid %d -> %d\n", inode,
 		     from_kuid(&init_user_ns, inode->i_uid),
@@ -2324,7 +2350,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, 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(&init_user_ns, inode, attr->ia_mode);
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 9c994effc51d..4aca8ce1c135 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -2529,8 +2529,7 @@ 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;
@@ -2543,6 +2542,21 @@ 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 = le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
+
+		authlen += offsetof(struct ceph_fscrypt_auth, cfa_blob);
+		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
 }
 
 /*
@@ -2591,6 +2605,13 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
 	len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
 		sizeof(struct ceph_timespec);
 	len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
+	len += sizeof(u32); // altname
+	len += sizeof(u32); // fscrypt_auth
+	if (req->r_fscrypt_auth) {
+		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
+		len += le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
+	}
+	len += sizeof(u32); // fscrypt_file
 
 	/* calculate (max) length for cap releases */
 	len += sizeof(struct ceph_mds_request_release) *
@@ -2621,7 +2642,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);
@@ -2672,7 +2693,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);
@@ -2781,7 +2802,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);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 0c3cc61fd038..800eed49c2fd 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -278,6 +278,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 */
 	const struct cred *r_cred;
 	int r_request_release_offset;
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index e032737fe472..ad62cde30e0b 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -1035,7 +1035,12 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
 }
 extern int ceph_permission(struct user_namespace *mnt_userns,
 			   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 user_namespace *mnt_userns,
 			struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct user_namespace *mnt_userns,
diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
index e41a811026f6..a45a82c7d432 100644
--- a/include/linux/ceph/ceph_fs.h
+++ b/include/linux/ceph/ceph_fs.h
@@ -355,14 +355,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.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 09/24] ceph: crypto context handling for ceph
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (7 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 10/24] ceph: implement -o test_dummy_encryption mount option Jeff Layton
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Have set_context do a setattr that sets the fscrypt_auth value, and
get_context just return the contents of that field.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/Makefile |  1 +
 fs/ceph/crypto.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ceph/crypto.h | 32 ++++++++++++++++++++
 fs/ceph/inode.c  |  3 ++
 fs/ceph/super.c  |  3 ++
 5 files changed, 116 insertions(+)
 create mode 100644 fs/ceph/crypto.c
 create mode 100644 fs/ceph/crypto.h

diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile
index 50c635dc7f71..1f77ca04c426 100644
--- a/fs/ceph/Makefile
+++ b/fs/ceph/Makefile
@@ -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
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
new file mode 100644
index 000000000000..cdca7660f835
--- /dev/null
+++ b/fs/ceph/crypto.c
@@ -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,
+	.max_namelen		= NAME_MAX,
+};
+
+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
index 000000000000..53c6a3f35c64
--- /dev/null
+++ b/fs/ceph/crypto.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Ceph fscrypt functionality
+ */
+
+#ifndef _CEPH_CRYPTO_H
+#define _CEPH_CRYPTO_H
+
+#include <linux/fscrypt.h>
+
+#define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
+
+#ifdef CONFIG_FS_ENCRYPTION
+
+#define CEPH_FSCRYPT_AUTH_VERSION	1
+struct ceph_fscrypt_auth {
+	__le32	cfa_version;
+	__le32	cfa_blob_len;
+	u8	cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
+} __packed;
+
+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
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 7821ba04eef3..fba139a4f57b 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -14,10 +14,12 @@
 #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>
 
 /*
@@ -647,6 +649,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);
 
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 9b1b7f4cfdd4..cdac6ff675e2 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -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>
@@ -988,6 +989,8 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
 	s->s_time_min = 0;
 	s->s_time_max = U32_MAX;
 
+	ceph_fscrypt_set_ops(s);
+
 	ret = set_anon_super_fc(s, fc);
 	if (ret != 0)
 		fsc->sb = NULL;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 10/24] ceph: implement -o test_dummy_encryption mount option
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (8 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 09/24] ceph: crypto context handling for ceph Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC Jeff Layton
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/crypto.c | 11 +++++++
 fs/ceph/crypto.h |  5 ++++
 fs/ceph/super.c  | 77 ++++++++++++++++++++++++++++++++++++++++++++++--
 fs/ceph/super.h  |  7 ++++-
 4 files changed, 97 insertions(+), 3 deletions(-)

diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index cdca7660f835..997a33e1d59f 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -64,9 +64,20 @@ static bool ceph_crypt_empty_dir(struct inode *inode)
 	return ci->i_rsubdirs + ci->i_rfiles == 1;
 }
 
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+	fscrypt_free_dummy_policy(&fsc->dummy_enc_policy);
+}
+
+static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb)
+{
+	return ceph_sb_to_client(sb)->dummy_enc_policy.policy;
+}
+
 static struct fscrypt_operations ceph_fscrypt_ops = {
 	.get_context		= ceph_crypt_get_context,
 	.set_context		= ceph_crypt_set_context,
+	.get_dummy_policy	= ceph_get_dummy_policy,
 	.empty_dir		= ceph_crypt_empty_dir,
 	.max_namelen		= NAME_MAX,
 };
diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
index 53c6a3f35c64..d2b1f8e7b300 100644
--- a/fs/ceph/crypto.h
+++ b/fs/ceph/crypto.h
@@ -21,12 +21,17 @@ struct ceph_fscrypt_auth {
 
 void ceph_fscrypt_set_ops(struct super_block *sb);
 
+void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
+
 #else /* CONFIG_FS_ENCRYPTION */
 
 static inline void ceph_fscrypt_set_ops(struct super_block *sb)
 {
 }
 
+static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
+{
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index cdac6ff675e2..48a99da4ff97 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -45,6 +45,7 @@ static void ceph_put_super(struct super_block *s)
 	struct ceph_fs_client *fsc = ceph_sb_to_client(s);
 
 	dout("put_super\n");
+	ceph_fscrypt_free_dummy_policy(fsc);
 	ceph_mdsc_close_sessions(fsc->mdsc);
 }
 
@@ -159,6 +160,7 @@ enum {
 	Opt_quotadf,
 	Opt_copyfrom,
 	Opt_wsync,
+	Opt_test_dummy_encryption,
 };
 
 enum ceph_recover_session_mode {
@@ -197,6 +199,8 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
 	fsparam_u32	("rsize",			Opt_rsize),
 	fsparam_string	("snapdirname",			Opt_snapdirname),
 	fsparam_string	("source",			Opt_source),
+	fsparam_flag	("test_dummy_encryption",	Opt_test_dummy_encryption),
+	fsparam_string	("test_dummy_encryption",	Opt_test_dummy_encryption),
 	fsparam_u32	("wsize",			Opt_wsize),
 	fsparam_flag_no	("wsync",			Opt_wsync),
 	{}
@@ -455,6 +459,16 @@ static int ceph_parse_mount_param(struct fs_context *fc,
 		else
 			fsopt->flags |= CEPH_MOUNT_OPT_ASYNC_DIROPS;
 		break;
+	case Opt_test_dummy_encryption:
+#ifdef CONFIG_FS_ENCRYPTION
+		kfree(fsopt->test_dummy_encryption);
+		fsopt->test_dummy_encryption = param->string;
+		param->string = NULL;
+		fsopt->flags |= CEPH_MOUNT_OPT_TEST_DUMMY_ENC;
+#else
+		warnfc(fc, "FS encryption not supported: test_dummy_encryption mount option ignored");
+#endif
+		break;
 	default:
 		BUG();
 	}
@@ -474,6 +488,7 @@ static void destroy_mount_options(struct ceph_mount_options *args)
 	kfree(args->mds_namespace);
 	kfree(args->server_path);
 	kfree(args->fscache_uniq);
+	kfree(args->test_dummy_encryption);
 	kfree(args);
 }
 
@@ -581,6 +596,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
 	if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
 		seq_puts(m, ",nowsync");
 
+	fscrypt_show_test_dummy_encryption(m, ',', root->d_sb);
+
 	if (fsopt->wsize != CEPH_MAX_WRITE_SIZE)
 		seq_printf(m, ",wsize=%u", fsopt->wsize);
 	if (fsopt->rsize != CEPH_MAX_READ_SIZE)
@@ -916,6 +933,52 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
 	return root;
 }
 
+#ifdef CONFIG_FS_ENCRYPTION
+static int ceph_set_test_dummy_encryption(struct super_block *sb, struct fs_context *fc,
+						struct ceph_mount_options *fsopt)
+{
+	struct ceph_fs_client *fsc = sb->s_fs_info;
+
+	/*
+	 * No changing encryption context on remount. Note that
+	 * fscrypt_set_test_dummy_encryption will validate the version
+	 * string passed in (if any).
+	 */
+	if (fsopt->flags & CEPH_MOUNT_OPT_TEST_DUMMY_ENC) {
+		int err = 0;
+
+		if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && !fsc->dummy_enc_policy.policy) {
+			errorfc(fc, "Can't set test_dummy_encryption on remount");
+			return -EEXIST;
+		}
+
+		err = fscrypt_set_test_dummy_encryption(sb,
+							fsc->mount_options->test_dummy_encryption,
+							&fsc->dummy_enc_policy);
+		if (err) {
+			if (err == -EEXIST)
+				errorfc(fc, "Can't change test_dummy_encryption on remount");
+			else if (err == -EINVAL)
+				errorfc(fc, "Value of option \"%s\" is unrecognized",
+					fsc->mount_options->test_dummy_encryption);
+			else
+				errorfc(fc, "Error processing option \"%s\" [%d]",
+					fsc->mount_options->test_dummy_encryption, err);
+			return err;
+		}
+		warnfc(fc, "test_dummy_encryption mode enabled");
+	}
+	return 0;
+}
+#else
+static inline int ceph_set_test_dummy_encryption(struct super_block *sb, struct fs_context *fc,
+						struct ceph_mount_options *fsopt)
+{
+	warnfc(fc, "test_dummy_encryption mode ignored");
+	return 0;
+}
+#endif
+
 /*
  * mount: join the ceph cluster, and open root directory.
  */
@@ -944,6 +1007,10 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
 				goto out;
 		}
 
+		err = ceph_set_test_dummy_encryption(fsc->sb, fc, fsc->mount_options);
+		if (err)
+			goto out;
+
 		dout("mount opening path '%s'\n", path);
 
 		ceph_fs_debugfs_init(fsc);
@@ -1138,16 +1205,22 @@ static void ceph_free_fc(struct fs_context *fc)
 
 static int ceph_reconfigure_fc(struct fs_context *fc)
 {
+	int err;
 	struct ceph_parse_opts_ctx *pctx = fc->fs_private;
 	struct ceph_mount_options *fsopt = pctx->opts;
-	struct ceph_fs_client *fsc = ceph_sb_to_client(fc->root->d_sb);
+	struct super_block *sb = fc->root->d_sb;
+	struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
+
+	err = ceph_set_test_dummy_encryption(sb, fc, fsopt);
+	if (err)
+		return err;
 
 	if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
 		ceph_set_mount_opt(fsc, ASYNC_DIROPS);
 	else
 		ceph_clear_mount_opt(fsc, ASYNC_DIROPS);
 
-	sync_filesystem(fc->root->d_sb);
+	sync_filesystem(sb);
 	return 0;
 }
 
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index ad62cde30e0b..534c2a76562d 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -17,6 +17,7 @@
 #include <linux/posix_acl.h>
 #include <linux/refcount.h>
 #include <linux/security.h>
+#include <linux/fscrypt.h>
 
 #include <linux/ceph/libceph.h>
 
@@ -45,6 +46,7 @@
 #define CEPH_MOUNT_OPT_NOQUOTADF       (1<<13) /* no root dir quota in statfs */
 #define CEPH_MOUNT_OPT_NOCOPYFROM      (1<<14) /* don't use RADOS 'copy-from' op */
 #define CEPH_MOUNT_OPT_ASYNC_DIROPS    (1<<15) /* allow async directory ops */
+#define CEPH_MOUNT_OPT_TEST_DUMMY_ENC  (1<<16) /* enable dummy encryption (for testing) */
 
 #define CEPH_MOUNT_OPT_DEFAULT			\
 	(CEPH_MOUNT_OPT_DCACHE |		\
@@ -97,6 +99,7 @@ struct ceph_mount_options {
 	char *mds_namespace;  /* default NULL */
 	char *server_path;    /* default NULL (means "/") */
 	char *fscache_uniq;   /* default NULL */
+	char *test_dummy_encryption;	/* default NULL */
 };
 
 struct ceph_fs_client {
@@ -136,9 +139,11 @@ struct ceph_fs_client {
 #ifdef CONFIG_CEPH_FSCACHE
 	struct fscache_cookie *fscache;
 #endif
+#ifdef CONFIG_FS_ENCRYPTION
+	struct fscrypt_dummy_policy dummy_enc_policy;
+#endif
 };
 
-
 /*
  * File i/o capability.  This tracks shared state with the metadata
  * server that allows us to cache or writeback attributes or to read
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (9 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 10/24] ceph: implement -o test_dummy_encryption mount option Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-07 10:48   ` Luis Henriques
  2021-06-25 13:58 ` [RFC PATCH v7 12/24] ceph: add fscrypt ioctls Jeff Layton
                   ` (12 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

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. Call the new function from
ceph_new_inode.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/crypto.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 fs/ceph/crypto.h | 25 +++++++++++++++++++++++++
 fs/ceph/inode.c  | 10 ++++++++--
 fs/ceph/super.h  |  5 +++++
 fs/ceph/xattr.c  |  3 +++
 5 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index 997a33e1d59f..675d41fd2eb0 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -4,6 +4,7 @@
 #include <linux/fscrypt.h>
 
 #include "super.h"
+#include "mds_client.h"
 #include "crypto.h"
 
 static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
@@ -86,3 +87,44 @@ void ceph_fscrypt_set_ops(struct super_block *sb)
 {
 	fscrypt_set_ops(sb, &ceph_fscrypt_ops);
 }
+
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+				 struct ceph_acl_sec_ctx *as)
+{
+	int ret, ctxsize;
+	bool encrypted = false;
+	struct ceph_inode_info *ci = ceph_inode(inode);
+
+	ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
+	if (ret)
+		return ret;
+	if (!encrypted)
+		return 0;
+
+	as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
+	if (!as->fscrypt_auth)
+		return -ENOMEM;
+
+	ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, inode);
+	if (ctxsize < 0)
+		return ctxsize;
+
+	as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
+	as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
+
+	WARN_ON_ONCE(ci->fscrypt_auth);
+	kfree(ci->fscrypt_auth);
+	ci->fscrypt_auth_len = ceph_fscrypt_auth_size(ctxsize);
+	ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, GFP_KERNEL);
+	if (!ci->fscrypt_auth)
+		return -ENOMEM;
+
+	inode->i_flags |= S_ENCRYPTED;
+
+	return 0;
+}
+
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as)
+{
+	swap(req->r_fscrypt_auth, as->fscrypt_auth);
+}
diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
index d2b1f8e7b300..bdf1ba47db16 100644
--- a/fs/ceph/crypto.h
+++ b/fs/ceph/crypto.h
@@ -11,6 +11,9 @@
 #define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
 
 #ifdef CONFIG_FS_ENCRYPTION
+struct ceph_fs_client;
+struct ceph_acl_sec_ctx;
+struct ceph_mds_request;
 
 #define CEPH_FSCRYPT_AUTH_VERSION	1
 struct ceph_fscrypt_auth {
@@ -19,10 +22,19 @@ struct ceph_fscrypt_auth {
 	u8	cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
 } __packed;
 
+static inline u32 ceph_fscrypt_auth_size(u32 ctxsize)
+{
+	return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
+}
+
 void ceph_fscrypt_set_ops(struct super_block *sb);
 
 void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
 
+int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+				 struct ceph_acl_sec_ctx *as);
+void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as);
+
 #else /* CONFIG_FS_ENCRYPTION */
 
 static inline void ceph_fscrypt_set_ops(struct super_block *sb)
@@ -32,6 +44,19 @@ static inline void ceph_fscrypt_set_ops(struct super_block *sb)
 static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
 {
 }
+
+static inline int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
+						struct ceph_acl_sec_ctx *as)
+{
+	if (IS_ENCRYPTED(dir))
+		return -EOPNOTSUPP;
+	return 0;
+}
+
+static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
+						struct ceph_acl_sec_ctx *as_ctx)
+{
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index fba139a4f57b..a0b311195e80 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -83,12 +83,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);
@@ -101,6 +106,7 @@ void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *a
 		req->r_pagelist = as_ctx->pagelist;
 		as_ctx->pagelist = NULL;
 	}
+	ceph_fscrypt_as_ctx_to_req(req, as_ctx);
 }
 
 /**
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 534c2a76562d..651d7909a443 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -26,6 +26,8 @@
 #include <linux/fscache.h>
 #endif
 
+#include "crypto.h"
+
 /* f_type in struct statfs */
 #define CEPH_SUPER_MAGIC 0x00c36400
 
@@ -1068,6 +1070,9 @@ struct ceph_acl_sec_ctx {
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
 	void *sec_ctx;
 	u32 sec_ctxlen;
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+	struct ceph_fscrypt_auth *fscrypt_auth;
 #endif
 	struct ceph_pagelist *pagelist;
 };
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 1242db8d3444..16a62a2bd61e 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -1362,6 +1362,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
 #endif
 #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
 	security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen);
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+	kfree(as_ctx->fscrypt_auth);
 #endif
 	if (as_ctx->pagelist)
 		ceph_pagelist_release(as_ctx->pagelist);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 12/24] ceph: add fscrypt ioctls
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (10 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-08  7:30   ` Xiubo Li
  2021-06-25 13:58 ` [RFC PATCH v7 13/24] ceph: decode alternate_name in lease info Jeff Layton
                   ` (11 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

We gate most of the ioctls on MDS feature support. The exception is the
key removal and status functions that we still want to work if the MDS's
were to (inexplicably) lose the feature.

For the set_policy ioctl, we take Fcx caps to ensure that nothing can
create files in the directory while the ioctl is running. That should
be enough to ensure that the "empty_dir" check is reliable.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/ioctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index 6e061bf62ad4..477ecc667aee 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -6,6 +6,7 @@
 #include "mds_client.h"
 #include "ioctl.h"
 #include <linux/ceph/striper.h>
+#include <linux/fscrypt.h>
 
 /*
  * ioctls
@@ -268,8 +269,54 @@ static long ceph_ioctl_syncio(struct file *file)
 	return 0;
 }
 
+static int vet_mds_for_fscrypt(struct file *file)
+{
+	int i, ret = -EOPNOTSUPP;
+	struct ceph_mds_client	*mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
+
+	mutex_lock(&mdsc->mutex);
+	for (i = 0; i < mdsc->max_sessions; i++) {
+		struct ceph_mds_session *s = mdsc->sessions[i];
+
+		if (!s)
+			continue;
+		if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
+			ret = 0;
+		break;
+	}
+	mutex_unlock(&mdsc->mutex);
+	return ret;
+}
+
+static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
+{
+	int ret, got = 0;
+	struct inode *inode = file_inode(file);
+	struct ceph_inode_info *ci = ceph_inode(inode);
+
+	ret = vet_mds_for_fscrypt(file);
+	if (ret)
+		return ret;
+
+	/*
+	 * Ensure we hold these caps so that we _know_ that the rstats check
+	 * in the empty_dir check is reliable.
+	 */
+	ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);
+	if (ret)
+		return ret;
+
+	ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
+	if (got)
+		ceph_put_cap_refs(ci, got);
+
+	return ret;
+}
+
 long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
+	int ret;
+
 	dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
 	switch (cmd) {
 	case CEPH_IOC_GET_LAYOUT:
@@ -289,6 +336,42 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 	case CEPH_IOC_SYNCIO:
 		return ceph_ioctl_syncio(file);
+
+	case FS_IOC_SET_ENCRYPTION_POLICY:
+		return ceph_set_encryption_policy(file, arg);
+
+	case FS_IOC_GET_ENCRYPTION_POLICY:
+		ret = vet_mds_for_fscrypt(file);
+		if (ret)
+			return ret;
+		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
+
+	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
+		ret = vet_mds_for_fscrypt(file);
+		if (ret)
+			return ret;
+		return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
+
+	case FS_IOC_ADD_ENCRYPTION_KEY:
+		ret = vet_mds_for_fscrypt(file);
+		if (ret)
+			return ret;
+		return fscrypt_ioctl_add_key(file, (void __user *)arg);
+
+	case FS_IOC_REMOVE_ENCRYPTION_KEY:
+		return fscrypt_ioctl_remove_key(file, (void __user *)arg);
+
+	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
+		return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg);
+
+	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
+		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
+
+	case FS_IOC_GET_ENCRYPTION_NONCE:
+		ret = vet_mds_for_fscrypt(file);
+		if (ret)
+			return ret;
+		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
 	}
 
 	return -ENOTTY;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 13/24] ceph: decode alternate_name in lease info
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (11 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 12/24] ceph: add fscrypt ioctls Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 14/24] ceph: make ceph_msdc_build_path use ref-walk Jeff Layton
                   ` (10 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Ceph is a bit different from local filesystems, in that we don't want
to store filenames as raw binary data, since we may also be dealing
with clients that don't support fscrypt.

We could just base64-encode the encrypted filenames, but that could
leave us with filenames longer than NAME_MAX. It turns out that the
MDS doesn't care much about filename length, but the clients do.

To manage this, we've added a new "alternate name" field that can be
optionally added to any dentry that we'll use to store the binary
crypttext of the filename if its base64-encoded value will be longer
than NAME_MAX. When a dentry has one of these names attached, the MDS
will send it along in the lease info, which we can then store for
later usage.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/mds_client.c | 40 ++++++++++++++++++++++++++++++----------
 fs/ceph/mds_client.h | 11 +++++++----
 2 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 4aca8ce1c135..08710b347c07 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -302,27 +302,44 @@ static int parse_reply_info_dir(void **p, void *end,
 
 static int parse_reply_info_lease(void **p, void *end,
 				  struct ceph_mds_reply_lease **lease,
-				  u64 features)
+				  u64 features, u32 *altname_len, u8 **altname)
 {
+	u8 struct_v;
+	u32 struct_len;
+
 	if (features == (u64)-1) {
-		u8 struct_v, struct_compat;
-		u32 struct_len;
+		u8 struct_compat;
+
 		ceph_decode_8_safe(p, end, struct_v, bad);
 		ceph_decode_8_safe(p, end, struct_compat, bad);
+
 		/* struct_v is expected to be >= 1. we only understand
 		 * encoding whose struct_compat == 1. */
 		if (!struct_v || struct_compat != 1)
 			goto bad;
+
 		ceph_decode_32_safe(p, end, struct_len, bad);
-		ceph_decode_need(p, end, struct_len, bad);
-		end = *p + struct_len;
+	} else {
+		struct_len = sizeof(**lease);
+		*altname_len = 0;
+		*altname = NULL;
 	}
 
-	ceph_decode_need(p, end, sizeof(**lease), bad);
+	ceph_decode_need(p, end, struct_len, bad);
 	*lease = *p;
 	*p += sizeof(**lease);
-	if (features == (u64)-1)
-		*p = end;
+
+	if (features == (u64)-1) {
+		if (struct_v >= 2) {
+			ceph_decode_32_safe(p, end, *altname_len, bad);
+			ceph_decode_need(p, end, *altname_len, bad);
+			*altname = *p;
+			*p += *altname_len;
+		} else {
+			*altname = NULL;
+			*altname_len = 0;
+		}
+	}
 	return 0;
 bad:
 	return -EIO;
@@ -352,7 +369,8 @@ static int parse_reply_info_trace(void **p, void *end,
 		info->dname = *p;
 		*p += info->dname_len;
 
-		err = parse_reply_info_lease(p, end, &info->dlease, features);
+		err = parse_reply_info_lease(p, end, &info->dlease, features,
+					     &info->altname_len, &info->altname);
 		if (err < 0)
 			goto out_bad;
 	}
@@ -419,9 +437,11 @@ static int parse_reply_info_readdir(void **p, void *end,
 		dout("parsed dir dname '%.*s'\n", rde->name_len, rde->name);
 
 		/* dentry lease */
-		err = parse_reply_info_lease(p, end, &rde->lease, features);
+		err = parse_reply_info_lease(p, end, &rde->lease, features,
+					     &rde->altname_len, &rde->altname);
 		if (err)
 			goto out_bad;
+
 		/* inode */
 		err = parse_reply_info_in(p, end, &rde->inode, features);
 		if (err < 0)
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 800eed49c2fd..40ff9da2d90b 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -29,8 +29,8 @@ enum ceph_feature_type {
 	CEPHFS_FEATURE_MULTI_RECONNECT,
 	CEPHFS_FEATURE_DELEG_INO,
 	CEPHFS_FEATURE_METRIC_COLLECT,
-
-	CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_METRIC_COLLECT,
+	CEPHFS_FEATURE_ALTERNATE_NAME,
+	CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_ALTERNATE_NAME,
 };
 
 /*
@@ -45,8 +45,7 @@ enum ceph_feature_type {
 	CEPHFS_FEATURE_MULTI_RECONNECT,		\
 	CEPHFS_FEATURE_DELEG_INO,		\
 	CEPHFS_FEATURE_METRIC_COLLECT,		\
-						\
-	CEPHFS_FEATURE_MAX,			\
+	CEPHFS_FEATURE_ALTERNATE_NAME,		\
 }
 #define CEPHFS_FEATURES_CLIENT_REQUIRED {}
 
@@ -98,7 +97,9 @@ struct ceph_mds_reply_info_in {
 
 struct ceph_mds_reply_dir_entry {
 	char                          *name;
+	u8			      *altname;
 	u32                           name_len;
+	u32			      altname_len;
 	struct ceph_mds_reply_lease   *lease;
 	struct ceph_mds_reply_info_in inode;
 	loff_t			      offset;
@@ -117,7 +118,9 @@ struct ceph_mds_reply_info_parsed {
 	struct ceph_mds_reply_info_in diri, targeti;
 	struct ceph_mds_reply_dirfrag *dirfrag;
 	char                          *dname;
+	u8			      *altname;
 	u32                           dname_len;
+	u32                           altname_len;
 	struct ceph_mds_reply_lease   *dlease;
 
 	/* extra */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 14/24] ceph: make ceph_msdc_build_path use ref-walk
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (12 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 13/24] ceph: decode alternate_name in lease info Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path Jeff Layton
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Encryption potentially requires allocation, at which point we'll need to
be in a non-atomic context. Convert ceph_msdc_build_path to take dentry
spinlocks and references instead of using rcu_read_lock to walk the
path.

This is slightly less efficient, and we may want to eventually allow
using RCU when the leaf dentry isn't encrypted.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/mds_client.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 08710b347c07..5b7872a61ad7 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -2396,7 +2396,8 @@ static inline  u64 __get_oldest_tid(struct ceph_mds_client *mdsc)
 char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
 			   int stop_on_nosnap)
 {
-	struct dentry *temp;
+	struct dentry *cur;
+	struct inode *inode;
 	char *path;
 	int pos;
 	unsigned seq;
@@ -2413,34 +2414,35 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
 	path[pos] = '\0';
 
 	seq = read_seqbegin(&rename_lock);
-	rcu_read_lock();
-	temp = dentry;
+	cur = dget(dentry);
 	for (;;) {
-		struct inode *inode;
+		struct dentry *temp;
 
-		spin_lock(&temp->d_lock);
-		inode = d_inode(temp);
+		spin_lock(&cur->d_lock);
+		inode = d_inode(cur);
 		if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
 			dout("build_path path+%d: %p SNAPDIR\n",
-			     pos, temp);
-		} else if (stop_on_nosnap && inode && dentry != temp &&
+			     pos, cur);
+		} else if (stop_on_nosnap && inode && dentry != cur &&
 			   ceph_snap(inode) == CEPH_NOSNAP) {
-			spin_unlock(&temp->d_lock);
+			spin_unlock(&cur->d_lock);
 			pos++; /* get rid of any prepended '/' */
 			break;
 		} else {
-			pos -= temp->d_name.len;
+			pos -= cur->d_name.len;
 			if (pos < 0) {
-				spin_unlock(&temp->d_lock);
+				spin_unlock(&cur->d_lock);
 				break;
 			}
-			memcpy(path + pos, temp->d_name.name, temp->d_name.len);
+			memcpy(path + pos, cur->d_name.name, cur->d_name.len);
 		}
+		temp = cur;
 		spin_unlock(&temp->d_lock);
-		temp = READ_ONCE(temp->d_parent);
+		cur = dget_parent(temp);
+		dput(temp);
 
 		/* Are we at the root? */
-		if (IS_ROOT(temp))
+		if (IS_ROOT(cur))
 			break;
 
 		/* Are we out of buffer? */
@@ -2449,8 +2451,9 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
 
 		path[pos] = '/';
 	}
-	base = ceph_ino(d_inode(temp));
-	rcu_read_unlock();
+	inode = d_inode(cur);
+	base = inode ? ceph_ino(inode) : 0;
+	dput(cur);
 
 	if (read_seqretry(&rename_lock, seq))
 		goto retry;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (13 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 14/24] ceph: make ceph_msdc_build_path use ref-walk Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-07-11 22:53   ` Eric Biggers
  2021-06-25 13:58 ` [RFC PATCH v7 16/24] ceph: send altname in MClientRequest Jeff Layton
                   ` (8 subsequent siblings)
  23 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Allow ceph_mdsc_build_path to encrypt and base64 encode the filename
when the parent is encrypted and we're sending the path to the MDS.

In most cases, we just encrypt the filenames and base64 encode them,
but when the name is longer than CEPH_NOHASH_NAME_MAX, we use a similar
scheme to fscrypt proper, and hash the remaning bits with sha256.

When doing this, we then send along the full crypttext of the name in
the new alternate_name field of the MClientRequest. The MDS can then
send that along in readdir responses and traces.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/crypto.h     |  15 +++++
 fs/ceph/mds_client.c | 138 +++++++++++++++++++++++++++++++++++++------
 2 files changed, 135 insertions(+), 18 deletions(-)

diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
index bdf1ba47db16..60cc92f25fed 100644
--- a/fs/ceph/crypto.h
+++ b/fs/ceph/crypto.h
@@ -6,6 +6,7 @@
 #ifndef _CEPH_CRYPTO_H
 #define _CEPH_CRYPTO_H
 
+#include <crypto/sha2.h>
 #include <linux/fscrypt.h>
 
 #define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
@@ -27,6 +28,20 @@ static inline u32 ceph_fscrypt_auth_size(u32 ctxsize)
 	return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
 }
 
+/*
+ * We want to encrypt filenames when creating them, but the encrypted
+ * versions of those names may have illegal characters in them. To mitigate
+ * that, we base64 encode them, but that gives us a result that can exceed
+ * NAME_MAX.
+ *
+ * Follow a similar scheme to fscrypt itself, and cap the filename to a
+ * smaller size. If the cleartext name is longer than the value below, then
+ * sha256 hash the remaining bytes.
+ *
+ * 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255)
+ */
+#define CEPH_NOHASH_NAME_MAX (189 - SHA256_DIGEST_SIZE)
+
 void ceph_fscrypt_set_ops(struct super_block *sb);
 
 void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 5b7872a61ad7..2692468d2dc4 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -13,6 +13,7 @@
 #include <linux/ktime.h>
 
 #include "super.h"
+#include "crypto.h"
 #include "mds_client.h"
 
 #include <linux/ceph/ceph_features.h>
@@ -2383,18 +2384,85 @@ static inline  u64 __get_oldest_tid(struct ceph_mds_client *mdsc)
 	return mdsc->oldest_tid;
 }
 
-/*
- * Build a dentry's path.  Allocate on heap; caller must kfree.  Based
- * on build_path_from_dentry in fs/cifs/dir.c.
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+static int encode_encrypted_fname(const struct inode *parent, struct dentry *dentry, char *buf)
+{
+	u32 len;
+	int elen;
+	int ret;
+	u8 *cryptbuf;
+
+	WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
+
+	/*
+	 * convert cleartext dentry name to ciphertext
+	 * if result is longer than CEPH_NOKEY_NAME_MAX,
+	 * sha256 the remaining bytes
+	 *
+	 * See: fscrypt_setup_filename
+	 */
+	if (!fscrypt_fname_encrypted_size(parent, dentry->d_name.len, NAME_MAX, &len))
+		return -ENAMETOOLONG;
+
+	/* If we have to hash the end, then we need a full-length buffer */
+	if (len > CEPH_NOHASH_NAME_MAX)
+		len = NAME_MAX;
+
+	cryptbuf = kmalloc(len, GFP_KERNEL);
+	if (!cryptbuf)
+		return -ENOMEM;
+
+	ret = fscrypt_fname_encrypt(parent, &dentry->d_name, cryptbuf, len);
+	if (ret) {
+		kfree(cryptbuf);
+		return ret;
+	}
+
+	/* hash the end if the name is long enough */
+	if (len > CEPH_NOHASH_NAME_MAX) {
+		u8 hash[SHA256_DIGEST_SIZE];
+		u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
+
+		/* hash the extra bytes and overwrite crypttext beyond that point with it */
+		sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
+		memcpy(extra, hash, SHA256_DIGEST_SIZE);
+		len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
+	}
+
+	/* base64 encode the encrypted name */
+	elen = fscrypt_base64_encode(cryptbuf, len, buf);
+	kfree(cryptbuf);
+	dout("base64-encoded ciphertext name = %.*s\n", len, buf);
+	return elen;
+}
+#else
+static int encode_encrypted_fname(const struct inode *parent, struct dentry *dentry, char *buf)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+/**
+ * ceph_mdsc_build_path - build a path string to a given dentry
+ * @dentry: dentry to which path should be built
+ * @plen: returned length of string
+ * @pbase: returned base inode number
+ * @for_wire: is this path going to be sent to the MDS?
+ *
+ * Build a string that represents the path to the dentry. This is mostly called
+ * for two different purposes:
  *
- * If @stop_on_nosnap, generate path relative to the first non-snapped
- * inode.
+ * 1) we need to build a path string to send to the MDS (for_wire == true)
+ * 2) we need a path string for local presentation (e.g. debugfs) (for_wire == false)
+ *
+ * The path is built in reverse, starting with the dentry. Walk back up toward
+ * the root, building the path until the first non-snapped inode is reached (for_wire)
+ * or the root inode is reached (!for_wire).
  *
  * Encode hidden .snap dirs as a double /, i.e.
  *   foo/.snap/bar -> foo//bar
  */
-char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
-			   int stop_on_nosnap)
+char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, int for_wire)
 {
 	struct dentry *cur;
 	struct inode *inode;
@@ -2416,30 +2484,65 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
 	seq = read_seqbegin(&rename_lock);
 	cur = dget(dentry);
 	for (;;) {
-		struct dentry *temp;
+		struct dentry *parent;
 
 		spin_lock(&cur->d_lock);
 		inode = d_inode(cur);
 		if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
 			dout("build_path path+%d: %p SNAPDIR\n",
 			     pos, cur);
-		} else if (stop_on_nosnap && inode && dentry != cur &&
-			   ceph_snap(inode) == CEPH_NOSNAP) {
+			spin_unlock(&cur->d_lock);
+			parent = dget_parent(cur);
+		} else if (for_wire && inode && dentry != cur && ceph_snap(inode) == CEPH_NOSNAP) {
 			spin_unlock(&cur->d_lock);
 			pos++; /* get rid of any prepended '/' */
 			break;
-		} else {
+		} else if (!for_wire || !IS_ENCRYPTED(d_inode(cur->d_parent))) {
 			pos -= cur->d_name.len;
 			if (pos < 0) {
 				spin_unlock(&cur->d_lock);
 				break;
 			}
 			memcpy(path + pos, cur->d_name.name, cur->d_name.len);
+			spin_unlock(&cur->d_lock);
+			parent = dget_parent(cur);
+		} else {
+			int len, ret;
+			char buf[FSCRYPT_BASE64_CHARS(NAME_MAX)];
+
+			/*
+			 * Proactively copy name into buf, in case we need to present
+			 * it as-is.
+			 */
+			memcpy(buf, cur->d_name.name, cur->d_name.len);
+			len = cur->d_name.len;
+			spin_unlock(&cur->d_lock);
+			parent = dget_parent(cur);
+
+			ret = __fscrypt_prepare_readdir(d_inode(parent));
+			if (ret < 0) {
+				dput(parent);
+				dput(cur);
+				return ERR_PTR(ret);
+			}
+
+			if (fscrypt_has_encryption_key(d_inode(parent))) {
+				len = encode_encrypted_fname(d_inode(parent), cur, buf);
+				if (len < 0) {
+					dput(parent);
+					dput(cur);
+					return ERR_PTR(len);
+				}
+			}
+			pos -= len;
+			if (pos < 0) {
+				dput(parent);
+				break;
+			}
+			memcpy(path + pos, buf, len);
 		}
-		temp = cur;
-		spin_unlock(&temp->d_lock);
-		cur = dget_parent(temp);
-		dput(temp);
+		dput(cur);
+		cur = parent;
 
 		/* Are we at the root? */
 		if (IS_ROOT(cur))
@@ -2463,8 +2566,7 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase,
 		 * A rename didn't occur, but somehow we didn't end up where
 		 * we thought we would. Throw a warning and try again.
 		 */
-		pr_warn("build_path did not end path lookup where "
-			"expected, pos is %d\n", pos);
+		pr_warn("build_path did not end path lookup where expected (pos = %d)\n", pos);
 		goto retry;
 	}
 
@@ -2484,7 +2586,7 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
 	rcu_read_lock();
 	if (!dir)
 		dir = d_inode_rcu(dentry->d_parent);
-	if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP) {
+	if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP && !IS_ENCRYPTED(dir)) {
 		*pino = ceph_ino(dir);
 		rcu_read_unlock();
 		*ppath = dentry->d_name.name;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 16/24] ceph: send altname in MClientRequest
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (14 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 17/24] ceph: properly set DCACHE_NOKEY_NAME flag in lookup Jeff Layton
                   ` (7 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

In the event that we have a filename longer than CEPH_NOHASH_NAME_MAX,
we'll need to hash the tail of the filename. The client however will
still need to know the full name of the file if it has a key.

To support this, the MClientRequest field has grown a new alternate_name
field that we populate with the full (binary) crypttext of the filename.
This is then transmitted to the clients in readdir or traces as part of
the dentry lease.

Add support for populating this field when the filenames are very long.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/mds_client.c | 72 +++++++++++++++++++++++++++++++++++++++++---
 fs/ceph/mds_client.h |  3 ++
 2 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 2692468d2dc4..25d1b09781e2 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -917,6 +917,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_altname);
 	put_request_session(req);
 	ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation);
 	WARN_ON_ONCE(!list_empty(&req->r_wait));
@@ -2435,11 +2436,66 @@ static int encode_encrypted_fname(const struct inode *parent, struct dentry *den
 	dout("base64-encoded ciphertext name = %.*s\n", len, buf);
 	return elen;
 }
+
+static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
+{
+	struct inode *dir = req->r_parent;
+	struct dentry *dentry = req->r_dentry;
+	u8 *cryptbuf = NULL;
+	u32 len = 0;
+	int ret = 0;
+
+	/* only encode if we have parent and dentry */
+	if (!dir || !dentry)
+		goto success;
+
+	/* No-op unless this is encrypted */
+	if (!IS_ENCRYPTED(dir))
+		goto success;
+
+	ret = __fscrypt_prepare_readdir(dir);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/* No key? Just ignore it. */
+	if (!fscrypt_has_encryption_key(dir))
+		goto success;
+
+	if (!fscrypt_fname_encrypted_size(dir, dentry->d_name.len, NAME_MAX, &len)) {
+		WARN_ON_ONCE(1);
+		return ERR_PTR(-ENAMETOOLONG);
+	}
+
+	/* No need to append altname if name is short enough */
+	if (len <= CEPH_NOHASH_NAME_MAX) {
+		len = 0;
+		goto success;
+	}
+
+	cryptbuf = kmalloc(len, GFP_KERNEL);
+	if (!cryptbuf)
+		return ERR_PTR(-ENOMEM);
+
+	ret = fscrypt_fname_encrypt(dir, &dentry->d_name, cryptbuf, len);
+	if (ret) {
+		kfree(cryptbuf);
+		return ERR_PTR(ret);
+	}
+success:
+	*plen = len;
+	return cryptbuf;
+}
 #else
 static int encode_encrypted_fname(const struct inode *parent, struct dentry *dentry, char *buf)
 {
 	return -EOPNOTSUPP;
 }
+
+static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
+{
+	*plen = 0;
+	return NULL;
+}
 #endif
 
 /**
@@ -2662,14 +2718,15 @@ static void encode_mclientrequest_tail(void **p, const struct ceph_mds_request *
 	ceph_encode_timespec64(&ts, &req->r_stamp);
 	ceph_encode_copy(p, &ts, sizeof(ts));
 
-	/* gid_list */
+	/* v4: gid_list */
 	ceph_encode_32(p, req->r_cred->group_info->ngroups);
 	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);
+	/* v5: altname */
+	ceph_encode_32(p, req->r_altname_len);
+	ceph_encode_copy(p, req->r_altname, req->r_altname_len);
 
 	/* v6: fscrypt_auth and fscrypt_file */
 	if (req->r_fscrypt_auth) {
@@ -2726,11 +2783,18 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
 		goto out_free1;
 	}
 
+	req->r_altname = get_fscrypt_altname(req, &req->r_altname_len);
+	if (IS_ERR(req->r_altname)) {
+		msg = ERR_CAST(req->r_altname);
+		req->r_altname = NULL;
+		goto out_free2;
+	}
+
 	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);
-	len += sizeof(u32); // altname
+	len += sizeof(u32) + req->r_altname_len;
 	len += sizeof(u32); // fscrypt_auth
 	if (req->r_fscrypt_auth) {
 		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 40ff9da2d90b..c58fad3f2a87 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -284,6 +284,9 @@ struct ceph_mds_request {
 
 	struct ceph_fscrypt_auth *r_fscrypt_auth;
 
+	u8 *r_altname;		    /* fscrypt binary crypttext for long filenames */
+	u32 r_altname_len;	    /* length of r_altname */
+
 	int r_fmode;        /* file mode, if expecting cap */
 	const struct cred *r_cred;
 	int r_request_release_offset;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 17/24] ceph: properly set DCACHE_NOKEY_NAME flag in lookup
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (15 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 16/24] ceph: send altname in MClientRequest Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 18/24] ceph: make d_revalidate call fscrypt revalidator for encrypted dentries Jeff Layton
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

This is required so that we know to invalidate these dentries when the
directory is unlocked.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/dir.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 8b6a1c960afa..7d6ad4ea535c 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -751,6 +751,17 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
 	if (dentry->d_name.len > NAME_MAX)
 		return ERR_PTR(-ENAMETOOLONG);
 
+	if (IS_ENCRYPTED(dir)) {
+		err = __fscrypt_prepare_readdir(dir);
+		if (err)
+			return ERR_PTR(err);
+		if (!fscrypt_has_encryption_key(dir)) {
+			spin_lock(&dentry->d_lock);
+			dentry->d_flags |= DCACHE_NOKEY_NAME;
+			spin_unlock(&dentry->d_lock);
+		}
+	}
+
 	/* can we conclude ENOENT locally? */
 	if (d_really_is_negative(dentry)) {
 		struct ceph_inode_info *ci = ceph_inode(dir);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 18/24] ceph: make d_revalidate call fscrypt revalidator for encrypted dentries
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (16 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 17/24] ceph: properly set DCACHE_NOKEY_NAME flag in lookup Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 19/24] ceph: add helpers for converting names for userland presentation Jeff Layton
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

If we have a dentry which represents a no-key name, then we need to test
whether the parent directory's encryption key has since been added.  Do
that before we test anything else about the dentry.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/dir.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 7d6ad4ea535c..7a795e409007 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1700,6 +1700,10 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
 	struct inode *dir, *inode;
 	struct ceph_mds_client *mdsc;
 
+	valid = fscrypt_d_revalidate(dentry, flags);
+	if (valid <= 0)
+		return valid;
+
 	if (flags & LOOKUP_RCU) {
 		parent = READ_ONCE(dentry->d_parent);
 		dir = d_inode_rcu(parent);
@@ -1712,8 +1716,8 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
 		inode = d_inode(dentry);
 	}
 
-	dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry,
-	     dentry, inode, ceph_dentry(dentry)->offset);
+	dout("d_revalidate %p '%pd' inode %p offset 0x%llx nokey %d\n", dentry,
+	     dentry, inode, ceph_dentry(dentry)->offset, !!(dentry->d_flags & DCACHE_NOKEY_NAME));
 
 	mdsc = ceph_sb_to_client(dir->i_sb)->mdsc;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 19/24] ceph: add helpers for converting names for userland presentation
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (17 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 18/24] ceph: make d_revalidate call fscrypt revalidator for encrypted dentries Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 20/24] ceph: add fscrypt support to ceph_fill_trace Jeff Layton
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/crypto.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ceph/crypto.h | 42 ++++++++++++++++++++++++++
 2 files changed, 118 insertions(+)

diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index 675d41fd2eb0..55c7f45c062b 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -128,3 +128,79 @@ void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_se
 {
 	swap(req->r_fscrypt_auth, as->fscrypt_auth);
 }
+
+/**
+ * ceph_fname_to_usr - convert a filename for userland presentation
+ * @fname: ceph_fname to be converted
+ * @tname: temporary name buffer to use for conversion (may be NULL)
+ * @oname: where converted name should be placed
+ * @is_nokey: set to true if key wasn't available during conversion (may be NULL)
+ *
+ * Given a filename (usually from the MDS), format it for presentation to
+ * userland. If @parent is not encrypted, just pass it back as-is.
+ *
+ * Otherwise, base64 decode the string, and then ask fscrypt to format it
+ * for userland presentation.
+ *
+ * Returns 0 on success or negative error code on error.
+ */
+int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
+		      struct fscrypt_str *oname, bool *is_nokey)
+{
+	int ret;
+	struct fscrypt_str _tname = FSTR_INIT(NULL, 0);
+	struct fscrypt_str iname;
+
+	if (!IS_ENCRYPTED(fname->dir)) {
+		oname->name = fname->name;
+		oname->len = fname->name_len;
+		return 0;
+	}
+
+	/* Sanity check that the resulting name will fit in the buffer */
+	if (fname->name_len > FSCRYPT_BASE64_CHARS(NAME_MAX))
+		return -EIO;
+
+	ret = __fscrypt_prepare_readdir(fname->dir);
+	if (ret)
+		return ret;
+
+	/*
+	 * Use the raw dentry name as sent by the MDS instead of
+	 * generating a nokey name via fscrypt.
+	 */
+	if (!fscrypt_has_encryption_key(fname->dir)) {
+		memcpy(oname->name, fname->name, fname->name_len);
+		oname->len = fname->name_len;
+		if (is_nokey)
+			*is_nokey = true;
+		return 0;
+	}
+
+	if (fname->ctext_len == 0) {
+		int declen;
+
+		if (!tname) {
+			ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname);
+			if (ret)
+				return ret;
+			tname = &_tname;
+		}
+
+		declen = fscrypt_base64_decode(fname->name, fname->name_len, tname->name);
+		if (declen <= 0) {
+			ret = -EIO;
+			goto out;
+		}
+		iname.name = tname->name;
+		iname.len = declen;
+	} else {
+		iname.name = fname->ctext;
+		iname.len = fname->ctext_len;
+	}
+
+	ret = fscrypt_fname_disk_to_usr(fname->dir, 0, 0, &iname, oname);
+out:
+	fscrypt_fname_free_buffer(&_tname);
+	return ret;
+}
diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
index 60cc92f25fed..879f95972e41 100644
--- a/fs/ceph/crypto.h
+++ b/fs/ceph/crypto.h
@@ -11,6 +11,14 @@
 
 #define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
 
+struct ceph_fname {
+	struct inode	*dir;
+	char 		*name;		// b64 encoded, possibly hashed
+	unsigned char	*ctext;		// binary crypttext (if any)
+	u32		name_len;	// length of name buffer
+	u32		ctext_len;	// length of crypttext
+};
+
 #ifdef CONFIG_FS_ENCRYPTION
 struct ceph_fs_client;
 struct ceph_acl_sec_ctx;
@@ -50,6 +58,22 @@ int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
 				 struct ceph_acl_sec_ctx *as);
 void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as);
 
+static inline int ceph_fname_alloc_buffer(struct inode *parent, struct fscrypt_str *fname)
+{
+	if (!IS_ENCRYPTED(parent))
+		return 0;
+	return fscrypt_fname_alloc_buffer(NAME_MAX, fname);
+}
+
+static inline void ceph_fname_free_buffer(struct inode *parent, struct fscrypt_str *fname)
+{
+	if (IS_ENCRYPTED(parent))
+		fscrypt_fname_free_buffer(fname);
+}
+
+int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
+			struct fscrypt_str *oname, bool *is_nokey);
+
 #else /* CONFIG_FS_ENCRYPTION */
 
 static inline void ceph_fscrypt_set_ops(struct super_block *sb)
@@ -72,6 +96,24 @@ static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
 						struct ceph_acl_sec_ctx *as_ctx)
 {
 }
+
+static inline int ceph_fname_alloc_buffer(struct inode *parent, struct fscrypt_str *fname)
+{
+	return 0;
+}
+
+static inline void ceph_fname_free_buffer(struct inode *parent, struct fscrypt_str *fname)
+{
+}
+
+static inline int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname,
+				    struct fscrypt_str *oname, bool *is_nokey)
+{
+	oname->name = fname->name;
+	oname->len = fname->name_len;
+	return 0;
+}
+
 #endif /* CONFIG_FS_ENCRYPTION */
 
 #endif
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 20/24] ceph: add fscrypt support to ceph_fill_trace
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (18 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 19/24] ceph: add helpers for converting names for userland presentation Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 21/24] ceph: add support to readdir for encrypted filenames Jeff Layton
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

When we get a dentry in a trace, decrypt the name so we can properly
instantiate the dentry.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/inode.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index a0b311195e80..09f98b89ee1a 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1401,8 +1401,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
 		if (dir && req->r_op == CEPH_MDS_OP_LOOKUPNAME &&
 		    test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags) &&
 		    !test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) {
+			bool is_nokey = false;
 			struct qstr dname;
 			struct dentry *dn, *parent;
+			struct fscrypt_str oname = FSTR_INIT(NULL, 0);
+			struct ceph_fname fname = { .dir	= dir,
+						    .name	= rinfo->dname,
+						    .ctext	= rinfo->altname,
+						    .name_len	= rinfo->dname_len,
+						    .ctext_len	= rinfo->altname_len };
 
 			BUG_ON(!rinfo->head->is_target);
 			BUG_ON(req->r_dentry);
@@ -1410,8 +1417,20 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
 			parent = d_find_any_alias(dir);
 			BUG_ON(!parent);
 
-			dname.name = rinfo->dname;
-			dname.len = rinfo->dname_len;
+			err = ceph_fname_alloc_buffer(dir, &oname);
+			if (err < 0) {
+				dput(parent);
+				goto done;
+			}
+
+			err = ceph_fname_to_usr(&fname, NULL, &oname, &is_nokey);
+			if (err < 0) {
+				dput(parent);
+				ceph_fname_free_buffer(dir, &oname);
+				goto done;
+			}
+			dname.name = oname.name;
+			dname.len = oname.len;
 			dname.hash = full_name_hash(parent, dname.name, dname.len);
 			tvino.ino = le64_to_cpu(rinfo->targeti.in->ino);
 			tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
@@ -1426,9 +1445,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
 				     dname.len, dname.name, dn);
 				if (!dn) {
 					dput(parent);
+					ceph_fname_free_buffer(dir, &oname);
 					err = -ENOMEM;
 					goto done;
 				}
+				if (is_nokey) {
+					spin_lock(&dn->d_lock);
+					dn->d_flags |= DCACHE_NOKEY_NAME;
+					spin_unlock(&dn->d_lock);
+				}
 				err = 0;
 			} else if (d_really_is_positive(dn) &&
 				   (ceph_ino(d_inode(dn)) != tvino.ino ||
@@ -1440,6 +1465,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
 				dput(dn);
 				goto retry_lookup;
 			}
+			ceph_fname_free_buffer(dir, &oname);
 
 			req->r_dentry = dn;
 			dput(parent);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 21/24] ceph: add support to readdir for encrypted filenames
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (19 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 20/24] ceph: add fscrypt support to ceph_fill_trace Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 22/24] ceph: create symlinks with encrypted and base64-encoded targets Jeff Layton
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Add helper functions for buffer management and for decrypting filenames
returned by the MDS. Wire those into the readdir codepaths.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/dir.c   | 62 +++++++++++++++++++++++++++++++++++++++----------
 fs/ceph/inode.c | 38 +++++++++++++++++++++++++++---
 2 files changed, 85 insertions(+), 15 deletions(-)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 7a795e409007..533bc5718b02 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -9,6 +9,7 @@
 
 #include "super.h"
 #include "mds_client.h"
+#include "crypto.h"
 
 /*
  * Directory operations: readdir, lookup, create, link, unlink,
@@ -241,7 +242,9 @@ static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
 		di = ceph_dentry(dentry);
 		if (d_unhashed(dentry) ||
 		    d_really_is_negative(dentry) ||
-		    di->lease_shared_gen != shared_gen) {
+		    di->lease_shared_gen != shared_gen ||
+		    ((dentry->d_flags & DCACHE_NOKEY_NAME) &&
+		     fscrypt_has_encryption_key(dir))) {
 			spin_unlock(&dentry->d_lock);
 			dput(dentry);
 			err = -EAGAIN;
@@ -313,6 +316,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 	int err;
 	unsigned frag = -1;
 	struct ceph_mds_reply_info_parsed *rinfo;
+	struct fscrypt_str tname = FSTR_INIT(NULL, 0);
+	struct fscrypt_str oname = FSTR_INIT(NULL, 0);
 
 	dout("readdir %p file %p pos %llx\n", inode, file, ctx->pos);
 	if (dfi->file_info.flags & CEPH_F_ATEND)
@@ -340,6 +345,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 		ctx->pos = 2;
 	}
 
+	err = fscrypt_prepare_readdir(inode);
+	if (err)
+		goto out;
+
 	spin_lock(&ci->i_ceph_lock);
 	/* request Fx cap. if have Fx, we don't need to release Fs cap
 	 * for later create/unlink. */
@@ -360,6 +369,14 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 		spin_unlock(&ci->i_ceph_lock);
 	}
 
+	err = ceph_fname_alloc_buffer(inode, &tname);
+	if (err < 0)
+		goto out;
+
+	err = ceph_fname_alloc_buffer(inode, &oname);
+	if (err < 0)
+		goto out;
+
 	/* proceed with a normal readdir */
 more:
 	/* do we have the correct frag content buffered? */
@@ -387,12 +404,14 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 		dout("readdir fetching %llx.%llx frag %x offset '%s'\n",
 		     ceph_vinop(inode), frag, dfi->last_name);
 		req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
-		if (IS_ERR(req))
-			return PTR_ERR(req);
+		if (IS_ERR(req)) {
+			err = PTR_ERR(req);
+			goto out;
+		}
 		err = ceph_alloc_readdir_reply_buffer(req, inode);
 		if (err) {
 			ceph_mdsc_put_request(req);
-			return err;
+			goto out;
 		}
 		/* hints to request -> mds selection code */
 		req->r_direct_mode = USE_AUTH_MDS;
@@ -405,7 +424,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 			req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL);
 			if (!req->r_path2) {
 				ceph_mdsc_put_request(req);
-				return -ENOMEM;
+				err = -ENOMEM;
+				goto out;
 			}
 		} else if (is_hash_order(ctx->pos)) {
 			req->r_args.readdir.offset_hash =
@@ -426,7 +446,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 		err = ceph_mdsc_do_request(mdsc, NULL, req);
 		if (err < 0) {
 			ceph_mdsc_put_request(req);
-			return err;
+			goto out;
 		}
 		dout("readdir got and parsed readdir result=%d on "
 		     "frag %x, end=%d, complete=%d, hash_order=%d\n",
@@ -479,7 +499,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 			err = note_last_dentry(dfi, rde->name, rde->name_len,
 					       next_offset);
 			if (err)
-				return err;
+				goto out;
 		} else if (req->r_reply_info.dir_end) {
 			dfi->next_offset = 2;
 			/* keep last name */
@@ -507,22 +527,37 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 	}
 	for (; i < rinfo->dir_nr; i++) {
 		struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
+		struct ceph_fname fname = { .dir	= inode,
+					    .name	= rde->name,
+					    .name_len	= rde->name_len,
+					    .ctext	= rde->altname,
+					    .ctext_len	= rde->altname_len };
+		u32 olen = oname.len;
 
 		BUG_ON(rde->offset < ctx->pos);
+		BUG_ON(!rde->inode.in);
 
 		ctx->pos = rde->offset;
 		dout("readdir (%d/%d) -> %llx '%.*s' %p\n",
 		     i, rinfo->dir_nr, ctx->pos,
 		     rde->name_len, rde->name, &rde->inode.in);
 
-		BUG_ON(!rde->inode.in);
+		err = ceph_fname_to_usr(&fname, &tname, &oname, NULL);
+		if (err) {
+			dout("Unable to decode %.*s. Skipping it.\n", rde->name_len, rde->name);
+			continue;
+		}
 
-		if (!dir_emit(ctx, rde->name, rde->name_len,
+		if (!dir_emit(ctx, oname.name, oname.len,
 			      ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)),
 			      le32_to_cpu(rde->inode.in->mode) >> 12)) {
 			dout("filldir stopping us...\n");
-			return 0;
+			err = 0;
+			goto out;
 		}
+
+		/* Reset the lengths to their original allocated vals */
+		oname.len = olen;
 		ctx->pos++;
 	}
 
@@ -577,9 +612,12 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
 					dfi->dir_ordered_count);
 		spin_unlock(&ci->i_ceph_lock);
 	}
-
+	err = 0;
 	dout("readdir %p file %p done.\n", inode, file);
-	return 0;
+out:
+	ceph_fname_free_buffer(inode, &tname);
+	ceph_fname_free_buffer(inode, &oname);
+	return err;
 }
 
 static void reset_readdir(struct ceph_dir_file_info *dfi)
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 09f98b89ee1a..6c7e235a5706 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1745,7 +1745,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 			     struct ceph_mds_session *session)
 {
 	struct dentry *parent = req->r_dentry;
-	struct ceph_inode_info *ci = ceph_inode(d_inode(parent));
+	struct inode *inode = d_inode(parent);
+	struct ceph_inode_info *ci = ceph_inode(inode);
 	struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
 	struct qstr dname;
 	struct dentry *dn;
@@ -1755,6 +1756,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 	u32 last_hash = 0;
 	u32 fpos_offset;
 	struct ceph_readdir_cache_control cache_ctl = {};
+	struct fscrypt_str tname = FSTR_INIT(NULL, 0);
+	struct fscrypt_str oname = FSTR_INIT(NULL, 0);
 
 	if (test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags))
 		return readdir_prepopulate_inodes_only(req, session);
@@ -1806,14 +1809,36 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 	cache_ctl.index = req->r_readdir_cache_idx;
 	fpos_offset = req->r_readdir_offset;
 
+	err = ceph_fname_alloc_buffer(inode, &tname);
+	if (err < 0)
+		goto out;
+
+	err = ceph_fname_alloc_buffer(inode, &oname);
+	if (err < 0)
+		goto out;
+
 	/* FIXME: release caps/leases if error occurs */
 	for (i = 0; i < rinfo->dir_nr; i++) {
+		bool is_nokey = false;
 		struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
 		struct ceph_vino tvino;
+		u32 olen = oname.len;
+		struct ceph_fname fname = { .dir	= inode,
+					    .name	= rde->name,
+					    .name_len	= rde->name_len,
+					    .ctext	= rde->altname,
+					    .ctext_len	= rde->altname_len };
+
+		err = ceph_fname_to_usr(&fname, &tname, &oname, &is_nokey);
+		if (err) {
+			dout("Unable to decode %.*s. Skipping it.", rde->name_len, rde->name);
+			continue;
+		}
 
-		dname.name = rde->name;
-		dname.len = rde->name_len;
+		dname.name = oname.name;
+		dname.len = oname.len;
 		dname.hash = full_name_hash(parent, dname.name, dname.len);
+		oname.len = olen;
 
 		tvino.ino = le64_to_cpu(rde->inode.in->ino);
 		tvino.snap = le64_to_cpu(rde->inode.in->snapid);
@@ -1844,6 +1869,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 				err = -ENOMEM;
 				goto out;
 			}
+			if (is_nokey) {
+				spin_lock(&dn->d_lock);
+				dn->d_flags |= DCACHE_NOKEY_NAME;
+				spin_unlock(&dn->d_lock);
+			}
 		} else if (d_really_is_positive(dn) &&
 			   (ceph_ino(d_inode(dn)) != tvino.ino ||
 			    ceph_snap(d_inode(dn)) != tvino.snap)) {
@@ -1932,6 +1962,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
 		req->r_readdir_cache_idx = cache_ctl.index;
 	}
 	ceph_readdir_cache_release(&cache_ctl);
+	ceph_fname_free_buffer(inode, &tname);
+	ceph_fname_free_buffer(inode, &oname);
 	dout("readdir_prepopulate done\n");
 	return err;
 }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 22/24] ceph: create symlinks with encrypted and base64-encoded targets
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (20 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 21/24] ceph: add support to readdir for encrypted filenames Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 23/24] ceph: make ceph_get_name decrypt filenames Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 24/24] ceph: add a new ceph.fscrypt.auth vxattr Jeff Layton
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

When creating symlinks in encrypted directories, encrypt and
base64-encode the target with the new inode's key before sending to the
MDS.

When filling a symlinked inode, base64-decode it into a buffer that
we'll keep in ci->i_symlink. When get_link is called, decrypt the buffer
into a new one that will hang off i_link.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/dir.c   | 51 +++++++++++++++++++++++---
 fs/ceph/inode.c | 95 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 129 insertions(+), 17 deletions(-)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 533bc5718b02..c945b33e265a 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -948,6 +948,40 @@ static int ceph_create(struct user_namespace *mnt_userns, struct inode *dir,
 	return ceph_mknod(mnt_userns, dir, dentry, mode, 0);
 }
 
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+static int prep_encrypted_symlink_target(struct ceph_mds_request *req, const char *dest)
+{
+	int err;
+	int len = strlen(dest);
+	struct fscrypt_str osd_link = FSTR_INIT(NULL, 0);
+
+	err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX, &osd_link);
+	if (err)
+		goto out;
+
+	err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link);
+	if (err)
+		goto out;
+
+	req->r_path2 = kmalloc(FSCRYPT_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL);
+	if (!req->r_path2) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	len = fscrypt_base64_encode(osd_link.name, osd_link.len, req->r_path2);
+	req->r_path2[len] = '\0';
+out:
+	fscrypt_fname_free_buffer(&osd_link);
+	return err;
+}
+#else
+static int prep_encrypted_symlink_target(struct ceph_mds_request *req, const char *dest)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 			struct dentry *dentry, const char *dest)
 {
@@ -979,14 +1013,21 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 		goto out_req;
 	}
 
-	req->r_path2 = kstrdup(dest, GFP_KERNEL);
-	if (!req->r_path2) {
-		err = -ENOMEM;
-		goto out_req;
-	}
 	req->r_parent = dir;
 	ihold(dir);
 
+	if (IS_ENCRYPTED(req->r_new_inode)) {
+		err = prep_encrypted_symlink_target(req, dest);
+		if (err)
+			goto out_req;
+	} else {
+		req->r_path2 = kstrdup(dest, GFP_KERNEL);
+		if (!req->r_path2) {
+			err = -ENOMEM;
+			goto out_req;
+		}
+	}
+
 	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
 	req->r_dentry = dget(dentry);
 	req->r_num_caps = 2;
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 6c7e235a5706..971c66393929 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -35,6 +35,7 @@
  */
 
 static const struct inode_operations ceph_symlink_iops;
+static const struct inode_operations ceph_encrypted_symlink_iops;
 
 static void ceph_inode_work(struct work_struct *work);
 
@@ -637,6 +638,7 @@ void ceph_free_inode(struct inode *inode)
 #ifdef CONFIG_FS_ENCRYPTION
 	kfree(ci->fscrypt_auth);
 #endif
+	fscrypt_free_inode(inode);
 	kmem_cache_free(ceph_inode_cachep, ci);
 }
 
@@ -837,6 +839,33 @@ void ceph_fill_file_time(struct inode *inode, int issued,
 		     inode, time_warp_seq, ci->i_time_warp_seq);
 }
 
+#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
+static int decode_encrypted_symlink(const char *encsym, int enclen, u8 **decsym)
+{
+	int declen;
+	u8 *sym;
+
+	sym = kmalloc(enclen + 1, GFP_NOFS);
+	if (!sym)
+		return -ENOMEM;
+
+	declen = fscrypt_base64_decode(encsym, enclen, sym);
+	if (declen < 0) {
+		pr_err("%s: can't decode symlink (%d). Content: %.*s\n", __func__, declen, enclen, encsym);
+		kfree(sym);
+		return -EIO;
+	}
+	sym[declen + 1] = '\0';
+	*decsym = sym;
+	return declen;
+}
+#else
+static int decode_encrypted_symlink(const char *encsym, int symlen, u8 **decsym)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 /*
  * Populate an inode based on info from mds.  May be called on new or
  * existing inodes.
@@ -1068,26 +1097,39 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 		inode->i_fop = &ceph_file_fops;
 		break;
 	case S_IFLNK:
-		inode->i_op = &ceph_symlink_iops;
 		if (!ci->i_symlink) {
 			u32 symlen = iinfo->symlink_len;
 			char *sym;
 
 			spin_unlock(&ci->i_ceph_lock);
 
-			if (symlen != i_size_read(inode)) {
-				pr_err("%s %llx.%llx BAD symlink "
-					"size %lld\n", __func__,
-					ceph_vinop(inode),
-					i_size_read(inode));
+			if (IS_ENCRYPTED(inode)) {
+				if (symlen != i_size_read(inode))
+					pr_err("%s %llx.%llx BAD symlink size %lld\n",
+						__func__, ceph_vinop(inode), i_size_read(inode));
+
+				err = decode_encrypted_symlink(iinfo->symlink, symlen, (u8 **)&sym);
+				if (err < 0) {
+					pr_err("%s decoding encrypted symlink failed: %d\n",
+						__func__, err);
+					goto out;
+				}
+				symlen = err;
 				i_size_write(inode, symlen);
 				inode->i_blocks = calc_inode_blocks(symlen);
-			}
+			} else {
+				if (symlen != i_size_read(inode)) {
+					pr_err("%s %llx.%llx BAD symlink size %lld\n",
+						__func__, ceph_vinop(inode), i_size_read(inode));
+					i_size_write(inode, symlen);
+					inode->i_blocks = calc_inode_blocks(symlen);
+				}
 
-			err = -ENOMEM;
-			sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS);
-			if (!sym)
-				goto out;
+				err = -ENOMEM;
+				sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS);
+				if (!sym)
+					goto out;
+			}
 
 			spin_lock(&ci->i_ceph_lock);
 			if (!ci->i_symlink)
@@ -1095,7 +1137,18 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 			else
 				kfree(sym); /* lost a race */
 		}
-		inode->i_link = ci->i_symlink;
+
+		if (IS_ENCRYPTED(inode)) {
+			/*
+			 * Encrypted symlinks need to be decrypted before we can
+			 * cache their targets in i_link. Leave it blank for now.
+			 */
+			inode->i_link = NULL;
+			inode->i_op = &ceph_encrypted_symlink_iops;
+		} else {
+			inode->i_link = ci->i_symlink;
+			inode->i_op = &ceph_symlink_iops;
+		}
 		break;
 	case S_IFDIR:
 		inode->i_op = &ceph_dir_iops;
@@ -2143,6 +2196,17 @@ static void ceph_inode_work(struct work_struct *work)
 	iput(inode);
 }
 
+static const char *ceph_encrypted_get_link(struct dentry *dentry, struct inode *inode,
+					   struct delayed_call *done)
+{
+	struct ceph_inode_info *ci = ceph_inode(inode);
+
+	if (!dentry)
+		return ERR_PTR(-ECHILD);
+
+	return fscrypt_get_symlink(inode, ci->i_symlink, i_size_read(inode), done);
+}
+
 /*
  * symlinks
  */
@@ -2153,6 +2217,13 @@ static const struct inode_operations ceph_symlink_iops = {
 	.listxattr = ceph_listxattr,
 };
 
+static const struct inode_operations ceph_encrypted_symlink_iops = {
+	.get_link = ceph_encrypted_get_link,
+	.setattr = ceph_setattr,
+	.getattr = ceph_getattr,
+	.listxattr = ceph_listxattr,
+};
+
 int __ceph_setattr(struct inode *inode, struct iattr *attr, struct ceph_iattr *cia)
 {
 	struct ceph_inode_info *ci = ceph_inode(inode);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 23/24] ceph: make ceph_get_name decrypt filenames
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (21 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 22/24] ceph: create symlinks with encrypted and base64-encoded targets Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  2021-06-25 13:58 ` [RFC PATCH v7 24/24] ceph: add a new ceph.fscrypt.auth vxattr Jeff Layton
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

When we do a lookupino to the MDS, we get a filename in the trace.
ceph_get_name uses that name directly, so we must properly decrypt
it before copying it to the name buffer.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/export.c | 44 ++++++++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index 1d65934c1262..e468882068af 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -7,6 +7,7 @@
 
 #include "super.h"
 #include "mds_client.h"
+#include "crypto.h"
 
 /*
  * Basic fh
@@ -524,7 +525,9 @@ static int ceph_get_name(struct dentry *parent, char *name,
 {
 	struct ceph_mds_client *mdsc;
 	struct ceph_mds_request *req;
+	struct inode *dir = d_inode(parent);
 	struct inode *inode = d_inode(child);
+	struct ceph_mds_reply_info_parsed *rinfo;
 	int err;
 
 	if (ceph_snap(inode) != CEPH_NOSNAP)
@@ -536,30 +539,47 @@ static int ceph_get_name(struct dentry *parent, char *name,
 	if (IS_ERR(req))
 		return PTR_ERR(req);
 
-	inode_lock(d_inode(parent));
-
+	inode_lock(dir);
 	req->r_inode = inode;
 	ihold(inode);
 	req->r_ino2 = ceph_vino(d_inode(parent));
-	req->r_parent = d_inode(parent);
-	ihold(req->r_parent);
+	req->r_parent = dir;
+	ihold(dir);
 	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
 	req->r_num_caps = 2;
 	err = ceph_mdsc_do_request(mdsc, NULL, req);
+	inode_unlock(dir);
 
-	inode_unlock(d_inode(parent));
+	if (err)
+		goto out;
 
-	if (!err) {
-		struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
+	rinfo = &req->r_reply_info;
+	if (!IS_ENCRYPTED(dir)) {
 		memcpy(name, rinfo->dname, rinfo->dname_len);
 		name[rinfo->dname_len] = 0;
-		dout("get_name %p ino %llx.%llx name %s\n",
-		     child, ceph_vinop(inode), name);
 	} else {
-		dout("get_name %p ino %llx.%llx err %d\n",
-		     child, ceph_vinop(inode), err);
-	}
+		struct fscrypt_str oname = FSTR_INIT(NULL, 0);
+		struct ceph_fname fname = { .dir	= dir,
+					    .name	= rinfo->dname,
+					    .ctext	= rinfo->altname,
+					    .name_len	= rinfo->dname_len,
+					    .ctext_len	= rinfo->altname_len };
+
+		err = ceph_fname_alloc_buffer(dir, &oname);
+		if (err < 0)
+			goto out;
 
+		err = ceph_fname_to_usr(&fname, NULL, &oname, NULL);
+		if (!err) {
+			memcpy(name, oname.name, oname.len);
+			name[oname.len] = 0;
+		}
+		ceph_fname_free_buffer(dir, &oname);
+	}
+out:
+	dout("get_name %p ino %llx.%llx err %d %s%s\n",
+		     child, ceph_vinop(inode), err,
+		     err ? "" : "name ", err ? "" : name);
 	ceph_mdsc_put_request(req);
 	return err;
 }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* [RFC PATCH v7 24/24] ceph: add a new ceph.fscrypt.auth vxattr
  2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
                   ` (22 preceding siblings ...)
  2021-06-25 13:58 ` [RFC PATCH v7 23/24] ceph: make ceph_get_name decrypt filenames Jeff Layton
@ 2021-06-25 13:58 ` Jeff Layton
  23 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-06-25 13:58 UTC (permalink / raw)
  To: ceph-devel; +Cc: lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/ceph/xattr.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 16a62a2bd61e..b175b3029dc0 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -340,6 +340,16 @@ static ssize_t ceph_vxattrcb_caps(struct ceph_inode_info *ci, char *val,
 			      ceph_cap_string(issued), issued);
 }
 
+static ssize_t ceph_vxattrcb_fscrypt_auth(struct ceph_inode_info *ci, char *val, size_t size)
+{
+	if (size) {
+		if (size < ci->fscrypt_auth_len)
+			return -ERANGE;
+		memcpy(val, ci->fscrypt_auth, ci->fscrypt_auth_len);
+	}
+	return ci->fscrypt_auth_len;
+}
+
 #define CEPH_XATTR_NAME(_type, _name)	XATTR_CEPH_PREFIX #_type "." #_name
 #define CEPH_XATTR_NAME2(_type, _name, _name2)	\
 	XATTR_CEPH_PREFIX #_type "." #_name "." #_name2
@@ -473,6 +483,13 @@ static struct ceph_vxattr ceph_common_vxattrs[] = {
 		.exists_cb = NULL,
 		.flags = VXATTR_FLAG_READONLY,
 	},
+	{
+		.name = "ceph.fscrypt.auth",
+		.name_size = sizeof("ceph.fscrypt.auth"),
+		.getxattr_cb = ceph_vxattrcb_fscrypt_auth,
+		.exists_cb = NULL,
+		.flags = VXATTR_FLAG_READONLY,
+	},
 	{ .name = NULL, 0 }	/* Required table terminator */
 };
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one
  2021-06-25 13:58 ` [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one Jeff Layton
@ 2021-07-07  3:37   ` Xiubo Li
  2021-07-07 12:05     ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-07  3:37 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 6/25/21 9:58 PM, Jeff Layton wrote:
> When creating a new inode, we need to determine the crypto context
> before we can transmit the RPC. The fscrypt API has a routine for getting
> a crypto context before a create occurs, but it requires an inode.
>
> Change the ceph code to preallocate an inode in advance of a create of
> any sort (open(), mknod(), symlink(), etc). Move the existing code that
> generates the ACL and SELinux blobs into this routine since that's
> mostly common across all the different codepaths.
>
> In most cases, we just want to allow ceph_fill_trace to use that inode
> after the reply comes in, so add a new field to the MDS request for it
> (r_new_inode).
>
> The async create codepath is a bit different though. In that case, we
> want to hash the inode in advance of the RPC so that it can be used
> before the reply comes in. If the call subsequently fails with
> -EJUKEBOX, then just put the references and clean up the as_ctx. Note
> that with this change, we now need to regenerate the as_ctx when this
> occurs, but it's quite rare for it to happen.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/ceph/dir.c        | 70 ++++++++++++++++++++-----------------
>   fs/ceph/file.c       | 62 ++++++++++++++++++++-------------
>   fs/ceph/inode.c      | 82 ++++++++++++++++++++++++++++++++++++++++----
>   fs/ceph/mds_client.c |  3 +-
>   fs/ceph/mds_client.h |  1 +
>   fs/ceph/super.h      |  7 +++-
>   6 files changed, 160 insertions(+), 65 deletions(-)
>
[...]

> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index eb562e259347..f62785e4dbcb 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -52,17 +52,85 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
>   	return 0;
>   }
>   
> -struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino)
> +/**
> + * ceph_new_inode - allocate a new inode in advance of an expected create
> + * @dir: parent directory for new inode
> + * @dentry: dentry that may eventually point to new inode
> + * @mode: mode of new inode
> + * @as_ctx: pointer to inherited security context
> + *
> + * Allocate a new inode in advance of an operation to create a new inode.
> + * This allocates the inode and sets up the acl_sec_ctx with appropriate
> + * info for the new inode.
> + *
> + * Returns a pointer to the new inode or an ERR_PTR.
> + */
> +struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
> +			     umode_t *mode, struct ceph_acl_sec_ctx *as_ctx)
> +{
> +	int err;
> +	struct inode *inode;
> +
> +	inode = new_inode_pseudo(dir->i_sb);
> +	if (!inode)
> +		return ERR_PTR(-ENOMEM);
> +
> +	if (!S_ISLNK(*mode)) {
> +		err = ceph_pre_init_acls(dir, mode, as_ctx);
> +		if (err < 0)
> +			goto out_err;
> +	}
> +
> +	err = ceph_security_init_secctx(dentry, *mode, as_ctx);
> +	if (err < 0)
> +		goto out_err;
> +
> +	inode->i_state = 0;
> +	inode->i_mode = *mode;
> +	return inode;
> +out_err:
> +	iput(inode);
> +	return ERR_PTR(err);
> +}
> +
> +void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx)
> +{
> +	if (as_ctx->pagelist) {
> +		req->r_pagelist = as_ctx->pagelist;
> +		as_ctx->pagelist = NULL;
> +	}
> +}
> +
> +/**
> + * ceph_get_inode - find or create/hash a new inode
> + * @sb: superblock to search and allocate in
> + * @vino: vino to search for
> + * @newino: optional new inode to insert if one isn't found (may be NULL)
> + *
> + * Search for or insert a new inode into the hash for the given vino, and return a
> + * reference to it. If new is non-NULL, its reference is consumed.
> + */
> +struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, struct inode *newino)
>   {
>   	struct inode *inode;
>   
>   	if (ceph_vino_is_reserved(vino))
>   		return ERR_PTR(-EREMOTEIO);
>   
> -	inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
> -			     ceph_set_ino_cb, &vino);
> -	if (!inode)
> +	if (newino) {
> +		inode = inode_insert5(newino, (unsigned long)vino.ino, ceph_ino_compare,
> +					ceph_set_ino_cb, &vino);
> +		if (inode != newino)
> +			iput(newino);
> +	} else {
> +		inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
> +				     ceph_set_ino_cb, &vino);
> +	}
> +
> +	if (!inode) {
> +		dout("No inode found for %llx.%llx\n", vino.ino, vino.snap);
>   		return ERR_PTR(-ENOMEM);
> +	}
>   
>   	dout("get_inode on %llu=%llx.%llx got %p new %d\n", ceph_present_inode(inode),
>   	     ceph_vinop(inode), inode, !!(inode->i_state & I_NEW));
> @@ -78,7 +146,7 @@ struct inode *ceph_get_snapdir(struct inode *parent)
>   		.ino = ceph_ino(parent),
>   		.snap = CEPH_SNAPDIR,
>   	};
> -	struct inode *inode = ceph_get_inode(parent->i_sb, vino);
> +	struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL);
>   	struct ceph_inode_info *ci = ceph_inode(inode);
>   
>   	if (IS_ERR(inode))

Should we always check this just before using it before 'struct 
ceph_inode_info *ci = ceph_inode(inode);' ?

But it seems the 'ceph_inode()' won't introduce any issue here.

Thanks,

> @@ -1546,7 +1614,7 @@ static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req,
>   		vino.ino = le64_to_cpu(rde->inode.in->ino);
>   		vino.snap = le64_to_cpu(rde->inode.in->snapid);
>   
> -		in = ceph_get_inode(req->r_dentry->d_sb, vino);
> +		in = ceph_get_inode(req->r_dentry->d_sb, vino, NULL);
>   		if (IS_ERR(in)) {
>   			err = PTR_ERR(in);
>   			dout("new_inode badness got %d\n", err);
> @@ -1748,7 +1816,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
>   		if (d_really_is_positive(dn)) {
>   			in = d_inode(dn);
>   		} else {
> -			in = ceph_get_inode(parent->d_sb, tvino);
> +			in = ceph_get_inode(parent->d_sb, tvino, NULL);
>   			if (IS_ERR(in)) {
>   				dout("new_inode badness\n");
>   				d_drop(dn);
[...]


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-06-25 13:58 ` [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces Jeff Layton
@ 2021-07-07  3:53   ` Xiubo Li
  2021-07-07 12:09     ` Jeff Layton
  2021-07-07 10:47   ` Luis Henriques
  1 sibling, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-07  3:53 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 6/25/21 9:58 PM, Jeff Layton wrote:
> ...and store them in the ceph_inode_info.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/ceph/file.c       |  2 ++
>   fs/ceph/inode.c      | 18 ++++++++++++++++++
>   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
>   fs/ceph/mds_client.h |  4 ++++
>   fs/ceph/super.h      |  6 ++++++
>   5 files changed, 74 insertions(+)
>
> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> index 2cda398ba64d..ea0e85075b7b 100644
> --- a/fs/ceph/file.c
> +++ b/fs/ceph/file.c
> @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
>   	iinfo.xattr_data = xattr_buf;
>   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
>   
> +	/* FIXME: set fscrypt_auth and fscrypt_file */
> +
>   	in.ino = cpu_to_le64(vino.ino);
>   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
>   	in.version = cpu_to_le64(1);	// ???
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index f62785e4dbcb..b620281ea65b 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
>   
>   	ci->i_meta_err = 0;
>   
> +#ifdef CONFIG_FS_ENCRYPTION
> +	ci->fscrypt_auth = NULL;
> +	ci->fscrypt_auth_len = 0;
> +	ci->fscrypt_file = NULL;
> +	ci->fscrypt_file_len = 0;
> +#endif
> +
>   	return &ci->vfs_inode;
>   }
>   
> @@ -619,6 +626,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);
>   }
>   
> @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
>   		xattr_blob = NULL;
>   	}
>   
> +	if (iinfo->fscrypt_auth_len && !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);
> +	}
> +
>   	/* finally update i_version */
>   	if (le64_to_cpu(info->version) > ci->i_version)
>   		ci->i_version = le64_to_cpu(info->version);
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 3b3a14024ca0..9c994effc51d 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -183,8 +183,48 @@ 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);
> +
> +		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;

Should we kfree(info->fscrypt_auth) before return ?

I didn't anywhere is freeing it.

Thanks.


> +				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);
> @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>   
>   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>   {
> +	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;
>   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> index 64ea9d853b8d..0c3cc61fd038 100644
> --- a/fs/ceph/mds_client.h
> +++ b/fs/ceph/mds_client.h
> @@ -88,6 +88,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;
>   };
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 0cd94b296f5f..e032737fe472 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -429,6 +429,12 @@ struct ceph_inode_info {
>   
>   #ifdef CONFIG_CEPH_FSCACHE
>   	struct fscache_cookie *fscache;
> +#endif
> +#ifdef CONFIG_FS_ENCRYPTION
> +	u32 fscrypt_auth_len;
> +	u32 fscrypt_file_len;
> +	u8 *fscrypt_auth;
> +	u8 *fscrypt_file;
>   #endif
>   	errseq_t i_meta_err;
>   


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-06-25 13:58 ` [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c Jeff Layton
@ 2021-07-07  7:20   ` Xiubo Li
  2021-07-07 12:02     ` Jeff Layton
  2021-07-11 23:00   ` Eric Biggers
  1 sibling, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-07  7:20 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

Hi Jeff,

There has some following patches in your "fscrypt" branch, which is not 
posted yet, the commit is:

"3161d2f549db ceph: size handling for encrypted inodes in cap updates"

It seems buggy.

In the encode_cap_msg() you have removed the 'fscrypt_file_len' and and 
added a new 8 bytes' data encoding:

         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, fc->size);

That means no matter the 'arg->encrypted' is true or not, here it will 
always encode extra 8 bytes' data ?


But in cap_msg_size(), you are making it optional:


  static inline int cap_msg_size(struct cap_msg_args *arg)
  {
         return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len +
-                       arg->fscrypt_file_len;
+                       arg->encrypted ? sizeof(__le64) : 0;
  }


Have I missed something important here ?

Thanks


On 6/25/21 9:58 PM, Jeff Layton wrote:
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
>   1 file changed, 49 insertions(+), 13 deletions(-)
>
> diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
> index 038f59cc4250..1be6c5148700 100644
> --- a/fs/ceph/caps.c
> +++ b/fs/ceph/caps.c
> @@ -13,6 +13,7 @@
>   #include "super.h"
>   #include "mds_client.h"
>   #include "cache.h"
> +#include "crypto.h"
>   #include <linux/ceph/decode.h>
>   #include <linux/ceph/messenger.h>
>   
> @@ -1229,15 +1230,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)
>   {
> @@ -1253,7 +1251,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;
> @@ -1324,6 +1322,16 @@ 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);
> +
> +	/* 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);
>   }
>   
>   /*
> @@ -1445,6 +1453,26 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
>   		}
>   	}
>   	arg->flags = flags;
> +	if (ci->fscrypt_auth_len &&
> +	    WARN_ON_ONCE(ci->fscrypt_auth_len != sizeof(struct ceph_fscrypt_auth))) {
> +		/* Don't set this if it isn't right size */
> +		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;
> +}
> +
> +#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 + arg->fscrypt_auth_len +
> +			arg->fscrypt_file_len;
>   }
>   
>   /*
> @@ -1457,7 +1485,7 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
>   	struct ceph_msg *msg;
>   	struct inode *inode = &ci->vfs_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),
> @@ -1483,10 +1511,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;
> @@ -1524,6 +1548,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;


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr
  2021-06-25 13:58 ` [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr Jeff Layton
@ 2021-07-07  8:11   ` Xiubo Li
  2021-07-07 12:10     ` Jeff Layton
  2021-07-07 10:47   ` Luis Henriques
  1 sibling, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-07  8:11 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 6/25/21 9:58 PM, Jeff Layton wrote:
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/ceph/acl.c                |  4 ++--
>   fs/ceph/inode.c              | 30 ++++++++++++++++++++++++++++--
>   fs/ceph/mds_client.c         | 31 ++++++++++++++++++++++++++-----
>   fs/ceph/mds_client.h         |  3 +++
>   fs/ceph/super.h              |  7 ++++++-
>   include/linux/ceph/ceph_fs.h | 21 +++++++++++++--------
>   6 files changed, 78 insertions(+), 18 deletions(-)
>
> diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> index 529af59d9fd3..6e716f142022 100644
> --- a/fs/ceph/acl.c
> +++ b/fs/ceph/acl.c
> @@ -136,7 +136,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
>   		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;
>   	}
> @@ -147,7 +147,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
>   			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;
>   	}
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index b620281ea65b..7821ba04eef3 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -2086,7 +2086,7 @@ 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;
> @@ -2127,6 +2127,32 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
>   
>   	dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
>   
> +	if (cia && cia->fscrypt_auth) {
> +		u32 len = offsetof(struct ceph_fscrypt_auth, cfa_blob) +
> +			  le32_to_cpu(cia->fscrypt_auth->cfa_blob_len);
> +
> +		if (len > sizeof(*cia->fscrypt_auth))
> +			return -EINVAL;
> +

Possibly we can remove this check here since in the patch followed will 
make sure the 'cia' is valid.



> +		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) {
> +			req->r_fscrypt_auth = cia->fscrypt_auth;
> +			mask |= CEPH_SETATTR_FSCRYPT_AUTH;
> +			release |= CEPH_CAP_AUTH_SHARED;
> +		}
> +		cia->fscrypt_auth = NULL;
> +	}
> +
>   	if (ia_valid & ATTR_UID) {
>   		dout("setattr %p uid %d -> %d\n", inode,
>   		     from_kuid(&init_user_ns, inode->i_uid),
> @@ -2324,7 +2350,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, 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(&init_user_ns, inode, attr->ia_mode);
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 9c994effc51d..4aca8ce1c135 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -2529,8 +2529,7 @@ 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;
> @@ -2543,6 +2542,21 @@ 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 = le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> +
> +		authlen += offsetof(struct ceph_fscrypt_auth, cfa_blob);

For the authlen calculating, maybe we could add one helper ? I found 
there have some other places are also doing this.


> +		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
>   }
>   
>   /*
> @@ -2591,6 +2605,13 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
>   	len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
>   		sizeof(struct ceph_timespec);
>   	len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
> +	len += sizeof(u32); // altname
> +	len += sizeof(u32); // fscrypt_auth
> +	if (req->r_fscrypt_auth) {
> +		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> +		len += le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> +	}
> +	len += sizeof(u32); // fscrypt_file
>   
>   	/* calculate (max) length for cap releases */
>   	len += sizeof(struct ceph_mds_request_release) *
> @@ -2621,7 +2642,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);
> @@ -2672,7 +2693,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);
> @@ -2781,7 +2802,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);
> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> index 0c3cc61fd038..800eed49c2fd 100644
> --- a/fs/ceph/mds_client.h
> +++ b/fs/ceph/mds_client.h
> @@ -278,6 +278,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 */
>   	const struct cred *r_cred;
>   	int r_request_release_offset;
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index e032737fe472..ad62cde30e0b 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -1035,7 +1035,12 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
>   }
>   extern int ceph_permission(struct user_namespace *mnt_userns,
>   			   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 user_namespace *mnt_userns,
>   			struct dentry *dentry, struct iattr *attr);
>   extern int ceph_getattr(struct user_namespace *mnt_userns,
> diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
> index e41a811026f6..a45a82c7d432 100644
> --- a/include/linux/ceph/ceph_fs.h
> +++ b/include/linux/ceph/ceph_fs.h
> @@ -355,14 +355,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.


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-06-25 13:58 ` [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces Jeff Layton
  2021-07-07  3:53   ` Xiubo Li
@ 2021-07-07 10:47   ` Luis Henriques
  2021-07-07 11:19     ` Xiubo Li
  1 sibling, 1 reply; 59+ messages in thread
From: Luis Henriques @ 2021-07-07 10:47 UTC (permalink / raw)
  To: Jeff Layton; +Cc: ceph-devel, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
> ...and store them in the ceph_inode_info.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/ceph/file.c       |  2 ++
>  fs/ceph/inode.c      | 18 ++++++++++++++++++
>  fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
>  fs/ceph/mds_client.h |  4 ++++
>  fs/ceph/super.h      |  6 ++++++
>  5 files changed, 74 insertions(+)
> 
> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> index 2cda398ba64d..ea0e85075b7b 100644
> --- a/fs/ceph/file.c
> +++ b/fs/ceph/file.c
> @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
>  	iinfo.xattr_data = xattr_buf;
>  	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
>  
> +	/* FIXME: set fscrypt_auth and fscrypt_file */
> +
>  	in.ino = cpu_to_le64(vino.ino);
>  	in.snapid = cpu_to_le64(CEPH_NOSNAP);
>  	in.version = cpu_to_le64(1);	// ???
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index f62785e4dbcb..b620281ea65b 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
>  
>  	ci->i_meta_err = 0;
>  
> +#ifdef CONFIG_FS_ENCRYPTION
> +	ci->fscrypt_auth = NULL;
> +	ci->fscrypt_auth_len = 0;
> +	ci->fscrypt_file = NULL;
> +	ci->fscrypt_file_len = 0;
> +#endif
> +
>  	return &ci->vfs_inode;
>  }
>  
> @@ -619,6 +626,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);
>  }
>  
> @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
>  		xattr_blob = NULL;
>  	}
>  
> +	if (iinfo->fscrypt_auth_len && !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);
> +	}

I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
is already set.  Something like:

	if (iinfo->fscrypt_auth_len) {
		if (!ci->fscrypt_auth) {
			...
		} else {
			kfree(iinfo->fscrypt_auth);
			iinfo->fscrypt_auth = NULL;
		}
	}

> +
>  	/* finally update i_version */
>  	if (le64_to_cpu(info->version) > ci->i_version)
>  		ci->i_version = le64_to_cpu(info->version);
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 3b3a14024ca0..9c994effc51d 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -183,8 +183,48 @@ 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);
> +
> +		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;

As Xiubo already pointed out, there's a kfree(info->fscrypt_auth) missing
in this error path.

Cheers,
--
Luís

> +				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);
> @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>  
>  static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>  {
> +	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;
>  	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> index 64ea9d853b8d..0c3cc61fd038 100644
> --- a/fs/ceph/mds_client.h
> +++ b/fs/ceph/mds_client.h
> @@ -88,6 +88,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;
>  };
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 0cd94b296f5f..e032737fe472 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -429,6 +429,12 @@ struct ceph_inode_info {
>  
>  #ifdef CONFIG_CEPH_FSCACHE
>  	struct fscache_cookie *fscache;
> +#endif
> +#ifdef CONFIG_FS_ENCRYPTION
> +	u32 fscrypt_auth_len;
> +	u32 fscrypt_file_len;
> +	u8 *fscrypt_auth;
> +	u8 *fscrypt_file;
>  #endif
>  	errseq_t i_meta_err;
>  
> -- 
> 2.31.1
> 

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr
  2021-06-25 13:58 ` [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr Jeff Layton
  2021-07-07  8:11   ` Xiubo Li
@ 2021-07-07 10:47   ` Luis Henriques
  2021-07-07 12:25     ` Jeff Layton
  1 sibling, 1 reply; 59+ messages in thread
From: Luis Henriques @ 2021-07-07 10:47 UTC (permalink / raw)
  To: Jeff Layton; +Cc: ceph-devel, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:18AM -0400, Jeff Layton wrote:
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/ceph/acl.c                |  4 ++--
>  fs/ceph/inode.c              | 30 ++++++++++++++++++++++++++++--
>  fs/ceph/mds_client.c         | 31 ++++++++++++++++++++++++++-----
>  fs/ceph/mds_client.h         |  3 +++
>  fs/ceph/super.h              |  7 ++++++-
>  include/linux/ceph/ceph_fs.h | 21 +++++++++++++--------
>  6 files changed, 78 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> index 529af59d9fd3..6e716f142022 100644
> --- a/fs/ceph/acl.c
> +++ b/fs/ceph/acl.c
> @@ -136,7 +136,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
>  		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;
>  	}
> @@ -147,7 +147,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
>  			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;
>  	}
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index b620281ea65b..7821ba04eef3 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -2086,7 +2086,7 @@ 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;
> @@ -2127,6 +2127,32 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
>  
>  	dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
>  
> +	if (cia && cia->fscrypt_auth) {
> +		u32 len = offsetof(struct ceph_fscrypt_auth, cfa_blob) +
> +			  le32_to_cpu(cia->fscrypt_auth->cfa_blob_len);
> +
> +		if (len > sizeof(*cia->fscrypt_auth))
> +			return -EINVAL;

This error path needs to be handled differently.  We have 'req' and
'prealloc_cf' allocated and, more importantly, we're holding
ci->i_ceph_lock.

> +
> +		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);

If the WARN_ON_ONCE above is correct, we shouldn't need this kfree.

Cheers,
--
Luís

> +			ci->fscrypt_auth = (u8 *)cia->fscrypt_auth;
> +			ci->fscrypt_auth_len = len;
> +		} else if ((issued & CEPH_CAP_AUTH_SHARED) == 0) {
> +			req->r_fscrypt_auth = cia->fscrypt_auth;
> +			mask |= CEPH_SETATTR_FSCRYPT_AUTH;
> +			release |= CEPH_CAP_AUTH_SHARED;
> +		}
> +		cia->fscrypt_auth = NULL;
> +	}
> +
>  	if (ia_valid & ATTR_UID) {
>  		dout("setattr %p uid %d -> %d\n", inode,
>  		     from_kuid(&init_user_ns, inode->i_uid),
> @@ -2324,7 +2350,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, 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(&init_user_ns, inode, attr->ia_mode);
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 9c994effc51d..4aca8ce1c135 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -2529,8 +2529,7 @@ 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;
> @@ -2543,6 +2542,21 @@ 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 = le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> +
> +		authlen += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> +		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
>  }
>  
>  /*
> @@ -2591,6 +2605,13 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
>  	len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
>  		sizeof(struct ceph_timespec);
>  	len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
> +	len += sizeof(u32); // altname
> +	len += sizeof(u32); // fscrypt_auth
> +	if (req->r_fscrypt_auth) {
> +		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> +		len += le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> +	}
> +	len += sizeof(u32); // fscrypt_file
>  
>  	/* calculate (max) length for cap releases */
>  	len += sizeof(struct ceph_mds_request_release) *
> @@ -2621,7 +2642,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);
> @@ -2672,7 +2693,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);
> @@ -2781,7 +2802,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);
> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> index 0c3cc61fd038..800eed49c2fd 100644
> --- a/fs/ceph/mds_client.h
> +++ b/fs/ceph/mds_client.h
> @@ -278,6 +278,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 */
>  	const struct cred *r_cred;
>  	int r_request_release_offset;
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index e032737fe472..ad62cde30e0b 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -1035,7 +1035,12 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
>  }
>  extern int ceph_permission(struct user_namespace *mnt_userns,
>  			   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 user_namespace *mnt_userns,
>  			struct dentry *dentry, struct iattr *attr);
>  extern int ceph_getattr(struct user_namespace *mnt_userns,
> diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
> index e41a811026f6..a45a82c7d432 100644
> --- a/include/linux/ceph/ceph_fs.h
> +++ b/include/linux/ceph/ceph_fs.h
> @@ -355,14 +355,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.
> -- 
> 2.31.1
> 

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC
  2021-06-25 13:58 ` [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC Jeff Layton
@ 2021-07-07 10:48   ` Luis Henriques
  2021-07-07 12:29     ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Luis Henriques @ 2021-07-07 10:48 UTC (permalink / raw)
  To: Jeff Layton; +Cc: ceph-devel, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:21AM -0400, Jeff Layton wrote:
> 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. Call the new function from
> ceph_new_inode.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/ceph/crypto.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  fs/ceph/crypto.h | 25 +++++++++++++++++++++++++
>  fs/ceph/inode.c  | 10 ++++++++--
>  fs/ceph/super.h  |  5 +++++
>  fs/ceph/xattr.c  |  3 +++
>  5 files changed, 83 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
> index 997a33e1d59f..675d41fd2eb0 100644
> --- a/fs/ceph/crypto.c
> +++ b/fs/ceph/crypto.c
> @@ -4,6 +4,7 @@
>  #include <linux/fscrypt.h>
>  
>  #include "super.h"
> +#include "mds_client.h"
>  #include "crypto.h"
>  
>  static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
> @@ -86,3 +87,44 @@ void ceph_fscrypt_set_ops(struct super_block *sb)
>  {
>  	fscrypt_set_ops(sb, &ceph_fscrypt_ops);
>  }
> +
> +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> +				 struct ceph_acl_sec_ctx *as)
> +{
> +	int ret, ctxsize;
> +	bool encrypted = false;
> +	struct ceph_inode_info *ci = ceph_inode(inode);
> +
> +	ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
> +	if (ret)
> +		return ret;
> +	if (!encrypted)
> +		return 0;
> +
> +	as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
> +	if (!as->fscrypt_auth)
> +		return -ENOMEM;
> +
> +	ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, inode);
> +	if (ctxsize < 0)
> +		return ctxsize;
> +
> +	as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
> +	as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
> +
> +	WARN_ON_ONCE(ci->fscrypt_auth);
> +	kfree(ci->fscrypt_auth);

It's odd to have again a kfree() after a WARN_ON_ONCE() :-)

> +	ci->fscrypt_auth_len = ceph_fscrypt_auth_size(ctxsize);
> +	ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, GFP_KERNEL);
> +	if (!ci->fscrypt_auth)
> +		return -ENOMEM;
> +
> +	inode->i_flags |= S_ENCRYPTED;
> +
> +	return 0;
> +}
> +
> +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as)
> +{
> +	swap(req->r_fscrypt_auth, as->fscrypt_auth);
> +}

This means that req->r_fscrypt_auth will need to be freed in
ceph_mdsc_release_request().  (I believe you've moved this function to
some other commit in your experimental branch).

Cheers,
--
Luís

> diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
> index d2b1f8e7b300..bdf1ba47db16 100644
> --- a/fs/ceph/crypto.h
> +++ b/fs/ceph/crypto.h
> @@ -11,6 +11,9 @@
>  #define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
>  
>  #ifdef CONFIG_FS_ENCRYPTION
> +struct ceph_fs_client;
> +struct ceph_acl_sec_ctx;
> +struct ceph_mds_request;
>  
>  #define CEPH_FSCRYPT_AUTH_VERSION	1
>  struct ceph_fscrypt_auth {
> @@ -19,10 +22,19 @@ struct ceph_fscrypt_auth {
>  	u8	cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
>  } __packed;
>  
> +static inline u32 ceph_fscrypt_auth_size(u32 ctxsize)
> +{
> +	return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
> +}
> +
>  void ceph_fscrypt_set_ops(struct super_block *sb);
>  
>  void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
>  
> +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> +				 struct ceph_acl_sec_ctx *as);
> +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as);
> +
>  #else /* CONFIG_FS_ENCRYPTION */
>  
>  static inline void ceph_fscrypt_set_ops(struct super_block *sb)
> @@ -32,6 +44,19 @@ static inline void ceph_fscrypt_set_ops(struct super_block *sb)
>  static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
>  {
>  }
> +
> +static inline int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> +						struct ceph_acl_sec_ctx *as)
> +{
> +	if (IS_ENCRYPTED(dir))
> +		return -EOPNOTSUPP;
> +	return 0;
> +}
> +
> +static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
> +						struct ceph_acl_sec_ctx *as_ctx)
> +{
> +}
>  #endif /* CONFIG_FS_ENCRYPTION */
>  
>  #endif
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index fba139a4f57b..a0b311195e80 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -83,12 +83,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);
> @@ -101,6 +106,7 @@ void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *a
>  		req->r_pagelist = as_ctx->pagelist;
>  		as_ctx->pagelist = NULL;
>  	}
> +	ceph_fscrypt_as_ctx_to_req(req, as_ctx);
>  }
>  
>  /**
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 534c2a76562d..651d7909a443 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -26,6 +26,8 @@
>  #include <linux/fscache.h>
>  #endif
>  
> +#include "crypto.h"
> +
>  /* f_type in struct statfs */
>  #define CEPH_SUPER_MAGIC 0x00c36400
>  
> @@ -1068,6 +1070,9 @@ struct ceph_acl_sec_ctx {
>  #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
>  	void *sec_ctx;
>  	u32 sec_ctxlen;
> +#endif
> +#ifdef CONFIG_FS_ENCRYPTION
> +	struct ceph_fscrypt_auth *fscrypt_auth;
>  #endif
>  	struct ceph_pagelist *pagelist;
>  };
> diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
> index 1242db8d3444..16a62a2bd61e 100644
> --- a/fs/ceph/xattr.c
> +++ b/fs/ceph/xattr.c
> @@ -1362,6 +1362,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
>  #endif
>  #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
>  	security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen);
> +#endif
> +#ifdef CONFIG_FS_ENCRYPTION
> +	kfree(as_ctx->fscrypt_auth);
>  #endif
>  	if (as_ctx->pagelist)
>  		ceph_pagelist_release(as_ctx->pagelist);
> -- 
> 2.31.1
> 

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 10:47   ` Luis Henriques
@ 2021-07-07 11:19     ` Xiubo Li
  2021-07-07 12:19       ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-07 11:19 UTC (permalink / raw)
  To: Luis Henriques, Jeff Layton
  Cc: ceph-devel, linux-fsdevel, linux-fscrypt, dhowells


On 7/7/21 6:47 PM, Luis Henriques wrote:
> On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
>> ...and store them in the ceph_inode_info.
>>
>> Signed-off-by: Jeff Layton <jlayton@kernel.org>
>> ---
>>   fs/ceph/file.c       |  2 ++
>>   fs/ceph/inode.c      | 18 ++++++++++++++++++
>>   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
>>   fs/ceph/mds_client.h |  4 ++++
>>   fs/ceph/super.h      |  6 ++++++
>>   5 files changed, 74 insertions(+)
>>
>> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
>> index 2cda398ba64d..ea0e85075b7b 100644
>> --- a/fs/ceph/file.c
>> +++ b/fs/ceph/file.c
>> @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
>>   	iinfo.xattr_data = xattr_buf;
>>   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
>>   
>> +	/* FIXME: set fscrypt_auth and fscrypt_file */
>> +
>>   	in.ino = cpu_to_le64(vino.ino);
>>   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
>>   	in.version = cpu_to_le64(1);	// ???
>> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
>> index f62785e4dbcb..b620281ea65b 100644
>> --- a/fs/ceph/inode.c
>> +++ b/fs/ceph/inode.c
>> @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
>>   
>>   	ci->i_meta_err = 0;
>>   
>> +#ifdef CONFIG_FS_ENCRYPTION
>> +	ci->fscrypt_auth = NULL;
>> +	ci->fscrypt_auth_len = 0;
>> +	ci->fscrypt_file = NULL;
>> +	ci->fscrypt_file_len = 0;
>> +#endif
>> +
>>   	return &ci->vfs_inode;
>>   }
>>   
>> @@ -619,6 +626,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);
>>   }
>>   
>> @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
>>   		xattr_blob = NULL;
>>   	}
>>   
>> +	if (iinfo->fscrypt_auth_len && !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);
>> +	}
> I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
> is already set.  Something like:
>
> 	if (iinfo->fscrypt_auth_len) {
> 		if (!ci->fscrypt_auth) {
> 			...
> 		} else {
> 			kfree(iinfo->fscrypt_auth);
> 			iinfo->fscrypt_auth = NULL;
> 		}
> 	}
>
IMO, this should be okay because it will be freed in 
destroy_reply_info() when putting the request.


>> +
>>   	/* finally update i_version */
>>   	if (le64_to_cpu(info->version) > ci->i_version)
>>   		ci->i_version = le64_to_cpu(info->version);
>> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
>> index 3b3a14024ca0..9c994effc51d 100644
>> --- a/fs/ceph/mds_client.c
>> +++ b/fs/ceph/mds_client.c
>> @@ -183,8 +183,48 @@ 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);
>> +
>> +		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;
> As Xiubo already pointed out, there's a kfree(info->fscrypt_auth) missing
> in this error path.
>
> Cheers,
> --
> Luís
>
>> +				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);
>> @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>>   
>>   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>>   {
>> +	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;
>>   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
>> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
>> index 64ea9d853b8d..0c3cc61fd038 100644
>> --- a/fs/ceph/mds_client.h
>> +++ b/fs/ceph/mds_client.h
>> @@ -88,6 +88,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;
>>   };
>> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
>> index 0cd94b296f5f..e032737fe472 100644
>> --- a/fs/ceph/super.h
>> +++ b/fs/ceph/super.h
>> @@ -429,6 +429,12 @@ struct ceph_inode_info {
>>   
>>   #ifdef CONFIG_CEPH_FSCACHE
>>   	struct fscache_cookie *fscache;
>> +#endif
>> +#ifdef CONFIG_FS_ENCRYPTION
>> +	u32 fscrypt_auth_len;
>> +	u32 fscrypt_file_len;
>> +	u8 *fscrypt_auth;
>> +	u8 *fscrypt_file;
>>   #endif
>>   	errseq_t i_meta_err;
>>   
>> -- 
>> 2.31.1
>>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-07-07  7:20   ` Xiubo Li
@ 2021-07-07 12:02     ` Jeff Layton
  2021-07-07 12:47       ` Xiubo Li
  0 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:02 UTC (permalink / raw)
  To: Xiubo Li, ceph-devel; +Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 15:20 +0800, Xiubo Li wrote:
> Hi Jeff,
> 
> There has some following patches in your "fscrypt" branch, which is not 
> posted yet, the commit is:
> 
> "3161d2f549db ceph: size handling for encrypted inodes in cap updates"
> 
> It seems buggy.
> 

Yes. Those are still quite rough. If I haven't posted them, then YMMV. I
often push them to -experimental branches just for backup purposes. You
may want to wait on reviewing those until I've had a chance to clean
them up and post them.

> In the encode_cap_msg() you have removed the 'fscrypt_file_len' and and 
> added a new 8 bytes' data encoding:
> 
>          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, fc->size);
> 
> That means no matter the 'arg->encrypted' is true or not, here it will 
> always encode extra 8 bytes' data ?
> 
> 
> But in cap_msg_size(), you are making it optional:
> 
> 
>   static inline int cap_msg_size(struct cap_msg_args *arg)
>   {
>          return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len +
> -                       arg->fscrypt_file_len;
> +                       arg->encrypted ? sizeof(__le64) : 0;
>   }
> 
> 
> Have I missed something important here ?
> 
> Thanks
> 

Nope, you're right. I had fixed that one in my local branch already, and
just hadn't yet pushed it to the repo. I'll plan to clean this up a bit
later today and push an updated branch.

> 
> On 6/25/21 9:58 PM, Jeff Layton wrote:
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >   fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
> >   1 file changed, 49 insertions(+), 13 deletions(-)
> > 
> > diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
> > index 038f59cc4250..1be6c5148700 100644
> > --- a/fs/ceph/caps.c
> > +++ b/fs/ceph/caps.c
> > @@ -13,6 +13,7 @@
> >   #include "super.h"
> >   #include "mds_client.h"
> >   #include "cache.h"
> > +#include "crypto.h"
> >   #include <linux/ceph/decode.h>
> >   #include <linux/ceph/messenger.h>
> >   
> > @@ -1229,15 +1230,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)
> >   {
> > @@ -1253,7 +1251,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;
> > @@ -1324,6 +1322,16 @@ 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);
> > +
> > +	/* 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);
> >   }
> >   
> >   /*
> > @@ -1445,6 +1453,26 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
> >   		}
> >   	}
> >   	arg->flags = flags;
> > +	if (ci->fscrypt_auth_len &&
> > +	    WARN_ON_ONCE(ci->fscrypt_auth_len != sizeof(struct ceph_fscrypt_auth))) {
> > +		/* Don't set this if it isn't right size */
> > +		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;
> > +}
> > +
> > +#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 + arg->fscrypt_auth_len +
> > +			arg->fscrypt_file_len;
> >   }
> >   
> >   /*
> > @@ -1457,7 +1485,7 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
> >   	struct ceph_msg *msg;
> >   	struct inode *inode = &ci->vfs_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),
> > @@ -1483,10 +1511,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;
> > @@ -1524,6 +1548,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;
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one
  2021-07-07  3:37   ` Xiubo Li
@ 2021-07-07 12:05     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:05 UTC (permalink / raw)
  To: Xiubo Li, ceph-devel; +Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 11:37 +0800, Xiubo Li wrote:
> On 6/25/21 9:58 PM, Jeff Layton wrote:
> > When creating a new inode, we need to determine the crypto context
> > before we can transmit the RPC. The fscrypt API has a routine for getting
> > a crypto context before a create occurs, but it requires an inode.
> > 
> > Change the ceph code to preallocate an inode in advance of a create of
> > any sort (open(), mknod(), symlink(), etc). Move the existing code that
> > generates the ACL and SELinux blobs into this routine since that's
> > mostly common across all the different codepaths.
> > 
> > In most cases, we just want to allow ceph_fill_trace to use that inode
> > after the reply comes in, so add a new field to the MDS request for it
> > (r_new_inode).
> > 
> > The async create codepath is a bit different though. In that case, we
> > want to hash the inode in advance of the RPC so that it can be used
> > before the reply comes in. If the call subsequently fails with
> > -EJUKEBOX, then just put the references and clean up the as_ctx. Note
> > that with this change, we now need to regenerate the as_ctx when this
> > occurs, but it's quite rare for it to happen.
> > 
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >   fs/ceph/dir.c        | 70 ++++++++++++++++++++-----------------
> >   fs/ceph/file.c       | 62 ++++++++++++++++++++-------------
> >   fs/ceph/inode.c      | 82 ++++++++++++++++++++++++++++++++++++++++----
> >   fs/ceph/mds_client.c |  3 +-
> >   fs/ceph/mds_client.h |  1 +
> >   fs/ceph/super.h      |  7 +++-
> >   6 files changed, 160 insertions(+), 65 deletions(-)
> > 
> [...]
> 
> > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > index eb562e259347..f62785e4dbcb 100644
> > --- a/fs/ceph/inode.c
> > +++ b/fs/ceph/inode.c
> > @@ -52,17 +52,85 @@ static int ceph_set_ino_cb(struct inode *inode, void *data)
> >   	return 0;
> >   }
> >   
> > -struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino)
> > +/**
> > + * ceph_new_inode - allocate a new inode in advance of an expected create
> > + * @dir: parent directory for new inode
> > + * @dentry: dentry that may eventually point to new inode
> > + * @mode: mode of new inode
> > + * @as_ctx: pointer to inherited security context
> > + *
> > + * Allocate a new inode in advance of an operation to create a new inode.
> > + * This allocates the inode and sets up the acl_sec_ctx with appropriate
> > + * info for the new inode.
> > + *
> > + * Returns a pointer to the new inode or an ERR_PTR.
> > + */
> > +struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
> > +			     umode_t *mode, struct ceph_acl_sec_ctx *as_ctx)
> > +{
> > +	int err;
> > +	struct inode *inode;
> > +
> > +	inode = new_inode_pseudo(dir->i_sb);
> > +	if (!inode)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	if (!S_ISLNK(*mode)) {
> > +		err = ceph_pre_init_acls(dir, mode, as_ctx);
> > +		if (err < 0)
> > +			goto out_err;
> > +	}
> > +
> > +	err = ceph_security_init_secctx(dentry, *mode, as_ctx);
> > +	if (err < 0)
> > +		goto out_err;
> > +
> > +	inode->i_state = 0;
> > +	inode->i_mode = *mode;
> > +	return inode;
> > +out_err:
> > +	iput(inode);
> > +	return ERR_PTR(err);
> > +}
> > +
> > +void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx)
> > +{
> > +	if (as_ctx->pagelist) {
> > +		req->r_pagelist = as_ctx->pagelist;
> > +		as_ctx->pagelist = NULL;
> > +	}
> > +}
> > +
> > +/**
> > + * ceph_get_inode - find or create/hash a new inode
> > + * @sb: superblock to search and allocate in
> > + * @vino: vino to search for
> > + * @newino: optional new inode to insert if one isn't found (may be NULL)
> > + *
> > + * Search for or insert a new inode into the hash for the given vino, and return a
> > + * reference to it. If new is non-NULL, its reference is consumed.
> > + */
> > +struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, struct inode *newino)
> >   {
> >   	struct inode *inode;
> >   
> >   	if (ceph_vino_is_reserved(vino))
> >   		return ERR_PTR(-EREMOTEIO);
> >   
> > -	inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
> > -			     ceph_set_ino_cb, &vino);
> > -	if (!inode)
> > +	if (newino) {
> > +		inode = inode_insert5(newino, (unsigned long)vino.ino, ceph_ino_compare,
> > +					ceph_set_ino_cb, &vino);
> > +		if (inode != newino)
> > +			iput(newino);
> > +	} else {
> > +		inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare,
> > +				     ceph_set_ino_cb, &vino);
> > +	}
> > +
> > +	if (!inode) {
> > +		dout("No inode found for %llx.%llx\n", vino.ino, vino.snap);
> >   		return ERR_PTR(-ENOMEM);
> > +	}
> >   
> >   	dout("get_inode on %llu=%llx.%llx got %p new %d\n", ceph_present_inode(inode),
> >   	     ceph_vinop(inode), inode, !!(inode->i_state & I_NEW));
> > @@ -78,7 +146,7 @@ struct inode *ceph_get_snapdir(struct inode *parent)
> >   		.ino = ceph_ino(parent),
> >   		.snap = CEPH_SNAPDIR,
> >   	};
> > -	struct inode *inode = ceph_get_inode(parent->i_sb, vino);
> > +	struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL);
> >   	struct ceph_inode_info *ci = ceph_inode(inode);
> >   
> >   	if (IS_ERR(inode))
> 
> Should we always check this just before using it before 'struct 
> ceph_inode_info *ci = ceph_inode(inode);' ?
> 
> But it seems the 'ceph_inode()' won't introduce any issue here.
> 
> Thanks,
> 

Yeah, it's just doing pointer math. If it turns out to be an error,
it'll exit before it ever dereferences "ci".


> > @@ -1546,7 +1614,7 @@ static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req,
> >   		vino.ino = le64_to_cpu(rde->inode.in->ino);
> >   		vino.snap = le64_to_cpu(rde->inode.in->snapid);
> >   
> > -		in = ceph_get_inode(req->r_dentry->d_sb, vino);
> > +		in = ceph_get_inode(req->r_dentry->d_sb, vino, NULL);
> >   		if (IS_ERR(in)) {
> >   			err = PTR_ERR(in);
> >   			dout("new_inode badness got %d\n", err);
> > @@ -1748,7 +1816,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
> >   		if (d_really_is_positive(dn)) {
> >   			in = d_inode(dn);
> >   		} else {
> > -			in = ceph_get_inode(parent->d_sb, tvino);
> > +			in = ceph_get_inode(parent->d_sb, tvino, NULL);
> >   			if (IS_ERR(in)) {
> >   				dout("new_inode badness\n");
> >   				d_drop(dn);
> [...]
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07  3:53   ` Xiubo Li
@ 2021-07-07 12:09     ` Jeff Layton
  2021-07-07 12:46       ` Xiubo Li
  0 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:09 UTC (permalink / raw)
  To: Xiubo Li, ceph-devel; +Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 11:53 +0800, Xiubo Li wrote:
> On 6/25/21 9:58 PM, Jeff Layton wrote:
> > ...and store them in the ceph_inode_info.
> > 
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >   fs/ceph/file.c       |  2 ++
> >   fs/ceph/inode.c      | 18 ++++++++++++++++++
> >   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> >   fs/ceph/mds_client.h |  4 ++++
> >   fs/ceph/super.h      |  6 ++++++
> >   5 files changed, 74 insertions(+)
> > 
> > diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> > index 2cda398ba64d..ea0e85075b7b 100644
> > --- a/fs/ceph/file.c
> > +++ b/fs/ceph/file.c
> > @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
> >   	iinfo.xattr_data = xattr_buf;
> >   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
> >   
> > +	/* FIXME: set fscrypt_auth and fscrypt_file */
> > +
> >   	in.ino = cpu_to_le64(vino.ino);
> >   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
> >   	in.version = cpu_to_le64(1);	// ???
> > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > index f62785e4dbcb..b620281ea65b 100644
> > --- a/fs/ceph/inode.c
> > +++ b/fs/ceph/inode.c
> > @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
> >   
> >   	ci->i_meta_err = 0;
> >   
> > +#ifdef CONFIG_FS_ENCRYPTION
> > +	ci->fscrypt_auth = NULL;
> > +	ci->fscrypt_auth_len = 0;
> > +	ci->fscrypt_file = NULL;
> > +	ci->fscrypt_file_len = 0;
> > +#endif
> > +
> >   	return &ci->vfs_inode;
> >   }
> >   
> > @@ -619,6 +626,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);
> >   }
> >   
> > @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
> >   		xattr_blob = NULL;
> >   	}
> >   
> > +	if (iinfo->fscrypt_auth_len && !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);
> > +	}
> > +
> >   	/* finally update i_version */
> >   	if (le64_to_cpu(info->version) > ci->i_version)
> >   		ci->i_version = le64_to_cpu(info->version);
> > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> > index 3b3a14024ca0..9c994effc51d 100644
> > --- a/fs/ceph/mds_client.c
> > +++ b/fs/ceph/mds_client.c
> > @@ -183,8 +183,48 @@ 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);
> > +
> > +		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;
> 
> Should we kfree(info->fscrypt_auth) before return ?
> 
> I didn't anywhere is freeing it.
> 

It'll get freed in destroy_reply_info(). We could do it here, but it
should be fine to wait until we're tearing the "info" structure down.

> 
> 
> > +				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);
> > @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
> >   
> >   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
> >   {
> > +	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;
> >   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
> > diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> > index 64ea9d853b8d..0c3cc61fd038 100644
> > --- a/fs/ceph/mds_client.h
> > +++ b/fs/ceph/mds_client.h
> > @@ -88,6 +88,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;
> >   };
> > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > index 0cd94b296f5f..e032737fe472 100644
> > --- a/fs/ceph/super.h
> > +++ b/fs/ceph/super.h
> > @@ -429,6 +429,12 @@ struct ceph_inode_info {
> >   
> >   #ifdef CONFIG_CEPH_FSCACHE
> >   	struct fscache_cookie *fscache;
> > +#endif
> > +#ifdef CONFIG_FS_ENCRYPTION
> > +	u32 fscrypt_auth_len;
> > +	u32 fscrypt_file_len;
> > +	u8 *fscrypt_auth;
> > +	u8 *fscrypt_file;
> >   #endif
> >   	errseq_t i_meta_err;
> >   
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr
  2021-07-07  8:11   ` Xiubo Li
@ 2021-07-07 12:10     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:10 UTC (permalink / raw)
  To: Xiubo Li, ceph-devel; +Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 16:11 +0800, Xiubo Li wrote:
> On 6/25/21 9:58 PM, Jeff Layton wrote:
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >   fs/ceph/acl.c                |  4 ++--
> >   fs/ceph/inode.c              | 30 ++++++++++++++++++++++++++++--
> >   fs/ceph/mds_client.c         | 31 ++++++++++++++++++++++++++-----
> >   fs/ceph/mds_client.h         |  3 +++
> >   fs/ceph/super.h              |  7 ++++++-
> >   include/linux/ceph/ceph_fs.h | 21 +++++++++++++--------
> >   6 files changed, 78 insertions(+), 18 deletions(-)
> > 
> > diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> > index 529af59d9fd3..6e716f142022 100644
> > --- a/fs/ceph/acl.c
> > +++ b/fs/ceph/acl.c
> > @@ -136,7 +136,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
> >   		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;
> >   	}
> > @@ -147,7 +147,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
> >   			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;
> >   	}
> > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > index b620281ea65b..7821ba04eef3 100644
> > --- a/fs/ceph/inode.c
> > +++ b/fs/ceph/inode.c
> > @@ -2086,7 +2086,7 @@ 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;
> > @@ -2127,6 +2127,32 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
> >   
> >   	dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
> >   
> > +	if (cia && cia->fscrypt_auth) {
> > +		u32 len = offsetof(struct ceph_fscrypt_auth, cfa_blob) +
> > +			  le32_to_cpu(cia->fscrypt_auth->cfa_blob_len);
> > +
> > +		if (len > sizeof(*cia->fscrypt_auth))
> > +			return -EINVAL;
> > +
> 
> Possibly we can remove this check here since in the patch followed will 
> make sure the 'cia' is valid.
> 
> 

Good point. I'll plan to clean that up.

> 
> > +		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) {
> > +			req->r_fscrypt_auth = cia->fscrypt_auth;
> > +			mask |= CEPH_SETATTR_FSCRYPT_AUTH;
> > +			release |= CEPH_CAP_AUTH_SHARED;
> > +		}
> > +		cia->fscrypt_auth = NULL;
> > +	}
> > +
> >   	if (ia_valid & ATTR_UID) {
> >   		dout("setattr %p uid %d -> %d\n", inode,
> >   		     from_kuid(&init_user_ns, inode->i_uid),
> > @@ -2324,7 +2350,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, 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(&init_user_ns, inode, attr->ia_mode);
> > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> > index 9c994effc51d..4aca8ce1c135 100644
> > --- a/fs/ceph/mds_client.c
> > +++ b/fs/ceph/mds_client.c
> > @@ -2529,8 +2529,7 @@ 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;
> > @@ -2543,6 +2542,21 @@ 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 = le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> > +
> > +		authlen += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> 
> For the authlen calculating, maybe we could add one helper ? I found 
> there have some other places are also doing this.
> 
> 

Yes. I'll plan to do that.

> > +		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
> >   }
> >   
> >   /*
> > @@ -2591,6 +2605,13 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
> >   	len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
> >   		sizeof(struct ceph_timespec);
> >   	len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
> > +	len += sizeof(u32); // altname
> > +	len += sizeof(u32); // fscrypt_auth
> > +	if (req->r_fscrypt_auth) {
> > +		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> > +		len += le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> > +	}
> > +	len += sizeof(u32); // fscrypt_file
> >   
> >   	/* calculate (max) length for cap releases */
> >   	len += sizeof(struct ceph_mds_request_release) *
> > @@ -2621,7 +2642,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);
> > @@ -2672,7 +2693,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);
> > @@ -2781,7 +2802,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);
> > diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> > index 0c3cc61fd038..800eed49c2fd 100644
> > --- a/fs/ceph/mds_client.h
> > +++ b/fs/ceph/mds_client.h
> > @@ -278,6 +278,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 */
> >   	const struct cred *r_cred;
> >   	int r_request_release_offset;
> > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > index e032737fe472..ad62cde30e0b 100644
> > --- a/fs/ceph/super.h
> > +++ b/fs/ceph/super.h
> > @@ -1035,7 +1035,12 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
> >   }
> >   extern int ceph_permission(struct user_namespace *mnt_userns,
> >   			   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 user_namespace *mnt_userns,
> >   			struct dentry *dentry, struct iattr *attr);
> >   extern int ceph_getattr(struct user_namespace *mnt_userns,
> > diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
> > index e41a811026f6..a45a82c7d432 100644
> > --- a/include/linux/ceph/ceph_fs.h
> > +++ b/include/linux/ceph/ceph_fs.h
> > @@ -355,14 +355,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.
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 11:19     ` Xiubo Li
@ 2021-07-07 12:19       ` Jeff Layton
  2021-07-07 14:32         ` Luis Henriques
  0 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:19 UTC (permalink / raw)
  To: Xiubo Li, Luis Henriques
  Cc: ceph-devel, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 19:19 +0800, Xiubo Li wrote:
> On 7/7/21 6:47 PM, Luis Henriques wrote:
> > On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
> > > ...and store them in the ceph_inode_info.
> > > 
> > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > ---
> > >   fs/ceph/file.c       |  2 ++
> > >   fs/ceph/inode.c      | 18 ++++++++++++++++++
> > >   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> > >   fs/ceph/mds_client.h |  4 ++++
> > >   fs/ceph/super.h      |  6 ++++++
> > >   5 files changed, 74 insertions(+)
> > > 
> > > diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> > > index 2cda398ba64d..ea0e85075b7b 100644
> > > --- a/fs/ceph/file.c
> > > +++ b/fs/ceph/file.c
> > > @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
> > >   	iinfo.xattr_data = xattr_buf;
> > >   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
> > >   
> > > +	/* FIXME: set fscrypt_auth and fscrypt_file */
> > > +
> > >   	in.ino = cpu_to_le64(vino.ino);
> > >   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
> > >   	in.version = cpu_to_le64(1);	// ???
> > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > > index f62785e4dbcb..b620281ea65b 100644
> > > --- a/fs/ceph/inode.c
> > > +++ b/fs/ceph/inode.c
> > > @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
> > >   
> > >   	ci->i_meta_err = 0;
> > >   
> > > +#ifdef CONFIG_FS_ENCRYPTION
> > > +	ci->fscrypt_auth = NULL;
> > > +	ci->fscrypt_auth_len = 0;
> > > +	ci->fscrypt_file = NULL;
> > > +	ci->fscrypt_file_len = 0;
> > > +#endif
> > > +
> > >   	return &ci->vfs_inode;
> > >   }
> > >   
> > > @@ -619,6 +626,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);
> > >   }
> > >   
> > > @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
> > >   		xattr_blob = NULL;
> > >   	}
> > >   
> > > +	if (iinfo->fscrypt_auth_len && !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);
> > > +	}
> > I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
> > is already set.  Something like:
> > 
> > 	if (iinfo->fscrypt_auth_len) {
> > 		if (!ci->fscrypt_auth) {
> > 			...
> > 		} else {
> > 			kfree(iinfo->fscrypt_auth);
> > 			iinfo->fscrypt_auth = NULL;
> > 		}
> > 	}
> > 
> IMO, this should be okay because it will be freed in 
> destroy_reply_info() when putting the request.
> 
> 

Yes. All of that should get cleaned up with the request.

> > > +
> > >   	/* finally update i_version */
> > >   	if (le64_to_cpu(info->version) > ci->i_version)
> > >   		ci->i_version = le64_to_cpu(info->version);
> > > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> > > index 3b3a14024ca0..9c994effc51d 100644
> > > --- a/fs/ceph/mds_client.c
> > > +++ b/fs/ceph/mds_client.c
> > > @@ -183,8 +183,48 @@ 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);
> > > +
> > > +		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;
> > As Xiubo already pointed out, there's a kfree(info->fscrypt_auth) missing
> > in this error path.
> > 
> > Cheers,
> > --
> > Luís
> > 
> > > +				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);
> > > @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
> > >   
> > >   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
> > >   {
> > > +	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;
> > >   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
> > > diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> > > index 64ea9d853b8d..0c3cc61fd038 100644
> > > --- a/fs/ceph/mds_client.h
> > > +++ b/fs/ceph/mds_client.h
> > > @@ -88,6 +88,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;
> > >   };
> > > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > > index 0cd94b296f5f..e032737fe472 100644
> > > --- a/fs/ceph/super.h
> > > +++ b/fs/ceph/super.h
> > > @@ -429,6 +429,12 @@ struct ceph_inode_info {
> > >   
> > >   #ifdef CONFIG_CEPH_FSCACHE
> > >   	struct fscache_cookie *fscache;
> > > +#endif
> > > +#ifdef CONFIG_FS_ENCRYPTION
> > > +	u32 fscrypt_auth_len;
> > > +	u32 fscrypt_file_len;
> > > +	u8 *fscrypt_auth;
> > > +	u8 *fscrypt_file;
> > >   #endif
> > >   	errseq_t i_meta_err;
> > >   
> > > -- 
> > > 2.31.1
> > > 
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr
  2021-07-07 10:47   ` Luis Henriques
@ 2021-07-07 12:25     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:25 UTC (permalink / raw)
  To: Luis Henriques; +Cc: ceph-devel, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 11:47 +0100, Luis Henriques wrote:
> On Fri, Jun 25, 2021 at 09:58:18AM -0400, Jeff Layton wrote:
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >  fs/ceph/acl.c                |  4 ++--
> >  fs/ceph/inode.c              | 30 ++++++++++++++++++++++++++++--
> >  fs/ceph/mds_client.c         | 31 ++++++++++++++++++++++++++-----
> >  fs/ceph/mds_client.h         |  3 +++
> >  fs/ceph/super.h              |  7 ++++++-
> >  include/linux/ceph/ceph_fs.h | 21 +++++++++++++--------
> >  6 files changed, 78 insertions(+), 18 deletions(-)
> > 
> > diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> > index 529af59d9fd3..6e716f142022 100644
> > --- a/fs/ceph/acl.c
> > +++ b/fs/ceph/acl.c
> > @@ -136,7 +136,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
> >  		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;
> >  	}
> > @@ -147,7 +147,7 @@ int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
> >  			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;
> >  	}
> > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > index b620281ea65b..7821ba04eef3 100644
> > --- a/fs/ceph/inode.c
> > +++ b/fs/ceph/inode.c
> > @@ -2086,7 +2086,7 @@ 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;
> > @@ -2127,6 +2127,32 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr)
> >  
> >  	dout("setattr %p issued %s\n", inode, ceph_cap_string(issued));
> >  
> > +	if (cia && cia->fscrypt_auth) {
> > +		u32 len = offsetof(struct ceph_fscrypt_auth, cfa_blob) +
> > +			  le32_to_cpu(cia->fscrypt_auth->cfa_blob_len);
> > +
> > +		if (len > sizeof(*cia->fscrypt_auth))
> > +			return -EINVAL;
> 
> This error path needs to be handled differently.  We have 'req' and
> 'prealloc_cf' allocated and, more importantly, we're holding
> ci->i_ceph_lock.
> 

Good catch. I'll fix that.

> > +
> > +		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);
> 
> If the WARN_ON_ONCE above is correct, we shouldn't need this kfree.
> 

It's a bit odd here actually...

We have to allow this to be set via setattr, so what's the right thing
to do when someone tries to set fscrypt_auth on an inode that already
has it set?

In principle we should probably ignore it, but maybe we need to allow
admins a way to fix up inodes with a corrupted context? For now, I'm
planning to allow you to override it but throw the warning in this case.
It's not ideal though and we may want to change this before merging.

> 
> > +			ci->fscrypt_auth = (u8 *)cia->fscrypt_auth;
> > +			ci->fscrypt_auth_len = len;
> > +		} else if ((issued & CEPH_CAP_AUTH_SHARED) == 0) {
> > +			req->r_fscrypt_auth = cia->fscrypt_auth;
> > +			mask |= CEPH_SETATTR_FSCRYPT_AUTH;
> > +			release |= CEPH_CAP_AUTH_SHARED;
> > +		}
> > +		cia->fscrypt_auth = NULL;
> > +	}
> > +
> >  	if (ia_valid & ATTR_UID) {
> >  		dout("setattr %p uid %d -> %d\n", inode,
> >  		     from_kuid(&init_user_ns, inode->i_uid),
> > @@ -2324,7 +2350,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, 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(&init_user_ns, inode, attr->ia_mode);
> > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> > index 9c994effc51d..4aca8ce1c135 100644
> > --- a/fs/ceph/mds_client.c
> > +++ b/fs/ceph/mds_client.c
> > @@ -2529,8 +2529,7 @@ 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;
> > @@ -2543,6 +2542,21 @@ 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 = le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> > +
> > +		authlen += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> > +		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
> >  }
> >  
> >  /*
> > @@ -2591,6 +2605,13 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
> >  	len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
> >  		sizeof(struct ceph_timespec);
> >  	len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
> > +	len += sizeof(u32); // altname
> > +	len += sizeof(u32); // fscrypt_auth
> > +	if (req->r_fscrypt_auth) {
> > +		len += offsetof(struct ceph_fscrypt_auth, cfa_blob);
> > +		len += le32_to_cpu(req->r_fscrypt_auth->cfa_blob_len);
> > +	}
> > +	len += sizeof(u32); // fscrypt_file
> >  
> >  	/* calculate (max) length for cap releases */
> >  	len += sizeof(struct ceph_mds_request_release) *
> > @@ -2621,7 +2642,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);
> > @@ -2672,7 +2693,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);
> > @@ -2781,7 +2802,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);
> > diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> > index 0c3cc61fd038..800eed49c2fd 100644
> > --- a/fs/ceph/mds_client.h
> > +++ b/fs/ceph/mds_client.h
> > @@ -278,6 +278,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 */
> >  	const struct cred *r_cred;
> >  	int r_request_release_offset;
> > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > index e032737fe472..ad62cde30e0b 100644
> > --- a/fs/ceph/super.h
> > +++ b/fs/ceph/super.h
> > @@ -1035,7 +1035,12 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
> >  }
> >  extern int ceph_permission(struct user_namespace *mnt_userns,
> >  			   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 user_namespace *mnt_userns,
> >  			struct dentry *dentry, struct iattr *attr);
> >  extern int ceph_getattr(struct user_namespace *mnt_userns,
> > diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h
> > index e41a811026f6..a45a82c7d432 100644
> > --- a/include/linux/ceph/ceph_fs.h
> > +++ b/include/linux/ceph/ceph_fs.h
> > @@ -355,14 +355,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.
> > -- 
> > 2.31.1
> > 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC
  2021-07-07 10:48   ` Luis Henriques
@ 2021-07-07 12:29     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-07 12:29 UTC (permalink / raw)
  To: Luis Henriques; +Cc: ceph-devel, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 11:48 +0100, Luis Henriques wrote:
> On Fri, Jun 25, 2021 at 09:58:21AM -0400, Jeff Layton wrote:
> > 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. Call the new function from
> > ceph_new_inode.
> > 
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >  fs/ceph/crypto.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> >  fs/ceph/crypto.h | 25 +++++++++++++++++++++++++
> >  fs/ceph/inode.c  | 10 ++++++++--
> >  fs/ceph/super.h  |  5 +++++
> >  fs/ceph/xattr.c  |  3 +++
> >  5 files changed, 83 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
> > index 997a33e1d59f..675d41fd2eb0 100644
> > --- a/fs/ceph/crypto.c
> > +++ b/fs/ceph/crypto.c
> > @@ -4,6 +4,7 @@
> >  #include <linux/fscrypt.h>
> >  
> >  #include "super.h"
> > +#include "mds_client.h"
> >  #include "crypto.h"
> >  
> >  static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
> > @@ -86,3 +87,44 @@ void ceph_fscrypt_set_ops(struct super_block *sb)
> >  {
> >  	fscrypt_set_ops(sb, &ceph_fscrypt_ops);
> >  }
> > +
> > +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> > +				 struct ceph_acl_sec_ctx *as)
> > +{
> > +	int ret, ctxsize;
> > +	bool encrypted = false;
> > +	struct ceph_inode_info *ci = ceph_inode(inode);
> > +
> > +	ret = fscrypt_prepare_new_inode(dir, inode, &encrypted);
> > +	if (ret)
> > +		return ret;
> > +	if (!encrypted)
> > +		return 0;
> > +
> > +	as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL);
> > +	if (!as->fscrypt_auth)
> > +		return -ENOMEM;
> > +
> > +	ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, inode);
> > +	if (ctxsize < 0)
> > +		return ctxsize;
> > +
> > +	as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION);
> > +	as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize);
> > +
> > +	WARN_ON_ONCE(ci->fscrypt_auth);
> > +	kfree(ci->fscrypt_auth);
> 
> It's odd to have again a kfree() after a WARN_ON_ONCE() :-)
> 

Throw a warning but handle the case sanely? That seems normal to me :)

> > +	ci->fscrypt_auth_len = ceph_fscrypt_auth_size(ctxsize);
> > +	ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, GFP_KERNEL);
> > +	if (!ci->fscrypt_auth)
> > +		return -ENOMEM;
> > +
> > +	inode->i_flags |= S_ENCRYPTED;
> > +
> > +	return 0;
> > +}
> > +
> > +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as)
> > +{
> > +	swap(req->r_fscrypt_auth, as->fscrypt_auth);
> > +}
> 
> This means that req->r_fscrypt_auth will need to be freed in
> ceph_mdsc_release_request().  (I believe you've moved this function to
> some other commit in your experimental branch).
> 

Ahh yes. Good catch. I'll fix that up too.

> > diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h
> > index d2b1f8e7b300..bdf1ba47db16 100644
> > --- a/fs/ceph/crypto.h
> > +++ b/fs/ceph/crypto.h
> > @@ -11,6 +11,9 @@
> >  #define	CEPH_XATTR_NAME_ENCRYPTION_CONTEXT	"encryption.ctx"
> >  
> >  #ifdef CONFIG_FS_ENCRYPTION
> > +struct ceph_fs_client;
> > +struct ceph_acl_sec_ctx;
> > +struct ceph_mds_request;
> >  
> >  #define CEPH_FSCRYPT_AUTH_VERSION	1
> >  struct ceph_fscrypt_auth {
> > @@ -19,10 +22,19 @@ struct ceph_fscrypt_auth {
> >  	u8	cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE];
> >  } __packed;
> >  
> > +static inline u32 ceph_fscrypt_auth_size(u32 ctxsize)
> > +{
> > +	return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize;
> > +}
> > +
> >  void ceph_fscrypt_set_ops(struct super_block *sb);
> >  
> >  void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc);
> >  
> > +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> > +				 struct ceph_acl_sec_ctx *as);
> > +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as);
> > +
> >  #else /* CONFIG_FS_ENCRYPTION */
> >  
> >  static inline void ceph_fscrypt_set_ops(struct super_block *sb)
> > @@ -32,6 +44,19 @@ static inline void ceph_fscrypt_set_ops(struct super_block *sb)
> >  static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc)
> >  {
> >  }
> > +
> > +static inline int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode,
> > +						struct ceph_acl_sec_ctx *as)
> > +{
> > +	if (IS_ENCRYPTED(dir))
> > +		return -EOPNOTSUPP;
> > +	return 0;
> > +}
> > +
> > +static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req,
> > +						struct ceph_acl_sec_ctx *as_ctx)
> > +{
> > +}
> >  #endif /* CONFIG_FS_ENCRYPTION */
> >  
> >  #endif
> > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > index fba139a4f57b..a0b311195e80 100644
> > --- a/fs/ceph/inode.c
> > +++ b/fs/ceph/inode.c
> > @@ -83,12 +83,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);
> > @@ -101,6 +106,7 @@ void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *a
> >  		req->r_pagelist = as_ctx->pagelist;
> >  		as_ctx->pagelist = NULL;
> >  	}
> > +	ceph_fscrypt_as_ctx_to_req(req, as_ctx);
> >  }
> >  
> >  /**
> > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > index 534c2a76562d..651d7909a443 100644
> > --- a/fs/ceph/super.h
> > +++ b/fs/ceph/super.h
> > @@ -26,6 +26,8 @@
> >  #include <linux/fscache.h>
> >  #endif
> > 
> > +#include "crypto.h"
> > +
> >  /* f_type in struct statfs */
> >  #define CEPH_SUPER_MAGIC 0x00c36400
> >  
> > @@ -1068,6 +1070,9 @@ struct ceph_acl_sec_ctx {
> >  #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
> >  	void *sec_ctx;
> >  	u32 sec_ctxlen;
> > +#endif
> > +#ifdef CONFIG_FS_ENCRYPTION
> > +	struct ceph_fscrypt_auth *fscrypt_auth;
> >  #endif
> >  	struct ceph_pagelist *pagelist;
> >  };
> > diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
> > index 1242db8d3444..16a62a2bd61e 100644
> > --- a/fs/ceph/xattr.c
> > +++ b/fs/ceph/xattr.c
> > @@ -1362,6 +1362,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx)
> >  #endif
> >  #ifdef CONFIG_CEPH_FS_SECURITY_LABEL
> >  	security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen);
> > +#endif
> > +#ifdef CONFIG_FS_ENCRYPTION
> > +	kfree(as_ctx->fscrypt_auth);
> >  #endif
> >  	if (as_ctx->pagelist)
> >  		ceph_pagelist_release(as_ctx->pagelist);
> > -- 
> > 2.31.1
> > 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 12:09     ` Jeff Layton
@ 2021-07-07 12:46       ` Xiubo Li
  0 siblings, 0 replies; 59+ messages in thread
From: Xiubo Li @ 2021-07-07 12:46 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 7/7/21 8:09 PM, Jeff Layton wrote:
> On Wed, 2021-07-07 at 11:53 +0800, Xiubo Li wrote:
>> On 6/25/21 9:58 PM, Jeff Layton wrote:
>>> ...and store them in the ceph_inode_info.
>>>
>>> Signed-off-by: Jeff Layton <jlayton@kernel.org>
>>> ---
>>>    fs/ceph/file.c       |  2 ++
>>>    fs/ceph/inode.c      | 18 ++++++++++++++++++
>>>    fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
>>>    fs/ceph/mds_client.h |  4 ++++
>>>    fs/ceph/super.h      |  6 ++++++
>>>    5 files changed, 74 insertions(+)
>>>
>>> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
>>> index 2cda398ba64d..ea0e85075b7b 100644
>>> --- a/fs/ceph/file.c
>>> +++ b/fs/ceph/file.c
>>> @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
>>>    	iinfo.xattr_data = xattr_buf;
>>>    	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
>>>    
>>> +	/* FIXME: set fscrypt_auth and fscrypt_file */
>>> +
>>>    	in.ino = cpu_to_le64(vino.ino);
>>>    	in.snapid = cpu_to_le64(CEPH_NOSNAP);
>>>    	in.version = cpu_to_le64(1);	// ???
>>> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
>>> index f62785e4dbcb..b620281ea65b 100644
>>> --- a/fs/ceph/inode.c
>>> +++ b/fs/ceph/inode.c
>>> @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
>>>    
>>>    	ci->i_meta_err = 0;
>>>    
>>> +#ifdef CONFIG_FS_ENCRYPTION
>>> +	ci->fscrypt_auth = NULL;
>>> +	ci->fscrypt_auth_len = 0;
>>> +	ci->fscrypt_file = NULL;
>>> +	ci->fscrypt_file_len = 0;
>>> +#endif
>>> +
>>>    	return &ci->vfs_inode;
>>>    }
>>>    
>>> @@ -619,6 +626,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);
>>>    }
>>>    
>>> @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
>>>    		xattr_blob = NULL;
>>>    	}
>>>    
>>> +	if (iinfo->fscrypt_auth_len && !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);
>>> +	}
>>> +
>>>    	/* finally update i_version */
>>>    	if (le64_to_cpu(info->version) > ci->i_version)
>>>    		ci->i_version = le64_to_cpu(info->version);
>>> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
>>> index 3b3a14024ca0..9c994effc51d 100644
>>> --- a/fs/ceph/mds_client.c
>>> +++ b/fs/ceph/mds_client.c
>>> @@ -183,8 +183,48 @@ 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);
>>> +
>>> +		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;
>> Should we kfree(info->fscrypt_auth) before return ?
>>
>> I didn't anywhere is freeing it.
>>
> It'll get freed in destroy_reply_info(). We could do it here, but it
> should be fine to wait until we're tearing the "info" structure down.
>
Yeah, I just missed the destory_reply_info() code that time.



>>
>>> +				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);
>>> @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>>>    
>>>    static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>>>    {
>>> +	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;
>>>    	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
>>> diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
>>> index 64ea9d853b8d..0c3cc61fd038 100644
>>> --- a/fs/ceph/mds_client.h
>>> +++ b/fs/ceph/mds_client.h
>>> @@ -88,6 +88,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;
>>>    };
>>> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
>>> index 0cd94b296f5f..e032737fe472 100644
>>> --- a/fs/ceph/super.h
>>> +++ b/fs/ceph/super.h
>>> @@ -429,6 +429,12 @@ struct ceph_inode_info {
>>>    
>>>    #ifdef CONFIG_CEPH_FSCACHE
>>>    	struct fscache_cookie *fscache;
>>> +#endif
>>> +#ifdef CONFIG_FS_ENCRYPTION
>>> +	u32 fscrypt_auth_len;
>>> +	u32 fscrypt_file_len;
>>> +	u8 *fscrypt_auth;
>>> +	u8 *fscrypt_file;
>>>    #endif
>>>    	errseq_t i_meta_err;
>>>    


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-07-07 12:02     ` Jeff Layton
@ 2021-07-07 12:47       ` Xiubo Li
  0 siblings, 0 replies; 59+ messages in thread
From: Xiubo Li @ 2021-07-07 12:47 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 7/7/21 8:02 PM, Jeff Layton wrote:
> On Wed, 2021-07-07 at 15:20 +0800, Xiubo Li wrote:
>> Hi Jeff,
>>
>> There has some following patches in your "fscrypt" branch, which is not
>> posted yet, the commit is:
>>
>> "3161d2f549db ceph: size handling for encrypted inodes in cap updates"
>>
>> It seems buggy.
>>
> Yes. Those are still quite rough. If I haven't posted them, then YMMV. I
> often push them to -experimental branches just for backup purposes. You
> may want to wait on reviewing those until I've had a chance to clean
> them up and post them.

Yeah, sure.

I am reviewing the code from you experimental branch.


>> In the encode_cap_msg() you have removed the 'fscrypt_file_len' and and
>> added a new 8 bytes' data encoding:
>>
>>           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, fc->size);
>>
>> That means no matter the 'arg->encrypted' is true or not, here it will
>> always encode extra 8 bytes' data ?
>>
>>
>> But in cap_msg_size(), you are making it optional:
>>
>>
>>    static inline int cap_msg_size(struct cap_msg_args *arg)
>>    {
>>           return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len +
>> -                       arg->fscrypt_file_len;
>> +                       arg->encrypted ? sizeof(__le64) : 0;
>>    }
>>
>>
>> Have I missed something important here ?
>>
>> Thanks
>>
> Nope, you're right. I had fixed that one in my local branch already, and
> just hadn't yet pushed it to the repo. I'll plan to clean this up a bit
> later today and push an updated branch.

Cool.

Thanks.


>
>> On 6/25/21 9:58 PM, Jeff Layton wrote:
>>> Signed-off-by: Jeff Layton <jlayton@kernel.org>
>>> ---
>>>    fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
>>>    1 file changed, 49 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
>>> index 038f59cc4250..1be6c5148700 100644
>>> --- a/fs/ceph/caps.c
>>> +++ b/fs/ceph/caps.c
>>> @@ -13,6 +13,7 @@
>>>    #include "super.h"
>>>    #include "mds_client.h"
>>>    #include "cache.h"
>>> +#include "crypto.h"
>>>    #include <linux/ceph/decode.h>
>>>    #include <linux/ceph/messenger.h>
>>>    
>>> @@ -1229,15 +1230,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)
>>>    {
>>> @@ -1253,7 +1251,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;
>>> @@ -1324,6 +1322,16 @@ 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);
>>> +
>>> +	/* 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);
>>>    }
>>>    
>>>    /*
>>> @@ -1445,6 +1453,26 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
>>>    		}
>>>    	}
>>>    	arg->flags = flags;
>>> +	if (ci->fscrypt_auth_len &&
>>> +	    WARN_ON_ONCE(ci->fscrypt_auth_len != sizeof(struct ceph_fscrypt_auth))) {
>>> +		/* Don't set this if it isn't right size */
>>> +		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;
>>> +}
>>> +
>>> +#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 + arg->fscrypt_auth_len +
>>> +			arg->fscrypt_file_len;
>>>    }
>>>    
>>>    /*
>>> @@ -1457,7 +1485,7 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
>>>    	struct ceph_msg *msg;
>>>    	struct inode *inode = &ci->vfs_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),
>>> @@ -1483,10 +1511,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;
>>> @@ -1524,6 +1548,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;


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 12:19       ` Jeff Layton
@ 2021-07-07 14:32         ` Luis Henriques
  2021-07-07 14:56           ` Luis Henriques
  0 siblings, 1 reply; 59+ messages in thread
From: Luis Henriques @ 2021-07-07 14:32 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Xiubo Li, ceph-devel, linux-fsdevel, linux-fscrypt, dhowells

On Wed, Jul 07, 2021 at 08:19:25AM -0400, Jeff Layton wrote:
> On Wed, 2021-07-07 at 19:19 +0800, Xiubo Li wrote:
> > On 7/7/21 6:47 PM, Luis Henriques wrote:
> > > On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
> > > > ...and store them in the ceph_inode_info.
> > > > 
> > > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > > ---
> > > >   fs/ceph/file.c       |  2 ++
> > > >   fs/ceph/inode.c      | 18 ++++++++++++++++++
> > > >   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> > > >   fs/ceph/mds_client.h |  4 ++++
> > > >   fs/ceph/super.h      |  6 ++++++
> > > >   5 files changed, 74 insertions(+)
> > > > 
> > > > diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> > > > index 2cda398ba64d..ea0e85075b7b 100644
> > > > --- a/fs/ceph/file.c
> > > > +++ b/fs/ceph/file.c
> > > > @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
> > > >   	iinfo.xattr_data = xattr_buf;
> > > >   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
> > > >   
> > > > +	/* FIXME: set fscrypt_auth and fscrypt_file */
> > > > +
> > > >   	in.ino = cpu_to_le64(vino.ino);
> > > >   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
> > > >   	in.version = cpu_to_le64(1);	// ???
> > > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > > > index f62785e4dbcb..b620281ea65b 100644
> > > > --- a/fs/ceph/inode.c
> > > > +++ b/fs/ceph/inode.c
> > > > @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
> > > >   
> > > >   	ci->i_meta_err = 0;
> > > >   
> > > > +#ifdef CONFIG_FS_ENCRYPTION
> > > > +	ci->fscrypt_auth = NULL;
> > > > +	ci->fscrypt_auth_len = 0;
> > > > +	ci->fscrypt_file = NULL;
> > > > +	ci->fscrypt_file_len = 0;
> > > > +#endif
> > > > +
> > > >   	return &ci->vfs_inode;
> > > >   }
> > > >   
> > > > @@ -619,6 +626,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);
> > > >   }
> > > >   
> > > > @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
> > > >   		xattr_blob = NULL;
> > > >   	}
> > > >   
> > > > +	if (iinfo->fscrypt_auth_len && !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);
> > > > +	}
> > > I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
> > > is already set.  Something like:
> > > 
> > > 	if (iinfo->fscrypt_auth_len) {
> > > 		if (!ci->fscrypt_auth) {
> > > 			...
> > > 		} else {
> > > 			kfree(iinfo->fscrypt_auth);
> > > 			iinfo->fscrypt_auth = NULL;
> > > 		}
> > > 	}
> > > 
> > IMO, this should be okay because it will be freed in 
> > destroy_reply_info() when putting the request.
> > 
> > 
> 
> Yes. All of that should get cleaned up with the request.

Hmm... ok, so maybe I missed something because I *did* saw kmemleak
complaining.  Maybe it was on the READDIR path.  /me goes look again.

Cheers,
--
Luís


> 
> > > > +
> > > >   	/* finally update i_version */
> > > >   	if (le64_to_cpu(info->version) > ci->i_version)
> > > >   		ci->i_version = le64_to_cpu(info->version);
> > > > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> > > > index 3b3a14024ca0..9c994effc51d 100644
> > > > --- a/fs/ceph/mds_client.c
> > > > +++ b/fs/ceph/mds_client.c
> > > > @@ -183,8 +183,48 @@ 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);
> > > > +
> > > > +		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;
> > > As Xiubo already pointed out, there's a kfree(info->fscrypt_auth) missing
> > > in this error path.
> > > 
> > > Cheers,
> > > --
> > > Luís
> > > 
> > > > +				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);
> > > > @@ -625,6 +665,10 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
> > > >   
> > > >   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
> > > >   {
> > > > +	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;
> > > >   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
> > > > diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
> > > > index 64ea9d853b8d..0c3cc61fd038 100644
> > > > --- a/fs/ceph/mds_client.h
> > > > +++ b/fs/ceph/mds_client.h
> > > > @@ -88,6 +88,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;
> > > >   };
> > > > diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> > > > index 0cd94b296f5f..e032737fe472 100644
> > > > --- a/fs/ceph/super.h
> > > > +++ b/fs/ceph/super.h
> > > > @@ -429,6 +429,12 @@ struct ceph_inode_info {
> > > >   
> > > >   #ifdef CONFIG_CEPH_FSCACHE
> > > >   	struct fscache_cookie *fscache;
> > > > +#endif
> > > > +#ifdef CONFIG_FS_ENCRYPTION
> > > > +	u32 fscrypt_auth_len;
> > > > +	u32 fscrypt_file_len;
> > > > +	u8 *fscrypt_auth;
> > > > +	u8 *fscrypt_file;
> > > >   #endif
> > > >   	errseq_t i_meta_err;
> > > >   
> > > > -- 
> > > > 2.31.1
> > > > 
> > 
> 
> -- 
> Jeff Layton <jlayton@kernel.org>
> 

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 14:32         ` Luis Henriques
@ 2021-07-07 14:56           ` Luis Henriques
  2021-07-08  2:56             ` Xiubo Li
  2021-07-08 11:26             ` Jeff Layton
  0 siblings, 2 replies; 59+ messages in thread
From: Luis Henriques @ 2021-07-07 14:56 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Xiubo Li, ceph-devel, linux-fsdevel, linux-fscrypt, dhowells

On Wed, Jul 07, 2021 at 03:32:13PM +0100, Luis Henriques wrote:
> On Wed, Jul 07, 2021 at 08:19:25AM -0400, Jeff Layton wrote:
> > On Wed, 2021-07-07 at 19:19 +0800, Xiubo Li wrote:
> > > On 7/7/21 6:47 PM, Luis Henriques wrote:
> > > > On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
> > > > > ...and store them in the ceph_inode_info.
> > > > > 
> > > > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > > > ---
> > > > >   fs/ceph/file.c       |  2 ++
> > > > >   fs/ceph/inode.c      | 18 ++++++++++++++++++
> > > > >   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> > > > >   fs/ceph/mds_client.h |  4 ++++
> > > > >   fs/ceph/super.h      |  6 ++++++
> > > > >   5 files changed, 74 insertions(+)
> > > > > 
> > > > > diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> > > > > index 2cda398ba64d..ea0e85075b7b 100644
> > > > > --- a/fs/ceph/file.c
> > > > > +++ b/fs/ceph/file.c
> > > > > @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
> > > > >   	iinfo.xattr_data = xattr_buf;
> > > > >   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
> > > > >   
> > > > > +	/* FIXME: set fscrypt_auth and fscrypt_file */
> > > > > +
> > > > >   	in.ino = cpu_to_le64(vino.ino);
> > > > >   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
> > > > >   	in.version = cpu_to_le64(1);	// ???
> > > > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > > > > index f62785e4dbcb..b620281ea65b 100644
> > > > > --- a/fs/ceph/inode.c
> > > > > +++ b/fs/ceph/inode.c
> > > > > @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
> > > > >   
> > > > >   	ci->i_meta_err = 0;
> > > > >   
> > > > > +#ifdef CONFIG_FS_ENCRYPTION
> > > > > +	ci->fscrypt_auth = NULL;
> > > > > +	ci->fscrypt_auth_len = 0;
> > > > > +	ci->fscrypt_file = NULL;
> > > > > +	ci->fscrypt_file_len = 0;
> > > > > +#endif
> > > > > +
> > > > >   	return &ci->vfs_inode;
> > > > >   }
> > > > >   
> > > > > @@ -619,6 +626,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);
> > > > >   }
> > > > >   
> > > > > @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
> > > > >   		xattr_blob = NULL;
> > > > >   	}
> > > > >   
> > > > > +	if (iinfo->fscrypt_auth_len && !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);
> > > > > +	}
> > > > I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
> > > > is already set.  Something like:
> > > > 
> > > > 	if (iinfo->fscrypt_auth_len) {
> > > > 		if (!ci->fscrypt_auth) {
> > > > 			...
> > > > 		} else {
> > > > 			kfree(iinfo->fscrypt_auth);
> > > > 			iinfo->fscrypt_auth = NULL;
> > > > 		}
> > > > 	}
> > > > 
> > > IMO, this should be okay because it will be freed in 
> > > destroy_reply_info() when putting the request.
> > > 
> > > 
> > 
> > Yes. All of that should get cleaned up with the request.
> 
> Hmm... ok, so maybe I missed something because I *did* saw kmemleak
> complaining.  Maybe it was on the READDIR path.  /me goes look again.

Ah, that was indeed the problem.  So, here's a quick hack to fix
destroy_reply_info() so that it also frees the extra memory from READDIR:

@@ -686,12 +686,23 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
 
 static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
 {
+	int i = 0;
+
 	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;
+		if (rde->inode.fscrypt_auth_len)
+			kfree(rde->inode.fscrypt_auth);
+	}
+	       
 	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
 }
 

Cheers,
--
Luís

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 14:56           ` Luis Henriques
@ 2021-07-08  2:56             ` Xiubo Li
  2021-07-08 11:26             ` Jeff Layton
  1 sibling, 0 replies; 59+ messages in thread
From: Xiubo Li @ 2021-07-08  2:56 UTC (permalink / raw)
  To: Luis Henriques, Jeff Layton
  Cc: ceph-devel, linux-fsdevel, linux-fscrypt, dhowells


On 7/7/21 10:56 PM, Luis Henriques wrote:
> On Wed, Jul 07, 2021 at 03:32:13PM +0100, Luis Henriques wrote:
>> On Wed, Jul 07, 2021 at 08:19:25AM -0400, Jeff Layton wrote:
>>> On Wed, 2021-07-07 at 19:19 +0800, Xiubo Li wrote:
>>>> On 7/7/21 6:47 PM, Luis Henriques wrote:
>>>>> On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
>>>>>> ...and store them in the ceph_inode_info.
>>>>>>
>>>>>> Signed-off-by: Jeff Layton <jlayton@kernel.org>
>>>>>> ---
>>>>>>    fs/ceph/file.c       |  2 ++
>>>>>>    fs/ceph/inode.c      | 18 ++++++++++++++++++
>>>>>>    fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>>    fs/ceph/mds_client.h |  4 ++++
>>>>>>    fs/ceph/super.h      |  6 ++++++
>>>>>>    5 files changed, 74 insertions(+)
>>>>>>
>>>>>> diff --git a/fs/ceph/file.c b/fs/ceph/file.c
>>>>>> index 2cda398ba64d..ea0e85075b7b 100644
>>>>>> --- a/fs/ceph/file.c
>>>>>> +++ b/fs/ceph/file.c
>>>>>> @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
>>>>>>    	iinfo.xattr_data = xattr_buf;
>>>>>>    	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
>>>>>>    
>>>>>> +	/* FIXME: set fscrypt_auth and fscrypt_file */
>>>>>> +
>>>>>>    	in.ino = cpu_to_le64(vino.ino);
>>>>>>    	in.snapid = cpu_to_le64(CEPH_NOSNAP);
>>>>>>    	in.version = cpu_to_le64(1);	// ???
>>>>>> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
>>>>>> index f62785e4dbcb..b620281ea65b 100644
>>>>>> --- a/fs/ceph/inode.c
>>>>>> +++ b/fs/ceph/inode.c
>>>>>> @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
>>>>>>    
>>>>>>    	ci->i_meta_err = 0;
>>>>>>    
>>>>>> +#ifdef CONFIG_FS_ENCRYPTION
>>>>>> +	ci->fscrypt_auth = NULL;
>>>>>> +	ci->fscrypt_auth_len = 0;
>>>>>> +	ci->fscrypt_file = NULL;
>>>>>> +	ci->fscrypt_file_len = 0;
>>>>>> +#endif
>>>>>> +
>>>>>>    	return &ci->vfs_inode;
>>>>>>    }
>>>>>>    
>>>>>> @@ -619,6 +626,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);
>>>>>>    }
>>>>>>    
>>>>>> @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
>>>>>>    		xattr_blob = NULL;
>>>>>>    	}
>>>>>>    
>>>>>> +	if (iinfo->fscrypt_auth_len && !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);
>>>>>> +	}
>>>>> I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
>>>>> is already set.  Something like:
>>>>>
>>>>> 	if (iinfo->fscrypt_auth_len) {
>>>>> 		if (!ci->fscrypt_auth) {
>>>>> 			...
>>>>> 		} else {
>>>>> 			kfree(iinfo->fscrypt_auth);
>>>>> 			iinfo->fscrypt_auth = NULL;
>>>>> 		}
>>>>> 	}
>>>>>
>>>> IMO, this should be okay because it will be freed in
>>>> destroy_reply_info() when putting the request.
>>>>
>>>>
>>> Yes. All of that should get cleaned up with the request.
>> Hmm... ok, so maybe I missed something because I *did* saw kmemleak
>> complaining.  Maybe it was on the READDIR path.  /me goes look again.
> Ah, that was indeed the problem.  So, here's a quick hack to fix
> destroy_reply_info() so that it also frees the extra memory from READDIR:
>
> @@ -686,12 +686,23 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>   
>   static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>   {
> +	int i = 0;
> +
>   	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;
> +		if (rde->inode.fscrypt_auth_len)
> +			kfree(rde->inode.fscrypt_auth);
> +	}
> +	
>   	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
>   }
>   

Yeah, this looks nice.


> Cheers,
> --
> Luís
>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 12/24] ceph: add fscrypt ioctls
  2021-06-25 13:58 ` [RFC PATCH v7 12/24] ceph: add fscrypt ioctls Jeff Layton
@ 2021-07-08  7:30   ` Xiubo Li
  2021-07-08 11:26     ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Xiubo Li @ 2021-07-08  7:30 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 6/25/21 9:58 PM, Jeff Layton wrote:
> We gate most of the ioctls on MDS feature support. The exception is the
> key removal and status functions that we still want to work if the MDS's
> were to (inexplicably) lose the feature.
>
> For the set_policy ioctl, we take Fcx caps to ensure that nothing can
> create files in the directory while the ioctl is running. That should
> be enough to ensure that the "empty_dir" check is reliable.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/ceph/ioctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 83 insertions(+)
>
> diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
> index 6e061bf62ad4..477ecc667aee 100644
> --- a/fs/ceph/ioctl.c
> +++ b/fs/ceph/ioctl.c
> @@ -6,6 +6,7 @@
>   #include "mds_client.h"
>   #include "ioctl.h"
>   #include <linux/ceph/striper.h>
> +#include <linux/fscrypt.h>
>   
>   /*
>    * ioctls
> @@ -268,8 +269,54 @@ static long ceph_ioctl_syncio(struct file *file)
>   	return 0;
>   }
>   
> +static int vet_mds_for_fscrypt(struct file *file)
> +{
> +	int i, ret = -EOPNOTSUPP;
> +	struct ceph_mds_client	*mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
> +
> +	mutex_lock(&mdsc->mutex);
> +	for (i = 0; i < mdsc->max_sessions; i++) {
> +		struct ceph_mds_session *s = mdsc->sessions[i];
> +
> +		if (!s)
> +			continue;
> +		if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
> +			ret = 0;
> +		break;
> +	}
> +	mutex_unlock(&mdsc->mutex);
> +	return ret;
> +}
> +
> +static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
> +{
> +	int ret, got = 0;
> +	struct inode *inode = file_inode(file);
> +	struct ceph_inode_info *ci = ceph_inode(inode);
> +
> +	ret = vet_mds_for_fscrypt(file);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Ensure we hold these caps so that we _know_ that the rstats check
> +	 * in the empty_dir check is reliable.
> +	 */
> +	ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);

In the commit comment said it will host the Fsx, but here it is only 
trying to hold the Fs. Will the Fx really needed ?

Thanks


> +	if (ret)
> +		return ret;
> +
> +	ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
> +	if (got)
> +		ceph_put_cap_refs(ci, got);
> +
> +	return ret;
> +}
> +
>   long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>   {
> +	int ret;
> +
>   	dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
>   	switch (cmd) {
>   	case CEPH_IOC_GET_LAYOUT:
> @@ -289,6 +336,42 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>   
>   	case CEPH_IOC_SYNCIO:
>   		return ceph_ioctl_syncio(file);
> +
> +	case FS_IOC_SET_ENCRYPTION_POLICY:
> +		return ceph_set_encryption_policy(file, arg);
> +
> +	case FS_IOC_GET_ENCRYPTION_POLICY:
> +		ret = vet_mds_for_fscrypt(file);
> +		if (ret)
> +			return ret;
> +		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
> +
> +	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
> +		ret = vet_mds_for_fscrypt(file);
> +		if (ret)
> +			return ret;
> +		return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
> +
> +	case FS_IOC_ADD_ENCRYPTION_KEY:
> +		ret = vet_mds_for_fscrypt(file);
> +		if (ret)
> +			return ret;
> +		return fscrypt_ioctl_add_key(file, (void __user *)arg);
> +
> +	case FS_IOC_REMOVE_ENCRYPTION_KEY:
> +		return fscrypt_ioctl_remove_key(file, (void __user *)arg);
> +
> +	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
> +		return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg);
> +
> +	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
> +		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
> +
> +	case FS_IOC_GET_ENCRYPTION_NONCE:
> +		ret = vet_mds_for_fscrypt(file);
> +		if (ret)
> +			return ret;
> +		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
>   	}
>   
>   	return -ENOTTY;


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 12/24] ceph: add fscrypt ioctls
  2021-07-08  7:30   ` Xiubo Li
@ 2021-07-08 11:26     ` Jeff Layton
  2021-07-08 11:32       ` Xiubo Li
  0 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-07-08 11:26 UTC (permalink / raw)
  To: Xiubo Li, ceph-devel; +Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells

On Thu, 2021-07-08 at 15:30 +0800, Xiubo Li wrote:
> On 6/25/21 9:58 PM, Jeff Layton wrote:
> > We gate most of the ioctls on MDS feature support. The exception is the
> > key removal and status functions that we still want to work if the MDS's
> > were to (inexplicably) lose the feature.
> > 
> > For the set_policy ioctl, we take Fcx caps to ensure that nothing can
> > create files in the directory while the ioctl is running. That should
> > be enough to ensure that the "empty_dir" check is reliable.
> > 
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >   fs/ceph/ioctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 83 insertions(+)
> > 
> > diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
> > index 6e061bf62ad4..477ecc667aee 100644
> > --- a/fs/ceph/ioctl.c
> > +++ b/fs/ceph/ioctl.c
> > @@ -6,6 +6,7 @@
> >   #include "mds_client.h"
> >   #include "ioctl.h"
> >   #include <linux/ceph/striper.h>
> > +#include <linux/fscrypt.h>
> >   
> >   /*
> >    * ioctls
> > @@ -268,8 +269,54 @@ static long ceph_ioctl_syncio(struct file *file)
> >   	return 0;
> >   }
> >   
> > +static int vet_mds_for_fscrypt(struct file *file)
> > +{
> > +	int i, ret = -EOPNOTSUPP;
> > +	struct ceph_mds_client	*mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
> > +
> > +	mutex_lock(&mdsc->mutex);
> > +	for (i = 0; i < mdsc->max_sessions; i++) {
> > +		struct ceph_mds_session *s = mdsc->sessions[i];
> > +
> > +		if (!s)
> > +			continue;
> > +		if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
> > +			ret = 0;
> > +		break;
> > +	}
> > +	mutex_unlock(&mdsc->mutex);
> > +	return ret;
> > +}
> > +
> > +static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
> > +{
> > +	int ret, got = 0;
> > +	struct inode *inode = file_inode(file);
> > +	struct ceph_inode_info *ci = ceph_inode(inode);
> > +
> > +	ret = vet_mds_for_fscrypt(file);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * Ensure we hold these caps so that we _know_ that the rstats check
> > +	 * in the empty_dir check is reliable.
> > +	 */
> > +	ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);
> 
> In the commit comment said it will host the Fsx, but here it is only 
> trying to hold the Fs. Will the Fx really needed ?
> 

No. What we're interested in here is that the directory remains empty
while we're encrypting it. If we hold Fs caps, then no one else can
modify the directory, so this is enough to ensure that.

> 
> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
> > +	if (got)
> > +		ceph_put_cap_refs(ci, got);
> > +
> > +	return ret;
> > +}
> > +
> >   long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >   {
> > +	int ret;
> > +
> >   	dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
> >   	switch (cmd) {
> >   	case CEPH_IOC_GET_LAYOUT:
> > @@ -289,6 +336,42 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >   
> >   	case CEPH_IOC_SYNCIO:
> >   		return ceph_ioctl_syncio(file);
> > +
> > +	case FS_IOC_SET_ENCRYPTION_POLICY:
> > +		return ceph_set_encryption_policy(file, arg);
> > +
> > +	case FS_IOC_GET_ENCRYPTION_POLICY:
> > +		ret = vet_mds_for_fscrypt(file);
> > +		if (ret)
> > +			return ret;
> > +		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
> > +
> > +	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
> > +		ret = vet_mds_for_fscrypt(file);
> > +		if (ret)
> > +			return ret;
> > +		return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
> > +
> > +	case FS_IOC_ADD_ENCRYPTION_KEY:
> > +		ret = vet_mds_for_fscrypt(file);
> > +		if (ret)
> > +			return ret;
> > +		return fscrypt_ioctl_add_key(file, (void __user *)arg);
> > +
> > +	case FS_IOC_REMOVE_ENCRYPTION_KEY:
> > +		return fscrypt_ioctl_remove_key(file, (void __user *)arg);
> > +
> > +	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
> > +		return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg);
> > +
> > +	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
> > +		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
> > +
> > +	case FS_IOC_GET_ENCRYPTION_NONCE:
> > +		ret = vet_mds_for_fscrypt(file);
> > +		if (ret)
> > +			return ret;
> > +		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
> >   	}
> >   
> >   	return -ENOTTY;
> 

-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces
  2021-07-07 14:56           ` Luis Henriques
  2021-07-08  2:56             ` Xiubo Li
@ 2021-07-08 11:26             ` Jeff Layton
  1 sibling, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-08 11:26 UTC (permalink / raw)
  To: Luis Henriques
  Cc: Xiubo Li, ceph-devel, linux-fsdevel, linux-fscrypt, dhowells

On Wed, 2021-07-07 at 15:56 +0100, Luis Henriques wrote:
> On Wed, Jul 07, 2021 at 03:32:13PM +0100, Luis Henriques wrote:
> > On Wed, Jul 07, 2021 at 08:19:25AM -0400, Jeff Layton wrote:
> > > On Wed, 2021-07-07 at 19:19 +0800, Xiubo Li wrote:
> > > > On 7/7/21 6:47 PM, Luis Henriques wrote:
> > > > > On Fri, Jun 25, 2021 at 09:58:16AM -0400, Jeff Layton wrote:
> > > > > > ...and store them in the ceph_inode_info.
> > > > > > 
> > > > > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > > > > ---
> > > > > >   fs/ceph/file.c       |  2 ++
> > > > > >   fs/ceph/inode.c      | 18 ++++++++++++++++++
> > > > > >   fs/ceph/mds_client.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> > > > > >   fs/ceph/mds_client.h |  4 ++++
> > > > > >   fs/ceph/super.h      |  6 ++++++
> > > > > >   5 files changed, 74 insertions(+)
> > > > > > 
> > > > > > diff --git a/fs/ceph/file.c b/fs/ceph/file.c
> > > > > > index 2cda398ba64d..ea0e85075b7b 100644
> > > > > > --- a/fs/ceph/file.c
> > > > > > +++ b/fs/ceph/file.c
> > > > > > @@ -592,6 +592,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
> > > > > >   	iinfo.xattr_data = xattr_buf;
> > > > > >   	memset(iinfo.xattr_data, 0, iinfo.xattr_len);
> > > > > >   
> > > > > > +	/* FIXME: set fscrypt_auth and fscrypt_file */
> > > > > > +
> > > > > >   	in.ino = cpu_to_le64(vino.ino);
> > > > > >   	in.snapid = cpu_to_le64(CEPH_NOSNAP);
> > > > > >   	in.version = cpu_to_le64(1);	// ???
> > > > > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> > > > > > index f62785e4dbcb..b620281ea65b 100644
> > > > > > --- a/fs/ceph/inode.c
> > > > > > +++ b/fs/ceph/inode.c
> > > > > > @@ -611,6 +611,13 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
> > > > > >   
> > > > > >   	ci->i_meta_err = 0;
> > > > > >   
> > > > > > +#ifdef CONFIG_FS_ENCRYPTION
> > > > > > +	ci->fscrypt_auth = NULL;
> > > > > > +	ci->fscrypt_auth_len = 0;
> > > > > > +	ci->fscrypt_file = NULL;
> > > > > > +	ci->fscrypt_file_len = 0;
> > > > > > +#endif
> > > > > > +
> > > > > >   	return &ci->vfs_inode;
> > > > > >   }
> > > > > >   
> > > > > > @@ -619,6 +626,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);
> > > > > >   }
> > > > > >   
> > > > > > @@ -1021,6 +1031,14 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
> > > > > >   		xattr_blob = NULL;
> > > > > >   	}
> > > > > >   
> > > > > > +	if (iinfo->fscrypt_auth_len && !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);
> > > > > > +	}
> > > > > I think we also need to free iinfo->fscrypt_auth here if ci->fscrypt_auth
> > > > > is already set.  Something like:
> > > > > 
> > > > > 	if (iinfo->fscrypt_auth_len) {
> > > > > 		if (!ci->fscrypt_auth) {
> > > > > 			...
> > > > > 		} else {
> > > > > 			kfree(iinfo->fscrypt_auth);
> > > > > 			iinfo->fscrypt_auth = NULL;
> > > > > 		}
> > > > > 	}
> > > > > 
> > > > IMO, this should be okay because it will be freed in 
> > > > destroy_reply_info() when putting the request.
> > > > 
> > > > 
> > > 
> > > Yes. All of that should get cleaned up with the request.
> > 
> > Hmm... ok, so maybe I missed something because I *did* saw kmemleak
> > complaining.  Maybe it was on the READDIR path.  /me goes look again.
> 
> Ah, that was indeed the problem.  So, here's a quick hack to fix
> destroy_reply_info() so that it also frees the extra memory from READDIR:
> 
> @@ -686,12 +686,23 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
>  
>  static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info)
>  {
> +	int i = 0;
> +
>  	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;
> +		if (rde->inode.fscrypt_auth_len)
> +			kfree(rde->inode.fscrypt_auth);
> +	}
> +	       
>  	free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size));
>  }
>  
> 

Well spotted! I'll plan to incorporate a fix like that soon.
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 12/24] ceph: add fscrypt ioctls
  2021-07-08 11:26     ` Jeff Layton
@ 2021-07-08 11:32       ` Xiubo Li
  0 siblings, 0 replies; 59+ messages in thread
From: Xiubo Li @ 2021-07-08 11:32 UTC (permalink / raw)
  To: Jeff Layton, ceph-devel
  Cc: lhenriques, linux-fsdevel, linux-fscrypt, dhowells


On 7/8/21 7:26 PM, Jeff Layton wrote:
> On Thu, 2021-07-08 at 15:30 +0800, Xiubo Li wrote:
>> On 6/25/21 9:58 PM, Jeff Layton wrote:
>>> We gate most of the ioctls on MDS feature support. The exception is the
>>> key removal and status functions that we still want to work if the MDS's
>>> were to (inexplicably) lose the feature.
>>>
>>> For the set_policy ioctl, we take Fcx caps to ensure that nothing can
>>> create files in the directory while the ioctl is running. That should
>>> be enough to ensure that the "empty_dir" check is reliable.
>>>
>>> Signed-off-by: Jeff Layton <jlayton@kernel.org>
>>> ---
>>>    fs/ceph/ioctl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>    1 file changed, 83 insertions(+)
>>>
>>> diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
>>> index 6e061bf62ad4..477ecc667aee 100644
>>> --- a/fs/ceph/ioctl.c
>>> +++ b/fs/ceph/ioctl.c
>>> @@ -6,6 +6,7 @@
>>>    #include "mds_client.h"
>>>    #include "ioctl.h"
>>>    #include <linux/ceph/striper.h>
>>> +#include <linux/fscrypt.h>
>>>    
>>>    /*
>>>     * ioctls
>>> @@ -268,8 +269,54 @@ static long ceph_ioctl_syncio(struct file *file)
>>>    	return 0;
>>>    }
>>>    
>>> +static int vet_mds_for_fscrypt(struct file *file)
>>> +{
>>> +	int i, ret = -EOPNOTSUPP;
>>> +	struct ceph_mds_client	*mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
>>> +
>>> +	mutex_lock(&mdsc->mutex);
>>> +	for (i = 0; i < mdsc->max_sessions; i++) {
>>> +		struct ceph_mds_session *s = mdsc->sessions[i];
>>> +
>>> +		if (!s)
>>> +			continue;
>>> +		if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
>>> +			ret = 0;
>>> +		break;
>>> +	}
>>> +	mutex_unlock(&mdsc->mutex);
>>> +	return ret;
>>> +}
>>> +
>>> +static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
>>> +{
>>> +	int ret, got = 0;
>>> +	struct inode *inode = file_inode(file);
>>> +	struct ceph_inode_info *ci = ceph_inode(inode);
>>> +
>>> +	ret = vet_mds_for_fscrypt(file);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * Ensure we hold these caps so that we _know_ that the rstats check
>>> +	 * in the empty_dir check is reliable.
>>> +	 */
>>> +	ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);
>> In the commit comment said it will host the Fsx, but here it is only
>> trying to hold the Fs. Will the Fx really needed ?
>>
> No. What we're interested in here is that the directory remains empty
> while we're encrypting it. If we hold Fs caps, then no one else can
> modify the directory, so this is enough to ensure that.

Yeah, this is what I thought.

Thanks


>>
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
>>> +	if (got)
>>> +		ceph_put_cap_refs(ci, got);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>>    long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>    {
>>> +	int ret;
>>> +
>>>    	dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
>>>    	switch (cmd) {
>>>    	case CEPH_IOC_GET_LAYOUT:
>>> @@ -289,6 +336,42 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>>    
>>>    	case CEPH_IOC_SYNCIO:
>>>    		return ceph_ioctl_syncio(file);
>>> +
>>> +	case FS_IOC_SET_ENCRYPTION_POLICY:
>>> +		return ceph_set_encryption_policy(file, arg);
>>> +
>>> +	case FS_IOC_GET_ENCRYPTION_POLICY:
>>> +		ret = vet_mds_for_fscrypt(file);
>>> +		if (ret)
>>> +			return ret;
>>> +		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
>>> +		ret = vet_mds_for_fscrypt(file);
>>> +		if (ret)
>>> +			return ret;
>>> +		return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_ADD_ENCRYPTION_KEY:
>>> +		ret = vet_mds_for_fscrypt(file);
>>> +		if (ret)
>>> +			return ret;
>>> +		return fscrypt_ioctl_add_key(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_REMOVE_ENCRYPTION_KEY:
>>> +		return fscrypt_ioctl_remove_key(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
>>> +		return fscrypt_ioctl_remove_key_all_users(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
>>> +		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
>>> +
>>> +	case FS_IOC_GET_ENCRYPTION_NONCE:
>>> +		ret = vet_mds_for_fscrypt(file);
>>> +		if (ret)
>>> +			return ret;
>>> +		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
>>>    	}
>>>    
>>>    	return -ENOTTY;


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  2021-06-25 13:58 ` [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode Jeff Layton
@ 2021-07-11 17:40   ` Eric Biggers
  2021-07-12 11:55     ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Eric Biggers @ 2021-07-11 17:40 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

Some nits about comments:

On Fri, Jun 25, 2021 at 09:58:12AM -0400, Jeff Layton wrote:
> diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> index 6ca7d16593ff..32b1f50433ba 100644
> --- a/fs/crypto/fname.c
> +++ b/fs/crypto/fname.c
> @@ -178,10 +178,8 @@ static int fname_decrypt(const struct inode *inode,
>  static const char lookup_table[65] =
>  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
>  
> -#define BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
> -
>  /**
> - * base64_encode() - base64-encode some bytes
> + * fscrypt_base64_encode() - base64-encode some bytes
>   * @src: the bytes to encode
>   * @len: number of bytes to encode
>   * @dst: (output) the base64-encoded string.  Not NUL-terminated.
>   *
>   * Encodes the input string using characters from the set [A-Za-z0-9+,].
>   * The encoded string is roughly 4/3 times the size of the input string.
>   *
>   * Return: length of the encoded string
>   */
> -static int base64_encode(const u8 *src, int len, char *dst)
> +int fscrypt_base64_encode(const u8 *src, int len, char *dst)

As this function will be used more widely, this comment should be fixed to be
more precise.  "Roughly 4/3" isn't precise; it's actually exactly
FSCRYPT_BASE64_CHARS(len), right?  The following would be better:

 * Encode the input bytes using characters from the set [A-Za-z0-9+,].
 *
 * Return: length of the encoded string.  This will be equal to
 *         FSCRYPT_BASE64_CHARS(len).

> +/**
> + * fscrypt_base64_decode() - base64-decode some bytes
> + * @src: the bytes to decode
> + * @len: number of bytes to decode
> + * @dst: (output) decoded binary data

It's a bit confusing to talk about decoding "bytes"; it's really a string.
How about:

 * fscrypt_base64_decode() - base64-decode a string
 * @src: the string to decode
 * @len: length of the source string, in bytes
 * @dst: (output) decoded binary data
 *
 * Decode a string that was previously encoded using fscrypt_base64_encode().
 * The string doesn't need to be NUL-terminated.

> + * Return: length of the decoded binary data

Also the error return values should be documented, e.g.:

 * Return: length of the decoded binary data, or a negative number if the source
 *         string isn't a valid base64-encoded string.

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size
  2021-06-25 13:58 ` [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size Jeff Layton
@ 2021-07-11 17:43   ` Eric Biggers
  0 siblings, 0 replies; 59+ messages in thread
From: Eric Biggers @ 2021-07-11 17:43 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:13AM -0400, Jeff Layton wrote:
> +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
> +				  u32 max_len, u32 *encrypted_len_ret)
> +{
> +	return __fscrypt_fname_encrypted_size(&inode->i_crypt_info->ci_policy,
> +					      orig_len, max_len,
> +					      encrypted_len_ret);
> +}
> +EXPORT_SYMBOL(fscrypt_fname_encrypted_size);

This function could use a kerneldoc comment now that it will be exported.

> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
> index e300f6145ddc..b5c31baaa8bf 100644
> --- a/include/linux/fscrypt.h
> +++ b/include/linux/fscrypt.h
> @@ -212,6 +212,10 @@ int fscrypt_drop_inode(struct inode *inode);
>  /* fname.c */
>  int fscrypt_base64_encode(const u8 *src, int len, char *dst);
>  int fscrypt_base64_decode(const char *src, int len, u8 *dst);
> +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
> +				  u32 max_len, u32 *encrypted_len_ret);
> +int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
> +			  u8 *out, unsigned int olen);
>  int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname,
>  			   int lookup, struct fscrypt_name *fname);

Generally I try to keep declarations in .h files in the same order as the
corresponding definitions in .c files.

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode
  2021-06-25 13:58 ` [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode Jeff Layton
@ 2021-07-11 17:44   ` Eric Biggers
  0 siblings, 0 replies; 59+ messages in thread
From: Eric Biggers @ 2021-07-11 17:44 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:14AM -0400, Jeff Layton wrote:
> Most filesystems just call fscrypt_set_context on new inodes, which
> usually causes a setxattr. That's a bit late for ceph, which can send
> along a full set of attributes with the create request.
> 
> Doing so allows it to avoid race windows that where the new inode could
> be seen by other clients without the crypto context attached. It also
> avoids the separate round trip to the server.
> 
> Refactor the fscrypt code a bit to allow us to create a new crypto
> context, attach it to the inode, and write it to the buffer, but without
> calling set_context on it. ceph can later use this to marshal the
> context into the attributes we send along with the create request.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/crypto/policy.c      | 34 ++++++++++++++++++++++++++++------
>  include/linux/fscrypt.h |  1 +
>  2 files changed, 29 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
> index ed3d623724cd..6a895a31560f 100644
> --- a/fs/crypto/policy.c
> +++ b/fs/crypto/policy.c
> @@ -664,6 +664,31 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir)
>  	return fscrypt_get_dummy_policy(dir->i_sb);
>  }
>  
> +/**
> + * fscrypt_context_for_new_inode() - create an encryption context for a new inode
> + * @ctx: where context should be written
> + * @inode: inode from which to fetch policy and nonce
> + *
> + * Given an in-core "prepared" (via fscrypt_prepare_new_inode) inode,
> + * generate a new context and write it to ctx. ctx _must_ be at least
> + * FSCRYPT_SET_CONTEXT_MAX_SIZE bytes.
> + *
> + * Returns size of the resulting context or a negative error code.
> + */
> +int fscrypt_context_for_new_inode(void *ctx, struct inode *inode)

This generates a kerneldoc warning because "Returns" should be "Return:".

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path
  2021-06-25 13:58 ` [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path Jeff Layton
@ 2021-07-11 22:53   ` Eric Biggers
  2021-07-12 12:36     ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Eric Biggers @ 2021-07-11 22:53 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:25AM -0400, Jeff Layton wrote:
> +/*
> + * We want to encrypt filenames when creating them, but the encrypted
> + * versions of those names may have illegal characters in them. To mitigate
> + * that, we base64 encode them, but that gives us a result that can exceed
> + * NAME_MAX.
> + *
> + * Follow a similar scheme to fscrypt itself, and cap the filename to a
> + * smaller size. If the cleartext name is longer than the value below, then
> + * sha256 hash the remaining bytes.
> + *
> + * 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255)
> + */
> +#define CEPH_NOHASH_NAME_MAX (189 - SHA256_DIGEST_SIZE)

Shouldn't this say "If the ciphertext name is longer than the value below", not
"If the cleartext name is longer than the value below"?

It would also be helpful if the above comment mentioned that when the hashing is
done, the real encrypted name is stored separately.

> +#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
> +static int encode_encrypted_fname(const struct inode *parent, struct dentry *dentry, char *buf)
> +{
> +	u32 len;
> +	int elen;
> +	int ret;
> +	u8 *cryptbuf;
> +
> +	WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
> +
> +	/*
> +	 * convert cleartext dentry name to ciphertext
> +	 * if result is longer than CEPH_NOKEY_NAME_MAX,
> +	 * sha256 the remaining bytes
> +	 *
> +	 * See: fscrypt_setup_filename
> +	 */
> +	if (!fscrypt_fname_encrypted_size(parent, dentry->d_name.len, NAME_MAX, &len))
> +		return -ENAMETOOLONG;
> +
> +	/* If we have to hash the end, then we need a full-length buffer */
> +	if (len > CEPH_NOHASH_NAME_MAX)
> +		len = NAME_MAX;
> +
> +	cryptbuf = kmalloc(len, GFP_KERNEL);
> +	if (!cryptbuf)
> +		return -ENOMEM;
> +
> +	ret = fscrypt_fname_encrypt(parent, &dentry->d_name, cryptbuf, len);
> +	if (ret) {
> +		kfree(cryptbuf);
> +		return ret;
> +	}
> +
> +	/* hash the end if the name is long enough */
> +	if (len > CEPH_NOHASH_NAME_MAX) {
> +		u8 hash[SHA256_DIGEST_SIZE];
> +		u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
> +
> +		/* hash the extra bytes and overwrite crypttext beyond that point with it */
> +		sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
> +		memcpy(extra, hash, SHA256_DIGEST_SIZE);
> +		len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
> +	}

When the ciphertext name is longer than CEPH_NOHASH_NAME_MAX, why is the
filename being padded all the way to NAME_MAX?  That can produce a totally
different ciphertext from that produced by get_fscrypt_altname() in the next
patch.

The logical thing to do would be to do the encryption in the same way as
get_fscrypt_altname(), and then replace any bytes beyond CEPH_NOHASH_NAME_MAX
with their hash.

> +
> +	/* base64 encode the encrypted name */
> +	elen = fscrypt_base64_encode(cryptbuf, len, buf);
> +	kfree(cryptbuf);
> +	dout("base64-encoded ciphertext name = %.*s\n", len, buf);
> +	return elen;

The argument to dout() should be elen, not len.

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-06-25 13:58 ` [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c Jeff Layton
  2021-07-07  7:20   ` Xiubo Li
@ 2021-07-11 23:00   ` Eric Biggers
  2021-07-12 13:22     ` Jeff Layton
  1 sibling, 1 reply; 59+ messages in thread
From: Eric Biggers @ 2021-07-11 23:00 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Fri, Jun 25, 2021 at 09:58:17AM -0400, Jeff Layton wrote:
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>  fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 49 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
> index 038f59cc4250..1be6c5148700 100644
> --- a/fs/ceph/caps.c
> +++ b/fs/ceph/caps.c
> @@ -13,6 +13,7 @@
>  #include "super.h"
>  #include "mds_client.h"
>  #include "cache.h"
> +#include "crypto.h"
>  #include <linux/ceph/decode.h>
>  #include <linux/ceph/messenger.h>
>  
> @@ -1229,15 +1230,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
>  };

The naming of these is confusing to me.  If these are the fscrypt context and
the original file size, why aren't they called something like fscrypt_context
and fscrypt_file_size?

Also does the file size really need to be variable-length, or could it just be a
64-bit integer?

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  2021-07-11 17:40   ` Eric Biggers
@ 2021-07-12 11:55     ` Jeff Layton
  2021-07-12 14:22       ` Eric Biggers
  0 siblings, 1 reply; 59+ messages in thread
From: Jeff Layton @ 2021-07-12 11:55 UTC (permalink / raw)
  To: Eric Biggers
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Sun, 2021-07-11 at 12:40 -0500, Eric Biggers wrote:
> Some nits about comments:
> 
> On Fri, Jun 25, 2021 at 09:58:12AM -0400, Jeff Layton wrote:
> > diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> > index 6ca7d16593ff..32b1f50433ba 100644
> > --- a/fs/crypto/fname.c
> > +++ b/fs/crypto/fname.c
> > @@ -178,10 +178,8 @@ static int fname_decrypt(const struct inode *inode,
> >  static const char lookup_table[65] =
> >  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
> >  
> > -#define BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
> > -
> >  /**
> > - * base64_encode() - base64-encode some bytes
> > + * fscrypt_base64_encode() - base64-encode some bytes
> >   * @src: the bytes to encode
> >   * @len: number of bytes to encode
> >   * @dst: (output) the base64-encoded string.  Not NUL-terminated.
> >   *
> >   * Encodes the input string using characters from the set [A-Za-z0-9+,].
> >   * The encoded string is roughly 4/3 times the size of the input string.
> >   *
> >   * Return: length of the encoded string
> >   */
> > -static int base64_encode(const u8 *src, int len, char *dst)
> > +int fscrypt_base64_encode(const u8 *src, int len, char *dst)
> 
> As this function will be used more widely, this comment should be fixed to be
> more precise.  "Roughly 4/3" isn't precise; it's actually exactly
> FSCRYPT_BASE64_CHARS(len), right?  The following would be better:
> 
>  * Encode the input bytes using characters from the set [A-Za-z0-9+,].
>  *
>  * Return: length of the encoded string.  This will be equal to
>  *         FSCRYPT_BASE64_CHARS(len).
> 

I'm not certain, but I thought that FSCRYPT_BASE64_CHARS gave you a
worst-case estimate of the inflation. This returns the actual length of
the resulting encoded string, which may be less than
FSCRYPT_BASE64_CHARS(len).

> > +/**
> > + * fscrypt_base64_decode() - base64-decode some bytes
> > + * @src: the bytes to decode
> > + * @len: number of bytes to decode
> > + * @dst: (output) decoded binary data
> 
> It's a bit confusing to talk about decoding "bytes"; it's really a string.
> How about:
> 
>  * fscrypt_base64_decode() - base64-decode a string
>  * @src: the string to decode
>  * @len: length of the source string, in bytes
>  * @dst: (output) decoded binary data
>  *
>  * Decode a string that was previously encoded using fscrypt_base64_encode().
>  * The string doesn't need to be NUL-terminated.
> 
> > + * Return: length of the decoded binary data
> 
> Also the error return values should be documented, e.g.:
> 
>  * Return: length of the decoded binary data, or a negative number if the source
>  *         string isn't a valid base64-encoded string.
> 

That update looks reasonable.

Thanks,
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path
  2021-07-11 22:53   ` Eric Biggers
@ 2021-07-12 12:36     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-12 12:36 UTC (permalink / raw)
  To: Eric Biggers
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Sun, 2021-07-11 at 17:53 -0500, Eric Biggers wrote:
> On Fri, Jun 25, 2021 at 09:58:25AM -0400, Jeff Layton wrote:
> > +/*
> > + * We want to encrypt filenames when creating them, but the encrypted
> > + * versions of those names may have illegal characters in them. To mitigate
> > + * that, we base64 encode them, but that gives us a result that can exceed
> > + * NAME_MAX.
> > + *
> > + * Follow a similar scheme to fscrypt itself, and cap the filename to a
> > + * smaller size. If the cleartext name is longer than the value below, then
> > + * sha256 hash the remaining bytes.
> > + *
> > + * 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255)
> > + */
> > +#define CEPH_NOHASH_NAME_MAX (189 - SHA256_DIGEST_SIZE)
> 
> Shouldn't this say "If the ciphertext name is longer than the value below", not
> "If the cleartext name is longer than the value below"?
> 
> It would also be helpful if the above comment mentioned that when the hashing is
> done, the real encrypted name is stored separately.
> 
> > +#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
> > +static int encode_encrypted_fname(const struct inode *parent, struct dentry *dentry, char *buf)
> > +{
> > +	u32 len;
> > +	int elen;
> > +	int ret;
> > +	u8 *cryptbuf;
> > +
> > +	WARN_ON_ONCE(!fscrypt_has_encryption_key(parent));
> > +
> > +	/*
> > +	 * convert cleartext dentry name to ciphertext
> > +	 * if result is longer than CEPH_NOKEY_NAME_MAX,
> > +	 * sha256 the remaining bytes
> > +	 *
> > +	 * See: fscrypt_setup_filename
> > +	 */
> > +	if (!fscrypt_fname_encrypted_size(parent, dentry->d_name.len, NAME_MAX, &len))
> > +		return -ENAMETOOLONG;
> > +
> > +	/* If we have to hash the end, then we need a full-length buffer */
> > +	if (len > CEPH_NOHASH_NAME_MAX)
> > +		len = NAME_MAX;
> > +
> > +	cryptbuf = kmalloc(len, GFP_KERNEL);
> > +	if (!cryptbuf)
> > +		return -ENOMEM;
> > +
> > +	ret = fscrypt_fname_encrypt(parent, &dentry->d_name, cryptbuf, len);
> > +	if (ret) {
> > +		kfree(cryptbuf);
> > +		return ret;
> > +	}
> > +
> > +	/* hash the end if the name is long enough */
> > +	if (len > CEPH_NOHASH_NAME_MAX) {
> > +		u8 hash[SHA256_DIGEST_SIZE];
> > +		u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
> > +
> > +		/* hash the extra bytes and overwrite crypttext beyond that point with it */
> > +		sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
> > +		memcpy(extra, hash, SHA256_DIGEST_SIZE);
> > +		len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
> > +	}
> 
> When the ciphertext name is longer than CEPH_NOHASH_NAME_MAX, why is the
> filename being padded all the way to NAME_MAX?  That can produce a totally
> different ciphertext from that produced by get_fscrypt_altname() in the next
> patch.
> 

Oh, I misunderstood the meaning of the last parameter to
fscrypt_fname_encrypt. I had thought that was the length of the target
buffer, but it's not -- it's the length of the resulting filename (which
we need to precompute). I'll fix that up.

> The logical thing to do would be to do the encryption in the same way as
> get_fscrypt_altname(), and then replace any bytes beyond CEPH_NOHASH_NAME_MAX
> with their hash.
> 

That might make more sense.

> > +
> > +	/* base64 encode the encrypted name */
> > +	elen = fscrypt_base64_encode(cryptbuf, len, buf);
> > +	kfree(cryptbuf);
> > +	dout("base64-encoded ciphertext name = %.*s\n", len, buf);
> > +	return elen;
> 
> The argument to dout() should be elen, not len.
> 

Will fix, thanks.
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c
  2021-07-11 23:00   ` Eric Biggers
@ 2021-07-12 13:22     ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-12 13:22 UTC (permalink / raw)
  To: Eric Biggers
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Sun, 2021-07-11 at 18:00 -0500, Eric Biggers wrote:
> On Fri, Jun 25, 2021 at 09:58:17AM -0400, Jeff Layton wrote:
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > ---
> >  fs/ceph/caps.c | 62 +++++++++++++++++++++++++++++++++++++++-----------
> >  1 file changed, 49 insertions(+), 13 deletions(-)
> > 
> > diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
> > index 038f59cc4250..1be6c5148700 100644
> > --- a/fs/ceph/caps.c
> > +++ b/fs/ceph/caps.c
> > @@ -13,6 +13,7 @@
> >  #include "super.h"
> >  #include "mds_client.h"
> >  #include "cache.h"
> > +#include "crypto.h"
> >  #include <linux/ceph/decode.h>
> >  #include <linux/ceph/messenger.h>
> >  
> > @@ -1229,15 +1230,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
> >  };
> 
> The naming of these is confusing to me.  If these are the fscrypt context and
> the original file size, why aren't they called something like fscrypt_context
> and fscrypt_file_size?
> 
> Also does the file size really need to be variable-length, or could it just be a
> 64-bit integer?
> 

Fscrypt is really a kernel client-side feature. Both of these new fields
are treated as opaque by the MDS and are wholly managed by the client.

We need two fields because they are governed by different cephfs
capabilities (aka "caps"). AUTH caps for the context and FILE caps for
the size. So we have two new fields -- fscrypt_file and fscrypt_auth. 

The size could be a __le64 or something, but I think it makes sense to
allow it to be opaque as we aren't certain what other info we might want
to keep in there. We might also want to encrypt the fscrypt_file field
to cloak the true size of a file from anyone without the key.

Now, all that said, the fact that the MDS largely handles truncation
poses some special challenges for the content encryption piece. We may
ultimately end up making this more special-purpose than it is now.
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  2021-07-12 11:55     ` Jeff Layton
@ 2021-07-12 14:22       ` Eric Biggers
  2021-07-12 14:32         ` Jeff Layton
  0 siblings, 1 reply; 59+ messages in thread
From: Eric Biggers @ 2021-07-12 14:22 UTC (permalink / raw)
  To: Jeff Layton
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Mon, Jul 12, 2021 at 07:55:37AM -0400, Jeff Layton wrote:
> On Sun, 2021-07-11 at 12:40 -0500, Eric Biggers wrote:
> > Some nits about comments:
> > 
> > On Fri, Jun 25, 2021 at 09:58:12AM -0400, Jeff Layton wrote:
> > > diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> > > index 6ca7d16593ff..32b1f50433ba 100644
> > > --- a/fs/crypto/fname.c
> > > +++ b/fs/crypto/fname.c
> > > @@ -178,10 +178,8 @@ static int fname_decrypt(const struct inode *inode,
> > >  static const char lookup_table[65] =
> > >  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
> > >  
> > > -#define BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
> > > -
> > >  /**
> > > - * base64_encode() - base64-encode some bytes
> > > + * fscrypt_base64_encode() - base64-encode some bytes
> > >   * @src: the bytes to encode
> > >   * @len: number of bytes to encode
> > >   * @dst: (output) the base64-encoded string.  Not NUL-terminated.
> > >   *
> > >   * Encodes the input string using characters from the set [A-Za-z0-9+,].
> > >   * The encoded string is roughly 4/3 times the size of the input string.
> > >   *
> > >   * Return: length of the encoded string
> > >   */
> > > -static int base64_encode(const u8 *src, int len, char *dst)
> > > +int fscrypt_base64_encode(const u8 *src, int len, char *dst)
> > 
> > As this function will be used more widely, this comment should be fixed to be
> > more precise.  "Roughly 4/3" isn't precise; it's actually exactly
> > FSCRYPT_BASE64_CHARS(len), right?  The following would be better:
> > 
> >  * Encode the input bytes using characters from the set [A-Za-z0-9+,].
> >  *
> >  * Return: length of the encoded string.  This will be equal to
> >  *         FSCRYPT_BASE64_CHARS(len).
> > 
> 
> I'm not certain, but I thought that FSCRYPT_BASE64_CHARS gave you a
> worst-case estimate of the inflation. This returns the actual length of
> the resulting encoded string, which may be less than
> FSCRYPT_BASE64_CHARS(len).
> 

As far as I can tell, it's the exact amount.

- Eric

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode
  2021-07-12 14:22       ` Eric Biggers
@ 2021-07-12 14:32         ` Jeff Layton
  0 siblings, 0 replies; 59+ messages in thread
From: Jeff Layton @ 2021-07-12 14:32 UTC (permalink / raw)
  To: Eric Biggers
  Cc: ceph-devel, lhenriques, xiubli, linux-fsdevel, linux-fscrypt, dhowells

On Mon, 2021-07-12 at 09:22 -0500, Eric Biggers wrote:
> On Mon, Jul 12, 2021 at 07:55:37AM -0400, Jeff Layton wrote:
> > On Sun, 2021-07-11 at 12:40 -0500, Eric Biggers wrote:
> > > Some nits about comments:
> > > 
> > > On Fri, Jun 25, 2021 at 09:58:12AM -0400, Jeff Layton wrote:
> > > > diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> > > > index 6ca7d16593ff..32b1f50433ba 100644
> > > > --- a/fs/crypto/fname.c
> > > > +++ b/fs/crypto/fname.c
> > > > @@ -178,10 +178,8 @@ static int fname_decrypt(const struct inode *inode,
> > > >  static const char lookup_table[65] =
> > > >  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
> > > >  
> > > > -#define BASE64_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)
> > > > -
> > > >  /**
> > > > - * base64_encode() - base64-encode some bytes
> > > > + * fscrypt_base64_encode() - base64-encode some bytes
> > > >   * @src: the bytes to encode
> > > >   * @len: number of bytes to encode
> > > >   * @dst: (output) the base64-encoded string.  Not NUL-terminated.
> > > >   *
> > > >   * Encodes the input string using characters from the set [A-Za-z0-9+,].
> > > >   * The encoded string is roughly 4/3 times the size of the input string.
> > > >   *
> > > >   * Return: length of the encoded string
> > > >   */
> > > > -static int base64_encode(const u8 *src, int len, char *dst)
> > > > +int fscrypt_base64_encode(const u8 *src, int len, char *dst)
> > > 
> > > As this function will be used more widely, this comment should be fixed to be
> > > more precise.  "Roughly 4/3" isn't precise; it's actually exactly
> > > FSCRYPT_BASE64_CHARS(len), right?  The following would be better:
> > > 
> > >  * Encode the input bytes using characters from the set [A-Za-z0-9+,].
> > >  *
> > >  * Return: length of the encoded string.  This will be equal to
> > >  *         FSCRYPT_BASE64_CHARS(len).
> > > 
> > 
> > I'm not certain, but I thought that FSCRYPT_BASE64_CHARS gave you a
> > worst-case estimate of the inflation. This returns the actual length of
> > the resulting encoded string, which may be less than
> > FSCRYPT_BASE64_CHARS(len).
> > 
> 
> As far as I can tell, it's the exact amount.

Yeah, now that I went back and re-read the code, I think you're right.
I'll fix the comment.

Thanks,
-- 
Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 59+ messages in thread

end of thread, other threads:[~2021-07-12 14:32 UTC | newest]

Thread overview: 59+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-25 13:58 [RFC PATCH v7 00/24] ceph+fscrypt: context, filename and symlink support Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 01/24] vfs: export new_inode_pseudo Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 02/24] fscrypt: export fscrypt_base64_encode and fscrypt_base64_decode Jeff Layton
2021-07-11 17:40   ` Eric Biggers
2021-07-12 11:55     ` Jeff Layton
2021-07-12 14:22       ` Eric Biggers
2021-07-12 14:32         ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 03/24] fscrypt: export fscrypt_fname_encrypt and fscrypt_fname_encrypted_size Jeff Layton
2021-07-11 17:43   ` Eric Biggers
2021-06-25 13:58 ` [RFC PATCH v7 04/24] fscrypt: add fscrypt_context_for_new_inode Jeff Layton
2021-07-11 17:44   ` Eric Biggers
2021-06-25 13:58 ` [RFC PATCH v7 05/24] ceph: preallocate inode for ops that may create one Jeff Layton
2021-07-07  3:37   ` Xiubo Li
2021-07-07 12:05     ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 06/24] ceph: parse new fscrypt_auth and fscrypt_file fields in inode traces Jeff Layton
2021-07-07  3:53   ` Xiubo Li
2021-07-07 12:09     ` Jeff Layton
2021-07-07 12:46       ` Xiubo Li
2021-07-07 10:47   ` Luis Henriques
2021-07-07 11:19     ` Xiubo Li
2021-07-07 12:19       ` Jeff Layton
2021-07-07 14:32         ` Luis Henriques
2021-07-07 14:56           ` Luis Henriques
2021-07-08  2:56             ` Xiubo Li
2021-07-08 11:26             ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 07/24] ceph: add fscrypt_* handling to caps.c Jeff Layton
2021-07-07  7:20   ` Xiubo Li
2021-07-07 12:02     ` Jeff Layton
2021-07-07 12:47       ` Xiubo Li
2021-07-11 23:00   ` Eric Biggers
2021-07-12 13:22     ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 08/24] ceph: add ability to set fscrypt_auth via setattr Jeff Layton
2021-07-07  8:11   ` Xiubo Li
2021-07-07 12:10     ` Jeff Layton
2021-07-07 10:47   ` Luis Henriques
2021-07-07 12:25     ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 09/24] ceph: crypto context handling for ceph Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 10/24] ceph: implement -o test_dummy_encryption mount option Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 11/24] ceph: add routine to create fscrypt context prior to RPC Jeff Layton
2021-07-07 10:48   ` Luis Henriques
2021-07-07 12:29     ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 12/24] ceph: add fscrypt ioctls Jeff Layton
2021-07-08  7:30   ` Xiubo Li
2021-07-08 11:26     ` Jeff Layton
2021-07-08 11:32       ` Xiubo Li
2021-06-25 13:58 ` [RFC PATCH v7 13/24] ceph: decode alternate_name in lease info Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 14/24] ceph: make ceph_msdc_build_path use ref-walk Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 15/24] ceph: add encrypted fname handling to ceph_mdsc_build_path Jeff Layton
2021-07-11 22:53   ` Eric Biggers
2021-07-12 12:36     ` Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 16/24] ceph: send altname in MClientRequest Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 17/24] ceph: properly set DCACHE_NOKEY_NAME flag in lookup Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 18/24] ceph: make d_revalidate call fscrypt revalidator for encrypted dentries Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 19/24] ceph: add helpers for converting names for userland presentation Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 20/24] ceph: add fscrypt support to ceph_fill_trace Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 21/24] ceph: add support to readdir for encrypted filenames Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 22/24] ceph: create symlinks with encrypted and base64-encoded targets Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 23/24] ceph: make ceph_get_name decrypt filenames Jeff Layton
2021-06-25 13:58 ` [RFC PATCH v7 24/24] ceph: add a new ceph.fscrypt.auth vxattr Jeff Layton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).