public inbox for linux-btrfs@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/13] fscrypt: add extent encryption
@ 2023-09-02  5:54 Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 01/13] fscrypt: factor getting info for a specific block Sweet Tea Dorminy
                   ` (14 more replies)
  0 siblings, 15 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

This is a replacement for the former changeset (previously v3). This
doesn't reflect all the smaller feedback on v3: it's an attempt to address
the major points of giving extents and inodes different objects, and to
clearly define lightweight and heavyweight extent contexts. Currently,
with minor changes to the btrfs patchset building on it, it passes
tests.

Hopefully I understood the proposed alternate design and this is indeed
more elegant, reviewable, and maintainable. 

This applies atop [3], which itself is based on kdave/misc-next.

Changelog:
RFC:
 - Split fscrypt_info into a general fscrypt_common_info, an
   inode-specific fscrypt_info, and an extent-specific
   fscrypt_extent_info. All external interfaces use either an inode or
   extent specific structure; most internal functions handle the common
   structure.
 - Tried to fix up more places to refer to infos instead of inodes and
   files.
 - Changed to use lightweight extent contexts containing just a nonce,
   and then a following change to do heavyweight extent contexts
   identical to inode contexts, so they're easily comparable.
 - Dropped factoring lock_master_key() and adding super block pointer to
   fscrypt_info changes, as they didn't seem necessary.
 - Temporarily dropped optimization where leaf inodes with extents don't
   have on-disk fscrypt_contexts. It's a convenient optimization and
   affects btrfs disk format, but it's not very big and not strictly
   needed to check whether the new structural arrangement is better.

v3:
 - Added four additional changes:
   - soft-deleting keys that extent infos might later need to use, so
     the behavior of an open file after key removal matches inode-based
     fscrypt.
   - a set of changes to allow asynchronous info freeing for extents,
     necessary due to locking constraints in btrfs.
 - https://lore.kernel.org/linux-fscrypt/cover.1691505882.git.sweettea-kernel@dorminy.me/

v2: 
 - https://lore.kernel.org/linux-fscrypt/cover.1688927487.git.sweettea-kernel@dorminy.me/T/#t


[3] https://lore.kernel.org/linux-fscrypt/cover.1691505830.git.sweettea-kernel@dorminy.me/

Sweet Tea Dorminy (13):
  fscrypt: factor getting info for a specific block
  fscrypt: adjust effective lblks based on extents
  fscrypt: move function call warning of busy inodes
  fscrypt: split fscrypt_info into general and inode specific parts
  fscrypt: add creation/usage/freeing of per-extent infos
  fscrypt: allow load/save of extent contexts
  fscrypt: store full fscrypt_contexts for each extent
  fscrypt: save session key credentials for extent infos
  fscrypt: revamp key removal for extent encryption
  fscrypt: allow multiple extents to reference one info
  fscrypt: cache list of inlinecrypt devices
  fscrypt: allow asynchronous info freeing
  fscrypt: update documentation for per-extent keys

 Documentation/filesystems/fscrypt.rst |  43 ++-
 fs/crypto/crypto.c                    |  48 ++-
 fs/crypto/fname.c                     |  13 +-
 fs/crypto/fscrypt_private.h           | 245 +++++++++---
 fs/crypto/hooks.c                     |   6 +-
 fs/crypto/inline_crypt.c              |  93 +++--
 fs/crypto/keyring.c                   | 110 +++---
 fs/crypto/keysetup.c                  | 530 ++++++++++++++++++++------
 fs/crypto/keysetup_v1.c               |  77 ++--
 fs/crypto/policy.c                    |  34 +-
 include/linux/fscrypt.h               |  60 +++
 11 files changed, 919 insertions(+), 340 deletions(-)


base-commit: 764e1420e0806a3536b53b4c52c1b08ae8425f7e
-- 
2.41.0


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

* [RFC PATCH 01/13] fscrypt: factor getting info for a specific block
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 02/13] fscrypt: adjust effective lblks based on extents Sweet Tea Dorminy
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

For filesystems using extent-based encryption, the content of each
extent will be encrypted with a different fscrypt_info for each extent.
Meanwhile, directories and symlinks will continue to use the
fscrypt_info for the inode. Therefore, merely grabbing
inode->i_crypt_info will be insufficient; the caller must specifically
request the inode info or the info for a specific block.

Add fscrypt_get_lblk_info() to get info for a specific block, and update
all relevant callsites.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/crypto.c          |  3 ++-
 fs/crypto/fscrypt_private.h | 29 +++++++++++++++++++++++++++++
 fs/crypto/inline_crypt.c    | 10 ++++++----
 3 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 9f3bda18c797..1b7e375b1c6b 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -107,7 +107,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	struct skcipher_request *req = NULL;
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist dst, src;
-	struct fscrypt_info *ci = inode->i_crypt_info;
+	struct fscrypt_info *ci =
+		fscrypt_get_lblk_info(inode, lblk_num, NULL, NULL);
 	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
 	int res = 0;
 
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index e2acd8894ea7..8a1fd1d33cfc 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -277,6 +277,35 @@ typedef enum {
 	FS_ENCRYPT,
 } fscrypt_direction_t;
 
+/**
+ * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block
+ *
+ * @inode:      the inode to which the block belongs
+ * @lblk:       the offset of the block within the file which the inode
+ *              references
+ * @offset:     a pointer to return the offset of the block from the first block
+ *              that the info covers. For inode-based encryption, this will
+ *              always be @lblk; for extent-based encryption, this will be in
+ *              the range [0, lblk]. Can be NULL
+ * @extent_len: a pointer to return the minimum number of lblks starting at
+ *              this offset which also belong to the same fscrypt_info. Can be
+ *              NULL
+ *
+ * Return: the appropriate fscrypt_info if there is one, else NULL.
+ */
+static inline struct fscrypt_info *
+fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
+		      u64 *extent_len)
+{
+	if (offset)
+		*offset = lblk;
+	if (extent_len)
+		*extent_len = U64_MAX;
+
+	return inode->i_crypt_info;
+}
+
+
 /* crypto.c */
 extern struct kmem_cache *fscrypt_info_cachep;
 int fscrypt_initialize(struct super_block *sb);
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 2063f7941ce6..885a2ec3d711 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -270,7 +270,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 
 	if (!fscrypt_inode_uses_inline_crypto(inode))
 		return;
-	ci = inode->i_crypt_info;
+	ci = fscrypt_get_lblk_info(inode, first_lblk, NULL, NULL);
 
 	fscrypt_generate_dun(ci, first_lblk, dun);
 	bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask);
@@ -349,21 +349,23 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 {
 	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
 	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+	struct fscrypt_info *ci;
 
 	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
 		return false;
 	if (!bc)
 		return true;
 
+	ci = fscrypt_get_lblk_info(inode, next_lblk, NULL, NULL);
 	/*
 	 * Comparing the key pointers is good enough, as all I/O for each key
 	 * uses the same pointer.  I.e., there's currently no need to support
 	 * merging requests where the keys are the same but the pointers differ.
 	 */
-	if (bc->bc_key != inode->i_crypt_info->ci_enc_key->blk_key)
+	if (bc->bc_key != ci->ci_enc_key->blk_key)
 		return false;
 
-	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
+	fscrypt_generate_dun(ci, next_lblk, next_dun);
 	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
 }
 EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio);
@@ -465,7 +467,7 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
 	if (nr_blocks <= 1)
 		return nr_blocks;
 
-	ci = inode->i_crypt_info;
+	ci = fscrypt_get_lblk_info(inode, lblk, NULL, NULL);
 	if (!(fscrypt_policy_flags(&ci->ci_policy) &
 	      FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
 		return nr_blocks;
-- 
2.41.0


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

* [RFC PATCH 02/13] fscrypt: adjust effective lblks based on extents
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 01/13] fscrypt: factor getting info for a specific block Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 03/13] fscrypt: move function call warning of busy inodes Sweet Tea Dorminy
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

If a filesystem uses extent-based encryption, then the offset within a
file is not a constant which can be used for calculating an IV.
For instance, the same extent could be blocks 0-8 in one file, and
blocks 100-108 in another file. Instead, the block offset within the
extent must be used instead.

Update all uses of logical block offset within the file to use logical
block offset within the extent, if applicable.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/crypto.c       |  3 ++-
 fs/crypto/inline_crypt.c | 20 ++++++++++++++------
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 1b7e375b1c6b..d75f1b3f5795 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -107,8 +107,9 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	struct skcipher_request *req = NULL;
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist dst, src;
+	u64 ci_offset = 0;
 	struct fscrypt_info *ci =
-		fscrypt_get_lblk_info(inode, lblk_num, NULL, NULL);
+		fscrypt_get_lblk_info(inode, lblk_num, &ci_offset, NULL);
 	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
 	int res = 0;
 
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 885a2ec3d711..2d08abbf5892 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -267,12 +267,13 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 {
 	const struct fscrypt_info *ci;
 	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+	u64 ci_offset = 0;
 
 	if (!fscrypt_inode_uses_inline_crypto(inode))
 		return;
-	ci = fscrypt_get_lblk_info(inode, first_lblk, NULL, NULL);
+	ci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL);
 
-	fscrypt_generate_dun(ci, first_lblk, dun);
+	fscrypt_generate_dun(ci, ci_offset, dun);
 	bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask);
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
@@ -350,13 +351,14 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
 	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
 	struct fscrypt_info *ci;
+	u64 ci_offset = 0;
 
 	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
 		return false;
 	if (!bc)
 		return true;
 
-	ci = fscrypt_get_lblk_info(inode, next_lblk, NULL, NULL);
+	ci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL);
 	/*
 	 * Comparing the key pointers is good enough, as all I/O for each key
 	 * uses the same pointer.  I.e., there's currently no need to support
@@ -365,7 +367,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 	if (bc->bc_key != ci->ci_enc_key->blk_key)
 		return false;
 
-	fscrypt_generate_dun(ci, next_lblk, next_dun);
+	fscrypt_generate_dun(ci, ci_offset, next_dun);
 	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
 }
 EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio);
@@ -460,6 +462,8 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
 {
 	const struct fscrypt_info *ci;
 	u32 dun;
+	u64 ci_offset = 0;
+	u64 extent_len = 0;
 
 	if (!fscrypt_inode_uses_inline_crypto(inode))
 		return nr_blocks;
@@ -467,14 +471,18 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
 	if (nr_blocks <= 1)
 		return nr_blocks;
 
-	ci = fscrypt_get_lblk_info(inode, lblk, NULL, NULL);
+	ci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len);
+
+	/* Spanning an extent boundary will change the DUN */
+	nr_blocks = min_t(u64, nr_blocks, extent_len);
+
 	if (!(fscrypt_policy_flags(&ci->ci_policy) &
 	      FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
 		return nr_blocks;
 
 	/* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */
 
-	dun = ci->ci_hashed_ino + lblk;
+	dun = ci->ci_hashed_ino + ci_offset;
 
 	return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun);
 }
-- 
2.41.0


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

* [RFC PATCH 03/13] fscrypt: move function call warning of busy inodes
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 01/13] fscrypt: factor getting info for a specific block Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 02/13] fscrypt: adjust effective lblks based on extents Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 04/13] fscrypt: split fscrypt_info into general and inode specific parts Sweet Tea Dorminy
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

Extent encryption will want to attempt to evict inodes, and not warn of
busy ones, before removing the key instead of after as it is at present.
Therefore pull the call for check_for_busy_inodes() out of
try_to_lock_encrypted_files() into its only callsite.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keyring.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 7cbb1fd872ac..d153988b7403 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -938,8 +938,7 @@ static int check_for_busy_inodes(struct super_block *sb,
 static int try_to_lock_encrypted_files(struct super_block *sb,
 				       struct fscrypt_master_key *mk)
 {
-	int err1;
-	int err2;
+	int err;
 
 	/*
 	 * An inode can't be evicted while it is dirty or has dirty pages.
@@ -951,7 +950,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb,
 	 * already call sync_filesystem() via sys_syncfs() or sys_sync().
 	 */
 	down_read(&sb->s_umount);
-	err1 = sync_filesystem(sb);
+	err = sync_filesystem(sb);
 	up_read(&sb->s_umount);
 	/* If a sync error occurs, still try to evict as much as possible. */
 
@@ -963,16 +962,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb,
 	 */
 	evict_dentries_for_decrypted_inodes(mk);
 
-	/*
-	 * evict_dentries_for_decrypted_inodes() already iput() each inode in
-	 * the list; any inodes for which that dropped the last reference will
-	 * have been evicted due to fscrypt_drop_inode() detecting the key
-	 * removal and telling the VFS to evict the inode.  So to finish, we
-	 * just need to check whether any inodes couldn't be evicted.
-	 */
-	err2 = check_for_busy_inodes(sb, mk);
-
-	return err1 ?: err2;
+	return err;
 }
 
 /*
@@ -1064,8 +1054,17 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users)
 	up_write(&mk->mk_sem);
 
 	if (inodes_remain) {
+		int err2;
 		/* Some inodes still reference this key; try to evict them. */
 		err = try_to_lock_encrypted_files(sb, mk);
+		/* We already tried to iput() each inode referencing this key
+		 * which would cause the inode to be evicted if that was the
+		 * last reference (since fscrypt_drop_inode() would see the
+		 * key removal). So the only remaining inodes referencing this
+		 * key are still busy and couldn't be evicted; check for them.
+		 */
+		err2 = check_for_busy_inodes(sb, mk);
+		err = err ?: err2;
 		if (err == -EBUSY) {
 			status_flags |=
 				FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY;
-- 
2.41.0


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

* [RFC PATCH 04/13] fscrypt: split fscrypt_info into general and inode specific parts
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (2 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 03/13] fscrypt: move function call warning of busy inodes Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 05/13] fscrypt: add creation/usage/freeing of per-extent infos Sweet Tea Dorminy
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

In preparation for adding extent infos, split up the existing
fscrypt_info structure into a fscrypt_common_info structure which tracks
the information needed for data encryption and for freeing itself, and a
fscrypt_info structure containing a fscrypt_common_info and the parts only needed for inodes.
All external users continue to use a fscrypt_info, and most functions
don't need to deal with inode-specific parts so they use fscrypt_common_infos.

Similarly, split up the creation and freeing paths to have common parts that deal
with fscrypt_common_infos, and inode-specific parts dealing in
fscrypt_infos.

Alternately, the common struct could be a fscrypt_info, and only the
internal parts that need to deal in a specialized one could cast to the
enclosing specialized type; however, this seems to be less typesafe.
---
 fs/crypto/crypto.c          |  32 ++--
 fs/crypto/fname.c           |  13 +-
 fs/crypto/fscrypt_private.h | 122 +++++++++------
 fs/crypto/hooks.c           |   6 +-
 fs/crypto/inline_crypt.c    |  62 ++++----
 fs/crypto/keyring.c         |  40 ++---
 fs/crypto/keysetup.c        | 296 +++++++++++++++++++++---------------
 fs/crypto/keysetup_v1.c     |  74 ++++-----
 fs/crypto/policy.c          |  13 +-
 9 files changed, 371 insertions(+), 287 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index d75f1b3f5795..d5c9326a1919 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * This contains encryption functions for per-file encryption.
+ * This contains encryption functions for fscrypt encryption.
  *
  * Copyright (C) 2015, Google, Inc.
  * Copyright (C) 2015, Motorola Mobility
@@ -39,7 +39,7 @@ static mempool_t *fscrypt_bounce_page_pool = NULL;
 static struct workqueue_struct *fscrypt_read_workqueue;
 static DEFINE_MUTEX(fscrypt_init_mutex);
 
-struct kmem_cache *fscrypt_info_cachep;
+struct kmem_cache *fscrypt_inode_info_cachep;
 
 void fscrypt_enqueue_decrypt_work(struct work_struct *work)
 {
@@ -70,7 +70,7 @@ void fscrypt_free_bounce_page(struct page *bounce_page)
 EXPORT_SYMBOL(fscrypt_free_bounce_page);
 
 /*
- * Generate the IV for the given logical block number within the given file.
+ * Generate the IV for the given logical block number within the given info.
  * For filenames encryption, lblk_num == 0.
  *
  * Keep this in sync with fscrypt_limit_io_blocks().  fscrypt_limit_io_blocks()
@@ -78,21 +78,21 @@ EXPORT_SYMBOL(fscrypt_free_bounce_page);
  * simply contain the lblk_num (e.g., IV_INO_LBLK_32).
  */
 void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
-			 const struct fscrypt_info *ci)
+			 const struct fscrypt_common_info *cci)
 {
-	u8 flags = fscrypt_policy_flags(&ci->ci_policy);
+	u8 flags = fscrypt_policy_flags(&cci->ci_policy);
 
-	memset(iv, 0, ci->ci_mode->ivsize);
+	memset(iv, 0, cci->ci_mode->ivsize);
 
 	if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
 		WARN_ON_ONCE(lblk_num > U32_MAX);
-		WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX);
-		lblk_num |= (u64)ci->ci_inode->i_ino << 32;
+		WARN_ON_ONCE(cci->ci_inode->i_ino > U32_MAX);
+		lblk_num |= (u64)cci->ci_inode->i_ino << 32;
 	} else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
 		WARN_ON_ONCE(lblk_num > U32_MAX);
-		lblk_num = (u32)(ci->ci_hashed_ino + lblk_num);
+		lblk_num = (u32)(cci->ci_hashed_ino + lblk_num);
 	} else if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
-		memcpy(iv->nonce, ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
+		memcpy(iv->nonce, cci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
 	}
 	iv->lblk_num = cpu_to_le64(lblk_num);
 }
@@ -108,9 +108,9 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist dst, src;
 	u64 ci_offset = 0;
-	struct fscrypt_info *ci =
+	struct fscrypt_common_info *cci =
 		fscrypt_get_lblk_info(inode, lblk_num, &ci_offset, NULL);
-	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
+	struct crypto_skcipher *tfm = cci->ci_enc_key->tfm;
 	int res = 0;
 
 	if (WARN_ON_ONCE(len <= 0))
@@ -118,7 +118,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0))
 		return -EINVAL;
 
-	fscrypt_generate_iv(&iv, lblk_num, ci);
+	fscrypt_generate_iv(&iv, lblk_num, cci);
 
 	req = skcipher_request_alloc(tfm, gfp_flags);
 	if (!req)
@@ -393,8 +393,8 @@ static int __init fscrypt_init(void)
 	if (!fscrypt_read_workqueue)
 		goto fail;
 
-	fscrypt_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT);
-	if (!fscrypt_info_cachep)
+	fscrypt_inode_info_cachep = KMEM_CACHE(fscrypt_info, SLAB_RECLAIM_ACCOUNT);
+	if (!fscrypt_inode_info_cachep)
 		goto fail_free_queue;
 
 	err = fscrypt_init_keyring();
@@ -404,7 +404,7 @@ static int __init fscrypt_init(void)
 	return 0;
 
 fail_free_info:
-	kmem_cache_destroy(fscrypt_info_cachep);
+	kmem_cache_destroy(fscrypt_inode_info_cachep);
 fail_free_queue:
 	destroy_workqueue(fscrypt_read_workqueue);
 fail:
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index edb78cd1b0e7..ae20a886dbdf 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -101,7 +101,8 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 	struct skcipher_request *req = NULL;
 	DECLARE_CRYPTO_WAIT(wait);
 	const struct fscrypt_info *ci = inode->i_crypt_info;
-	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
+	const struct fscrypt_common_info *cci = &ci->info;
+	struct crypto_skcipher *tfm = cci->ci_enc_key->tfm;
 	union fscrypt_iv iv;
 	struct scatterlist sg;
 	int res;
@@ -116,7 +117,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 	memset(out + iname->len, 0, olen - iname->len);
 
 	/* Initialize the IV */
-	fscrypt_generate_iv(&iv, 0, ci);
+	fscrypt_generate_iv(&iv, 0, cci);
 
 	/* Set up the encryption request */
 	req = skcipher_request_alloc(tfm, GFP_NOFS);
@@ -158,7 +159,8 @@ static int fname_decrypt(const struct inode *inode,
 	DECLARE_CRYPTO_WAIT(wait);
 	struct scatterlist src_sg, dst_sg;
 	const struct fscrypt_info *ci = inode->i_crypt_info;
-	struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
+	const struct fscrypt_common_info *cci = &ci->info;
+	struct crypto_skcipher *tfm = cci->ci_enc_key->tfm;
 	union fscrypt_iv iv;
 	int res;
 
@@ -171,7 +173,7 @@ static int fname_decrypt(const struct inode *inode,
 		crypto_req_done, &wait);
 
 	/* Initialize IV */
-	fscrypt_generate_iv(&iv, 0, ci);
+	fscrypt_generate_iv(&iv, 0, cci);
 
 	/* Create decryption request */
 	sg_init_one(&src_sg, iname->name, iname->len);
@@ -299,7 +301,8 @@ bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
 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,
+	struct fscrypt_common_info *cci = &inode->i_crypt_info->info;
+	return __fscrypt_fname_encrypted_size(&cci->ci_policy,
 					      orig_len, max_len,
 					      encrypted_len_ret);
 }
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 8a1fd1d33cfc..cc1a61ade2a4 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -53,14 +53,15 @@ struct fscrypt_context_v2 {
 };
 
 /*
- * fscrypt_context - the encryption context of an inode
+ * fscrypt_context - the encryption context of an object
  *
  * This is the on-disk equivalent of an fscrypt_policy, stored alongside each
- * encrypted file usually in a hidden extended attribute.  It contains the
- * fields from the fscrypt_policy, in order to identify the encryption algorithm
- * and key with which the file is encrypted.  It also contains a nonce that was
- * randomly generated by fscrypt itself; this is used as KDF input or as a tweak
- * to cause different files to be encrypted differently.
+ * encrypted object, usually in a hidden extended attribute for a file.  It
+ * contains the fields from the fscrypt_policy, in order to identify the
+ * encryption algorithm and key with which the file is encrypted.  It also
+ * contains a nonce that was randomly generated by fscrypt itself; this is used
+ * as KDF input or as a tweak to cause different objects to be encrypted
+ * differently.
  */
 union fscrypt_context {
 	u8 version;
@@ -209,32 +210,41 @@ struct fscrypt_prepared_key {
 	enum fscrypt_prepared_key_type type;
 };
 
+typedef enum {
+	CI_INODE = 1,
+	CI_EXTENT,
+} __packed fscrypt_ci_type_t;
+
 /*
- * fscrypt_info - the "encryption key" for an inode
+ * fscrypt_common_info -- shared objects needed for data encryption
  *
- * When an encrypted file's key is made available, an instance of this struct is
- * allocated and stored in ->i_crypt_info.  Once created, it remains until the
- * inode is evicted.
+ * This keeps track of the information needed to actually encrypt/decrypt data
+ * (the prepared key, nonce, inode, policy, superblock) and the master key
+ * information needed to free this info.
  */
-struct fscrypt_info {
+struct fscrypt_common_info {
 
 	/* The key in a form prepared for actual encryption/decryption */
 	struct fscrypt_prepared_key *ci_enc_key;
 
-	/* True if ci_enc_key should be freed when this fscrypt_info is freed */
+	/*
+	 * True if ci_enc_key should be freed when this fscrypt_common_info is
+	 * freed.
+	 */
 	bool ci_owns_key;
 
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 	/*
-	 * True if this inode will use inline encryption (blk-crypto) instead of
+	 * True if this info will use inline encryption (blk-crypto) instead of
 	 * the traditional filesystem-layer encryption.
 	 */
 	bool ci_inlinecrypt;
 #endif
 
+	fscrypt_ci_type_t ci_type;
 	/*
-	 * Encryption mode used for this inode.  It corresponds to either the
-	 * contents or filenames encryption mode, depending on the inode type.
+	 * Encryption mode used for this info.  It corresponds to either the
+	 * contents or filenames encryption mode, depending on the info.
 	 */
 	struct fscrypt_mode *ci_mode;
 
@@ -242,18 +252,38 @@ struct fscrypt_info {
 	struct inode *ci_inode;
 
 	/*
-	 * The master key with which this inode was unlocked (decrypted).  This
+	 * The master key with which this info was unlocked (decrypted).  This
 	 * will be NULL if the master key was found in a process-subscribed
 	 * keyring rather than in the filesystem-level keyring.
 	 */
 	struct fscrypt_master_key *ci_master_key;
 
 	/*
-	 * Link in list of inodes that were unlocked with the master key.
+	 * Link in list of infos that were unlocked with the master key.
 	 * Only used when ->ci_master_key is set.
 	 */
 	struct list_head ci_master_key_link;
 
+	/* The encryption policy used by this object */
+	union fscrypt_policy ci_policy;
+
+	/* This object's nonce, copied from the fscrypt_context */
+	u8 ci_nonce[FSCRYPT_FILE_NONCE_SIZE];
+
+	/* Hashed inode number.  Only set for IV_INO_LBLK_32 */
+	u32 ci_hashed_ino;
+};
+
+/*
+ * fscrypt_info - the "encryption key" for an inode
+ *
+ * When an encrypted file's key is made available, an instance of this struct is
+ * allocated and stored in ->i_crypt_info.  Once created, it remains until the
+ * inode is evicted.
+ */
+struct fscrypt_info {
+	struct fscrypt_common_info info;
+
 	/*
 	 * This inode's hash key for filenames.  This is a 128-bit SipHash-2-4
 	 * key.  This is only set for directories that use a keyed dirhash over
@@ -261,24 +291,19 @@ struct fscrypt_info {
 	 */
 	siphash_key_t ci_dirhash_key;
 	bool ci_dirhash_key_initialized;
-
-	/* The encryption policy used by this inode */
-	union fscrypt_policy ci_policy;
-
-	/* This inode's nonce, copied from the fscrypt_context */
-	u8 ci_nonce[FSCRYPT_FILE_NONCE_SIZE];
-
-	/* Hashed inode number.  Only set for IV_INO_LBLK_32 */
-	u32 ci_hashed_ino;
 };
 
+/*
+ */
+
 typedef enum {
 	FS_DECRYPT = 0,
 	FS_ENCRYPT,
 } fscrypt_direction_t;
 
 /**
- * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block
+ * fscrypt_get_lblk_info() - get the fscrypt_common_info to crypt a particular
+ *			     block
  *
  * @inode:      the inode to which the block belongs
  * @lblk:       the offset of the block within the file which the inode
@@ -293,7 +318,7 @@ typedef enum {
  *
  * Return: the appropriate fscrypt_info if there is one, else NULL.
  */
-static inline struct fscrypt_info *
+static inline struct fscrypt_common_info *
 fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
 		      u64 *extent_len)
 {
@@ -302,12 +327,12 @@ fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
 	if (extent_len)
 		*extent_len = U64_MAX;
 
-	return inode->i_crypt_info;
+	return &inode->i_crypt_info->info;
 }
 
 
 /* crypto.c */
-extern struct kmem_cache *fscrypt_info_cachep;
+extern struct kmem_cache *fscrypt_inode_info_cachep;
 int fscrypt_initialize(struct super_block *sb);
 int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 			u64 lblk_num, struct page *src_page,
@@ -338,7 +363,7 @@ union fscrypt_iv {
 };
 
 void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
-			 const struct fscrypt_info *ci);
+			 const struct fscrypt_common_info *ci);
 
 /* fname.c */
 bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy,
@@ -376,17 +401,17 @@ void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
 
 /* inline_crypt.c */
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-int fscrypt_select_encryption_impl(struct fscrypt_info *ci);
+int fscrypt_select_encryption_impl(struct fscrypt_common_info *ci);
 
 static inline bool
-fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
+fscrypt_using_inline_encryption(const struct fscrypt_common_info *ci)
 {
 	return ci->ci_inlinecrypt;
 }
 
 int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				     const u8 *raw_key,
-				     const struct fscrypt_info *ci);
+				     const struct fscrypt_common_info *ci);
 
 void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
 				      struct fscrypt_prepared_key *prep_key);
@@ -397,7 +422,7 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
  */
 static inline bool
 fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
-			const struct fscrypt_info *ci)
+			const struct fscrypt_common_info *ci)
 {
 	/*
 	 * The two smp_load_acquire()'s here pair with the smp_store_release()'s
@@ -414,13 +439,13 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
 
 #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
 
-static inline int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
+static inline int fscrypt_select_encryption_impl(struct fscrypt_common_info *ci)
 {
 	return 0;
 }
 
 static inline bool
-fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
+fscrypt_using_inline_encryption(const struct fscrypt_common_info *ci)
 {
 	return false;
 }
@@ -428,7 +453,7 @@ fscrypt_using_inline_encryption(const struct fscrypt_info *ci)
 static inline int
 fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				 const u8 *raw_key,
-				 const struct fscrypt_info *ci)
+				 const struct fscrypt_common_info *ci)
 {
 	WARN_ON_ONCE(1);
 	return -EOPNOTSUPP;
@@ -442,7 +467,7 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb,
 
 static inline bool
 fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key,
-			const struct fscrypt_info *ci)
+			const struct fscrypt_common_info *ci)
 {
 	return smp_load_acquire(&prep_key->tfm) != NULL;
 }
@@ -499,7 +524,7 @@ struct fscrypt_master_key {
 	 * A structural ref only guarantees that the struct continues to exist.
 	 *
 	 * There is one active ref associated with ->mk_secret being present,
-	 * and one active ref for each inode in ->mk_decrypted_inodes.
+	 * and one active ref for each inode in ->mk_decrypted_infos.
 	 *
 	 * There is one structural ref associated with the active refcount being
 	 * nonzero.  Finding a key in the keyring also takes a structural ref,
@@ -513,7 +538,7 @@ struct fscrypt_master_key {
 	/*
 	 * The secret key material.  After FS_IOC_REMOVE_ENCRYPTION_KEY is
 	 * executed, this is wiped and no new inodes can be unlocked with this
-	 * key; however, there may still be inodes in ->mk_decrypted_inodes
+	 * key; however, there may still be inodes in ->mk_decrypted_infos
 	 * which could not be evicted.  As long as some inodes still remain,
 	 * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or
 	 * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
@@ -552,8 +577,8 @@ struct fscrypt_master_key {
 	 * List of inodes that were unlocked using this key.  This allows the
 	 * inodes to be evicted efficiently if the key is removed.
 	 */
-	struct list_head	mk_decrypted_inodes;
-	spinlock_t		mk_decrypted_inodes_lock;
+	struct list_head	mk_decrypted_infos;
+	spinlock_t		mk_decrypted_infos_lock;
 
 	/*
 	 * Per-mode encryption keys for the various types of encryption policies
@@ -642,17 +667,18 @@ struct fscrypt_mode {
 extern struct fscrypt_mode fscrypt_modes[];
 
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
-			const u8 *raw_key, const struct fscrypt_info *ci);
+			const u8 *raw_key,
+			const struct fscrypt_common_info *ci);
 
 void fscrypt_destroy_prepared_key(struct super_block *sb,
 				  struct fscrypt_prepared_key *prep_key);
 
-int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);
+int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *ci, const u8 *raw_key);
 
 int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
 			       const struct fscrypt_master_key *mk);
 
-void fscrypt_hash_inode_number(struct fscrypt_info *ci,
+void fscrypt_hash_inode_number(struct fscrypt_common_info *ci,
 			       const struct fscrypt_master_key *mk);
 
 int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported);
@@ -687,10 +713,10 @@ static inline int fscrypt_require_key(struct inode *inode)
 
 void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key);
 
-int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
+int fscrypt_setup_v1_info_key(struct fscrypt_common_info *ci,
 			      const u8 *raw_master_key);
 
-int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci);
+int fscrypt_setup_v1_info_key_via_subscribed_keyrings(struct fscrypt_common_info *ci);
 
 /* policy.c */
 
diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
index 6238dbcadcad..3c1d51724768 100644
--- a/fs/crypto/hooks.c
+++ b/fs/crypto/hooks.c
@@ -170,6 +170,7 @@ int fscrypt_prepare_setflags(struct inode *inode,
 			     unsigned int oldflags, unsigned int flags)
 {
 	struct fscrypt_info *ci;
+	struct fscrypt_common_info *cci;
 	struct fscrypt_master_key *mk;
 	int err;
 
@@ -183,9 +184,10 @@ int fscrypt_prepare_setflags(struct inode *inode,
 		if (err)
 			return err;
 		ci = inode->i_crypt_info;
-		if (ci->ci_policy.version != FSCRYPT_POLICY_V2)
+		cci = &ci->info;
+		if (cci->ci_policy.version != FSCRYPT_POLICY_V2)
 			return -EINVAL;
-		mk = ci->ci_master_key;
+		mk = cci->ci_master_key;
 		down_read(&mk->mk_sem);
 		if (is_master_key_secret_present(&mk->mk_secret))
 			err = fscrypt_derive_dirhash_key(ci, mk);
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 2d08abbf5892..09ec82b8a98a 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -39,10 +39,10 @@ static struct block_device **fscrypt_get_devices(struct super_block *sb,
 	return devs;
 }
 
-static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci)
+static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_common_info *cci)
 {
-	struct super_block *sb = ci->ci_inode->i_sb;
-	unsigned int flags = fscrypt_policy_flags(&ci->ci_policy);
+	struct super_block *sb = cci->ci_inode->i_sb;
+	unsigned int flags = fscrypt_policy_flags(&cci->ci_policy);
 	int ino_bits = 64, lblk_bits = 64;
 
 	if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
@@ -90,9 +90,9 @@ static void fscrypt_log_blk_crypto_impl(struct fscrypt_mode *mode,
 }
 
 /* Enable inline encryption for this file if supported. */
-int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
+int fscrypt_select_encryption_impl(struct fscrypt_common_info *cci)
 {
-	const struct inode *inode = ci->ci_inode;
+	const struct inode *inode = cci->ci_inode;
 	struct super_block *sb = inode->i_sb;
 	struct blk_crypto_config crypto_cfg;
 	struct block_device **devs;
@@ -104,7 +104,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
 		return 0;
 
 	/* The crypto mode must have a blk-crypto counterpart */
-	if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID)
+	if (cci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID)
 		return 0;
 
 	/* The filesystem must be mounted with -o inlinecrypt */
@@ -119,7 +119,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
 	 * doesn't work with IV_INO_LBLK_32. For now, simply exclude
 	 * IV_INO_LBLK_32 with blocksize != PAGE_SIZE from inline encryption.
 	 */
-	if ((fscrypt_policy_flags(&ci->ci_policy) &
+	if ((fscrypt_policy_flags(&cci->ci_policy) &
 	     FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
 	    sb->s_blocksize != PAGE_SIZE)
 		return 0;
@@ -128,9 +128,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
 	 * On all the filesystem's block devices, blk-crypto must support the
 	 * crypto configuration that the file would use.
 	 */
-	crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode;
+	crypto_cfg.crypto_mode = cci->ci_mode->blk_crypto_mode;
 	crypto_cfg.data_unit_size = sb->s_blocksize;
-	crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci);
+	crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(cci);
 
 	devs = fscrypt_get_devices(sb, &num_devs);
 	if (IS_ERR(devs))
@@ -141,9 +141,9 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
 			goto out_free_devs;
 	}
 
-	fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg);
+	fscrypt_log_blk_crypto_impl(cci->ci_mode, devs, num_devs, &crypto_cfg);
 
-	ci->ci_inlinecrypt = true;
+	cci->ci_inlinecrypt = true;
 out_free_devs:
 	kfree(devs);
 
@@ -152,11 +152,11 @@ int fscrypt_select_encryption_impl(struct fscrypt_info *ci)
 
 int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				     const u8 *raw_key,
-				     const struct fscrypt_info *ci)
+				     const struct fscrypt_common_info *cci)
 {
-	const struct inode *inode = ci->ci_inode;
+	const struct inode *inode = cci->ci_inode;
 	struct super_block *sb = inode->i_sb;
-	enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;
+	enum blk_crypto_mode_num crypto_mode = cci->ci_mode->blk_crypto_mode;
 	struct blk_crypto_key *blk_key;
 	struct block_device **devs;
 	unsigned int num_devs;
@@ -168,7 +168,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 		return -ENOMEM;
 
 	err = blk_crypto_init_key(blk_key, raw_key, crypto_mode,
-				  fscrypt_get_dun_bytes(ci), sb->s_blocksize);
+				  fscrypt_get_dun_bytes(cci), sb->s_blocksize);
 	if (err) {
 		fscrypt_err(inode, "error %d initializing blk-crypto key", err);
 		goto fail;
@@ -228,21 +228,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
 
 bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
 {
-	return inode->i_crypt_info->ci_inlinecrypt;
+	return inode->i_crypt_info->info.ci_inlinecrypt;
 }
 EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto);
 
-static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num,
+static void fscrypt_generate_dun(const struct fscrypt_common_info *cci, u64 lblk_num,
 				 u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE])
 {
 	union fscrypt_iv iv;
 	int i;
 
-	fscrypt_generate_iv(&iv, lblk_num, ci);
+	fscrypt_generate_iv(&iv, lblk_num, cci);
 
 	BUILD_BUG_ON(FSCRYPT_MAX_IV_SIZE > BLK_CRYPTO_MAX_IV_SIZE);
 	memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE);
-	for (i = 0; i < ci->ci_mode->ivsize/sizeof(dun[0]); i++)
+	for (i = 0; i < cci->ci_mode->ivsize/sizeof(dun[0]); i++)
 		dun[i] = le64_to_cpu(iv.dun[i]);
 }
 
@@ -265,16 +265,16 @@ static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num,
 void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 			       u64 first_lblk, gfp_t gfp_mask)
 {
-	const struct fscrypt_info *ci;
+	const struct fscrypt_common_info *cci;
 	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
 	u64 ci_offset = 0;
 
 	if (!fscrypt_inode_uses_inline_crypto(inode))
 		return;
-	ci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL);
+	cci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL);
 
-	fscrypt_generate_dun(ci, ci_offset, dun);
-	bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask);
+	fscrypt_generate_dun(cci, ci_offset, dun);
+	bio_crypt_set_ctx(bio, cci->ci_enc_key->blk_key, dun, gfp_mask);
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
 
@@ -350,7 +350,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 {
 	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
 	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
-	struct fscrypt_info *ci;
+	struct fscrypt_common_info *cci;
 	u64 ci_offset = 0;
 
 	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
@@ -358,16 +358,16 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 	if (!bc)
 		return true;
 
-	ci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL);
+	cci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL);
 	/*
 	 * Comparing the key pointers is good enough, as all I/O for each key
 	 * uses the same pointer.  I.e., there's currently no need to support
 	 * merging requests where the keys are the same but the pointers differ.
 	 */
-	if (bc->bc_key != ci->ci_enc_key->blk_key)
+	if (bc->bc_key != cci->ci_enc_key->blk_key)
 		return false;
 
-	fscrypt_generate_dun(ci, ci_offset, next_dun);
+	fscrypt_generate_dun(cci, ci_offset, next_dun);
 	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
 }
 EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio);
@@ -460,7 +460,7 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
  */
 u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
 {
-	const struct fscrypt_info *ci;
+	const struct fscrypt_common_info *cci;
 	u32 dun;
 	u64 ci_offset = 0;
 	u64 extent_len = 0;
@@ -471,18 +471,18 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
 	if (nr_blocks <= 1)
 		return nr_blocks;
 
-	ci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len);
+	cci = fscrypt_get_lblk_info(inode, lblk, &ci_offset, &extent_len);
 
 	/* Spanning an extent boundary will change the DUN */
 	nr_blocks = min_t(u64, nr_blocks, extent_len);
 
-	if (!(fscrypt_policy_flags(&ci->ci_policy) &
+	if (!(fscrypt_policy_flags(&cci->ci_policy) &
 	      FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
 		return nr_blocks;
 
 	/* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */
 
-	dun = ci->ci_hashed_ino + ci_offset;
+	dun = cci->ci_hashed_ino + ci_offset;
 
 	return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun);
 }
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index d153988b7403..27ae0345fa85 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -100,10 +100,10 @@ void fscrypt_put_master_key_activeref(struct super_block *sb,
 
 	/*
 	 * ->mk_active_refs == 0 implies that ->mk_secret is not present and
-	 * that ->mk_decrypted_inodes is empty.
+	 * that ->mk_decrypted_infos is empty.
 	 */
 	WARN_ON_ONCE(is_master_key_secret_present(&mk->mk_secret));
-	WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes));
+	WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_infos));
 
 	for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
 		fscrypt_destroy_prepared_key(
@@ -426,8 +426,8 @@ static int add_new_master_key(struct super_block *sb,
 	refcount_set(&mk->mk_struct_refs, 1);
 	mk->mk_spec = *mk_spec;
 
-	INIT_LIST_HEAD(&mk->mk_decrypted_inodes);
-	spin_lock_init(&mk->mk_decrypted_inodes_lock);
+	INIT_LIST_HEAD(&mk->mk_decrypted_infos);
+	spin_lock_init(&mk->mk_decrypted_infos_lock);
 
 	if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
 		err = allocate_master_key_users_keyring(mk);
@@ -865,16 +865,16 @@ static void shrink_dcache_inode(struct inode *inode)
 	d_prune_aliases(inode);
 }
 
-static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk)
+static void evict_dentries_for_decrypted_infos(struct fscrypt_master_key *mk)
 {
-	struct fscrypt_info *ci;
+	struct fscrypt_common_info *cci;
 	struct inode *inode;
 	struct inode *toput_inode = NULL;
 
-	spin_lock(&mk->mk_decrypted_inodes_lock);
+	spin_lock(&mk->mk_decrypted_infos_lock);
 
-	list_for_each_entry(ci, &mk->mk_decrypted_inodes, ci_master_key_link) {
-		inode = ci->ci_inode;
+	list_for_each_entry(cci, &mk->mk_decrypted_infos, ci_master_key_link) {
+		inode = cci->ci_inode;
 		spin_lock(&inode->i_lock);
 		if (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) {
 			spin_unlock(&inode->i_lock);
@@ -882,16 +882,16 @@ static void evict_dentries_for_decrypted_inodes(struct fscrypt_master_key *mk)
 		}
 		__iget(inode);
 		spin_unlock(&inode->i_lock);
-		spin_unlock(&mk->mk_decrypted_inodes_lock);
+		spin_unlock(&mk->mk_decrypted_infos_lock);
 
 		shrink_dcache_inode(inode);
 		iput(toput_inode);
 		toput_inode = inode;
 
-		spin_lock(&mk->mk_decrypted_inodes_lock);
+		spin_lock(&mk->mk_decrypted_infos_lock);
 	}
 
-	spin_unlock(&mk->mk_decrypted_inodes_lock);
+	spin_unlock(&mk->mk_decrypted_infos_lock);
 	iput(toput_inode);
 }
 
@@ -903,25 +903,25 @@ static int check_for_busy_inodes(struct super_block *sb,
 	unsigned long ino;
 	char ino_str[50] = "";
 
-	spin_lock(&mk->mk_decrypted_inodes_lock);
+	spin_lock(&mk->mk_decrypted_infos_lock);
 
-	list_for_each(pos, &mk->mk_decrypted_inodes)
+	list_for_each(pos, &mk->mk_decrypted_infos)
 		busy_count++;
 
 	if (busy_count == 0) {
-		spin_unlock(&mk->mk_decrypted_inodes_lock);
+		spin_unlock(&mk->mk_decrypted_infos_lock);
 		return 0;
 	}
 
 	{
 		/* select an example file to show for debugging purposes */
 		struct inode *inode =
-			list_first_entry(&mk->mk_decrypted_inodes,
-					 struct fscrypt_info,
+			list_first_entry(&mk->mk_decrypted_infos,
+					 struct fscrypt_common_info,
 					 ci_master_key_link)->ci_inode;
 		ino = inode->i_ino;
 	}
-	spin_unlock(&mk->mk_decrypted_inodes_lock);
+	spin_unlock(&mk->mk_decrypted_infos_lock);
 
 	/* If the inode is currently being created, ino may still be 0. */
 	if (ino)
@@ -942,7 +942,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb,
 
 	/*
 	 * An inode can't be evicted while it is dirty or has dirty pages.
-	 * Thus, we first have to clean the inodes in ->mk_decrypted_inodes.
+	 * Thus, we first have to clean the inodes in ->mk_decrypted_infos.
 	 *
 	 * Just do it the easy way: call sync_filesystem().  It's overkill, but
 	 * it works, and it's more important to minimize the amount of caches we
@@ -960,7 +960,7 @@ static int try_to_lock_encrypted_files(struct super_block *sb,
 	 * and inappropriate for use by unprivileged users.  So instead go
 	 * through the inodes' alias lists and try to evict each dentry.
 	 */
-	evict_dentries_for_decrypted_inodes(mk);
+	evict_dentries_for_decrypted_infos(mk);
 
 	return err;
 }
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index a19650f954e2..ae250e432dcd 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -101,7 +101,7 @@ select_encryption_mode(const union fscrypt_policy *policy,
 	if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
 		return &fscrypt_modes[fscrypt_policy_fnames_mode(policy)];
 
-	WARN_ONCE(1, "fscrypt: filesystem tried to load encryption info for inode %lu, which is not encryptable (file type %d)\n",
+	WARN_ONCE(1, "fscrypt: filesystem tried to load an encryption info for inode %lu, which is not encryptable (file type %d)\n",
 		  inode->i_ino, (inode->i_mode & S_IFMT));
 	return ERR_PTR(-EINVAL);
 }
@@ -159,14 +159,14 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
  * and IV generation method (@ci->ci_policy.flags).
  */
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
-			const u8 *raw_key, const struct fscrypt_info *ci)
+			const u8 *raw_key, const struct fscrypt_common_info *cci)
 {
 	struct crypto_skcipher *tfm;
 
-	if (fscrypt_using_inline_encryption(ci))
-		return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci);
+	if (fscrypt_using_inline_encryption(cci))
+		return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, cci);
 
-	tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
+	tfm = fscrypt_allocate_skcipher(cci->ci_mode, raw_key, cci->ci_inode);
 	if (IS_ERR(tfm))
 		return PTR_ERR(tfm);
 	/*
@@ -188,15 +188,16 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
 	memzero_explicit(prep_key, sizeof(*prep_key));
 }
 
-/* Given a per-file encryption key, set up the file's crypto transform object */
-int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key)
+/* Given a per-info encryption key, set up the info's crypto transform object */
+int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *cci,
+				 const u8 *raw_key)
 {
-	ci->ci_enc_key = kzalloc(sizeof(*ci->ci_enc_key), GFP_KERNEL);
-	if (!ci->ci_enc_key)
+	cci->ci_enc_key = kzalloc(sizeof(*cci->ci_enc_key), GFP_KERNEL);
+	if (!cci->ci_enc_key)
 		return -ENOMEM;
 
-	ci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO;
-	return fscrypt_prepare_key(ci->ci_enc_key, raw_key, ci);
+	cci->ci_enc_key->type = FSCRYPT_KEY_PER_INFO;
+	return fscrypt_prepare_key(cci->ci_enc_key, raw_key, cci);
 }
 
 static struct fscrypt_prepared_key *
@@ -219,15 +220,15 @@ mk_prepared_key_for_mode_policy(struct fscrypt_master_key *mk,
 }
 
 static size_t
-fill_hkdf_info_for_mode_key(const struct fscrypt_info *ci,
+fill_hkdf_info_for_mode_key(const struct fscrypt_common_info *cci,
 			    u8 hkdf_info[MAX_MODE_KEY_HKDF_INFO_SIZE])
 {
-	const u8 mode_num = ci->ci_mode - fscrypt_modes;
-	const struct super_block *sb = ci->ci_inode->i_sb;
+	const u8 mode_num = cci->ci_mode - fscrypt_modes;
+	const struct super_block *sb = cci->ci_inode->i_sb;
 	u8 hkdf_infolen = 0;
 
 	hkdf_info[hkdf_infolen++] = mode_num;
-	if (!(ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
+	if (!(cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
 		memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid,
 				sizeof(sb->s_uuid));
 		hkdf_infolen += sizeof(sb->s_uuid);
@@ -237,12 +238,12 @@ fill_hkdf_info_for_mode_key(const struct fscrypt_info *ci,
 
 static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 				       struct fscrypt_prepared_key *prep_key,
-				       const struct fscrypt_info *ci)
+				       const struct fscrypt_common_info *cci)
 {
-	const struct inode *inode = ci->ci_inode;
+	const struct inode *inode = cci->ci_inode;
 	const struct super_block *sb = inode->i_sb;
-	unsigned int policy_flags = fscrypt_policy_flags(&ci->ci_policy);
-	struct fscrypt_mode *mode = ci->ci_mode;
+	unsigned int policy_flags = fscrypt_policy_flags(&cci->ci_policy);
+	struct fscrypt_mode *mode = cci->ci_mode;
 	const u8 mode_num = mode - fscrypt_modes;
 	u8 mode_key[FSCRYPT_MAX_KEY_SIZE];
 	u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
@@ -263,8 +264,8 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	}
 
 	/*
-	 * For DIRECT_KEY policies: instead of deriving per-file encryption
-	 * keys, the per-file nonce will be included in all the IVs.  But
+	 * For DIRECT_KEY policies: instead of deriving per-info encryption
+	 * keys, the per-info nonce will be included in all the IVs.  But
 	 * unlike v1 policies, for v2 policies in this case we don't encrypt
 	 * with the master key directly but rather derive a per-mode encryption
 	 * key.  This ensures that the master key is consistently used only for
@@ -278,13 +279,13 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 
 	mutex_lock(&fscrypt_mode_key_setup_mutex);
 
-	if (fscrypt_is_key_prepared(prep_key, ci))
+	if (fscrypt_is_key_prepared(prep_key, cci))
 		goto out_unlock;
 
 	BUILD_BUG_ON(sizeof(mode_num) != 1);
 	BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
 	BUILD_BUG_ON(sizeof(hkdf_info) != MAX_MODE_KEY_HKDF_INFO_SIZE);
-	hkdf_infolen = fill_hkdf_info_for_mode_key(ci, hkdf_info);
+	hkdf_infolen = fill_hkdf_info_for_mode_key(cci, hkdf_info);
 
 	err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
 				  hkdf_context, hkdf_info, hkdf_infolen,
@@ -292,7 +293,7 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	if (err)
 		return err;
 	prep_key->type = FSCRYPT_KEY_MASTER_KEY;
-	err = fscrypt_prepare_key(prep_key, mode_key, ci);
+	err = fscrypt_prepare_key(prep_key, mode_key, cci);
 	memzero_explicit(mode_key, mode->keysize);
 
 out_unlock:
@@ -300,10 +301,10 @@ static int setup_new_mode_prepared_key(struct fscrypt_master_key *mk,
 	return err;
 }
 
-static int setup_mode_prepared_key(struct fscrypt_info *ci,
+static int setup_mode_prepared_key(struct fscrypt_common_info *cci,
 				  struct fscrypt_master_key *mk)
 {
-	struct fscrypt_mode *mode = ci->ci_mode;
+	struct fscrypt_mode *mode = cci->ci_mode;
 	const u8 mode_num = mode - fscrypt_modes;
 	struct fscrypt_prepared_key *prep_key;
 	int err;
@@ -311,19 +312,19 @@ static int setup_mode_prepared_key(struct fscrypt_info *ci,
 	if (WARN_ON_ONCE(mode_num > FSCRYPT_MODE_MAX))
 		return -EINVAL;
 
-	prep_key = mk_prepared_key_for_mode_policy(mk, &ci->ci_policy, mode);
+	prep_key = mk_prepared_key_for_mode_policy(mk, &cci->ci_policy, mode);
 	if (IS_ERR(prep_key))
 		return PTR_ERR(prep_key);
 
-	if (fscrypt_is_key_prepared(prep_key, ci)) {
-		ci->ci_enc_key = prep_key;
+	if (fscrypt_is_key_prepared(prep_key, cci)) {
+		cci->ci_enc_key = prep_key;
 		return 0;
 	}
-	err = setup_new_mode_prepared_key(mk, prep_key, ci);
+	err = setup_new_mode_prepared_key(mk, prep_key, cci);
 	if (err)
 		return err;
 
-	ci->ci_enc_key = prep_key;
+	cci->ci_enc_key = prep_key;
 	return 0;
 }
 
@@ -359,7 +360,7 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
 	int err;
 
 	err = fscrypt_derive_siphash_key(mk, HKDF_CONTEXT_DIRHASH_KEY,
-					 ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
+					 ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
 					 &ci->ci_dirhash_key);
 	if (err)
 		return err;
@@ -367,14 +368,14 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
 	return 0;
 }
 
-void fscrypt_hash_inode_number(struct fscrypt_info *ci,
+void fscrypt_hash_inode_number(struct fscrypt_common_info *cci,
 			       const struct fscrypt_master_key *mk)
 {
-	WARN_ON_ONCE(ci->ci_inode->i_ino == 0);
+	WARN_ON_ONCE(cci->ci_inode->i_ino == 0);
 	WARN_ON_ONCE(!mk->mk_ino_hash_key_initialized);
 
-	ci->ci_hashed_ino = (u32)siphash_1u64(ci->ci_inode->i_ino,
-					      &mk->mk_ino_hash_key);
+	cci->ci_hashed_ino = (u32)siphash_1u64(cci->ci_inode->i_ino,
+					       &mk->mk_ino_hash_key);
 }
 
 static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk)
@@ -403,25 +404,25 @@ static int fscrypt_setup_ino_hash_key(struct fscrypt_master_key *mk)
 	return err;
 }
 
-static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
+static int fscrypt_setup_v2_info_key(struct fscrypt_common_info *cci,
 				     struct fscrypt_master_key *mk)
 {
 	int err;
 
-	if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
-		err = setup_mode_prepared_key(ci, mk);
+	if (cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAGS_KEY_MASK) {
+		err = setup_mode_prepared_key(cci, mk);
 	} else {
 		u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
 
 		err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
 					  HKDF_CONTEXT_PER_FILE_ENC_KEY,
-					  ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
-					  derived_key, ci->ci_mode->keysize);
+					  cci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
+					  derived_key, cci->ci_mode->keysize);
 		if (err)
 			return err;
 
-		err = fscrypt_set_per_file_enc_key(ci, derived_key);
-		memzero_explicit(derived_key, ci->ci_mode->keysize);
+		err = fscrypt_set_per_info_enc_key(cci, derived_key);
+		memzero_explicit(derived_key, cci->ci_mode->keysize);
 	}
 
 	return err;
@@ -430,13 +431,13 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
 /*
  * Find or create the appropriate prepared key for an info.
  */
-static int fscrypt_setup_file_key(struct fscrypt_info *ci,
+static int fscrypt_setup_info_key(struct fscrypt_common_info *cci,
 				  struct fscrypt_master_key *mk)
 {
 	int err;
 
 	if (!mk) {
-		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
+		if (cci->ci_policy.version != FSCRYPT_POLICY_V1)
 			return -ENOKEY;
 
 		/*
@@ -445,15 +446,15 @@ static int fscrypt_setup_file_key(struct fscrypt_info *ci,
 		 * to before the search of ->s_master_keys, since users
 		 * shouldn't be able to override filesystem-level keys.
 		 */
-		return fscrypt_setup_v1_file_key_via_subscribed_keyrings(ci);
+		return fscrypt_setup_v1_info_key_via_subscribed_keyrings(cci);
 	}
 
-	switch (ci->ci_policy.version) {
+	switch (cci->ci_policy.version) {
 	case FSCRYPT_POLICY_V1:
-		err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);
+		err = fscrypt_setup_v1_info_key(cci, mk->mk_secret.raw);
 		break;
 	case FSCRYPT_POLICY_V2:
-		err = fscrypt_setup_v2_file_key(ci, mk);
+		err = fscrypt_setup_v2_info_key(cci, mk);
 		break;
 	default:
 		WARN_ON_ONCE(1);
@@ -465,30 +466,30 @@ static int fscrypt_setup_file_key(struct fscrypt_info *ci,
 
 /*
  * Check whether the size of the given master key (@mk) is appropriate for the
- * encryption settings which a particular file will use (@ci).
+ * encryption settings which a particular info will use (@cci).
  *
- * If the file uses a v1 encryption policy, then the master key must be at least
+ * If the info uses a v1 encryption policy, then the master key must be at least
  * as long as the derived key, as this is a requirement of the v1 KDF.
  *
  * Otherwise, the KDF can accept any size key, so we enforce a slightly looser
  * requirement: we require that the size of the master key be at least the
  * maximum security strength of any algorithm whose key will be derived from it
- * (but in practice we only need to consider @ci->ci_mode, since any other
+ * (but in practice we only need to consider @cci->ci_mode, since any other
  * possible subkeys such as DIRHASH and INODE_HASH will never increase the
- * required key size over @ci->ci_mode).  This allows AES-256-XTS keys to be
+ * required key size over @cci->ci_mode).  This allows AES-256-XTS keys to be
  * derived from a 256-bit master key, which is cryptographically sufficient,
  * rather than requiring a 512-bit master key which is unnecessarily long.  (We
  * still allow 512-bit master keys if the user chooses to use them, though.)
  */
 static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk,
-					  const struct fscrypt_info *ci)
+					  const struct fscrypt_common_info *cci)
 {
 	unsigned int min_keysize;
 
-	if (ci->ci_policy.version == FSCRYPT_POLICY_V1)
-		min_keysize = ci->ci_mode->keysize;
+	if (cci->ci_policy.version == FSCRYPT_POLICY_V1)
+		min_keysize = cci->ci_mode->keysize;
 	else
-		min_keysize = ci->ci_mode->security_strength;
+		min_keysize = cci->ci_mode->security_strength;
 
 	if (mk->mk_secret.size < min_keysize) {
 		fscrypt_warn(NULL,
@@ -507,19 +508,19 @@ static bool fscrypt_valid_master_key_size(const struct fscrypt_master_key *mk,
  *
  * If the master key is found in the filesystem-level keyring, then it is
  * returned in *mk_ret with its semaphore read-locked.  This is needed to ensure
- * that only one task links the fscrypt_info into ->mk_decrypted_inodes (as
+ * that only one task links inode fscrypt_info into ->mk_decrypted_infos (as
  * multiple tasks may race to create an fscrypt_info for the same inode), and to
  * synchronize the master key being removed with a new inode starting to use it.
  */
-static int find_and_lock_master_key(const struct fscrypt_info *ci,
+static int find_and_lock_master_key(const struct fscrypt_common_info *cci,
 				    struct fscrypt_master_key **mk_ret)
 {
-	struct super_block *sb = ci->ci_inode->i_sb;
+	struct super_block *sb = cci->ci_inode->i_sb;
 	struct fscrypt_key_specifier mk_spec;
 	struct fscrypt_master_key *mk;
 	int err;
 
-	err = fscrypt_policy_to_key_spec(&ci->ci_policy, &mk_spec);
+	err = fscrypt_policy_to_key_spec(&cci->ci_policy, &mk_spec);
 	if (err)
 		return err;
 
@@ -529,13 +530,13 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci,
 			fscrypt_get_dummy_policy(sb);
 
 		/*
-		 * Add the test_dummy_encryption key on-demand.  In principle,
+		 * Add the test_dummy_encryption key on-demand.  In princciple,
 		 * it should be added at mount time.  Do it here instead so that
 		 * the individual filesystems don't need to worry about adding
 		 * this key at mount time and cleaning up on mount failure.
 		 */
 		if (dummy_policy &&
-		    fscrypt_policies_equal(dummy_policy, &ci->ci_policy)) {
+		    fscrypt_policies_equal(dummy_policy, &cci->ci_policy)) {
 			err = fscrypt_add_test_dummy_key(sb, &mk_spec);
 			if (err)
 				return err;
@@ -544,7 +545,7 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci,
 	}
 
 	if (unlikely(!mk)) {
-		if (ci->ci_policy.version != FSCRYPT_POLICY_V1)
+		if (cci->ci_policy.version != FSCRYPT_POLICY_V1)
 			return -ENOKEY;
 
 		/*
@@ -564,7 +565,7 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci,
 		goto out_release_key;
 	}
 
-	if (!fscrypt_valid_master_key_size(mk, ci)) {
+	if (!fscrypt_valid_master_key_size(mk, cci)) {
 		err = -ENOKEY;
 		goto out_release_key;
 	}
@@ -578,26 +579,24 @@ static int find_and_lock_master_key(const struct fscrypt_info *ci,
 	return err;
 }
 
-static void put_crypt_info(struct fscrypt_info *ci)
+static void free_prepared_key(struct fscrypt_common_info *cci)
 {
-	struct fscrypt_master_key *mk;
-
-	if (!ci)
-		return;
-
-	if (ci->ci_enc_key) {
-		enum fscrypt_prepared_key_type type = ci->ci_enc_key->type;
+	if (cci->ci_enc_key) {
+		enum fscrypt_prepared_key_type type = cci->ci_enc_key->type;
 
 		if (type == FSCRYPT_KEY_DIRECT_V1)
-			fscrypt_put_direct_key(ci->ci_enc_key);
+			fscrypt_put_direct_key(cci->ci_enc_key);
 		if (type == FSCRYPT_KEY_PER_INFO) {
-			fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
-						     ci->ci_enc_key);
-			kfree_sensitive(ci->ci_enc_key);
+			fscrypt_destroy_prepared_key(cci->ci_inode->i_sb,
+						     cci->ci_enc_key);
+			kfree_sensitive(cci->ci_enc_key);
 		}
 	}
+}
 
-	mk = ci->ci_master_key;
+static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci)
+{
+	struct fscrypt_master_key *mk = cci->ci_master_key;
 	if (mk) {
 		/*
 		 * Remove this inode from the list of inodes that were unlocked
@@ -605,22 +604,36 @@ static void put_crypt_info(struct fscrypt_info *ci)
 		 * inode from a master key struct that already had its secret
 		 * removed, then complete the full removal of the struct.
 		 */
-		spin_lock(&mk->mk_decrypted_inodes_lock);
-		list_del(&ci->ci_master_key_link);
-		spin_unlock(&mk->mk_decrypted_inodes_lock);
-		fscrypt_put_master_key_activeref(ci->ci_inode->i_sb, mk);
+		spin_lock(&mk->mk_decrypted_infos_lock);
+		list_del(&cci->ci_master_key_link);
+		spin_unlock(&mk->mk_decrypted_infos_lock);
+		fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk);
 	}
+}
+
+static void put_crypt_inode_info(struct fscrypt_info *ci)
+{
+	if (!ci)
+		return;
+
+	free_prepared_key(&ci->info);
+	remove_info_from_mk_decrypted_list(&ci->info);
+
 	memzero_explicit(ci, sizeof(*ci));
-	kmem_cache_free(fscrypt_info_cachep, ci);
+	kmem_cache_free(fscrypt_inode_info_cachep, ci);
 }
 
-static int
-fscrypt_setup_encryption_info(struct inode *inode,
-			      const union fscrypt_policy *policy,
-			      const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
-			      bool need_dirhash_key)
+/*
+ * Sets up the fscrypt_common_info structure, and returns the relevant master
+ * key, if any, locked, for further type-specific processing.
+ */
+static int fscrypt_setup_common_info(struct fscrypt_common_info *crypt_info,
+				     struct inode *inode,
+				     const union fscrypt_policy *policy,
+				     const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
+				     fscrypt_ci_type_t type,
+				     struct fscrypt_master_key **mk_ret)
 {
-	struct fscrypt_info *crypt_info;
 	struct fscrypt_mode *mode;
 	struct fscrypt_master_key *mk = NULL;
 	int res;
@@ -629,12 +642,10 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	if (res)
 		return res;
 
-	crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_KERNEL);
-	if (!crypt_info)
-		return -ENOMEM;
-
 	crypt_info->ci_inode = inode;
 	crypt_info->ci_policy = *policy;
+	crypt_info->ci_type = type;
+
 	memcpy(crypt_info->ci_nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
 
 	mode = select_encryption_mode(&crypt_info->ci_policy, inode);
@@ -653,7 +664,66 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	if (res)
 		goto out;
 
-	res = fscrypt_setup_file_key(crypt_info, mk);
+	res = fscrypt_setup_info_key(crypt_info, mk);
+	if (res)
+		goto out;
+
+	/*
+	 * The IV_INO_LBLK_32 policy needs a hashed inode number, but new
+	 * inodes may not have an inode number assigned yet.
+	 */
+	if (policy->version == FSCRYPT_POLICY_V2 &&
+	    (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) {
+		res = fscrypt_setup_ino_hash_key(mk);
+		if (res)
+			goto out;
+
+		if (inode->i_ino)
+			fscrypt_hash_inode_number(crypt_info, mk);
+	}
+
+	*mk_ret = mk;
+	return res;
+
+out:
+	if (mk) {
+		up_read(&mk->mk_sem);
+		fscrypt_put_master_key(mk);
+	}
+	return res;
+}
+
+static void add_info_to_mk_decrypted_list(struct fscrypt_common_info *cci,
+					  struct fscrypt_master_key *mk)
+{
+	if (mk) {
+		cci->ci_master_key = mk;
+		refcount_inc(&mk->mk_active_refs);
+		spin_lock(&mk->mk_decrypted_infos_lock);
+		list_add(&cci->ci_master_key_link, &mk->mk_decrypted_infos);
+		spin_unlock(&mk->mk_decrypted_infos_lock);
+	}
+}
+
+static int
+fscrypt_setup_encryption_info(struct inode *inode,
+			      const union fscrypt_policy *policy,
+			      const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
+			      bool need_dirhash_key)
+{
+	struct fscrypt_info *crypt_inode_info;
+	struct fscrypt_common_info *crypt_info;
+	struct fscrypt_master_key *mk = NULL;
+	int res;
+
+	crypt_inode_info = kmem_cache_zalloc(fscrypt_inode_info_cachep,
+					     GFP_KERNEL);
+	if (!crypt_inode_info)
+		return -ENOMEM;
+	crypt_info = &crypt_inode_info->info;
+
+	res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce,
+					CI_INODE, &mk);
 	if (res)
 		goto out;
 
@@ -668,23 +738,9 @@ fscrypt_setup_encryption_info(struct inode *inode,
 			goto out;
 		}
 
-		res = fscrypt_derive_dirhash_key(crypt_info, mk);
-		if (res)
-			goto out;
-	}
-
-	/*
-	 * The IV_INO_LBLK_32 policy needs a hashed inode number, but new
-	 * inodes may not have an inode number assigned yet.
-	 */
-	if (policy->version == FSCRYPT_POLICY_V2 &&
-	    (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) {
-		res = fscrypt_setup_ino_hash_key(mk);
+		res = fscrypt_derive_dirhash_key(crypt_inode_info, mk);
 		if (res)
 			goto out;
-
-		if (inode->i_ino)
-			fscrypt_hash_inode_number(crypt_info, mk);
 	}
 
 	/*
@@ -693,20 +749,14 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	 * fscrypt_get_info().  I.e., here we publish ->i_crypt_info with a
 	 * RELEASE barrier so that other tasks can ACQUIRE it.
 	 */
-	if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
+	if (cmpxchg_release(&inode->i_crypt_info, NULL,
+			    crypt_inode_info) == NULL) {
 		/*
 		 * We won the race and set ->i_crypt_info to our crypt_info.
 		 * Now link it into the master key's inode list.
 		 */
-		if (mk) {
-			crypt_info->ci_master_key = mk;
-			refcount_inc(&mk->mk_active_refs);
-			spin_lock(&mk->mk_decrypted_inodes_lock);
-			list_add(&crypt_info->ci_master_key_link,
-				 &mk->mk_decrypted_inodes);
-			spin_unlock(&mk->mk_decrypted_inodes_lock);
-		}
-		crypt_info = NULL;
+		add_info_to_mk_decrypted_list(crypt_info, mk);
+		crypt_inode_info = NULL;
 	}
 	res = 0;
 out:
@@ -714,7 +764,7 @@ fscrypt_setup_encryption_info(struct inode *inode,
 		up_read(&mk->mk_sem);
 		fscrypt_put_master_key(mk);
 	}
-	put_crypt_info(crypt_info);
+	put_crypt_inode_info(crypt_inode_info);
 	return res;
 }
 
@@ -843,7 +893,7 @@ EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
  */
 void fscrypt_put_encryption_info(struct inode *inode)
 {
-	put_crypt_info(inode->i_crypt_info);
+	put_crypt_inode_info(inode->i_crypt_info);
 	inode->i_crypt_info = NULL;
 }
 EXPORT_SYMBOL(fscrypt_put_encryption_info);
@@ -884,7 +934,7 @@ int fscrypt_drop_inode(struct inode *inode)
 	 * was provided via the legacy mechanism of the process-subscribed
 	 * keyrings, so we don't know whether it's been removed or not.
 	 */
-	if (!ci || !ci->ci_master_key)
+	if (!ci || !ci->info.ci_master_key)
 		return 0;
 
 	/*
@@ -904,6 +954,6 @@ int fscrypt_drop_inode(struct inode *inode)
 	 * then the thread removing the key will either evict the inode itself
 	 * or will correctly detect that it wasn't evicted due to the race.
 	 */
-	return !is_master_key_secret_present(&ci->ci_master_key->mk_secret);
+	return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret);
 }
 EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index 1e785cedead0..b57ed49ac201 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -9,7 +9,7 @@
  * This file implements compatibility functions for the original encryption
  * policy version ("v1"), including:
  *
- * - Deriving per-file encryption keys using the AES-128-ECB based KDF
+ * - Deriving per-info encryption keys using the AES-128-ECB based KDF
  *   (rather than the new method of using HKDF-SHA512)
  *
  * - Retrieving fscrypt master keys from process-subscribed keyrings
@@ -181,7 +181,8 @@ void fscrypt_put_direct_key(struct fscrypt_prepared_key *prep_key)
  */
 static struct fscrypt_direct_key *
 find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
-			  const u8 *raw_key, const struct fscrypt_info *ci)
+			  const u8 *raw_key,
+			  const struct fscrypt_common_info *cci)
 {
 	unsigned long hash_key;
 	struct fscrypt_direct_key *dk;
@@ -193,19 +194,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
 	 */
 
 	BUILD_BUG_ON(sizeof(hash_key) > FSCRYPT_KEY_DESCRIPTOR_SIZE);
-	memcpy(&hash_key, ci->ci_policy.v1.master_key_descriptor,
+	memcpy(&hash_key, cci->ci_policy.v1.master_key_descriptor,
 	       sizeof(hash_key));
 
 	spin_lock(&fscrypt_direct_keys_lock);
 	hash_for_each_possible(fscrypt_direct_keys, dk, dk_node, hash_key) {
-		if (memcmp(ci->ci_policy.v1.master_key_descriptor,
+		if (memcmp(cci->ci_policy.v1.master_key_descriptor,
 			   dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
 			continue;
-		if (ci->ci_mode != dk->dk_mode)
+		if (cci->ci_mode != dk->dk_mode)
 			continue;
-		if (!fscrypt_is_key_prepared(&dk->dk_key, ci))
+		if (!fscrypt_is_key_prepared(&dk->dk_key, cci))
 			continue;
-		if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))
+		if (crypto_memneq(raw_key, dk->dk_raw, cci->ci_mode->keysize))
 			continue;
 		/* using existing tfm with same (descriptor, mode, raw_key) */
 		refcount_inc(&dk->dk_refcount);
@@ -221,13 +222,13 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
 
 /* Prepare to encrypt directly using the master key in the given mode */
 static struct fscrypt_direct_key *
-fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
+fscrypt_get_direct_key(const struct fscrypt_common_info *cci, const u8 *raw_key)
 {
 	struct fscrypt_direct_key *dk;
 	int err;
 
 	/* Is there already a tfm for this key? */
-	dk = find_or_insert_direct_key(NULL, raw_key, ci);
+	dk = find_or_insert_direct_key(NULL, raw_key, cci);
 	if (dk)
 		return dk;
 
@@ -235,18 +236,18 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
 	dk = kzalloc(sizeof(*dk), GFP_KERNEL);
 	if (!dk)
 		return ERR_PTR(-ENOMEM);
-	dk->dk_sb = ci->ci_inode->i_sb;
+	dk->dk_sb = cci->ci_inode->i_sb;
 	refcount_set(&dk->dk_refcount, 1);
-	dk->dk_mode = ci->ci_mode;
+	dk->dk_mode = cci->ci_mode;
 	dk->dk_key.type = FSCRYPT_KEY_DIRECT_V1;
-	err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);
+	err = fscrypt_prepare_key(&dk->dk_key, raw_key, cci);
 	if (err)
 		goto err_free_dk;
-	memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor,
+	memcpy(dk->dk_descriptor, cci->ci_policy.v1.master_key_descriptor,
 	       FSCRYPT_KEY_DESCRIPTOR_SIZE);
-	memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize);
+	memcpy(dk->dk_raw, raw_key, cci->ci_mode->keysize);
 
-	return find_or_insert_direct_key(dk, raw_key, ci);
+	return find_or_insert_direct_key(dk, raw_key, cci);
 
 err_free_dk:
 	free_direct_key(dk);
@@ -254,20 +255,20 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
 }
 
 /* v1 policy, DIRECT_KEY: use the master key directly */
-static int setup_v1_file_key_direct(struct fscrypt_info *ci,
+static int setup_v1_info_key_direct(struct fscrypt_common_info *cci,
 				    const u8 *raw_master_key)
 {
 	struct fscrypt_direct_key *dk;
 
-	dk = fscrypt_get_direct_key(ci, raw_master_key);
+	dk = fscrypt_get_direct_key(cci, raw_master_key);
 	if (IS_ERR(dk))
 		return PTR_ERR(dk);
-	ci->ci_enc_key = &dk->dk_key;
+	cci->ci_enc_key = &dk->dk_key;
 	return 0;
 }
 
-/* v1 policy, !DIRECT_KEY: derive the file's encryption key */
-static int setup_v1_file_key_derived(struct fscrypt_info *ci,
+/* v1 policy, !DIRECT_KEY: derive the info's encryption key */
+static int setup_v1_info_key_derived(struct fscrypt_common_info *cci,
 				     const u8 *raw_master_key)
 {
 	u8 *derived_key;
@@ -277,47 +278,48 @@ static int setup_v1_file_key_derived(struct fscrypt_info *ci,
 	 * This cannot be a stack buffer because it will be passed to the
 	 * scatterlist crypto API during derive_key_aes().
 	 */
-	derived_key = kmalloc(ci->ci_mode->keysize, GFP_KERNEL);
+	derived_key = kmalloc(cci->ci_mode->keysize, GFP_KERNEL);
 	if (!derived_key)
 		return -ENOMEM;
 
-	err = derive_key_aes(raw_master_key, ci->ci_nonce,
-			     derived_key, ci->ci_mode->keysize);
+	err = derive_key_aes(raw_master_key, cci->ci_nonce,
+			     derived_key, cci->ci_mode->keysize);
 	if (err)
 		goto out;
 
-	err = fscrypt_set_per_file_enc_key(ci, derived_key);
+	err = fscrypt_set_per_info_enc_key(cci, derived_key);
 out:
 	kfree_sensitive(derived_key);
 	return err;
 }
 
-int fscrypt_setup_v1_file_key(struct fscrypt_info *ci, const u8 *raw_master_key)
+int fscrypt_setup_v1_info_key(struct fscrypt_common_info *cci,
+			      const u8 *raw_master_key)
 {
-	if (ci->ci_policy.v1.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
-		return setup_v1_file_key_direct(ci, raw_master_key);
+	if (cci->ci_policy.v1.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)
+		return setup_v1_info_key_direct(cci, raw_master_key);
 	else
-		return setup_v1_file_key_derived(ci, raw_master_key);
+		return setup_v1_info_key_derived(cci, raw_master_key);
 }
 
-int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci)
+int fscrypt_setup_v1_info_key_via_subscribed_keyrings(struct fscrypt_common_info *cci)
 {
 	struct key *key;
 	const struct fscrypt_key *payload;
 	int err;
 
 	key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
-					ci->ci_policy.v1.master_key_descriptor,
-					ci->ci_mode->keysize, &payload);
-	if (key == ERR_PTR(-ENOKEY) && ci->ci_inode->i_sb->s_cop->key_prefix) {
-		key = find_and_lock_process_key(ci->ci_inode->i_sb->s_cop->key_prefix,
-						ci->ci_policy.v1.master_key_descriptor,
-						ci->ci_mode->keysize, &payload);
+					cci->ci_policy.v1.master_key_descriptor,
+					cci->ci_mode->keysize, &payload);
+	if (key == ERR_PTR(-ENOKEY) && cci->ci_inode->i_sb->s_cop->key_prefix) {
+		key = find_and_lock_process_key(cci->ci_inode->i_sb->s_cop->key_prefix,
+						cci->ci_policy.v1.master_key_descriptor,
+						cci->ci_mode->keysize, &payload);
 	}
 	if (IS_ERR(key))
 		return PTR_ERR(key);
 
-	err = fscrypt_setup_v1_file_key(ci, payload->raw);
+	err = fscrypt_setup_v1_info_key(cci, payload->raw);
 	up_read(&key->sem);
 	key_put(key);
 	return err;
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index f4456ecb3f87..ceb648669832 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -412,7 +412,7 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy)
 	ci = fscrypt_get_info(inode);
 	if (ci) {
 		/* key available, use the cached policy */
-		*policy = ci->ci_policy;
+		*policy = ci->info.ci_policy;
 		return 0;
 	}
 
@@ -698,7 +698,7 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir)
 		err = fscrypt_require_key(dir);
 		if (err)
 			return ERR_PTR(err);
-		return &dir->i_crypt_info->ci_policy;
+		return &dir->i_crypt_info->info.ci_policy;
 	}
 
 	return fscrypt_get_dummy_policy(dir->i_sb);
@@ -726,7 +726,7 @@ int fscrypt_context_for_new_inode(void *ctx, struct inode *inode)
 	if (WARN_ON_ONCE(!ci))
 		return -ENOKEY;
 
-	return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce);
+	return fscrypt_new_context(ctx, &ci->info.ci_policy, ci->info.ci_nonce);
 }
 EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode);
 
@@ -743,6 +743,7 @@ EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode);
 int fscrypt_set_context(struct inode *inode, void *fs_data)
 {
 	struct fscrypt_info *ci = inode->i_crypt_info;
+	struct fscrypt_common_info *cci = &ci->info;
 	union fscrypt_context ctx;
 	int ctxsize;
 
@@ -754,9 +755,9 @@ int fscrypt_set_context(struct inode *inode, void *fs_data)
 	 * This may be the first time the inode number is available, so do any
 	 * delayed key setup that requires the inode number.
 	 */
-	if (ci->ci_policy.version == FSCRYPT_POLICY_V2 &&
-	    (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
-		fscrypt_hash_inode_number(ci, ci->ci_master_key);
+	if (cci->ci_policy.version == FSCRYPT_POLICY_V2 &&
+	    (cci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
+		fscrypt_hash_inode_number(cci, cci->ci_master_key);
 
 	return inode->i_sb->s_cop->set_context(inode, &ctx, ctxsize, fs_data);
 }
-- 
2.41.0


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

* [RFC PATCH 05/13] fscrypt: add creation/usage/freeing of per-extent infos
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (3 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 04/13] fscrypt: split fscrypt_info into general and inode specific parts Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 06/13] fscrypt: allow load/save of extent contexts Sweet Tea Dorminy
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

This change adds the superblock function pointer to get the info
corresponding to a specific block in an inode for a filesystem using
per-extent infos. It allows creating a info for a new extent and freeing
that info, and uses the extent's info if appropriate in encrypting
blocks of data.

It also makes sure that the return value of fscrypt_get_lblk_info() is
non-NULL before using it, since there's no longer a mechanical guarantee
that we'll never call fscrypt_get_lblk_info() without having the
relevant info loaded. We *oughtn't*, but we're not explicitly checking
that it's loaded before these points.

This change does not deal with saving and loading an extent's info, but
introduces the mechanics necessary therefore.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/crypto.c          | 14 +++++-
 fs/crypto/fscrypt_private.h | 53 ++++++++++++++++++--
 fs/crypto/inline_crypt.c    |  5 ++
 fs/crypto/keysetup.c        | 97 +++++++++++++++++++++++++++++++++++++
 include/linux/fscrypt.h     | 41 ++++++++++++++++
 5 files changed, 204 insertions(+), 6 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index d5c9326a1919..5a93c30e6b86 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -40,6 +40,7 @@ static struct workqueue_struct *fscrypt_read_workqueue;
 static DEFINE_MUTEX(fscrypt_init_mutex);
 
 struct kmem_cache *fscrypt_inode_info_cachep;
+struct kmem_cache *fscrypt_extent_info_cachep;
 
 void fscrypt_enqueue_decrypt_work(struct work_struct *work)
 {
@@ -113,6 +114,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 	struct crypto_skcipher *tfm = cci->ci_enc_key->tfm;
 	int res = 0;
 
+	if (WARN_ON_ONCE(!cci))
+		return -EINVAL;
 	if (WARN_ON_ONCE(len <= 0))
 		return -EINVAL;
 	if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0))
@@ -397,13 +400,20 @@ static int __init fscrypt_init(void)
 	if (!fscrypt_inode_info_cachep)
 		goto fail_free_queue;
 
+	fscrypt_extent_info_cachep = KMEM_CACHE(fscrypt_extent_info,
+						SLAB_RECLAIM_ACCOUNT);
+	if (!fscrypt_extent_info_cachep)
+		goto fail_free_inode_info;
+
 	err = fscrypt_init_keyring();
 	if (err)
-		goto fail_free_info;
+		goto fail_free_extent_info;
 
 	return 0;
 
-fail_free_info:
+fail_free_extent_info:
+	kmem_cache_destroy(fscrypt_extent_info_cachep);
+fail_free_inode_info:
 	kmem_cache_destroy(fscrypt_inode_info_cachep);
 fail_free_queue:
 	destroy_workqueue(fscrypt_read_workqueue);
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index cc1a61ade2a4..9320428f8915 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -294,13 +294,47 @@ struct fscrypt_info {
 };
 
 /*
+ * fscrypt_extent_info - the "encryption key" for an extent
  */
+struct fscrypt_extent_info {
+	struct fscrypt_common_info info;
+};
 
 typedef enum {
 	FS_DECRYPT = 0,
 	FS_ENCRYPT,
 } fscrypt_direction_t;
 
+/**
+ * fscrypt_fs_uses_extent_encryption() -- whether a filesystem uses extent
+ *					  encryption
+ *
+ * @sb: the superblock of the relevant filesystem
+ *
+ * Return: true if the fs uses extent encryption, else false
+ */
+static inline bool
+fscrypt_fs_uses_extent_encryption(const struct super_block *sb)
+{
+	return !!sb->s_cop->get_extent_info;
+}
+
+/**
+ * fscrypt_uses_extent_encryption() -- whether an inode uses extent encryption
+ *
+ * @inode: the inode in question
+ *
+ * Return: true if the inode uses extent encryption, else false
+ */
+static inline bool fscrypt_uses_extent_encryption(const struct inode *inode)
+{
+	/* Non-regular files don't have extents. */
+	if (!S_ISREG(inode->i_mode))
+		return false;
+
+	return fscrypt_fs_uses_extent_encryption(inode->i_sb);
+}
+
 /**
  * fscrypt_get_lblk_info() - get the fscrypt_common_info to crypt a particular
  *			     block
@@ -313,15 +347,26 @@ typedef enum {
  *              always be @lblk; for extent-based encryption, this will be in
  *              the range [0, lblk]. Can be NULL
  * @extent_len: a pointer to return the minimum number of lblks starting at
- *              this offset which also belong to the same fscrypt_info. Can be
- *              NULL
+ *              this offset which also belong to the same fscrypt_common_info.
+ *              Can be NULL
  *
- * Return: the appropriate fscrypt_info if there is one, else NULL.
+ * Return: the appropriate fscrypt_common_info if there is one, else NULL.
  */
 static inline struct fscrypt_common_info *
 fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
 		      u64 *extent_len)
 {
+	if (fscrypt_uses_extent_encryption(inode)) {
+		struct fscrypt_extent_info *cei;
+		int res;
+
+		res = inode->i_sb->s_cop->get_extent_info(inode, lblk, &cei,
+							  offset, extent_len);
+		if (res == 0)
+			return &cei->info;
+		return NULL;
+	}
+
 	if (offset)
 		*offset = lblk;
 	if (extent_len)
@@ -330,9 +375,9 @@ fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
 	return &inode->i_crypt_info->info;
 }
 
-
 /* crypto.c */
 extern struct kmem_cache *fscrypt_inode_info_cachep;
+extern struct kmem_cache *fscrypt_extent_info_cachep;
 int fscrypt_initialize(struct super_block *sb);
 int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 			u64 lblk_num, struct page *src_page,
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 09ec82b8a98a..f0229234249c 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -272,6 +272,8 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 	if (!fscrypt_inode_uses_inline_crypto(inode))
 		return;
 	cci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL);
+	if (!cci)
+		return;
 
 	fscrypt_generate_dun(cci, ci_offset, dun);
 	bio_crypt_set_ctx(bio, cci->ci_enc_key->blk_key, dun, gfp_mask);
@@ -359,6 +361,9 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 		return true;
 
 	cci = fscrypt_get_lblk_info(inode, next_lblk, &ci_offset, NULL);
+	if (!cci)
+		return false;
+
 	/*
 	 * Comparing the key pointers is good enough, as all I/O for each key
 	 * uses the same pointer.  I.e., there's currently no need to support
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index ae250e432dcd..c9c16acf4c9b 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -957,3 +957,100 @@ int fscrypt_drop_inode(struct inode *inode)
 	return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret);
 }
 EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
+
+static void put_crypt_extent_info(struct fscrypt_extent_info *ci)
+{
+	if (!ci)
+		return;
+
+	free_prepared_key(&ci->info);
+	remove_info_from_mk_decrypted_list(&ci->info);
+
+	memzero_explicit(ci, sizeof(*ci));
+	kmem_cache_free(fscrypt_extent_info_cachep, ci);
+}
+
+static int
+fscrypt_setup_extent_info(struct inode *inode,
+			  const union fscrypt_policy *policy,
+			  const u8 nonce[FSCRYPT_FILE_NONCE_SIZE],
+			  struct fscrypt_extent_info **info_ptr)
+{
+	struct fscrypt_extent_info *crypt_extent_info;
+	struct fscrypt_common_info *crypt_info;
+	struct fscrypt_master_key *mk = NULL;
+	int res;
+
+	crypt_extent_info = kmem_cache_zalloc(fscrypt_extent_info_cachep,
+					      GFP_KERNEL);
+	if (!crypt_extent_info)
+		return -ENOMEM;
+	crypt_info = &crypt_extent_info->info;
+
+	res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce,
+					CI_EXTENT, &mk);
+	if (res)
+		goto out;
+
+	*info_ptr = crypt_extent_info;
+	add_info_to_mk_decrypted_list(crypt_info, mk);
+
+	crypt_extent_info = NULL;
+	res = 0;
+out:
+	if (mk) {
+		up_read(&mk->mk_sem);
+		fscrypt_put_master_key(mk);
+	}
+	put_crypt_extent_info(crypt_extent_info);
+	return res;
+}
+
+/**
+ * fscrypt_prepare_new_extent() - set up the fscrypt_extent_info for a new extent
+ * @inode: the inode to which the extent belongs
+ * @info_ptr: a pointer to return the extent's fscrypt_extent_info into
+ * *
+ * If the extent is part of an encrypted inode, set up a fscrypt_extent_info in
+ * preparation for encrypting data.
+ *
+ * This isn't %GFP_NOFS-safe.
+ *
+ * This doesn't persist the new extent's encryption context.  That still needs to
+ * be done later by calling fscrypt_set_extent_context().
+ *
+ * Return: 0 on success, -ENOKEY if the encryption key is missing, or another
+ *	   -errno code
+ */
+int fscrypt_prepare_new_extent(struct inode *inode,
+			       struct fscrypt_extent_info **info_ptr)
+{
+	const union fscrypt_policy *policy;
+	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+
+	policy = fscrypt_policy_to_inherit(inode);
+	if (policy == NULL)
+		return 0;
+	if (IS_ERR(policy))
+		return PTR_ERR(policy);
+
+	/* Only regular files can have extents.  */
+	if (WARN_ON_ONCE(!S_ISREG(inode->i_mode)))
+		return -EINVAL;
+
+	get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
+	return fscrypt_setup_extent_info(inode, policy, nonce,
+					 info_ptr);
+}
+EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
+
+/**
+ * fscrypt_free_extent_info() - free an extent's fscrypt_extent_info
+ * @info_ptr: a pointer containing the extent's fscrypt_extent_info pointer.
+ */
+void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr)
+{
+	put_crypt_extent_info(*info_ptr);
+	*info_ptr = NULL;
+}
+EXPORT_SYMBOL_GPL(fscrypt_free_extent_info);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index c895b12737a1..cc5de5ec888c 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -32,6 +32,7 @@
 
 union fscrypt_policy;
 struct fscrypt_info;
+struct fscrypt_extent_info;
 struct fs_parameter;
 struct seq_file;
 
@@ -113,6 +114,29 @@ struct fscrypt_operations {
 	int (*set_context)(struct inode *inode, const void *ctx, size_t len,
 			   void *fs_data);
 
+	/*
+	 * Get the fscrypt_info for the given inode at the given block, for
+	 * extent-based encryption only.
+	 *
+	 * @inode: the inode in question
+	 * @lblk: the logical block number in question
+	 * @ci: a pointer to return the fscrypt_extent_info
+	 * @offset: a pointer to return the offset of @lblk into the extent,
+	 *          in blocks (may be NULL)
+	 * @extent_len: a pointer to return the number of blocks in this extent
+	 *              starting at this point (may be NULL)
+	 *
+	 * May cause the filesystem to allocate memory, which the filesystem
+	 * must do with %GFP_NOFS, including calls into fscrypt to create or
+	 * load an fscrypt_info.
+	 *
+	 * Return: 0 if an extent is found with an info, -ENODATA if the key is
+	 *         unavailable, or another -errno.
+	 */
+	int (*get_extent_info)(const struct inode *inode, u64 lblk,
+			       struct fscrypt_extent_info **ci, u64 *offset,
+			       u64 *extent_len);
+
 	/*
 	 * Get the dummy fscrypt policy in use on the filesystem (if any).
 	 *
@@ -330,6 +354,10 @@ void fscrypt_put_encryption_info(struct inode *inode);
 void fscrypt_free_inode(struct inode *inode);
 int fscrypt_drop_inode(struct inode *inode);
 
+int fscrypt_prepare_new_extent(struct inode *inode,
+			       struct fscrypt_extent_info **info_ptr);
+void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr);
+
 /* fname.c */
 int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 			  u8 *out, unsigned int olen);
@@ -591,6 +619,19 @@ static inline int fscrypt_drop_inode(struct inode *inode)
 	return 0;
 }
 
+static inline int
+fscrypt_prepare_new_extent(struct inode *inode,
+			   struct fscrypt_extent_info **info_ptr)
+{
+	if (IS_ENCRYPTED(inode))
+		return -EOPNOTSUPP;
+	return 0;
+}
+
+static inline void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr)
+{
+}
+
  /* fname.c */
 static inline int fscrypt_setup_filename(struct inode *dir,
 					 const struct qstr *iname,
-- 
2.41.0


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

* [RFC PATCH 06/13] fscrypt: allow load/save of extent contexts
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (4 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 05/13] fscrypt: add creation/usage/freeing of per-extent infos Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent Sweet Tea Dorminy
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

The other half of using per-extent infos is saving and loading them from
disk.

This is the one change which cares about whether a lightweight or
heavyweight extent context is stored on disk. This implements the
lightweight version.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c    | 39 +++++++++++++++++++++++++++++++++++++++
 fs/crypto/policy.c      | 22 ++++++++++++++++++++++
 include/linux/fscrypt.h | 19 +++++++++++++++++++
 3 files changed, 80 insertions(+)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index c9c16acf4c9b..90143377cc61 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -1044,6 +1044,45 @@ int fscrypt_prepare_new_extent(struct inode *inode,
 }
 EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
 
+/**
+ * fscrypt_load_extent_info() - load a preexisting extent's fscrypt_extent_info
+ * @inode: the inode to which the extent belongs. Must be encrypted.
+ * @buf: a buffer containing the extent's stored context
+ * @len: the length of the @ctx buffer
+ * @info_ptr: a pointer to return the extent's fscrypt_extent_info into
+ *
+ * This is not %GFP_NOFS safe, so the caller is expected to call
+ * memalloc_nofs_save/restore() if appropriate.
+ *
+ * Return: 0 if successful, or -errno if it fails.
+ */
+int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
+			     struct fscrypt_extent_info **info_ptr)
+{
+	int res;
+	union fscrypt_context ctx;
+	const union fscrypt_policy *policy;
+
+	if (!fscrypt_has_encryption_key(inode))
+		return -EINVAL;
+
+	if (len != FSCRYPT_FILE_NONCE_SIZE) {
+		fscrypt_warn(inode,
+			     "Unrecognized or corrupt encryption context");
+		return -EINVAL;
+	}
+
+	policy = fscrypt_policy_to_inherit(inode);
+	if (policy == NULL)
+		return 0;
+	if (IS_ERR(policy))
+		return PTR_ERR(policy);
+
+	return fscrypt_setup_extent_info(inode, policy, buf,
+					 info_ptr);
+}
+EXPORT_SYMBOL_GPL(fscrypt_load_extent_info);
+
 /**
  * fscrypt_free_extent_info() - free an extent's fscrypt_extent_info
  * @info_ptr: a pointer containing the extent's fscrypt_extent_info pointer.
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index ceb648669832..cfbe83aee847 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -763,6 +763,28 @@ int fscrypt_set_context(struct inode *inode, void *fs_data)
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_context);
 
+/**
+ * fscrypt_set_extent_context() - Set the fscrypt extent context for an extent
+ * @ci: info from which to fetch policy and nonce
+ * @ctx: where context should be written
+ * @len: the size of ctx
+ *
+ * Given an fscrypt_extent_info belonging to an extent (generated via
+ * fscrypt_prepare_new_extent()), generate a new context and write it to @ctx.
+ * len is checked to be at least FSCRYPT_EXTENT_CONTEXT_MAX_SIZE bytes.
+ *
+ * Return: size of the resulting context or a negative error code.
+ */
+int fscrypt_set_extent_context(struct fscrypt_extent_info *ci, void *ctx,
+			       size_t len)
+{
+	if (len < FSCRYPT_EXTENT_CONTEXT_MAX_SIZE)
+		return -EINVAL;
+	memcpy(ctx, ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
+	return FSCRYPT_FILE_NONCE_SIZE;
+}
+EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);
+
 /**
  * fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option
  * @param: the mount option
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index cc5de5ec888c..b57fc5645076 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -57,6 +57,7 @@ struct fscrypt_name {
 
 /* Maximum value for the third parameter of fscrypt_operations.set_context(). */
 #define FSCRYPT_SET_CONTEXT_MAX_SIZE	40
+#define FSCRYPT_EXTENT_CONTEXT_MAX_SIZE	16
 
 #ifdef CONFIG_FS_ENCRYPTION
 
@@ -317,6 +318,8 @@ 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);
+int fscrypt_set_extent_context(struct fscrypt_extent_info *info, void *ctx,
+			       size_t len);
 
 struct fscrypt_dummy_policy {
 	const union fscrypt_policy *policy;
@@ -357,6 +360,9 @@ int fscrypt_drop_inode(struct inode *inode);
 int fscrypt_prepare_new_extent(struct inode *inode,
 			       struct fscrypt_extent_info **info_ptr);
 void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr);
+int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
+			     struct fscrypt_extent_info **info_ptr);
+
 
 /* fname.c */
 int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
@@ -533,6 +539,12 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data)
 	return -EOPNOTSUPP;
 }
 
+static inline int fscrypt_set_extent_context(struct fscrypt_info *info,
+					     void *ctx, size_t len)
+{
+	return -EOPNOTSUPP;
+}
+
 struct fscrypt_dummy_policy {
 };
 
@@ -632,6 +644,13 @@ static inline void fscrypt_free_extent_info(struct fscrypt_extent_info **info_pt
 {
 }
 
+static inline int fscrypt_load_extent_info(struct inode *inode, void *buf,
+					   size_t len,
+					   struct fscrypt_info **info_ptr)
+{
+	return -EOPNOTSUPP;
+}
+
  /* fname.c */
 static inline int fscrypt_setup_filename(struct inode *dir,
 					 const struct qstr *iname,
-- 
2.41.0


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

* [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (5 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 06/13] fscrypt: allow load/save of extent contexts Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-05 22:10   ` Neal Gompa
  2023-09-02  5:54 ` [RFC PATCH 08/13] fscrypt: save session key credentials for extent infos Sweet Tea Dorminy
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

For contrast purposes, this patch contains the entirety of the changes
necessary to switch between lightweight and heavyweight extents. This
patch could be dropped, or rolled into the former change, without
changing anything else.

Lightweight extents relying on their parent inode's context for
key and policy information do take up less disk space. Additionally,
they guarantee that if inode open succeeds, then all extents will be
readable and writeable, matching the current inode-based fscrypt
behavior.

However, heavyweight extents permit greater flexibility for future
extensions:

- Any form of changing the key for a non-empty directory's
  future writes requires that extents have some sort of policy in
  addition to the nonce, which is essentially the contents of the full
  fscrypt_context.
  - This could be approximated using overlayfs writing to a new
    encrypted directory, but this would waste space used by overwritten
    data and makes it very difficult to have nested subvolumes each with
    their own key, so it's very preferable to support this natively in
    btrfs.

- Scrub (verifying checksums) currently iterates over extents,
without interacting with inodes; in an authenticated encryption world,
scrub verifying authentication tags would need to iterate over inodes (a
large departure from the present) or need heavyweight extents storing
the necessary key information.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/keysetup.c | 20 ++++++++++----------
 fs/crypto/policy.c   |  5 ++---
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 90143377cc61..4146b1380cb5 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -1061,25 +1061,25 @@ int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
 {
 	int res;
 	union fscrypt_context ctx;
-	const union fscrypt_policy *policy;
+	union fscrypt_policy policy;
 
 	if (!fscrypt_has_encryption_key(inode))
 		return -EINVAL;
 
-	if (len != FSCRYPT_FILE_NONCE_SIZE) {
+	memcpy(&ctx, buf, len);
+
+	res = fscrypt_policy_from_context(&policy, &ctx, len);
+	if (res) {
 		fscrypt_warn(inode,
 			     "Unrecognized or corrupt encryption context");
-		return -EINVAL;
+		return res;
 	}
 
-	policy = fscrypt_policy_to_inherit(inode);
-	if (policy == NULL)
-		return 0;
-	if (IS_ERR(policy))
-		return PTR_ERR(policy);
+	if (!fscrypt_supported_policy(&policy, inode))
+		return -EINVAL;
 
-	return fscrypt_setup_extent_info(inode, policy, buf,
-					 info_ptr);
+	return fscrypt_setup_extent_info(inode, &policy,
+					 fscrypt_context_nonce(&ctx), info_ptr);
 }
 EXPORT_SYMBOL_GPL(fscrypt_load_extent_info);
 
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index cfbe83aee847..314bb6e97cec 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -778,10 +778,9 @@ EXPORT_SYMBOL_GPL(fscrypt_set_context);
 int fscrypt_set_extent_context(struct fscrypt_extent_info *ci, void *ctx,
 			       size_t len)
 {
-	if (len < FSCRYPT_EXTENT_CONTEXT_MAX_SIZE)
+	if (len < FSCRYPT_SET_CONTEXT_MAX_SIZE)
 		return -EINVAL;
-	memcpy(ctx, ci->info.ci_nonce, FSCRYPT_FILE_NONCE_SIZE);
-	return FSCRYPT_FILE_NONCE_SIZE;
+	return fscrypt_new_context(ctx, &ci->info.ci_policy, ci->info.ci_nonce);
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);
 
-- 
2.41.0


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

* [RFC PATCH 08/13] fscrypt: save session key credentials for extent infos
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (6 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 09/13] fscrypt: revamp key removal for extent encryption Sweet Tea Dorminy
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

For v1 encryption policies using per-session keys, the thread which
opens the inode and therefore initializes the encryption info is part of
the session, so it can get the key from the session keyring. However,
for extent encryption, the extent infos are likely loaded from a
different thread, which does not have access to the session keyring.
This change saves the credentials of the inode opening thread and reuses
those credentials temporarily when dealing with extent infos, allowing
finding the encryption key correctly.

v1 encryption policies using per-session keys should probably not exist
for new usages such as extent encryption, but this makes more tests
work without change; maybe the right answer is to disallow v1 session
keys plus extent encryption and deal with editing tests to not use v1
session encryption so much.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h |  9 +++++++++
 fs/crypto/keysetup.c        | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 9320428f8915..fe48b61a524b 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -284,6 +284,15 @@ struct fscrypt_common_info {
 struct fscrypt_info {
 	struct fscrypt_common_info info;
 
+	/* Credential struct from the thread which created this info. This is
+	 * only used in v1 session keyrings with extent encryption; it allows
+	 * the thread creating extents for an inode to join the session
+	 * keyring temporarily, since otherwise the thread is usually part of
+	 * kernel writeback and therefore unrelated to the thread with the
+	 * right session key.
+	 */
+	struct cred *ci_session_creds;
+
 	/*
 	 * This inode's hash key for filenames.  This is a 128-bit SipHash-2-4
 	 * key.  This is only set for directories that use a keyed dirhash over
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 4146b1380cb5..5e944ec4e36f 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -619,6 +619,9 @@ static void put_crypt_inode_info(struct fscrypt_info *ci)
 	free_prepared_key(&ci->info);
 	remove_info_from_mk_decrypted_list(&ci->info);
 
+	if (ci->ci_session_creds)
+		abort_creds(ci->ci_session_creds);
+
 	memzero_explicit(ci, sizeof(*ci));
 	kmem_cache_free(fscrypt_inode_info_cachep, ci);
 }
@@ -727,6 +730,9 @@ fscrypt_setup_encryption_info(struct inode *inode,
 	if (res)
 		goto out;
 
+	if (!mk)
+		crypt_inode_info->ci_session_creds = prepare_creds();
+
 	/*
 	 * Derive a secret dirhash key for directories that need it. It
 	 * should be impossible to set flags such that a v1 policy sets
@@ -979,6 +985,7 @@ fscrypt_setup_extent_info(struct inode *inode,
 	struct fscrypt_extent_info *crypt_extent_info;
 	struct fscrypt_common_info *crypt_info;
 	struct fscrypt_master_key *mk = NULL;
+	const struct cred *creds = NULL;
 	int res;
 
 	crypt_extent_info = kmem_cache_zalloc(fscrypt_extent_info_cachep,
@@ -987,8 +994,20 @@ fscrypt_setup_extent_info(struct inode *inode,
 		return -ENOMEM;
 	crypt_info = &crypt_extent_info->info;
 
+	if (inode->i_crypt_info->ci_session_creds) {
+		/*
+		 * The inode this is being created for is using a session key,
+		 * so we have to join this thread to that session temporarily
+		 * in order to be able to find the right key...
+		 */
+		creds = override_creds(inode->i_crypt_info->ci_session_creds);
+	}
+
 	res = fscrypt_setup_common_info(crypt_info, inode, policy, nonce,
 					CI_EXTENT, &mk);
+	if (creds)
+		revert_creds(creds);
+
 	if (res)
 		goto out;
 
-- 
2.41.0


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

* [RFC PATCH 09/13] fscrypt: revamp key removal for extent encryption
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (7 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 08/13] fscrypt: save session key credentials for extent infos Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 10/13] fscrypt: allow multiple extents to reference one info Sweet Tea Dorminy
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

Currently, for inode encryption, once an inode is open IO will not fail
due to lack of key until the inode is closed. Even if the key is
removed, open inodes will continue to use a tfm or inline crypt context
set up with the key.

For extent encryption, it's a little harder, since the extent may not be
created or loaded until well after the REMOVE_KEY ioctl is called. If
the key is actually fully removed, then the extent will be unable to
load/create, since it has to set up a new inline crypt context using the
key. Therefore, make key
removal 'soft' for extent-based encryption: keep the key material around if any
inodes using extent encryption are using it, allowing extents for those
inodes to use the key material.

Currently, both the key secret and each inode using the key keep a
reference to the structure; when the remove ioctl is called, the key
secret is removed and its reference is dropped. However, if we need to
keep the key secret around, we can't wipe the secret there, so to
preserve the invariant, we move both wiping and dropping the secret's
reference to the last inode releasing a soft-deleted key.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 22 ++++++++++++++++-----
 fs/crypto/keyring.c         | 39 +++++++++++++++++++++++++++----------
 fs/crypto/keysetup.c        | 35 +++++++++++++++++++++++++++++++--
 3 files changed, 79 insertions(+), 17 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index fe48b61a524b..dd7740105264 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -591,11 +591,14 @@ struct fscrypt_master_key {
 
 	/*
 	 * The secret key material.  After FS_IOC_REMOVE_ENCRYPTION_KEY is
-	 * executed, this is wiped and no new inodes can be unlocked with this
-	 * key; however, there may still be inodes in ->mk_decrypted_infos
-	 * which could not be evicted.  As long as some inodes still remain,
-	 * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or
-	 * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
+	 * executed, no new inodes can be unlocked with this key; however,
+	 * there may still be inodes in ->mk_decrypted_infos which could not
+	 * be evicted. For inode-based encryption, the secret is wiped; for
+	 * extent-based encryption, the secret is preserved while inodes still
+	 * reference it, as they may need to create new extents using the
+	 * secret to service IO; @soft_deleted is set to true then. As long as
+	 * some inodes still remain, FS_IOC_REMOVE_ENCRYPTION_KEY can be
+	 * retried, or FS_IOC_ADD_ENCRYPTION_KEY can add the secret again.
 	 *
 	 * While ->mk_secret is present, one ref in ->mk_active_refs is held.
 	 *
@@ -634,6 +637,13 @@ struct fscrypt_master_key {
 	struct list_head	mk_decrypted_infos;
 	spinlock_t		mk_decrypted_infos_lock;
 
+	/*
+	 * Whether the key is unavailable to new inodes, but still available
+	 * to new extents within decrypted inodes. Protected by ->mk_sem, except
+	 * for race-okay access in fscrypt_drop_inode().
+	 */
+	bool			mk_soft_deleted;
+
 	/*
 	 * Per-mode encryption keys for the various types of encryption policies
 	 * that use them.  Allocated and derived on-demand.
@@ -661,6 +671,8 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret)
 	return READ_ONCE(secret->size) != 0;
 }
 
+void fscrypt_wipe_master_key_secret(struct fscrypt_master_key_secret *secret);
+
 static inline const char *master_key_spec_type(
 				const struct fscrypt_key_specifier *spec)
 {
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 27ae0345fa85..9235a5a9bcba 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -38,7 +38,7 @@ struct fscrypt_keyring {
 	struct hlist_head key_hashtable[128];
 };
 
-static void wipe_master_key_secret(struct fscrypt_master_key_secret *secret)
+void fscrypt_wipe_master_key_secret(struct fscrypt_master_key_secret *secret)
 {
 	fscrypt_destroy_hkdf(&secret->hkdf);
 	memzero_explicit(secret, sizeof(*secret));
@@ -239,8 +239,9 @@ void fscrypt_destroy_keyring(struct super_block *sb)
 			 */
 			WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 1);
 			WARN_ON_ONCE(refcount_read(&mk->mk_struct_refs) != 1);
-			WARN_ON_ONCE(!is_master_key_secret_present(&mk->mk_secret));
-			wipe_master_key_secret(&mk->mk_secret);
+			WARN_ON_ONCE(!mk->mk_soft_deleted &&
+				     !is_master_key_secret_present(&mk->mk_secret));
+			fscrypt_wipe_master_key_secret(&mk->mk_secret);
 			fscrypt_put_master_key_activeref(sb, mk);
 		}
 	}
@@ -485,6 +486,8 @@ static int add_existing_master_key(struct fscrypt_master_key *mk,
 		move_master_key_secret(&mk->mk_secret, secret);
 	}
 
+	mk->mk_soft_deleted = false;
+
 	return 0;
 }
 
@@ -738,7 +741,7 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
 		goto out_wipe_secret;
 	err = 0;
 out_wipe_secret:
-	wipe_master_key_secret(&secret);
+	fscrypt_wipe_master_key_secret(&secret);
 	return err;
 }
 EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
@@ -770,7 +773,7 @@ int fscrypt_get_test_dummy_key_identifier(
 				  NULL, 0, key_identifier,
 				  FSCRYPT_KEY_IDENTIFIER_SIZE);
 out:
-	wipe_master_key_secret(&secret);
+	fscrypt_wipe_master_key_secret(&secret);
 	return err;
 }
 
@@ -794,7 +797,7 @@ int fscrypt_add_test_dummy_key(struct super_block *sb,
 
 	fscrypt_get_test_dummy_secret(&secret);
 	err = add_master_key(sb, &secret, key_spec);
-	wipe_master_key_secret(&secret);
+	fscrypt_wipe_master_key_secret(&secret);
 	return err;
 }
 
@@ -1017,6 +1020,12 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users)
 	mk = fscrypt_find_master_key(sb, &arg.key_spec);
 	if (!mk)
 		return -ENOKEY;
+
+	if (fscrypt_fs_uses_extent_encryption(sb)) {
+		/* Keep going even if this has an error. */
+		try_to_lock_encrypted_files(sb, mk);
+	}
+
 	down_write(&mk->mk_sem);
 
 	/* If relevant, remove current user's (or all users) claim to the key */
@@ -1043,13 +1052,23 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users)
 		}
 	}
 
-	/* No user claims remaining.  Go ahead and wipe the secret. */
+	/* No user claims remaining. */
 	err = -ENOKEY;
-	if (is_master_key_secret_present(&mk->mk_secret)) {
-		wipe_master_key_secret(&mk->mk_secret);
+	if (fscrypt_fs_uses_extent_encryption(sb) && refcount_read(&mk->mk_active_refs) > 1) {
+		mk->mk_soft_deleted = true;
+		err = 0;
+	} else if (is_master_key_secret_present(&mk->mk_secret)) {
+		fscrypt_wipe_master_key_secret(&mk->mk_secret);
 		fscrypt_put_master_key_activeref(sb, mk);
 		err = 0;
+	} else if (mk->mk_soft_deleted) {
+		/*
+		 * Was soft deleted, but all inodes have stopped using it, and
+		 * the secret was wiped by the last one.
+		 */
+		err = 0;
 	}
+
 	inodes_remain = refcount_read(&mk->mk_active_refs) > 0;
 	up_write(&mk->mk_sem);
 
@@ -1149,7 +1168,7 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg)
 	}
 	down_read(&mk->mk_sem);
 
-	if (!is_master_key_secret_present(&mk->mk_secret)) {
+	if (mk->mk_soft_deleted || !is_master_key_secret_present(&mk->mk_secret)) {
 		arg.status = refcount_read(&mk->mk_active_refs) > 0 ?
 			FSCRYPT_KEY_STATUS_INCOMPLETELY_REMOVED :
 			FSCRYPT_KEY_STATUS_ABSENT /* raced with full removal */;
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 5e944ec4e36f..34d4df4acb19 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -570,6 +570,12 @@ static int find_and_lock_master_key(const struct fscrypt_common_info *cci,
 		goto out_release_key;
 	}
 
+	if (cci->ci_type != CI_EXTENT && mk->mk_soft_deleted) {
+		/* Only extent infos can use keys that have been soft deleted */
+		err = -ENOKEY;
+		goto out_release_key;
+	}
+
 	*mk_ret = mk;
 	return 0;
 
@@ -598,6 +604,8 @@ static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci)
 {
 	struct fscrypt_master_key *mk = cci->ci_master_key;
 	if (mk) {
+		bool any_inodes;
+
 		/*
 		 * Remove this inode from the list of inodes that were unlocked
 		 * with the master key.  In addition, if we're removing the last
@@ -606,7 +614,28 @@ static void remove_info_from_mk_decrypted_list(struct fscrypt_common_info *cci)
 		 */
 		spin_lock(&mk->mk_decrypted_infos_lock);
 		list_del(&cci->ci_master_key_link);
+		any_inodes = list_empty(&mk->mk_decrypted_infos);
 		spin_unlock(&mk->mk_decrypted_infos_lock);
+		if (any_inodes) {
+			bool soft_deleted;
+			/* It might be that someone tried to remove this key,
+			 * but there were still inodes open that could need new
+			 * extents, which needed to be able to access the key
+			 * secret. But now this was the last reference. So we
+			 * can delete the key secret now. (We don't need to
+			 * check for new inodes on the decrypted_inode list
+			 * because once ->mk_soft_deleted is set, no new inode
+			 * can join the list.
+			 */
+			down_write(&mk->mk_sem);
+			soft_deleted = mk->mk_soft_deleted;
+			if (soft_deleted)
+				fscrypt_wipe_master_key_secret(&mk->mk_secret);
+			up_write(&mk->mk_sem);
+			if (soft_deleted)
+				fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk);
+		}
+
 		fscrypt_put_master_key_activeref(cci->ci_inode->i_sb, mk);
 	}
 }
@@ -933,6 +962,7 @@ EXPORT_SYMBOL(fscrypt_free_inode);
 int fscrypt_drop_inode(struct inode *inode)
 {
 	const struct fscrypt_info *ci = fscrypt_get_info(inode);
+	const struct fscrypt_common_info *cci = &ci->info;
 
 	/*
 	 * If ci is NULL, then the inode doesn't have an encryption key set up
@@ -940,7 +970,7 @@ int fscrypt_drop_inode(struct inode *inode)
 	 * was provided via the legacy mechanism of the process-subscribed
 	 * keyrings, so we don't know whether it's been removed or not.
 	 */
-	if (!ci || !ci->info.ci_master_key)
+	if (!ci || !cci->ci_master_key)
 		return 0;
 
 	/*
@@ -960,7 +990,8 @@ int fscrypt_drop_inode(struct inode *inode)
 	 * then the thread removing the key will either evict the inode itself
 	 * or will correctly detect that it wasn't evicted due to the race.
 	 */
-	return !is_master_key_secret_present(&ci->info.ci_master_key->mk_secret);
+	return cci->ci_master_key->mk_soft_deleted ||
+		!is_master_key_secret_present(&cci->ci_master_key->mk_secret);
 }
 EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
 
-- 
2.41.0


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

* [RFC PATCH 10/13] fscrypt: allow multiple extents to reference one info
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (8 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 09/13] fscrypt: revamp key removal for extent encryption Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 11/13] fscrypt: cache list of inlinecrypt devices Sweet Tea Dorminy
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

btrfs occasionally splits in-memory extents while holding a mutex. This
means we can't just copy the info, since setting up a new inlinecrypt
key requires taking a semaphore. Thus adding a mechanism to split
extents and merely take a new reference on the info is necessary.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h |  5 +++++
 fs/crypto/keysetup.c        | 19 ++++++++++++++++++-
 include/linux/fscrypt.h     |  2 +-
 3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index dd7740105264..cf1eb7fe546f 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -307,6 +307,11 @@ struct fscrypt_info {
  */
 struct fscrypt_extent_info {
 	struct fscrypt_common_info info;
+
+	/* Reference count. Normally 1, unless a extent info is shared by
+	 * several virtual extents.
+	 */
+	refcount_t refs;
 };
 
 typedef enum {
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 34d4df4acb19..f0f70b888bd8 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -919,6 +919,21 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
 }
 EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
 
+/**
+ * fscrypt_get_extent_info_ref() - mark a second extent using the same info
+ * @info: the info to be used by another extent
+ *
+ * Sometimes, an existing extent must be split into multiple extents in memory.
+ * In such a case, this function allows multiple extents to use the same extent
+ * info without allocating or taking any lock, which is necessary in certain IO
+ * paths.
+ */
+void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info)
+{
+	if (info)
+		refcount_inc(&info->refs);
+}
+
 /**
  * fscrypt_put_encryption_info() - free most of an inode's fscrypt data
  * @inode: an inode being evicted
@@ -997,7 +1012,7 @@ EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
 
 static void put_crypt_extent_info(struct fscrypt_extent_info *ci)
 {
-	if (!ci)
+	if (!ci || !refcount_dec_and_test(&ci->refs))
 		return;
 
 	free_prepared_key(&ci->info);
@@ -1042,6 +1057,8 @@ fscrypt_setup_extent_info(struct inode *inode,
 	if (res)
 		goto out;
 
+	refcount_set(&crypt_extent_info->refs, 1);
+
 	*info_ptr = crypt_extent_info;
 	add_info_to_mk_decrypted_list(crypt_info, mk);
 
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index b57fc5645076..577f9e0a6e97 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -362,7 +362,7 @@ int fscrypt_prepare_new_extent(struct inode *inode,
 void fscrypt_free_extent_info(struct fscrypt_extent_info **info_ptr);
 int fscrypt_load_extent_info(struct inode *inode, void *buf, size_t len,
 			     struct fscrypt_extent_info **info_ptr);
-
+void fscrypt_get_extent_info_ref(struct fscrypt_extent_info *info);
 
 /* fname.c */
 int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
-- 
2.41.0


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

* [RFC PATCH 11/13] fscrypt: cache list of inlinecrypt devices
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (9 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 10/13] fscrypt: allow multiple extents to reference one info Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 12/13] fscrypt: allow asynchronous info freeing Sweet Tea Dorminy
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

btrfs sometimes frees extents while holding a mutex, which makes it
impossible to free an inlinecrypt prepared key since that requires
taking a semaphore. Therefore, we will need to offload prepared key
freeing into an asynchronous process (rcu is insufficient since that can
run in softirq context which is also incompatible with taking a
semaphore). In order to avoid use-after-free on the filesystem
superblock for keys being freed during shutdown, we need to cache the
list of devices that the key has been loaded into, so that we can later
remove it without reference to the superblock.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 13 +++++++++++--
 fs/crypto/inline_crypt.c    | 20 +++++++++-----------
 fs/crypto/keysetup.c        |  2 +-
 3 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index cf1eb7fe546f..30459e219fc3 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -206,6 +206,16 @@ struct fscrypt_prepared_key {
 	struct crypto_skcipher *tfm;
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 	struct blk_crypto_key *blk_key;
+
+	/*
+	 * The list of devices that have this block key.
+	 */
+	struct block_device **devices;
+
+	/*
+	 * The number of devices in @ci_devices.
+	 */
+	size_t device_count;
 #endif
 	enum fscrypt_prepared_key_type type;
 };
@@ -472,8 +482,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 				     const u8 *raw_key,
 				     const struct fscrypt_common_info *ci);
 
-void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
-				      struct fscrypt_prepared_key *prep_key);
+void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key);
 
 /*
  * Check whether the crypto transform or blk-crypto key has been allocated in
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index f0229234249c..19ebdef8508b 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -185,12 +185,15 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 		if (err)
 			break;
 	}
-	kfree(devs);
+
 	if (err) {
 		fscrypt_err(inode, "error %d starting to use blk-crypto", err);
 		goto fail;
 	}
 
+	prep_key->devices = devs;
+	prep_key->device_count = num_devs;
+
 	/*
 	 * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
 	 * I.e., here we publish ->blk_key with a RELEASE barrier so that
@@ -205,24 +208,19 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 	return err;
 }
 
-void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
-				      struct fscrypt_prepared_key *prep_key)
+void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
 {
 	struct blk_crypto_key *blk_key = prep_key->blk_key;
-	struct block_device **devs;
-	unsigned int num_devs;
 	unsigned int i;
 
 	if (!blk_key)
 		return;
 
 	/* Evict the key from all the filesystem's block devices. */
-	devs = fscrypt_get_devices(sb, &num_devs);
-	if (!IS_ERR(devs)) {
-		for (i = 0; i < num_devs; i++)
-			blk_crypto_evict_key(devs[i], blk_key);
-		kfree(devs);
-	}
+	for (i = 0; i < prep_key->device_count; i++)
+		blk_crypto_evict_key(prep_key->devices[i], blk_key);
+
+	kfree(prep_key->devices);
 	kfree_sensitive(blk_key);
 }
 
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index f0f70b888bd8..4ea9b68363d5 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -184,7 +184,7 @@ void fscrypt_destroy_prepared_key(struct super_block *sb,
 				  struct fscrypt_prepared_key *prep_key)
 {
 	crypto_free_skcipher(prep_key->tfm);
-	fscrypt_destroy_inline_crypt_key(sb, prep_key);
+	fscrypt_destroy_inline_crypt_key(prep_key);
 	memzero_explicit(prep_key, sizeof(*prep_key));
 }
 
-- 
2.41.0


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

* [RFC PATCH 12/13] fscrypt: allow asynchronous info freeing
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (10 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 11/13] fscrypt: cache list of inlinecrypt devices Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-02  5:54 ` [RFC PATCH 13/13] fscrypt: update documentation for per-extent keys Sweet Tea Dorminy
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

btrfs sometimes frees extents while holding a mutex. This makes it hard
to free the prepared keys associated therewith, as the free process may
need to take a semaphore. Just offloading freeing to rcu doesn't work,
as rcu may call the callback in softirq context, which also doesn't
allow taking a semaphore. Thus, for extent infos, offload their freeing
to the general system workqueue.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/crypto/fscrypt_private.h | 12 +++++++++---
 fs/crypto/keyring.c         |  6 +++---
 fs/crypto/keysetup.c        | 31 +++++++++++++++++++++++++++----
 fs/crypto/keysetup_v1.c     |  3 +--
 4 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 30459e219fc3..67f33ad704a3 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -217,6 +217,12 @@ struct fscrypt_prepared_key {
 	 */
 	size_t device_count;
 #endif
+	/*
+	 * For destroying asynchronously.
+	 */
+	struct work_struct work;
+	/* A pointer to free after destroy. */
+	void *ptr_to_free;
 	enum fscrypt_prepared_key_type type;
 };
 
@@ -528,8 +534,7 @@ fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 }
 
 static inline void
-fscrypt_destroy_inline_crypt_key(struct super_block *sb,
-				 struct fscrypt_prepared_key *prep_key)
+fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
 {
 }
 
@@ -751,7 +756,8 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 			const struct fscrypt_common_info *ci);
 
 void fscrypt_destroy_prepared_key(struct super_block *sb,
-				  struct fscrypt_prepared_key *prep_key);
+				  struct fscrypt_prepared_key *prep_key,
+				  void *ptr_to_free);
 
 int fscrypt_set_per_info_enc_key(struct fscrypt_common_info *ci, const u8 *raw_key);
 
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 9235a5a9bcba..d4ec4ff27266 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -107,11 +107,11 @@ void fscrypt_put_master_key_activeref(struct super_block *sb,
 
 	for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
 		fscrypt_destroy_prepared_key(
-				sb, &mk->mk_direct_keys[i]);
+				sb, &mk->mk_direct_keys[i], NULL);
 		fscrypt_destroy_prepared_key(
-				sb, &mk->mk_iv_ino_lblk_64_keys[i]);
+				sb, &mk->mk_iv_ino_lblk_64_keys[i], NULL);
 		fscrypt_destroy_prepared_key(
-				sb, &mk->mk_iv_ino_lblk_32_keys[i]);
+				sb, &mk->mk_iv_ino_lblk_32_keys[i], NULL);
 	}
 	memzero_explicit(&mk->mk_ino_hash_key,
 			 sizeof(mk->mk_ino_hash_key));
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index 4ea9b68363d5..293a7d765ca7 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -179,13 +179,36 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
 	return 0;
 }
 
-/* Destroy a crypto transform object and/or blk-crypto key. */
-void fscrypt_destroy_prepared_key(struct super_block *sb,
-				  struct fscrypt_prepared_key *prep_key)
+static void __destroy_key(struct fscrypt_prepared_key *prep_key)
 {
+	void *ptr_to_free = prep_key->ptr_to_free;
+
 	crypto_free_skcipher(prep_key->tfm);
 	fscrypt_destroy_inline_crypt_key(prep_key);
 	memzero_explicit(prep_key, sizeof(*prep_key));
+	if (ptr_to_free)
+		kfree_sensitive(ptr_to_free);
+}
+
+static void __destroy_key_work(struct work_struct *work)
+{
+	struct fscrypt_prepared_key *prep_key =
+		container_of(work, struct fscrypt_prepared_key, work);
+
+	__destroy_key(prep_key);
+}
+
+/* Destroy a crypto transform object and/or blk-crypto key. */
+void fscrypt_destroy_prepared_key(struct super_block *sb,
+				  struct fscrypt_prepared_key *prep_key,
+				  void *ptr_to_free)
+{
+	prep_key->ptr_to_free = ptr_to_free;
+	if (fscrypt_fs_uses_extent_encryption(sb)) {
+		INIT_WORK(&prep_key->work, __destroy_key_work);
+		queue_work(system_unbound_wq, &prep_key->work);
+	} else
+		__destroy_key(prep_key);
 }
 
 /* Given a per-info encryption key, set up the info's crypto transform object */
@@ -594,8 +617,8 @@ static void free_prepared_key(struct fscrypt_common_info *cci)
 			fscrypt_put_direct_key(cci->ci_enc_key);
 		if (type == FSCRYPT_KEY_PER_INFO) {
 			fscrypt_destroy_prepared_key(cci->ci_inode->i_sb,
+						     cci->ci_enc_key,
 						     cci->ci_enc_key);
-			kfree_sensitive(cci->ci_enc_key);
 		}
 	}
 }
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index b57ed49ac201..8ccd8d4154d0 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -155,8 +155,7 @@ struct fscrypt_direct_key {
 static void free_direct_key(struct fscrypt_direct_key *dk)
 {
 	if (dk) {
-		fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key);
-		kfree_sensitive(dk);
+		fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key, dk);
 	}
 }
 
-- 
2.41.0


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

* [RFC PATCH 13/13] fscrypt: update documentation for per-extent keys
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (11 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 12/13] fscrypt: allow asynchronous info freeing Sweet Tea Dorminy
@ 2023-09-02  5:54 ` Sweet Tea Dorminy
  2023-09-05 17:33 ` [RFC PATCH 00/13] fscrypt: add extent encryption Josef Bacik
  2023-09-07  5:52 ` Eric Biggers
  14 siblings, 0 replies; 17+ messages in thread
From: Sweet Tea Dorminy @ 2023-09-02  5:54 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers,
	ngompa13
  Cc: Sweet Tea Dorminy

Add some documentation of how extent-based encryption works, hopefully
enough for future filesystem users.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 Documentation/filesystems/fscrypt.rst | 43 +++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 6 deletions(-)

diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index eccd327e6df5..e862d59bd5b5 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -31,7 +31,7 @@ However, except for filenames, fscrypt does not encrypt filesystem
 metadata.
 
 Unlike eCryptfs, which is a stacked filesystem, fscrypt is integrated
-directly into supported filesystems --- currently ext4, F2FS, and
+directly into supported filesystems --- currently btrfs, ext4, F2FS, and
 UBIFS.  This allows encrypted files to be read and written without
 caching both the decrypted and encrypted pages in the pagecache,
 thereby nearly halving the memory used and bringing it in line with
@@ -125,6 +125,11 @@ However, these ioctls have some limitations:
   well as kill any processes whose working directory is in an affected
   encrypted directory.
 
+- If the filesystem is using extent-based encryption, the master
+  encryption key will *not* be wiped from kernel memory until all
+  inodes using the key have been evicted (requiring that all files
+  using the key are closed).
+
 - The kernel cannot magically wipe copies of the master key(s) that
   userspace might have as well.  Therefore, userspace must wipe all
   copies of the master key(s) it makes as well; normally this should
@@ -280,6 +285,11 @@ included in the IV.  Moreover:
   key derived using the KDF.  Users may use the same master key for
   other v2 encryption policies.
 
+For filesystems with extent-based content encryption (e.g. btrfs),
+this is the only choice. Data shared among multiple inodes must share
+the exact same key, therefore necessitating inodes using the same key
+for contents encryption.
+
 IV_INO_LBLK_64 policies
 -----------------------
 
@@ -381,12 +391,13 @@ to individual filesystems.  However, authenticated encryption (AE)
 modes are not currently supported because of the difficulty of dealing
 with ciphertext expansion.
 
-Contents encryption
--------------------
+Inode-based contents encryption
+-------------------------------
 
-For file contents, each filesystem block is encrypted independently.
-Starting from Linux kernel 5.5, encryption of filesystems with block
-size less than system's page size is supported.
+Most filesystems use the previously discussed per-file keys. For these
+filesystems, for file contents, each filesystem block is encrypted
+independently.  Starting from Linux kernel 5.5, encryption of filesystems
+with block size less than system's page size is supported.
 
 Each block's IV is set to the logical block number within the file as
 a little endian number, except that:
@@ -410,6 +421,26 @@ Note that because file logical block numbers are included in the IVs,
 filesystems must enforce that blocks are never shifted around within
 encrypted files, e.g. via "collapse range" or "insert range".
 
+Extent-based contents encryption
+--------------------------------
+
+For certain filesystems (currently only btrfs), data is encrypted on a
+per-extent basis, for whatever the filesystem's notion of an extent is. The
+scheme is exactly as with inode-based contents encryption, except that the
+'inode number' for an extent is requested from the filesystem instead of from
+the file's inode, and the 'logical block number' refers to an offset within the
+extent.
+
+Because the encryption material is per-extent instead of per-inode, as long
+as the extent's encryption context does not change, the filesystem may shift
+around the position of the extent, and may have multiple files referring to
+the same encrypted extent.
+
+Not all extents within a file are decrypted simultaneously, so it is possible
+for a file read to fail partway through the file if it crosses into an extent
+whose key is unavailable.  However, all writes will succeed, unless the key is
+removed mid-write.
+
 Filenames encryption
 --------------------
 
-- 
2.41.0


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

* Re: [RFC PATCH 00/13] fscrypt: add extent encryption
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (12 preceding siblings ...)
  2023-09-02  5:54 ` [RFC PATCH 13/13] fscrypt: update documentation for per-extent keys Sweet Tea Dorminy
@ 2023-09-05 17:33 ` Josef Bacik
  2023-09-07  5:52 ` Eric Biggers
  14 siblings, 0 replies; 17+ messages in thread
From: Josef Bacik @ 2023-09-05 17:33 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, David Sterba, Theodore Y . Ts'o, Jaegeuk Kim,
	kernel-team, linux-btrfs, linux-fscrypt, ebiggers, ngompa13

On Sat, Sep 02, 2023 at 01:54:18AM -0400, Sweet Tea Dorminy wrote:
> This is a replacement for the former changeset (previously v3). This
> doesn't reflect all the smaller feedback on v3: it's an attempt to address
> the major points of giving extents and inodes different objects, and to
> clearly define lightweight and heavyweight extent contexts. Currently,
> with minor changes to the btrfs patchset building on it, it passes
> tests.
> 
> Hopefully I understood the proposed alternate design and this is indeed
> more elegant, reviewable, and maintainable. 
> 
> This applies atop [3], which itself is based on kdave/misc-next.
> 
> Changelog:
> RFC:
>  - Split fscrypt_info into a general fscrypt_common_info, an
>    inode-specific fscrypt_info, and an extent-specific
>    fscrypt_extent_info. All external interfaces use either an inode or
>    extent specific structure; most internal functions handle the common
>    structure.
>  - Tried to fix up more places to refer to infos instead of inodes and
>    files.
>  - Changed to use lightweight extent contexts containing just a nonce,
>    and then a following change to do heavyweight extent contexts
>    identical to inode contexts, so they're easily comparable.
>  - Dropped factoring lock_master_key() and adding super block pointer to
>    fscrypt_info changes, as they didn't seem necessary.
>  - Temporarily dropped optimization where leaf inodes with extents don't
>    have on-disk fscrypt_contexts. It's a convenient optimization and
>    affects btrfs disk format, but it's not very big and not strictly
>    needed to check whether the new structural arrangement is better.

I've gone through this, does seem a bit cleaner, and the uses of ->ci_type are
limited to the soft deletion part, so the overlapping thing that Eric was
worried about seems to be very contained.

Eric, I asked Sweet Tea to do a rough run at this to see if this was more in
your liking, I specifically told him to get it down and get it out so apologies
for the rough edges.  What I'm looking for is wether or not this is an
acceptable approach, and if there's any other big changes you want to see.  If
this looks good then Sweet Tea can clean it up and we can hopefully start making
progress on the other things.  Thanks,

Josef

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

* Re: [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent
  2023-09-02  5:54 ` [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent Sweet Tea Dorminy
@ 2023-09-05 22:10   ` Neal Gompa
  0 siblings, 0 replies; 17+ messages in thread
From: Neal Gompa @ 2023-09-05 22:10 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ebiggers

On Sat, Sep 2, 2023 at 1:56 AM Sweet Tea Dorminy
<sweettea-kernel@dorminy.me> wrote:
>
> For contrast purposes, this patch contains the entirety of the changes
> necessary to switch between lightweight and heavyweight extents. This
> patch could be dropped, or rolled into the former change, without
> changing anything else.
>
> Lightweight extents relying on their parent inode's context for
> key and policy information do take up less disk space. Additionally,
> they guarantee that if inode open succeeds, then all extents will be
> readable and writeable, matching the current inode-based fscrypt
> behavior.
>
> However, heavyweight extents permit greater flexibility for future
> extensions:
>
> - Any form of changing the key for a non-empty directory's
>   future writes requires that extents have some sort of policy in
>   addition to the nonce, which is essentially the contents of the full
>   fscrypt_context.
>   - This could be approximated using overlayfs writing to a new
>     encrypted directory, but this would waste space used by overwritten
>     data and makes it very difficult to have nested subvolumes each with
>     their own key, so it's very preferable to support this natively in
>     btrfs.
>
> - Scrub (verifying checksums) currently iterates over extents,
> without interacting with inodes; in an authenticated encryption world,
> scrub verifying authentication tags would need to iterate over inodes (a
> large departure from the present) or need heavyweight extents storing
> the necessary key information.
>

I've been thinking about this patch set a bit since it was posted, and
I've got some thoughts specifically about this.

A use-case that is extremely important to me is enabling background
encryption of parts or the whole filesystem, but also enabling
rekeying the encrypted data too. This can be necessary for a variety
of reasons. This is supported in LUKS/dm-crypt and I would like to
have this supported in Btrfs native encryption through fscrypt.

My understanding of this (after following all the discussions and
patch sets) is that heavyweight extents make this a fundamentally
cheaper and safer operation because there's no need to traverse
through the inodes and change them. This would be fairly expensive
and create a situation where inode sharing becomes much more limited
as rekeying occurs on active data while not occurring on old
snapshots, for example.

It's certainly a trade-off that leads to more complexity, but I think
it's worth it because key properties that people rely on with Btrfs
can be preserved even with encrypted data.



--
真実はいつも一つ!/ Always, there's only one truth!

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

* Re: [RFC PATCH 00/13] fscrypt: add extent encryption
  2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
                   ` (13 preceding siblings ...)
  2023-09-05 17:33 ` [RFC PATCH 00/13] fscrypt: add extent encryption Josef Bacik
@ 2023-09-07  5:52 ` Eric Biggers
  14 siblings, 0 replies; 17+ messages in thread
From: Eric Biggers @ 2023-09-07  5:52 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
	Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt, ngompa13

On Sat, Sep 02, 2023 at 01:54:18AM -0400, Sweet Tea Dorminy wrote:
> This is a replacement for the former changeset (previously v3). This
> doesn't reflect all the smaller feedback on v3: it's an attempt to address
> the major points of giving extents and inodes different objects, and to
> clearly define lightweight and heavyweight extent contexts.

In general it would be helpful if more of the feedback I've already given was
addressed, e.g.
https://lore.kernel.org/r/20230812223408.GA41642@sol.localdomain
https://lore.kernel.org/r/20230812224144.GB41642@sol.localdomain
https://lore.kernel.org/r/20230812224301.GC41642@sol.localdomain.
It's hard to review this when things that are being proposed "for real" are
mixed in with things that just haven't had time to be fixed yet, and it's
not obvious which are which.
https://lore.kernel.org/r/20230812223408.GA41642@sol.localdomain in particular
is important and would affect the parts which it seems I'm being asked to
review.

> Changelog:
> RFC:
>  - Split fscrypt_info into a general fscrypt_common_info, an
>    inode-specific fscrypt_info, and an extent-specific
>    fscrypt_extent_info. All external interfaces use either an inode or
>    extent specific structure; most internal functions handle the common
>    structure.

If we indeed go this route, the inode one should be called fscrypt_inode_info,
right?  Also, this could be done in 3 patches to make it easier to review: (1)
rename fscrypt_info => fscrypt_inode_info, (2) add fscrypt_common_info and put
it in fscrypt_inode_info, and (3) add fscrypt_extent_info.

>  - Tried to fix up more places to refer to infos instead of inodes and
>    files.

I don't think the things that are being encrypted should be called "infos".
"Info" is just what fs/crypto/ happens to the call the data structure that holds
the key, encryption settings, etc. for an object (inode or extent) subject to FS
encryption.  It's not a great name, and in any case it's not the same as the
object itself.  When we're not literally talking about the data structure
itself, code comments should say something like "object represented by the info
@ci" or "file or extent for the info @ci".  Other suggestions appreciated, of
course.  But we should not refer to the things being encrypted as "infos".
"Info" is the C struct that the code happens to use, nothing more.

>  - Changed to use lightweight extent contexts containing just a nonce,
>    and then a following change to do heavyweight extent contexts
>    identical to inode contexts, so they're easily comparable.

This seems to be referring to "fscrypt: store full fscrypt_contexts for each
extent".  But, that just changes what is stored for each extent.  The rest of
this patchset still very much assumes the heavyweight design, and it brings in
the complexity from that.  E.g., the proposed fscrypt_extent_info has a lot of
fields which are not necessary if there was an associated per-inode struct that
the policy, mode, master key, etc. could be pulled from.  Also the master key
link, since only inodes really need to be in the master key's list, right?

I understand that you want to be able to assign an encryption policy to an
unencrypted file and have new extents be encrypted using that policy.  I never
received a real answer to my question about what the plan is to recursively
change a whole directory tree, but sure, let's assume this will be supported by
iterating through every file and directory and setting something on them (for
directories it would have to be a new kind of inherit-only policy; also for this
to work at all you'd have to relax the current fscrypt semantics described in
https://www.kernel.org/doc/html/next/filesystems/fscrypt.html#encryption-policy-enforcement).
This still means that the encryption policy for each extent, if it has one, will
match the file that contains it.  So I don't see why it's necessary to have all
complexity of setting up everything completely independently for each extent.

It does sound like you still want to store the full information for each extent
anyway.  In that case, how about doing that but just making the kernel validate
at runtime that it matches the corresponding inode's?  (Yes, extent => inode is
a one-to-many relationship on-disk, but in the cache it's one-to-one I think.)
I think that would be a good middle ground.  It would allow the implementation
to be simple for now, with lightweight "struct fscrypt_extent_info", but all the
information for each extent would be stored on-disk for futureproofing.

- Eric

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

end of thread, other threads:[~2023-09-07 16:36 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-02  5:54 [RFC PATCH 00/13] fscrypt: add extent encryption Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 01/13] fscrypt: factor getting info for a specific block Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 02/13] fscrypt: adjust effective lblks based on extents Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 03/13] fscrypt: move function call warning of busy inodes Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 04/13] fscrypt: split fscrypt_info into general and inode specific parts Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 05/13] fscrypt: add creation/usage/freeing of per-extent infos Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 06/13] fscrypt: allow load/save of extent contexts Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 07/13] fscrypt: store full fscrypt_contexts for each extent Sweet Tea Dorminy
2023-09-05 22:10   ` Neal Gompa
2023-09-02  5:54 ` [RFC PATCH 08/13] fscrypt: save session key credentials for extent infos Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 09/13] fscrypt: revamp key removal for extent encryption Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 10/13] fscrypt: allow multiple extents to reference one info Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 11/13] fscrypt: cache list of inlinecrypt devices Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 12/13] fscrypt: allow asynchronous info freeing Sweet Tea Dorminy
2023-09-02  5:54 ` [RFC PATCH 13/13] fscrypt: update documentation for per-extent keys Sweet Tea Dorminy
2023-09-05 17:33 ` [RFC PATCH 00/13] fscrypt: add extent encryption Josef Bacik
2023-09-07  5:52 ` Eric Biggers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox