All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/46] btrfs: add fscrypt support
@ 2023-12-01 22:10 Josef Bacik
  2023-12-01 22:10 ` [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super Josef Bacik
                   ` (48 more replies)
  0 siblings, 49 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:10 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Hello,

v3 can be found here

https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/

There's been a longer delay between versions than I'd like, this was mostly due
to Plumbers, Holidays, and then uncovering a bunch of new issues with '-o
test_dummy_encryption'.  I'm still working through some of the btrfs specific
failures, but the fscrypt side appears to be stable.  I had to add a few changes
to fscrypt since the last time, but nothing earth shattering, just moving the
keyring destruction and adding a helper we need for btrfs send to work properly.

This is passing a good chunk of the fstests, at this point the majority appear
to be cases where I need to exclude the test when using test_dummy_encryption
because of various limitations of our tools or other infrastructure related
things.

I likely will have a follow-up series with more fixes, but the bulk of this is
unchanged since the last posting.  There were some bug fixes and such but the
overall design remains the same.  Thanks,

Josef

v3->v4:
- Added support for '-o test_dummy_encryption' at Eric's suggestion, this
  uncovered a load of issues.
- Preliminary work to support decrypting names for our various name resolution
  ioctls.  I didn't get everything but I got the ones we depend on in fstests.
- Preliminary work for send of an encrypted directory with the key loaded.
  There's probably still bugs in here, but it doesn't crash anymore.
- Fixed how we limit the bio size to work with direct and buffered io.
- Fixed using the wrong fscrypt extent context for writes into prealloc extents.

Josef Bacik (31):
  fs: move fscrypt keyring destruction to after ->put_super
  fscrypt: add per-extent encryption support
  fscrypt: add a fscrypt_inode_open helper
  fscrypt: conditionally don't wipe mk secret until the last active user
    is done
  blk-crypto: add a process bio callback
  fscrypt: add documentation about extent encryption
  btrfs: add infrastructure for safe em freeing
  btrfs: add fscrypt_info and encryption_type to ordered_extent
  btrfs: plumb through setting the fscrypt_info for ordered extents
  btrfs: plumb the fscrypt extent context through create_io_em
  btrfs: populate the ordered_extent with the fscrypt context
  btrfs: keep track of fscrypt info and orig_start for dio reads
  btrfs: add an optional encryption context to the end of file extents
  btrfs: pass through fscrypt_extent_info to the file extent helpers
  btrfs: pass the fscrypt_info through the replace extent infrastructure
  btrfs: implement the fscrypt extent encryption hooks
  btrfs: setup fscrypt_extent_info for new extents
  btrfs: populate ordered_extent with the orig offset
  btrfs: set the bio fscrypt context when applicable
  btrfs: add a bio argument to btrfs_csum_one_bio
  btrfs: add orig_logical to btrfs_bio
  btrfs: limit encrypted writes to 256 segments
  btrfs: implement process_bio cb for fscrypt
  btrfs: add test_dummy_encryption support
  btrfs: don't rewrite ret from inode_permission
  btrfs: move inode_to_path higher in backref.c
  btrfs: make btrfs_ref_to_path handle encrypted filenames
  btrfs: don't search back for dir inode item in INO_LOOKUP_USER
  btrfs: deal with encrypted symlinks in send
  btrfs: decrypt file names for send
  btrfs: load the inode context before sending writes

Omar Sandoval (7):
  fscrypt: expose fscrypt_nokey_name
  btrfs: disable various operations on encrypted inodes
  btrfs: start using fscrypt hooks
  btrfs: add inode encryption contexts
  btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
  btrfs: adapt readdir for encrypted and nokey names
  btrfs: implement fscrypt ioctls

Sweet Tea Dorminy (8):
  btrfs: disable verity on encrypted inodes
  btrfs: handle nokey names.
  btrfs: add encryption to CONFIG_BTRFS_DEBUG
  btrfs: add get_devices hook for fscrypt
  btrfs: turn on inlinecrypt mount option for encrypt
  btrfs: set file extent encryption excplicitly
  btrfs: add fscrypt_info and encryption_type to extent_map
  btrfs: explicitly track file extent length for replace and drop

 Documentation/filesystems/fscrypt.rst |  41 ++
 block/blk-crypto-fallback.c           |  40 ++
 block/blk-crypto-internal.h           |   8 +
 block/blk-crypto-profile.c            |   2 +
 block/blk-crypto.c                    |   6 +-
 fs/btrfs/Makefile                     |   1 +
 fs/btrfs/accessors.h                  |  50 +++
 fs/btrfs/backref.c                    | 114 ++++--
 fs/btrfs/bio.c                        |  75 +++-
 fs/btrfs/bio.h                        |   6 +
 fs/btrfs/btrfs_inode.h                |   3 +-
 fs/btrfs/compression.c                |   6 +
 fs/btrfs/ctree.h                      |   4 +
 fs/btrfs/defrag.c                     |  10 +-
 fs/btrfs/delayed-inode.c              |  29 +-
 fs/btrfs/delayed-inode.h              |   6 +-
 fs/btrfs/dir-item.c                   | 108 +++++-
 fs/btrfs/dir-item.h                   |  11 +-
 fs/btrfs/disk-io.c                    |   1 +
 fs/btrfs/extent_io.c                  | 114 +++++-
 fs/btrfs/extent_io.h                  |   3 +
 fs/btrfs/extent_map.c                 | 102 ++++-
 fs/btrfs/extent_map.h                 |  12 +
 fs/btrfs/file-item.c                  |  17 +-
 fs/btrfs/file-item.h                  |   7 +-
 fs/btrfs/file.c                       |  16 +-
 fs/btrfs/fs.h                         |   6 +-
 fs/btrfs/fscrypt.c                    | 412 ++++++++++++++++++++
 fs/btrfs/fscrypt.h                    | 112 ++++++
 fs/btrfs/inode.c                      | 518 ++++++++++++++++++++------
 fs/btrfs/ioctl.c                      |  68 ++--
 fs/btrfs/ordered-data.c               |  36 +-
 fs/btrfs/ordered-data.h               |  21 +-
 fs/btrfs/reflink.c                    |   8 +
 fs/btrfs/root-tree.c                  |   8 +-
 fs/btrfs/root-tree.h                  |   2 +-
 fs/btrfs/send.c                       | 133 ++++++-
 fs/btrfs/super.c                      |  75 ++++
 fs/btrfs/sysfs.c                      |   6 +
 fs/btrfs/tree-checker.c               |  66 +++-
 fs/btrfs/tree-log.c                   |  26 +-
 fs/btrfs/verity.c                     |   3 +
 fs/crypto/crypto.c                    |  10 +-
 fs/crypto/fname.c                     |  39 +-
 fs/crypto/fscrypt_private.h           |  44 +++
 fs/crypto/hooks.c                     |  42 +++
 fs/crypto/inline_crypt.c              |  87 ++++-
 fs/crypto/keyring.c                   |  18 +-
 fs/crypto/keysetup.c                  | 155 ++++++++
 fs/crypto/policy.c                    |  59 +++
 fs/super.c                            |  12 +-
 include/linux/blk-crypto.h            |   9 +-
 include/linux/fscrypt.h               | 130 +++++++
 include/uapi/linux/btrfs.h            |   1 +
 include/uapi/linux/btrfs_tree.h       |  35 +-
 55 files changed, 2619 insertions(+), 314 deletions(-)
 create mode 100644 fs/btrfs/fscrypt.c
 create mode 100644 fs/btrfs/fscrypt.h

-- 
2.41.0


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

* [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
@ 2023-12-01 22:10 ` Josef Bacik
  2023-12-05  1:58   ` Eric Biggers
  2023-12-01 22:10 ` [PATCH v4 02/46] fscrypt: add per-extent encryption support Josef Bacik
                   ` (47 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:10 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

btrfs has a variety of asynchronous things we do with inodes that can
potentially last until ->put_super, when we shut everything down and
clean up all of our async work.  Due to this we need to move
fscrypt_destroy_keyring() to after ->put_super, otherwise we get
warnings about still having active references on the master key.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/super.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 076392396e72..faf7d248145d 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -681,12 +681,6 @@ void generic_shutdown_super(struct super_block *sb)
 		fsnotify_sb_delete(sb);
 		security_sb_delete(sb);
 
-		/*
-		 * Now that all potentially-encrypted inodes have been evicted,
-		 * the fscrypt keyring can be destroyed.
-		 */
-		fscrypt_destroy_keyring(sb);
-
 		if (sb->s_dio_done_wq) {
 			destroy_workqueue(sb->s_dio_done_wq);
 			sb->s_dio_done_wq = NULL;
@@ -695,6 +689,12 @@ void generic_shutdown_super(struct super_block *sb)
 		if (sop->put_super)
 			sop->put_super(sb);
 
+		/*
+		 * Now that all potentially-encrypted inodes have been evicted,
+		 * the fscrypt keyring can be destroyed.
+		 */
+		fscrypt_destroy_keyring(sb);
+
 		if (CHECK_DATA_CORRUPTION(!list_empty(&sb->s_inodes),
 				"VFS: Busy inodes after unmount of %s (%s)",
 				sb->s_id, sb->s_type->name)) {
-- 
2.41.0


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

* [PATCH v4 02/46] fscrypt: add per-extent encryption support
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
  2023-12-01 22:10 ` [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super Josef Bacik
@ 2023-12-01 22:10 ` Josef Bacik
  2023-12-05  3:58   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper Josef Bacik
                   ` (46 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:10 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

This adds the code necessary for per-extent encryption.  We will store a
nonce for every extent we create, and then use the inode's policy and
the extents nonce to derive a per-extent key.

This is meant to be flexible, if we choose to expand the on-disk extent
information in the future we have a version number we can use to change
what exists on disk.

The file system indicates it wants to use per-extent encryption by
setting s_cop->set_extent_context.  This also requires the use of inline
block encryption.

The support is relatively straightforward, the only "extra" bit is we're
deriving a per-extent key to use for the encryption, the inode still
controls the policy and access to the master key.

Since extent based encryption uses a lot of keys, we're requiring the
use of inline block crypto if you use extent-based encryption.  This
enables us to take advantage of the built in pooling and reclamation of
the crypto structures that underpin all of the encryption.

The different encryption related options for fscrypt are too numerous to
support for extent based encryption.  Support for a few of these options
could possibly be added, but since they're niche options simply reject
them for file systems using extent based encryption.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/crypto/crypto.c          |  10 ++-
 fs/crypto/fscrypt_private.h |  44 ++++++++++
 fs/crypto/inline_crypt.c    |  84 +++++++++++++++++++
 fs/crypto/keysetup.c        | 155 ++++++++++++++++++++++++++++++++++++
 fs/crypto/policy.c          |  59 ++++++++++++++
 include/linux/fscrypt.h     |  71 +++++++++++++++++
 6 files changed, 422 insertions(+), 1 deletion(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 328470d40dec..18bd96b9db4e 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)
 {
@@ -414,12 +415,19 @@ 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_inode_info;
+		goto fail_free_extent_info;
 
 	return 0;
 
+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:
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 1892356cf924..03c006e5c47d 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -30,6 +30,8 @@
 #define FSCRYPT_CONTEXT_V1	1
 #define FSCRYPT_CONTEXT_V2	2
 
+#define FSCRYPT_EXTENT_CONTEXT_V1	1
+
 /* Keep this in sync with include/uapi/linux/fscrypt.h */
 #define FSCRYPT_MODE_MAX	FSCRYPT_MODE_AES_256_HCTR2
 
@@ -53,6 +55,28 @@ struct fscrypt_context_v2 {
 	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
 };
 
+/*
+ * fscrypt_extent_context - the encryption context of an extent
+ *
+ * This is the on-disk information stored for an extent.  The policy and
+ * relevante information is stored in the inode, the per-extent information is
+ * simply the nonce that's used in as KDF input in conjunction with the inode
+ * context to derive a per-extent key for encryption.
+ *
+ * At this point the master_key_identifier exists only for possible future
+ * expansion.  This will allow for an inode to have extents with multiple master
+ * keys, although sharing the same encryption mode.  This would be for re-keying
+ * or for reflinking between two differently encrypted inodes.  For now the
+ * master_key_descriptor must match the inode's, and we'll be using the inode's
+ * for all key derivation.
+ */
+struct fscrypt_extent_context {
+	u8 version; /* FSCRYPT_EXTENT_CONTEXT_V2 */
+	u8 encryption_mode;
+	u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
+	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+};
+
 /*
  * fscrypt_context - the encryption context of an inode
  *
@@ -288,6 +312,25 @@ struct fscrypt_inode_info {
 	u32 ci_hashed_ino;
 };
 
+/*
+ * fscrypt_extent_info - the "encryption key" for an extent.
+ *
+ * This contains the dervied key for the given extent and the nonce for the
+ * extent.
+ */
+struct fscrypt_extent_info {
+	refcount_t refs;
+
+	/* The derived key for this extent. */
+	struct fscrypt_prepared_key prep_key;
+
+	/* The super block that this extent belongs to. */
+	struct super_block *sb;
+
+	/* This is the extents nonce, loaded from the fscrypt_extent_context */
+	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+};
+
 typedef enum {
 	FS_DECRYPT = 0,
 	FS_ENCRYPT,
@@ -295,6 +338,7 @@ typedef enum {
 
 /* 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_data_unit(const struct fscrypt_inode_info *ci,
 			    fscrypt_direction_t rw, u64 index,
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index b4002aea7cdb..4eeb75410ba8 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -279,6 +279,40 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
 }
 EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
 
+/**
+ * fscrypt_set_bio_crypt_ctx() - prepare a file contents bio for inline crypto
+ * @bio: a bio which will eventually be submitted to the file
+ * @inode: the file's inode
+ * @ei: the extent's crypto info
+ * @first_lblk: the first file logical block number in the I/O
+ * @gfp_mask: memory allocation flags - these must be a waiting mask so that
+ *					bio_crypt_set_ctx can't fail.
+ *
+ * If the contents of the file should be encrypted (or decrypted) with inline
+ * encryption, then assign the appropriate encryption context to the bio.
+ *
+ * Normally the bio should be newly allocated (i.e. no pages added yet), as
+ * otherwise fscrypt_mergeable_bio() won't work as intended.
+ *
+ * The encryption context will be freed automatically when the bio is freed.
+ */
+void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
+					   const struct inode *inode,
+					   const struct fscrypt_extent_info *ei,
+					   u64 first_lblk, gfp_t gfp_mask)
+{
+	const struct fscrypt_inode_info *ci;
+	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+
+	if (!fscrypt_inode_uses_inline_crypto(inode))
+		return;
+	ci = inode->i_crypt_info;
+
+	fscrypt_generate_dun(ci, first_lblk, dun);
+	bio_crypt_set_ctx(bio, ei->prep_key.blk_key, dun, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_from_extent);
+
 /* Extract the inode and logical block number from a buffer_head. */
 static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh,
 				      const struct inode **inode_ret,
@@ -370,6 +404,56 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 }
 EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio);
 
+/**
+ * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio
+ * @bio: the bio being built up
+ * @inode: the inode for the next part of the I/O
+ * @ei: the fscrypt_extent_info for this extent
+ * @next_lblk: the next file logical block number in the I/O
+ *
+ * When building a bio which may contain data which should undergo inline
+ * encryption (or decryption) via fscrypt, filesystems should call this function
+ * to ensure that the resulting bio contains only contiguous data unit numbers.
+ * This will return false if the next part of the I/O cannot be merged with the
+ * bio because either the encryption key would be different or the encryption
+ * data unit numbers would be discontiguous.
+ *
+ * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called on the
+ * bio.
+ *
+ * This function isn't required in cases where crypto-mergeability is ensured in
+ * another way, such as I/O targeting only a single file (and thus a single key)
+ * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity.
+ *
+ * Return: true iff the I/O is mergeable
+ */
+bool fscrypt_mergeable_extent_bio(struct bio *bio, const struct inode *inode,
+				  const struct fscrypt_extent_info *ei,
+				  u64 next_lblk)
+{
+	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
+	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
+
+	if (!ei)
+		return true;
+	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
+		return false;
+	if (!bc)
+		return true;
+
+	/*
+	 * 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 != ei->prep_key.blk_key)
+		return false;
+
+	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
+	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
+}
+EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio);
+
 /**
  * fscrypt_mergeable_bio_bh() - test whether data can be added to a bio
  * @bio: the bio being built up
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index d71f7c799e79..c19bf6239dcd 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -812,3 +812,158 @@ int fscrypt_drop_inode(struct inode *inode)
 	return !READ_ONCE(ci->ci_master_key->mk_present);
 }
 EXPORT_SYMBOL_GPL(fscrypt_drop_inode);
+
+static struct fscrypt_extent_info *
+setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_SIZE])
+{
+	struct fscrypt_extent_info *ei;
+	struct fscrypt_inode_info *ci;
+	struct fscrypt_master_key *mk;
+	u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
+	int err;
+
+	ci = inode->i_crypt_info;
+	mk = ci->ci_master_key;
+	if (!mk)
+		return ERR_PTR(-ENOKEY);
+
+	ei = kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL);
+	if (!ei)
+		return ERR_PTR(-ENOMEM);
+
+	refcount_set(&ei->refs, 1);
+	memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
+	ei->sb = inode->i_sb;
+
+	down_read(&mk->mk_sem);
+	/*
+	 * We specifically don't do is_master_key_secret_present() here because
+	 * if the inode is open and has a reference on the master key then it
+	 * should be available for us to use.
+	 */
+
+	err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
+				  HKDF_CONTEXT_PER_FILE_ENC_KEY, ei->nonce,
+				  FSCRYPT_FILE_NONCE_SIZE, derived_key,
+				  ci->ci_mode->keysize);
+	if (err)
+		goto out_free;
+
+	err = fscrypt_prepare_inline_crypt_key(&ei->prep_key, derived_key, ci);
+	memzero_explicit(derived_key, ci->ci_mode->keysize);
+	if (err)
+		goto out_free;
+	up_read(&mk->mk_sem);
+	return ei;
+out_free:
+	up_read(&mk->mk_sem);
+	memzero_explicit(ei, sizeof(*ei));
+	kmem_cache_free(fscrypt_extent_info_cachep, ei);
+	return ERR_PTR(err);
+}
+
+/**
+ * fscrypt_prepare_new_extent() - prepare to create a new extent for a file
+ * @inode: the possibly-encrypted inode
+ *
+ * If the inode is encrypted, setup the fscrypt_extent_info for a new extent.
+ * This will include the nonce and the derived key necessary for the extent to
+ * be encrypted.  This is only meant to be used with inline crypto.
+ *
+ * This doesn't persist the new extents encryption context, this is done later
+ * by calling fscrypt_set_extent_context().
+ *
+ * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
+ *	   we're not encrypted, or another -errno code
+ */
+struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode)
+{
+	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+
+	if (!fscrypt_inode_uses_inline_crypto(inode))
+		return ERR_PTR(-EOPNOTSUPP);
+
+	get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
+	return setup_extent_info(inode, nonce);
+}
+EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
+
+/**
+ * fscrypt_load_extent_info() - create an fscrypt_extent_info from the context
+ * @inode: the inode
+ * @ctx: the context buffer
+ * @ctx_size: the size of the context buffer
+ *
+ * Create the file_extent_info and derive the key based on the
+ * fscrypt_extent_context buffer that is probided.
+ *
+ * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
+ *	   we're not encrypted, or another -errno code
+ */
+struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
+						     u8 *ctx, size_t ctx_size)
+{
+	struct fscrypt_extent_context extent_ctx;
+	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
+	const struct fscrypt_policy_v2 *policy = &ci->ci_policy.v2;
+
+	if (!fscrypt_inode_uses_inline_crypto(inode))
+		return ERR_PTR(-EOPNOTSUPP);
+	if (ctx_size < sizeof(extent_ctx))
+		return ERR_PTR(-EINVAL);
+
+	memcpy(&extent_ctx, ctx, sizeof(extent_ctx));
+
+	/*
+	 * For now we need to validate that the master key for the inode matches
+	 * the extent.
+	 */
+	if (memcmp(extent_ctx.master_key_identifier,
+		   policy->master_key_identifier,
+		   sizeof(extent_ctx.master_key_identifier)))
+		return ERR_PTR(-EINVAL);
+
+	return setup_extent_info(inode, extent_ctx.nonce);
+}
+EXPORT_SYMBOL(fscrypt_load_extent_info);
+
+/**
+ * fscrypt_put_extent_info() - free the extent_info fscrypt data
+ * @ei: the extent_info being evicted
+ *
+ * Drop a reference and possibly free the fscrypt_extent_info.
+ *
+ * Note this will unload the key from the block layer, which takes the crypto
+ * profile semaphore to unload the key.  Make sure you're not dropping this in a
+ * context that can't sleep.
+ */
+void fscrypt_put_extent_info(struct fscrypt_extent_info *ei)
+{
+	if (!ei)
+		return;
+
+	if (!refcount_dec_and_test(&ei->refs))
+		return;
+
+	fscrypt_destroy_prepared_key(ei->sb, &ei->prep_key);
+	memzero_explicit(ei, sizeof(*ei));
+	kmem_cache_free(fscrypt_extent_info_cachep, ei);
+}
+EXPORT_SYMBOL_GPL(fscrypt_put_extent_info);
+
+/**
+ * fscrypt_get_extent_info() - get a ref on the fscrypt extent info
+ * @ei: the extent_info to get.
+ *
+ * Get a reference on the fscrypt_extent_info.
+ *
+ * Return: the ei with an extra ref, NULL if there was no ei passed in.
+ */
+struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei)
+{
+	if (!ei)
+		return NULL;
+	refcount_inc(&ei->refs);
+	return ei;
+}
+EXPORT_SYMBOL_GPL(fscrypt_get_extent_info);
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 701259991277..75a69f02f11d 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -209,6 +209,12 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
 		return false;
 	}
 
+	if (inode->i_sb->s_cop->has_per_extent_encryption) {
+		fscrypt_warn(inode,
+			     "v1 policies can't be used on file systems that use extent encryption");
+		return false;
+	}
+
 	return true;
 }
 
@@ -269,6 +275,12 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
 		}
 	}
 
+	if ((inode->i_sb->s_cop->has_per_extent_encryption) && count) {
+		fscrypt_warn(inode,
+			     "Encryption flags aren't supported on file systems that use extent encryption");
+		return false;
+	}
+
 	if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
 	    !supported_direct_key_modes(inode, policy->contents_encryption_mode,
 					policy->filenames_encryption_mode))
@@ -789,6 +801,53 @@ 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 of a new extent
+ * @inode: the inode this extent belongs to
+ * @ei: the fscrypt_extent_info for the given extent
+ * @buf: the buffer to copy the fscrypt extent context into
+ *
+ * This should be called after fscrypt_prepare_new_extent(), using the
+ * fscrypt_extent_info that was created at that point.
+ *
+ * Return: the size of the fscrypt_extent_context, errno if the inode has the
+ *	   wrong policy version.
+ */
+ssize_t fscrypt_set_extent_context(struct inode *inode,
+				   struct fscrypt_extent_info *ei, u8 *buf)
+{
+	struct fscrypt_extent_context *ctx = (struct fscrypt_extent_context *)buf;
+	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
+
+	if (ci->ci_policy.version != 2)
+		return -EINVAL;
+
+	ctx->version = 1;
+	memcpy(ctx->master_key_identifier,
+	       ci->ci_policy.v2.master_key_identifier,
+	       sizeof(ctx->master_key_identifier));
+	memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE);
+	return sizeof(struct fscrypt_extent_context);
+}
+EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);
+
+/**
+ * fscrypt_extent_context_size() - Return the size of the on-disk extent context
+ * @inode: the inode this extent belongs to.
+ *
+ * Return the size of the extent context for this inode.  Since there is only
+ * one extent context version currently this is just the size of the extent
+ * context if the inode is encrypted.
+ */
+size_t fscrypt_extent_context_size(struct inode *inode)
+{
+	if (!IS_ENCRYPTED(inode))
+		return 0;
+
+	return sizeof(struct fscrypt_extent_context);
+}
+EXPORT_SYMBOL_GPL(fscrypt_extent_context_size);
+
 /**
  * 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 12f9e455d569..ea8fdc6f3b83 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -32,6 +32,7 @@
 
 union fscrypt_policy;
 struct fscrypt_inode_info;
+struct fscrypt_extent_info;
 struct fs_parameter;
 struct seq_file;
 
@@ -97,6 +98,14 @@ struct fscrypt_operations {
 	 */
 	unsigned int supports_subblock_data_units : 1;
 
+	/*
+	 * If set then extent based encryption will be used for this file
+	 * system, and fs/crypto/ will enforce limits on the policies that are
+	 * allowed to be chosen.  Currently this means only plain v2 policies
+	 * are supported.
+	 */
+	unsigned int has_per_extent_encryption : 1;
+
 	/*
 	 * This field exists only for backwards compatibility reasons and should
 	 * only be set by the filesystems that are setting it already.  It
@@ -308,6 +317,11 @@ 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);
+ssize_t fscrypt_set_extent_context(struct inode *inode,
+				   struct fscrypt_extent_info *ei, u8 *buf);
+struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
+						     u8 *ctx, size_t ctx_size);
+size_t fscrypt_extent_context_size(struct inode *inode);
 
 struct fscrypt_dummy_policy {
 	const union fscrypt_policy *policy;
@@ -344,6 +358,9 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
 void fscrypt_put_encryption_info(struct inode *inode);
 void fscrypt_free_inode(struct inode *inode);
 int fscrypt_drop_inode(struct inode *inode);
+struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode);
+void fscrypt_put_extent_info(struct fscrypt_extent_info *ei);
+struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei);
 
 /* fname.c */
 int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
@@ -555,6 +572,24 @@ fscrypt_free_dummy_policy(struct fscrypt_dummy_policy *dummy_policy)
 {
 }
 
+static inline ssize_t
+fscrypt_set_extent_context(struct inode *inode, struct fscrypt_extent_info *ei,
+			   u8 *buf)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline struct fscrypt_extent_info *
+fscrypt_load_extent_info(struct inode *inode, u8 *ctx, size_t ctx_size)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline size_t fscrypt_extent_context_size(struct inode *inode)
+{
+	return 0;
+}
+
 /* keyring.c */
 static inline void fscrypt_destroy_keyring(struct super_block *sb)
 {
@@ -607,6 +642,20 @@ static inline int fscrypt_drop_inode(struct inode *inode)
 	return 0;
 }
 
+static inline struct fscrypt_extent_info *
+fscrypt_prepare_new_extent(struct inode *inode)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline void fscrypt_put_extent_info(struct fscrypt_extent_info *ei) { }
+
+static inline struct fscrypt_extent_info *
+fscrypt_get_extent_info(struct fscrypt_extent_info *ei)
+{
+	return ei;
+}
+
  /* fname.c */
 static inline int fscrypt_setup_filename(struct inode *dir,
 					 const struct qstr *iname,
@@ -788,6 +837,11 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio,
 			       const struct inode *inode, u64 first_lblk,
 			       gfp_t gfp_mask);
 
+void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
+					   const struct inode *inode,
+					   const struct fscrypt_extent_info *ei,
+					   u64 first_lblk, gfp_t gfp_mask);
+
 void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio,
 				  const struct buffer_head *first_bh,
 				  gfp_t gfp_mask);
@@ -798,6 +852,10 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
 bool fscrypt_mergeable_bio_bh(struct bio *bio,
 			      const struct buffer_head *next_bh);
 
+bool fscrypt_mergeable_extent_bio(struct bio *bio, const struct inode *inode,
+				  const struct fscrypt_extent_info *ei,
+				  u64 next_lblk);
+
 bool fscrypt_dio_supported(struct inode *inode);
 
 u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks);
@@ -813,6 +871,11 @@ static inline void fscrypt_set_bio_crypt_ctx(struct bio *bio,
 					     const struct inode *inode,
 					     u64 first_lblk, gfp_t gfp_mask) { }
 
+static inline void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
+					const struct inode *inode,
+					const struct fscrypt_extent_info *ei,
+					u64 first_lblk, gfp_t gfp_mask) { }
+
 static inline void fscrypt_set_bio_crypt_ctx_bh(
 					 struct bio *bio,
 					 const struct buffer_head *first_bh,
@@ -825,6 +888,14 @@ static inline bool fscrypt_mergeable_bio(struct bio *bio,
 	return true;
 }
 
+static inline bool fscrypt_mergeable_extent_bio(struct bio *bio,
+						const struct inode *inode,
+						const struct fscrypt_extent_info *ei,
+						u64 next_lblk)
+{
+	return true;
+}
+
 static inline bool fscrypt_mergeable_bio_bh(struct bio *bio,
 					    const struct buffer_head *next_bh)
 {
-- 
2.41.0


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

* [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
  2023-12-01 22:10 ` [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super Josef Bacik
  2023-12-01 22:10 ` [PATCH v4 02/46] fscrypt: add per-extent encryption support Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  4:14   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 04/46] fscrypt: conditionally don't wipe mk secret until the last active user is done Josef Bacik
                   ` (45 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We have fscrypt_file_open() which is meant to be called on files being
opened so that their key is loaded when we start reading data from them.

However for btrfs send we are opening the inode directly without a filp,
so we need a different helper to make sure we can load the fscrypt
context for the inode before reading its contents.

We need a different helper as opposed to simply using
fscrypt_has_permitted_context() directly because of '-o
test_dummy_encryption', which allows for encrypted files to be created
with !IS_ENCRYPTED set on the directory (the root directory in this
case).  fscrypt_file_open() already does the appropriate check where it
simply doesn't call fscrypt_has_permitted_context() if the parent
directory isn't marked with IS_ENCRYPTED in order to facilitate this
invariant when using '-o test_dummy_encryption'.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/crypto/hooks.c       | 42 +++++++++++++++++++++++++++++++++++++++++
 include/linux/fscrypt.h |  8 ++++++++
 2 files changed, 50 insertions(+)

diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
index 52504dd478d3..a391a987c58f 100644
--- a/fs/crypto/hooks.c
+++ b/fs/crypto/hooks.c
@@ -49,6 +49,48 @@ int fscrypt_file_open(struct inode *inode, struct file *filp)
 }
 EXPORT_SYMBOL_GPL(fscrypt_file_open);
 
+/**
+ * fscrypt_inode_open() - prepare to open a possibly-encrypted regular file
+ * @dir: the directory that contains this inode
+ * @inode: the inode being opened
+ *
+ * Currently, an encrypted regular file can only be opened if its encryption key
+ * is available; access to the raw encrypted contents is not supported.
+ * Therefore, we first set up the inode's encryption key (if not already done)
+ * and return an error if it's unavailable.
+ *
+ * We also verify that if the parent directory is encrypted, then the inode
+ * being opened uses the same encryption policy.  This is needed as part of the
+ * enforcement that all files in an encrypted directory tree use the same
+ * encryption policy, as a protection against certain types of offline attacks.
+ * Note that this check is needed even when opening an *unencrypted* file, since
+ * it's forbidden to have an unencrypted file in an encrypted directory.
+ *
+ * File systems should be using fscrypt_file_open in their open callback.  This
+ * is for file systems that may need to open inodes outside of the normal file
+ * open path, btrfs send for example.
+ *
+ * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code
+ */
+int fscrypt_inode_open(struct inode *dir, struct inode *inode)
+{
+	int err;
+
+	err = fscrypt_require_key(inode);
+	if (err)
+		return err;
+
+	if (IS_ENCRYPTED(dir) &&
+	    !fscrypt_has_permitted_context(dir, inode)) {
+		fscrypt_warn(inode,
+			     "Inconsistent encryption context (parent directory: %lu)",
+			     dir->i_ino);
+		err = -EPERM;
+	}
+	return err;
+}
+EXPORT_SYMBOL_GPL(fscrypt_inode_open);
+
 int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
 			   struct dentry *dentry)
 {
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index ea8fdc6f3b83..756f23fc3e83 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -394,6 +394,7 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
 
 /* hooks.c */
 int fscrypt_file_open(struct inode *inode, struct file *filp);
+int fscrypt_inode_open(struct inode *dir, struct inode *inode);
 int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
 			   struct dentry *dentry);
 int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -738,6 +739,13 @@ static inline int fscrypt_file_open(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+static inline int fscrypt_inode_open(struct inode *dir, struct inode *inode)
+{
+	if (IS_ENCRYPTED(inode))
+		return -EOPNOTSUPP;
+	return 0;
+}
+
 static inline int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
 					 struct dentry *dentry)
 {
-- 
2.41.0


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

* [PATCH v4 04/46] fscrypt: conditionally don't wipe mk secret until the last active user is done
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (2 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 05/46] blk-crypto: add a process bio callback Josef Bacik
                   ` (44 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Previously we were wiping the master key secret when we do
FS_IOC_REMOVE_ENCRYPTION_KEY, and then using the fact that it was
cleared as the mechanism from keeping new users from being setup.  This
works with inode based encryption, as the per-inode key is derived at
setup time, so the secret disappearing doesn't affect any currently open
files from being able to continue working.

However for extent based encryption we do our key derivation at page
writeout and readpage time, which means we need the master key secret to
be available while we still have our file open.

Since the master key lifetime is controlled by a flag, move the clearing
of the secret to the mk_active_users cleanup stage if we have extent
based encryption enabled on this super block.  This counter represents
the actively open files that still exist on the file system, and thus
should still be able to operate normally.  Once the last user is closed
we can clear the secret.  Until then no new users are allowed, and this
allows currently open files to continue to operate until they're closed.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/crypto/keyring.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index f34a9b0b9e92..acf5e7b3196d 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -105,6 +105,14 @@ void fscrypt_put_master_key_activeref(struct super_block *sb,
 	WARN_ON_ONCE(mk->mk_present);
 	WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes));
 
+	/* We can't wipe the master key secret until the last activeref is
+	 * dropped on the master key with per-extent encryption since the key
+	 * derivation continues to happen as long as there are active refs.
+	 * Wipe it here now that we're done using it.
+	 */
+	if (sb->s_cop->has_per_extent_encryption)
+		wipe_master_key_secret(&mk->mk_secret);
+
 	for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
 		fscrypt_destroy_prepared_key(
 				sb, &mk->mk_direct_keys[i]);
@@ -129,7 +137,15 @@ static void fscrypt_initiate_key_removal(struct super_block *sb,
 					 struct fscrypt_master_key *mk)
 {
 	WRITE_ONCE(mk->mk_present, false);
-	wipe_master_key_secret(&mk->mk_secret);
+
+	/*
+	 * Per-extent encryption requires the master key to stick around until
+	 * writeout has completed as we derive the per-extent keys at writeout
+	 * time.  Once the activeref drops to 0 we'll wipe the master secret
+	 * key.
+	 */
+	if (!sb->s_cop->has_per_extent_encryption)
+		wipe_master_key_secret(&mk->mk_secret);
 	fscrypt_put_master_key_activeref(sb, mk);
 }
 
-- 
2.41.0


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

* [PATCH v4 05/46] blk-crypto: add a process bio callback
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (3 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 04/46] fscrypt: conditionally don't wipe mk secret until the last active user is done Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  4:54   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name Josef Bacik
                   ` (43 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Btrfs does checksumming, and the checksums need to match the bytes on
disk.  In order to facilitate this add a process bio callback for the
blk-crypto layer.  This allows the file system to specify a callback and
then can process the encrypted bio as necessary.

For btrfs, writes will have the checksums calculated and saved into our
relevant data structures for storage once the write completes.  For
reads we will validate the checksums match what is on disk and error out
if there is a mismatch.

This is incompatible with native encryption obviously, so make sure we
don't use native encryption if this callback is set.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 block/blk-crypto-fallback.c | 40 +++++++++++++++++++++++++++++++++++++
 block/blk-crypto-internal.h |  8 ++++++++
 block/blk-crypto-profile.c  |  2 ++
 block/blk-crypto.c          |  6 +++++-
 fs/crypto/inline_crypt.c    |  3 ++-
 include/linux/blk-crypto.h  |  9 +++++++--
 include/linux/fscrypt.h     | 14 +++++++++++++
 7 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c
index e6468eab2681..1254fed9ffeb 100644
--- a/block/blk-crypto-fallback.c
+++ b/block/blk-crypto-fallback.c
@@ -346,6 +346,15 @@ static bool blk_crypto_fallback_encrypt_bio(struct bio **bio_ptr)
 		}
 	}
 
+	/* Process the encrypted bio before we submit it. */
+	if (bc->bc_key->crypto_cfg.process_bio) {
+		blk_st = bc->bc_key->crypto_cfg.process_bio(src_bio, enc_bio);
+		if (blk_st != BLK_STS_OK) {
+			src_bio->bi_status = blk_st;
+			goto out_free_bounce_pages;
+		}
+	}
+
 	enc_bio->bi_private = src_bio;
 	enc_bio->bi_end_io = blk_crypto_fallback_encrypt_endio;
 	*bio_ptr = enc_bio;
@@ -391,6 +400,24 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
 	unsigned int i;
 	blk_status_t blk_st;
 
+	/*
+	 * Process the bio first before trying to decrypt.
+	 *
+	 * NOTE: btrfs expects that this bio is the same that was submitted.  If
+	 * at any point this changes we will need to update process_bio to take
+	 * f_ctx->crypt_iter in order to make sure we can iterate the pages for
+	 * checksumming.  We're currently saving this in our btrfs_bio, so this
+	 * works, but if at any point in the future we start allocating a bounce
+	 * bio or something we need to update this callback.
+	 */
+	if (bc->bc_key->crypto_cfg.process_bio) {
+		blk_st = bc->bc_key->crypto_cfg.process_bio(bio, bio);
+		if (blk_st != BLK_STS_OK) {
+			bio->bi_status = blk_st;
+			goto out_no_keyslot;
+		}
+	}
+
 	/*
 	 * Get a blk-crypto-fallback keyslot that contains a crypto_skcipher for
 	 * this bio's algorithm and key.
@@ -440,6 +467,19 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
 	bio_endio(bio);
 }
 
+/**
+ * blk_crypto_cfg_supports_process_bio - check if this config supports
+ *					 process_bio
+ * @profile: the profile we're checking
+ *
+ * This is just a quick check to make sure @profile is the fallback profile, as
+ * no other offload implementations support process_bio.
+ */
+bool blk_crypto_cfg_supports_process_bio(struct blk_crypto_profile *profile)
+{
+	return profile == blk_crypto_fallback_profile;
+}
+
 /**
  * blk_crypto_fallback_decrypt_endio - queue bio for fallback decryption
  *
diff --git a/block/blk-crypto-internal.h b/block/blk-crypto-internal.h
index 93a141979694..6664a04cb6ad 100644
--- a/block/blk-crypto-internal.h
+++ b/block/blk-crypto-internal.h
@@ -213,6 +213,8 @@ bool blk_crypto_fallback_bio_prep(struct bio **bio_ptr);
 
 int blk_crypto_fallback_evict_key(const struct blk_crypto_key *key);
 
+bool blk_crypto_cfg_supports_process_bio(struct blk_crypto_profile *profile);
+
 #else /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */
 
 static inline int
@@ -235,6 +237,12 @@ blk_crypto_fallback_evict_key(const struct blk_crypto_key *key)
 	return 0;
 }
 
+static inline bool
+blk_crypto_cfg_supports_process_bio(struct blk_crypto_profile *profile)
+{
+	return false;
+}
+
 #endif /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */
 
 #endif /* __LINUX_BLK_CRYPTO_INTERNAL_H */
diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c
index 7fabc883e39f..5d26d49d9372 100644
--- a/block/blk-crypto-profile.c
+++ b/block/blk-crypto-profile.c
@@ -352,6 +352,8 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile,
 		return false;
 	if (profile->max_dun_bytes_supported < cfg->dun_bytes)
 		return false;
+	if (cfg->process_bio && !blk_crypto_cfg_supports_process_bio(profile))
+		return false;
 	return true;
 }
 
diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index 4d760b092deb..50556952df19 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -321,6 +321,8 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio,
  * @dun_bytes: number of bytes that will be used to specify the DUN when this
  *	       key is used
  * @data_unit_size: the data unit size to use for en/decryption
+ * @process_bio: the call back if the upper layer needs to process the encrypted
+ *		 bio
  *
  * Return: 0 on success, -errno on failure.  The caller is responsible for
  *	   zeroizing both blk_key and raw_key when done with them.
@@ -328,7 +330,8 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio,
 int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key,
 			enum blk_crypto_mode_num crypto_mode,
 			unsigned int dun_bytes,
-			unsigned int data_unit_size)
+			unsigned int data_unit_size,
+			blk_crypto_process_bio_t process_bio)
 {
 	const struct blk_crypto_mode *mode;
 
@@ -350,6 +353,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key,
 	blk_key->crypto_cfg.crypto_mode = crypto_mode;
 	blk_key->crypto_cfg.dun_bytes = dun_bytes;
 	blk_key->crypto_cfg.data_unit_size = data_unit_size;
+	blk_key->crypto_cfg.process_bio = process_bio;
 	blk_key->data_unit_size_bits = ilog2(data_unit_size);
 	blk_key->size = mode->keysize;
 	memcpy(blk_key->raw, raw_key, mode->keysize);
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 4eeb75410ba8..57776c548a06 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -168,7 +168,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
 
 	err = blk_crypto_init_key(blk_key, raw_key, crypto_mode,
 				  fscrypt_get_dun_bytes(ci),
-				  1U << ci->ci_data_unit_bits);
+				  1U << ci->ci_data_unit_bits,
+				  sb->s_cop->process_bio);
 	if (err) {
 		fscrypt_err(inode, "error %d initializing blk-crypto key", err);
 		goto fail;
diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h
index 5e5822c18ee4..194c1d727013 100644
--- a/include/linux/blk-crypto.h
+++ b/include/linux/blk-crypto.h
@@ -6,7 +6,7 @@
 #ifndef __LINUX_BLK_CRYPTO_H
 #define __LINUX_BLK_CRYPTO_H
 
-#include <linux/types.h>
+#include <linux/blk_types.h>
 
 enum blk_crypto_mode_num {
 	BLK_ENCRYPTION_MODE_INVALID,
@@ -17,6 +17,9 @@ enum blk_crypto_mode_num {
 	BLK_ENCRYPTION_MODE_MAX,
 };
 
+typedef blk_status_t (blk_crypto_process_bio_t)(struct bio *orig_bio,
+						struct bio *enc_bio);
+
 #define BLK_CRYPTO_MAX_KEY_SIZE		64
 /**
  * struct blk_crypto_config - an inline encryption key's crypto configuration
@@ -31,6 +34,7 @@ struct blk_crypto_config {
 	enum blk_crypto_mode_num crypto_mode;
 	unsigned int data_unit_size;
 	unsigned int dun_bytes;
+	blk_crypto_process_bio_t *process_bio;
 };
 
 /**
@@ -90,7 +94,8 @@ bool bio_crypt_dun_is_contiguous(const struct bio_crypt_ctx *bc,
 int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key,
 			enum blk_crypto_mode_num crypto_mode,
 			unsigned int dun_bytes,
-			unsigned int data_unit_size);
+			unsigned int data_unit_size,
+			blk_crypto_process_bio_t process_bio);
 
 int blk_crypto_start_using_key(struct block_device *bdev,
 			       const struct blk_crypto_key *key);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 756f23fc3e83..5f5efb472fc9 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -16,6 +16,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
+#include <linux/blk-crypto.h>
 #include <uapi/linux/fscrypt.h>
 
 /*
@@ -199,6 +200,19 @@ struct fscrypt_operations {
 	 */
 	struct block_device **(*get_devices)(struct super_block *sb,
 					     unsigned int *num_devs);
+
+	/*
+	 * A callback if the file system requires the ability to process the
+	 * encrypted bio.
+	 *
+	 * @orig_bio: the original bio submitted.
+	 * @enc_bio: the encrypted bio.
+	 *
+	 * For writes the enc_bio will be different from the orig_bio, for reads
+	 * they will be the same.  For reads we get the bio before it is
+	 * decrypted, for writes we get the bio before it is submitted.
+	 */
+	blk_crypto_process_bio_t *process_bio;
 };
 
 static inline struct fscrypt_inode_info *
-- 
2.41.0


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

* [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (4 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 05/46] blk-crypto: add a process bio callback Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:03   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 07/46] fscrypt: add documentation about extent encryption Josef Bacik
                   ` (42 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

btrfs stores its data structures, including filenames in directories, in
its own buffer implementation, struct extent_buffer, composed of
several non-contiguous pages. We could copy filenames into a
temporary buffer and use fscrypt_match_name() against that buffer, such
extensive memcpying would be expensive. Instead, exposing
fscrypt_nokey_name as in this change allows btrfs to recapitulate
fscrypt_match_name() using methods on struct extent_buffer instead of
dealing with a raw byte array.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/crypto/fname.c       | 39 +--------------------------------------
 include/linux/fscrypt.h | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 7b3fc189593a..5607ee52703e 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -14,7 +14,6 @@
 #include <linux/namei.h>
 #include <linux/scatterlist.h>
 #include <crypto/hash.h>
-#include <crypto/sha2.h>
 #include <crypto/skcipher.h>
 #include "fscrypt_private.h"
 
@@ -26,43 +25,7 @@
 #define FSCRYPT_FNAME_MIN_MSG_LEN 16
 
 /*
- * struct fscrypt_nokey_name - identifier for directory entry when key is absent
- *
- * When userspace lists an encrypted directory without access to the key, the
- * filesystem must present a unique "no-key name" for each filename that allows
- * it to find the directory entry again if requested.  Naively, that would just
- * mean using the ciphertext filenames.  However, since the ciphertext filenames
- * can contain illegal characters ('\0' and '/'), they must be encoded in some
- * way.  We use base64url.  But that can cause names to exceed NAME_MAX (255
- * bytes), so we also need to use a strong hash to abbreviate long names.
- *
- * The filesystem may also need another kind of hash, the "dirhash", to quickly
- * find the directory entry.  Since filesystems normally compute the dirhash
- * over the on-disk filename (i.e. the ciphertext), it's not computable from
- * no-key names that abbreviate the ciphertext using the strong hash to fit in
- * NAME_MAX.  It's also not computable if it's a keyed hash taken over the
- * plaintext (but it may still be available in the on-disk directory entry);
- * casefolded directories use this type of dirhash.  At least in these cases,
- * each no-key name must include the name's dirhash too.
- *
- * To meet all these requirements, we base64url-encode the following
- * variable-length structure.  It contains the dirhash, or 0's if the filesystem
- * didn't provide one; up to 149 bytes of the ciphertext name; and for
- * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes.
- *
- * This ensures that each no-key name contains everything needed to find the
- * directory entry again, contains only legal characters, doesn't exceed
- * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only
- * take the performance hit of SHA-256 on very long filenames (which are rare).
- */
-struct fscrypt_nokey_name {
-	u32 dirhash[2];
-	u8 bytes[149];
-	u8 sha256[SHA256_DIGEST_SIZE];
-}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */
-
-/*
- * Decoded size of max-size no-key name, i.e. a name that was abbreviated using
+ * Decoded size of max-size nokey name, i.e. a name that was abbreviated using
  * the strong hash and thus includes the 'sha256' field.  This isn't simply
  * sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included.
  */
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 5f5efb472fc9..f57601b40e18 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/blk-crypto.h>
+#include <crypto/sha2.h>
 #include <uapi/linux/fscrypt.h>
 
 /*
@@ -56,6 +57,42 @@ struct fscrypt_name {
 #define fname_name(p)		((p)->disk_name.name)
 #define fname_len(p)		((p)->disk_name.len)
 
+/*
+ * struct fscrypt_nokey_name - identifier for directory entry when key is absent
+ *
+ * When userspace lists an encrypted directory without access to the key, the
+ * filesystem must present a unique "no-key name" for each filename that allows
+ * it to find the directory entry again if requested.  Naively, that would just
+ * mean using the ciphertext filenames.  However, since the ciphertext filenames
+ * can contain illegal characters ('\0' and '/'), they must be encoded in some
+ * way.  We use base64url.  But that can cause names to exceed NAME_MAX (255
+ * bytes), so we also need to use a strong hash to abbreviate long names.
+ *
+ * The filesystem may also need another kind of hash, the "dirhash", to quickly
+ * find the directory entry.  Since filesystems normally compute the dirhash
+ * over the on-disk filename (i.e. the ciphertext), it's not computable from
+ * no-key names that abbreviate the ciphertext using the strong hash to fit in
+ * NAME_MAX.  It's also not computable if it's a keyed hash taken over the
+ * plaintext (but it may still be available in the on-disk directory entry);
+ * casefolded directories use this type of dirhash.  At least in these cases,
+ * each no-key name must include the name's dirhash too.
+ *
+ * To meet all these requirements, we base64url-encode the following
+ * variable-length structure.  It contains the dirhash, or 0's if the filesystem
+ * didn't provide one; up to 149 bytes of the ciphertext name; and for
+ * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes.
+ *
+ * This ensures that each no-key name contains everything needed to find the
+ * directory entry again, contains only legal characters, doesn't exceed
+ * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only
+ * take the performance hit of SHA-256 on very long filenames (which are rare).
+ */
+struct fscrypt_nokey_name {
+	u32 dirhash[2];
+	u8 bytes[149];
+	u8 sha256[SHA256_DIGEST_SIZE];
+}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */
+
 /* Maximum value for the third parameter of fscrypt_operations.set_context(). */
 #define FSCRYPT_SET_CONTEXT_MAX_SIZE	40
 
-- 
2.41.0


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

* [PATCH v4 07/46] fscrypt: add documentation about extent encryption
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (5 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 08/46] btrfs: add infrastructure for safe em freeing Josef Bacik
                   ` (41 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Add a couple of sections to the fscrypt documentation about per-extent
encryption.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 Documentation/filesystems/fscrypt.rst | 41 +++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 1b84f818e574..dc8bf04484ba 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -256,6 +256,21 @@ alternative master keys or to support rotating master keys.  Instead,
 the master keys may be wrapped in userspace, e.g. as is done by the
 `fscrypt <https://github.com/google/fscrypt>`_ tool.
 
+Per-extent encryption keys
+--------------------------
+
+For certain file systems, such as btrfs, it's desired to derive a
+per-extent encryption key.  This is to enable features such as snapshots
+and reflink, where you could have different inodes pointing at the same
+extent.  When a new extent is created fscrypt randomly generates a
+16-byte nonce and the file system stores it along side the extent.
+Then, it uses a KDF (as described in `Key derivation function`_) to
+derive the extent's key from the master key and nonce.
+
+Currently the inode's master key and encryption policy must match the
+extent, so you cannot share extents between inodes that were encrypted
+differently.
+
 DIRECT_KEY policies
 -------------------
 
@@ -1394,6 +1409,27 @@ by the kernel and is used as KDF input or as a tweak to cause
 different files to be encrypted differently; see `Per-file encryption
 keys`_ and `DIRECT_KEY policies`_.
 
+Extent encryption context
+-------------------------
+
+The extent encryption context mirrors the important parts of the above
+`Encryption context`_, with a few ommisions.  The struct is defined as
+follows::
+
+        struct fscrypt_extent_context {
+                u8 version;
+                u8 encryption_mode;
+                u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
+                u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+        };
+
+Currently all fields much match the containing inode's encryption
+context, with the exception of the nonce.
+
+Additionally extent encryption is only supported with
+FSCRYPT_EXTENT_CONTEXT_V2 using the standard policy, all other policies
+are disallowed.
+
 Data path changes
 -----------------
 
@@ -1417,6 +1453,11 @@ buffer.  Some filesystems, such as UBIFS, already use temporary
 buffers regardless of encryption.  Other filesystems, such as ext4 and
 F2FS, have to allocate bounce pages specially for encryption.
 
+Inline encryption is not optional for extent encryption based file
+systems, the amount of objects required to be kept around is too much.
+Inline encryption handles the object lifetime details which results in a
+cleaner implementation.
+
 Filename hashing and encoding
 -----------------------------
 
-- 
2.41.0


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

* [PATCH v4 08/46] btrfs: add infrastructure for safe em freeing
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (6 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 07/46] fscrypt: add documentation about extent encryption Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 09/46] btrfs: disable various operations on encrypted inodes Josef Bacik
                   ` (40 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

When we add fscrypt support we're going to have fscrypt objects hanging
off of extent_maps.  This includes a block key, which if we're the last
one freeing the key we may have to unregister it from the block layer.
This requires taking a semaphore in the block layer, which means we
can't free em's under the extent map tree lock.

Thankfully we only do this in two places, one where we're dropping a
range of extent maps, and when we're freeing logged extents.  Add a
free_extent_map_safe() which will add the em to a list in the em_tree if
we free'd the object.  Currently this is unconditional but will be
changed to conditional on the fscrypt object we will add in a later
patch.

To process these delayed objects add a free_pending_extent_maps() that
is called after the lock has been dropped on the em_tree.  This will
process the extent maps on the freed list and do the appropriate freeing
work in a safe manner.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent_map.c | 69 +++++++++++++++++++++++++++++++++++++++++--
 fs/btrfs/extent_map.h | 10 +++++++
 fs/btrfs/tree-log.c   |  6 ++--
 3 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index c956b1ced69f..20d347fa6f8a 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -35,7 +35,9 @@ void __cold extent_map_exit(void)
 void extent_map_tree_init(struct extent_map_tree *tree)
 {
 	tree->map = RB_ROOT_CACHED;
+	tree->flags = 0;
 	INIT_LIST_HEAD(&tree->modified_extents);
+	INIT_LIST_HEAD(&tree->freed_extents);
 	rwlock_init(&tree->lock);
 }
 
@@ -53,6 +55,7 @@ struct extent_map *alloc_extent_map(void)
 	em->compress_type = BTRFS_COMPRESS_NONE;
 	refcount_set(&em->refs, 1);
 	INIT_LIST_HEAD(&em->list);
+	INIT_LIST_HEAD(&em->free_list);
 	return em;
 }
 
@@ -71,6 +74,65 @@ void free_extent_map(struct extent_map *em)
 	}
 }
 
+/*
+ * Drop a ref for the extent map in the given tree.
+ *
+ * @tree:	tree that the em is a part of.
+ * @em:		the em to drop the reference to.
+ *
+ * Drop the reference count on @em by one, if the reference count hits 0 and
+ * there is an object on the em that can't be safely freed in the current
+ * context (if we are holding the extent_map_tree->lock for example), then add
+ * it to the freed_extents list on the extent_map_tree for later processing.
+ *
+ * This must be followed by a free_pending_extent_maps() to clear the pending
+ * frees.
+ */
+void free_extent_map_safe(struct extent_map_tree *tree,
+			  struct extent_map *em)
+{
+	lockdep_assert_held_write(&tree->lock);
+
+	if (!em)
+		return;
+
+	if (refcount_dec_and_test(&em->refs)) {
+		WARN_ON(extent_map_in_tree(em));
+		WARN_ON(!list_empty(&em->list));
+		list_add_tail(&em->free_list, &tree->freed_extents);
+		set_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags);
+	}
+}
+
+/*
+ * Free the em objects that exist on the em tree
+ *
+ * @tree:	the tree to free the objects from.
+ *
+ * If there are any objects on the em->freed_extents list go ahead and free them
+ * here in a safe way.  This is to be coupled with any uses of
+ * free_extent_map_safe().
+ */
+void free_pending_extent_maps(struct extent_map_tree *tree)
+{
+	struct extent_map *em;
+
+	/* Avoid taking the write lock if we don't have any pending frees. */
+	if (!test_and_clear_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags))
+		return;
+
+	write_lock(&tree->lock);
+	while ((em = list_first_entry_or_null(&tree->freed_extents,
+					      struct extent_map, free_list))) {
+		list_del_init(&em->free_list);
+		write_unlock(&tree->lock);
+		__free_extent_map(em);
+		cond_resched();
+		write_lock(&tree->lock);
+	}
+	write_unlock(&tree->lock);
+}
+
 /* Do the math around the end of an extent, handling wrapping. */
 static u64 range_end(u64 start, u64 len)
 {
@@ -645,10 +707,12 @@ static void drop_all_extent_maps_fast(struct extent_map_tree *tree)
 		clear_bit(EXTENT_FLAG_PINNED, &em->flags);
 		clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
 		remove_extent_mapping(tree, em);
-		free_extent_map(em);
+		free_extent_map_safe(tree, em);
 		cond_resched_rwlock_write(&tree->lock);
 	}
 	write_unlock(&tree->lock);
+
+	free_pending_extent_maps(tree);
 }
 
 /*
@@ -869,13 +933,14 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
 		free_extent_map(em);
 next:
 		/* Once for us (for our lookup reference). */
-		free_extent_map(em);
+		free_extent_map_safe(em_tree, em);
 
 		em = next_em;
 	}
 
 	write_unlock(&em_tree->lock);
 
+	free_pending_extent_maps(em_tree);
 	free_extent_map(split);
 	free_extent_map(split2);
 }
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index bae14af197ef..fb8905f88f7c 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -51,11 +51,18 @@ struct extent_map {
 	refcount_t refs;
 	unsigned int compress_type;
 	struct list_head list;
+	struct list_head free_list;
+};
+
+enum extent_map_flags {
+	EXTENT_MAP_TREE_PENDING_FREES,
 };
 
 struct extent_map_tree {
 	struct rb_root_cached map;
+	unsigned long flags;
 	struct list_head modified_extents;
+	struct list_head freed_extents;
 	rwlock_t lock;
 };
 
@@ -84,6 +91,9 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre,
 
 struct extent_map *alloc_extent_map(void);
 void free_extent_map(struct extent_map *em);
+void free_extent_map_safe(struct extent_map_tree *tree,
+			  struct extent_map *em);
+void free_pending_extent_maps(struct extent_map_tree *tree);
 int __init extent_map_init(void);
 void __cold extent_map_exit(void);
 int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, u64 gen);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 7d6729d9fd2f..00b6f0c71e1d 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4883,7 +4883,7 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
 		 */
 		if (ret) {
 			clear_em_logging(tree, em);
-			free_extent_map(em);
+			free_extent_map_safe(tree, em);
 			continue;
 		}
 
@@ -4892,11 +4892,13 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
 		ret = log_one_extent(trans, inode, em, path, ctx);
 		write_lock(&tree->lock);
 		clear_em_logging(tree, em);
-		free_extent_map(em);
+		free_extent_map_safe(tree, em);
 	}
 	WARN_ON(!list_empty(&extents));
 	write_unlock(&tree->lock);
 
+	free_pending_extent_maps(tree);
+
 	if (!ret)
 		ret = btrfs_log_prealloc_extents(trans, inode, path);
 	if (ret)
-- 
2.41.0


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

* [PATCH v4 09/46] btrfs: disable various operations on encrypted inodes
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (7 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 08/46] btrfs: add infrastructure for safe em freeing Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 10/46] btrfs: disable verity " Josef Bacik
                   ` (39 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

Initially, only normal data extents will be encrypted. This change
forbids various other bits:
- allows reflinking only if both inodes have the same encryption status
- disable inline data on encrypted inodes

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c   | 3 ++-
 fs/btrfs/reflink.c | 7 +++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f8647d8271b7..108804a5ba82 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -639,7 +639,8 @@ static noinline int cow_file_range_inline(struct btrfs_inode *inode, u64 size,
 	 * compressed) data fits in a leaf and the configured maximum inline
 	 * size.
 	 */
-	if (size < i_size_read(&inode->vfs_inode) ||
+	if (IS_ENCRYPTED(&inode->vfs_inode) ||
+	    size < i_size_read(&inode->vfs_inode) ||
 	    size > fs_info->sectorsize ||
 	    data_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info) ||
 	    data_len > fs_info->max_inline)
diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index f88b0c2ac3fe..c39de2a43a00 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include <linux/blkdev.h>
+#include <linux/fscrypt.h>
 #include <linux/iversion.h>
 #include "ctree.h"
 #include "fs.h"
@@ -809,6 +810,12 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 		ASSERT(inode_in->i_sb == inode_out->i_sb);
 	}
 
+	/*
+	 * Can only reflink encrypted files if both files are encrypted.
+	 */
+	if (IS_ENCRYPTED(inode_in) != IS_ENCRYPTED(inode_out))
+		return -EINVAL;
+
 	/* Don't make the dst file partly checksummed */
 	if ((BTRFS_I(inode_in)->flags & BTRFS_INODE_NODATASUM) !=
 	    (BTRFS_I(inode_out)->flags & BTRFS_INODE_NODATASUM)) {
-- 
2.41.0


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

* [PATCH v4 10/46] btrfs: disable verity on encrypted inodes
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (8 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 09/46] btrfs: disable various operations on encrypted inodes Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:07   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 11/46] btrfs: start using fscrypt hooks Josef Bacik
                   ` (38 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

Right now there isn't a way to encrypt things that aren't either
filenames in directories or data on blocks on disk with extent
encryption, so for now, disable verity usage with encryption on btrfs.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/verity.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 66e2270b0dae..92536913df04 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -588,6 +588,9 @@ static int btrfs_begin_enable_verity(struct file *filp)
 
 	ASSERT(inode_is_locked(file_inode(filp)));
 
+	if (IS_ENCRYPTED(&inode->vfs_inode))
+		return -EINVAL;
+
 	if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags))
 		return -EBUSY;
 
-- 
2.41.0


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

* [PATCH v4 11/46] btrfs: start using fscrypt hooks
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (9 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 10/46] btrfs: disable verity " Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 12/46] btrfs: add inode encryption contexts Josef Bacik
                   ` (37 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

In order to appropriately encrypt, create, open, rename, and various
symlink operations must call fscrypt hooks. These determine whether the
inode should be encrypted and do other preparatory actions. The
superblock must have fscrypt operations registered, so implement the
minimal set also, and introduce the new fscrypt.[ch] files to hold the
fscrypt-specific functionality.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/Makefile      |   1 +
 fs/btrfs/btrfs_inode.h |   1 +
 fs/btrfs/file.c        |   3 ++
 fs/btrfs/fscrypt.c     |   7 +++
 fs/btrfs/fscrypt.h     |  10 ++++
 fs/btrfs/inode.c       | 110 ++++++++++++++++++++++++++++++++++-------
 fs/btrfs/super.c       |   2 +
 7 files changed, 116 insertions(+), 18 deletions(-)
 create mode 100644 fs/btrfs/fscrypt.c
 create mode 100644 fs/btrfs/fscrypt.h

diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 525af975f61c..6e51d054c17a 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -39,6 +39,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
 btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o
 btrfs-$(CONFIG_BLK_DEV_ZONED) += zoned.o
 btrfs-$(CONFIG_FS_VERITY) += verity.o
+btrfs-$(CONFIG_FS_ENCRYPTION) += fscrypt.o
 
 btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
 	tests/extent-buffer-tests.o tests/btrfs-tests.o \
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index de9f71743b6b..048eb211b54a 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -457,6 +457,7 @@ struct btrfs_new_inode_args {
 	struct posix_acl *default_acl;
 	struct posix_acl *acl;
 	struct fscrypt_name fname;
+	bool encrypt;
 };
 
 int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e9c4b947a5aa..b4a503731e79 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3711,6 +3711,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
 
 	filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC | FMODE_BUF_WASYNC |
 		        FMODE_CAN_ODIRECT;
+	ret = fscrypt_file_open(inode, filp);
+	if (ret)
+		return ret;
 
 	ret = fsverity_file_open(inode, filp);
 	if (ret)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
new file mode 100644
index 000000000000..48ab99dfe48d
--- /dev/null
+++ b/fs/btrfs/fscrypt.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "ctree.h"
+#include "fscrypt.h"
+
+const struct fscrypt_operations btrfs_fscrypt_ops = {
+};
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
new file mode 100644
index 000000000000..7f4e6888bd43
--- /dev/null
+++ b/fs/btrfs/fscrypt.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef BTRFS_FSCRYPT_H
+#define BTRFS_FSCRYPT_H
+
+#include <linux/fscrypt.h>
+
+extern const struct fscrypt_operations btrfs_fscrypt_ops;
+
+#endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 108804a5ba82..d58003eb447d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5058,6 +5058,10 @@ static int btrfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
 	if (err)
 		return err;
 
+	err = fscrypt_prepare_setattr(dentry, attr);
+	if (err)
+		return err;
+
 	if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
 		err = btrfs_setsize(inode, attr);
 		if (err)
@@ -5212,11 +5216,8 @@ void btrfs_evict_inode(struct inode *inode)
 
 	trace_btrfs_inode_evict(inode);
 
-	if (!root) {
-		fsverity_cleanup_inode(inode);
-		clear_inode(inode);
-		return;
-	}
+	if (!root)
+		goto cleanup;
 
 	evict_inode_truncate_pages(inode);
 
@@ -5316,6 +5317,9 @@ void btrfs_evict_inode(struct inode *inode)
 	 * to retry these periodically in the future.
 	 */
 	btrfs_remove_delayed_node(BTRFS_I(inode));
+
+cleanup:
+	fscrypt_put_encryption_info(inode);
 	fsverity_cleanup_inode(inode);
 	clear_inode(inode);
 }
@@ -6106,6 +6110,12 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
 		return ret;
 	}
 
+	ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt);
+	if (ret) {
+		fscrypt_free_filename(&args->fname);
+		return ret;
+	}
+
 	/* 1 to add inode item */
 	*trans_num_items = 1;
 	/* 1 to add compression property */
@@ -6587,9 +6597,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 	if (inode->i_nlink >= BTRFS_LINK_MAX)
 		return -EMLINK;
 
+	err = fscrypt_prepare_link(old_dentry, dir, dentry);
+	if (err)
+		return err;
+
 	err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
 	if (err)
-		goto fail;
+		return err;
 
 	err = btrfs_set_inode_index(BTRFS_I(dir), &index);
 	if (err)
@@ -8550,6 +8564,7 @@ void btrfs_test_destroy_inode(struct inode *inode)
 
 void btrfs_free_inode(struct inode *inode)
 {
+	fscrypt_free_inode(inode);
 	kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
 }
 
@@ -8620,8 +8635,7 @@ int btrfs_drop_inode(struct inode *inode)
 	/* the snap/subvol tree is on deleting */
 	if (btrfs_root_refs(&root->root_item) == 0)
 		return 1;
-	else
-		return generic_drop_inode(inode);
+	return generic_drop_inode(inode) || fscrypt_drop_inode(inode);
 }
 
 static void init_once(void *foo)
@@ -9201,6 +9215,11 @@ static int btrfs_rename2(struct mnt_idmap *idmap, struct inode *old_dir,
 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
 		return -EINVAL;
 
+	ret = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry,
+				     flags);
+	if (ret)
+		return ret;
+
 	if (flags & RENAME_EXCHANGE)
 		ret = btrfs_rename_exchange(old_dir, old_dentry, new_dir,
 					    new_dentry);
@@ -9415,15 +9434,31 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	};
 	unsigned int trans_num_items;
 	int err;
-	int name_len;
 	int datasize;
 	unsigned long ptr;
 	struct btrfs_file_extent_item *ei;
 	struct extent_buffer *leaf;
+	struct fscrypt_str disk_link;
+	size_t max_len;
+	u32 name_len = strlen(symname);
 
-	name_len = strlen(symname);
-	if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
-		return -ENAMETOOLONG;
+	/*
+	 * BTRFS_MAX_INLINE_DATA_SIZE() isn't actually telling the truth, we
+	 * actually limit inline data extents to
+	 * min(BTRFS_MAX_INLINE_DATA_SIZE(), sectorsize), so adjust max_len
+	 * given this wonderful bit of inconsistency.
+	 */
+	max_len = min_t(size_t, BTRFS_MAX_INLINE_DATA_SIZE(fs_info),
+			fs_info->sectorsize);
+
+	/*
+	 * fscrypt sets disk_link.len to be len + 1, including a NUL terminator, but we
+	 * don't store that '\0' character.
+	 */
+	err = fscrypt_prepare_symlink(dir, symname, name_len, max_len + 1,
+				      &disk_link);
+	if (err)
+		return err;
 
 	inode = new_inode(dir->i_sb);
 	if (!inode)
@@ -9432,8 +9467,8 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	inode->i_op = &btrfs_symlink_inode_operations;
 	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &btrfs_aops;
-	btrfs_i_size_write(BTRFS_I(inode), name_len);
-	inode_set_bytes(inode, name_len);
+	btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1);
+	inode_set_bytes(inode, disk_link.len - 1);
 
 	new_inode_args.inode = inode;
 	err = btrfs_new_inode_prepare(&new_inode_args, &trans_num_items);
@@ -9460,10 +9495,23 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
 		inode = NULL;
 		goto out;
 	}
+
+	if (IS_ENCRYPTED(inode)) {
+		err = fscrypt_encrypt_symlink(inode, symname, name_len,
+					      &disk_link);
+		if (err) {
+			btrfs_abort_transaction(trans, err);
+			btrfs_free_path(path);
+			discard_new_inode(inode);
+			inode = NULL;
+			goto out;
+		}
+	}
+
 	key.objectid = btrfs_ino(BTRFS_I(inode));
 	key.offset = 0;
 	key.type = BTRFS_EXTENT_DATA_KEY;
-	datasize = btrfs_file_extent_calc_inline_size(name_len);
+	datasize = btrfs_file_extent_calc_inline_size(disk_link.len - 1);
 	err = btrfs_insert_empty_item(trans, root, path, &key,
 				      datasize);
 	if (err) {
@@ -9482,10 +9530,10 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	btrfs_set_file_extent_encryption(leaf, ei, 0);
 	btrfs_set_file_extent_compression(leaf, ei, 0);
 	btrfs_set_file_extent_other_encoding(leaf, ei, 0);
-	btrfs_set_file_extent_ram_bytes(leaf, ei, name_len);
+	btrfs_set_file_extent_ram_bytes(leaf, ei, disk_link.len - 1);
 
 	ptr = btrfs_file_extent_inline_start(ei);
-	write_extent_buffer(leaf, symname, ptr, name_len);
+	write_extent_buffer(leaf, disk_link.name, ptr, disk_link.len - 1);
 	btrfs_mark_buffer_dirty(trans, leaf);
 	btrfs_free_path(path);
 
@@ -9502,6 +9550,29 @@ static int btrfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	return err;
 }
 
+static const char *btrfs_get_link(struct dentry *dentry, struct inode *inode,
+				  struct delayed_call *done)
+{
+	struct page *cpage;
+	const char *paddr;
+	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
+
+	if (!IS_ENCRYPTED(inode))
+		return page_get_link(dentry, inode, done);
+
+	if (!dentry)
+		return ERR_PTR(-ECHILD);
+
+	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(cpage))
+		return ERR_CAST(cpage);
+
+	paddr = fscrypt_get_symlink(inode, page_address(cpage),
+				    BTRFS_MAX_INLINE_DATA_SIZE(fs_info), done);
+	put_page(cpage);
+	return paddr;
+}
+
 static struct btrfs_trans_handle *insert_prealloc_file_extent(
 				       struct btrfs_trans_handle *trans_in,
 				       struct btrfs_inode *inode,
@@ -10983,7 +11054,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
 	.update_time	= btrfs_update_time,
 };
 static const struct inode_operations btrfs_symlink_inode_operations = {
-	.get_link	= page_get_link,
+	.get_link	= btrfs_get_link,
 	.getattr	= btrfs_getattr,
 	.setattr	= btrfs_setattr,
 	.permission	= btrfs_permission,
@@ -10993,4 +11064,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
 
 const struct dentry_operations btrfs_dentry_operations = {
 	.d_delete	= btrfs_dentry_delete,
+#ifdef CONFIG_FS_ENCRYPTION
+	.d_revalidate	= fscrypt_d_revalidate,
+#endif
 };
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 63dcb7f7b42c..4b92d6ffde5e 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -50,6 +50,7 @@
 #include "tests/btrfs-tests.h"
 #include "block-group.h"
 #include "discard.h"
+#include "fscrypt.h"
 #include "qgroup.h"
 #include "raid56.h"
 #include "fs.h"
@@ -929,6 +930,7 @@ static int btrfs_fill_super(struct super_block *sb,
 	sb->s_vop = &btrfs_verityops;
 #endif
 	sb->s_xattr = btrfs_xattr_handlers;
+	fscrypt_set_ops(sb, &btrfs_fscrypt_ops);
 	sb->s_time_gran = 1;
 	sb->s_iflags |= SB_I_CGROUPWB;
 
-- 
2.41.0


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

* [PATCH v4 12/46] btrfs: add inode encryption contexts
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (10 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 11/46] btrfs: start using fscrypt hooks Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:22   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 13/46] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Josef Bacik
                   ` (36 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

In order to store encryption information for directories, symlinks,
etc., fscrypt stores a context item with each encrypted non-regular
inode. fscrypt provides an arbitrary blob for the filesystem to store,
and it does not clearly fit into an existing structure, so this goes in
a new item type.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/fscrypt.c              | 117 ++++++++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h              |   2 +
 fs/btrfs/inode.c                |  19 ++++++
 fs/btrfs/ioctl.c                |   8 ++-
 include/uapi/linux/btrfs_tree.h |  10 +++
 5 files changed, 154 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 48ab99dfe48d..0e4011d6b1cd 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -1,7 +1,124 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#include <linux/iversion.h>
 #include "ctree.h"
+#include "accessors.h"
+#include "btrfs_inode.h"
+#include "disk-io.h"
+#include "fs.h"
 #include "fscrypt.h"
+#include "ioctl.h"
+#include "messages.h"
+#include "transaction.h"
+#include "xattr.h"
+
+static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+	struct btrfs_key key = {
+		.objectid = btrfs_ino(BTRFS_I(inode)),
+		.type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
+		.offset = 0,
+	};
+	struct btrfs_path *path;
+	struct extent_buffer *leaf;
+	unsigned long ptr;
+	int ret;
+
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
+	if (ret) {
+		len = -ENOENT;
+		goto out;
+	}
+
+	leaf = path->nodes[0];
+	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+	/* fscrypt provides max context length, but it could be less */
+	len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
+	read_extent_buffer(leaf, ctx, ptr, len);
+
+out:
+	btrfs_free_path(path);
+	return len;
+}
+
+static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
+				     size_t len, void *fs_data)
+{
+	struct btrfs_trans_handle *trans = fs_data;
+	struct btrfs_key key = {
+		.objectid = btrfs_ino(BTRFS_I(inode)),
+		.type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
+		.offset = 0,
+	};
+	struct btrfs_path *path = NULL;
+	struct extent_buffer *leaf;
+	unsigned long ptr;
+	int ret;
+
+	if (!trans)
+		trans = btrfs_start_transaction(BTRFS_I(inode)->root, 2);
+	if (IS_ERR(trans))
+		return PTR_ERR(trans);
+
+	path = btrfs_alloc_path();
+	if (!path) {
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
+	if (ret < 0)
+		goto out_err;
+
+	if (ret > 0) {
+		btrfs_release_path(path);
+		ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len);
+		if (ret)
+			goto out_err;
+	}
+
+	leaf = path->nodes[0];
+	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+
+	len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
+	write_extent_buffer(leaf, ctx, ptr, len);
+	btrfs_mark_buffer_dirty(trans, leaf);
+	btrfs_release_path(path);
+
+	if (fs_data)
+		return ret;
+
+	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+	btrfs_sync_inode_flags_to_i_flags(inode);
+	inode_inc_iversion(inode);
+	inode_set_ctime_current(inode);
+	ret = btrfs_update_inode(trans, BTRFS_I(inode));
+	if (ret)
+		goto out_abort;
+	btrfs_free_path(path);
+	btrfs_end_transaction(trans);
+	return 0;
+out_abort:
+	btrfs_abort_transaction(trans, ret);
+out_err:
+	if (!fs_data)
+		btrfs_end_transaction(trans);
+	btrfs_free_path(path);
+	return ret;
+}
+
+static bool btrfs_fscrypt_empty_dir(struct inode *inode)
+{
+	return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
+}
 
 const struct fscrypt_operations btrfs_fscrypt_ops = {
+	.get_context = btrfs_fscrypt_get_context,
+	.set_context = btrfs_fscrypt_set_context,
+	.empty_dir = btrfs_fscrypt_empty_dir,
 };
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 7f4e6888bd43..80adb7e56826 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -5,6 +5,8 @@
 
 #include <linux/fscrypt.h>
 
+#include "fs.h"
+
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
 
 #endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index d58003eb447d..cc52af10a520 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -62,6 +62,7 @@
 #include "defrag.h"
 #include "dir-item.h"
 #include "file-item.h"
+#include "fscrypt.h"
 #include "uuid-tree.h"
 #include "ioctl.h"
 #include "file.h"
@@ -6097,6 +6098,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
 	struct inode *inode = args->inode;
 	int ret;
 
+	if (fscrypt_is_nokey_name(args->dentry))
+		return -ENOKEY;
+
 	if (!args->orphan) {
 		ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0,
 					     &args->fname);
@@ -6132,6 +6136,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
 	if (dir->i_security)
 		(*trans_num_items)++;
 #endif
+	/* 1 to add fscrypt item */
+	if (args->encrypt)
+		(*trans_num_items)++;
 	if (args->orphan) {
 		/* 1 to add orphan item */
 		(*trans_num_items)++;
@@ -6317,6 +6324,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
 	BTRFS_I(inode)->i_otime_sec = ts.tv_sec;
 	BTRFS_I(inode)->i_otime_nsec = ts.tv_nsec;
 
+	if (args->encrypt) {
+		BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+		btrfs_sync_inode_flags_to_i_flags(inode);
+	}
+
 	/*
 	 * We're going to fill the inode item now, so at this point the inode
 	 * must be fully initialized.
@@ -6391,6 +6403,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
 			goto discard;
 		}
 	}
+	if (args->encrypt) {
+		ret = fscrypt_set_context(inode, trans);
+		if (ret) {
+			btrfs_abort_transaction(trans, ret);
+			goto discard;
+		}
+	}
 
 	inode_tree_add(BTRFS_I(inode));
 
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index dfe257e1845b..6acf898f693e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -156,6 +156,8 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode)
 		iflags |= FS_DIRSYNC_FL;
 	if (flags & BTRFS_INODE_NODATACOW)
 		iflags |= FS_NOCOW_FL;
+	if (flags & BTRFS_INODE_ENCRYPT)
+		iflags |= FS_ENCRYPT_FL;
 	if (ro_flags & BTRFS_INODE_RO_VERITY)
 		iflags |= FS_VERITY_FL;
 
@@ -185,12 +187,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
 		new_fl |= S_NOATIME;
 	if (binode->flags & BTRFS_INODE_DIRSYNC)
 		new_fl |= S_DIRSYNC;
+	if (binode->flags & BTRFS_INODE_ENCRYPT)
+		new_fl |= S_ENCRYPTED;
 	if (binode->ro_flags & BTRFS_INODE_RO_VERITY)
 		new_fl |= S_VERITY;
 
 	set_mask_bits(&inode->i_flags,
 		      S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC |
-		      S_VERITY, new_fl);
+		      S_VERITY | S_ENCRYPTED, new_fl);
 }
 
 /*
@@ -203,7 +207,7 @@ static int check_fsflags(unsigned int old_flags, unsigned int flags)
 		      FS_NOATIME_FL | FS_NODUMP_FL | \
 		      FS_SYNC_FL | FS_DIRSYNC_FL | \
 		      FS_NOCOMP_FL | FS_COMPR_FL |
-		      FS_NOCOW_FL))
+		      FS_NOCOW_FL | FS_ENCRYPT_FL))
 		return -EOPNOTSUPP;
 
 	/* COMPR and NOCOMP on new/old are valid */
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index d24e8e121507..08f561da33cd 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -164,6 +164,8 @@
 #define BTRFS_VERITY_DESC_ITEM_KEY	36
 #define BTRFS_VERITY_MERKLE_ITEM_KEY	37
 
+#define BTRFS_FSCRYPT_CTX_ITEM_KEY	41
+
 #define BTRFS_ORPHAN_ITEM_KEY		48
 /* reserve 2-15 close to the inode for later flexibility */
 
@@ -424,6 +426,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags)
 #define BTRFS_INODE_NOATIME		(1U << 9)
 #define BTRFS_INODE_DIRSYNC		(1U << 10)
 #define BTRFS_INODE_COMPRESS		(1U << 11)
+#define BTRFS_INODE_ENCRYPT	(1U << 12)
 
 #define BTRFS_INODE_ROOT_ITEM_INIT	(1U << 31)
 
@@ -440,6 +443,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags)
 	 BTRFS_INODE_NOATIME |						\
 	 BTRFS_INODE_DIRSYNC |						\
 	 BTRFS_INODE_COMPRESS |						\
+	 BTRFS_INODE_ENCRYPT |						\
 	 BTRFS_INODE_ROOT_ITEM_INIT)
 
 #define BTRFS_INODE_RO_VERITY		(1U << 0)
@@ -1069,6 +1073,12 @@ enum {
 	BTRFS_NR_FILE_EXTENT_TYPES = 3,
 };
 
+enum {
+	BTRFS_ENCRYPTION_NONE,
+	BTRFS_ENCRYPTION_FSCRYPT,
+	BTRFS_NR_ENCRYPTION_TYPES,
+};
+
 struct btrfs_file_extent_item {
 	/*
 	 * transaction id that created this extent
-- 
2.41.0


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

* [PATCH v4 13/46] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (11 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 12/46] btrfs: add inode encryption contexts Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 14/46] btrfs: adapt readdir for encrypted and nokey names Josef Bacik
                   ` (35 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

As encrypted files will be incompatible with older filesystem versions,
new filesystems should be created with an incompat flag for fscrypt,
which will gate access to the encryption ioctls.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/fs.h              | 3 ++-
 fs/btrfs/super.c           | 5 +++++
 fs/btrfs/sysfs.c           | 6 ++++++
 include/uapi/linux/btrfs.h | 1 +
 4 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index f8bb73d6ab68..1340e71d026c 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -232,7 +232,8 @@ enum {
 #define BTRFS_FEATURE_INCOMPAT_SUPP		\
 	(BTRFS_FEATURE_INCOMPAT_SUPP_STABLE |	\
 	 BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE | \
-	 BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2)
+	 BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \
+	 BTRFS_FEATURE_INCOMPAT_ENCRYPT)
 
 #else
 
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4b92d6ffde5e..39a338e92115 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2445,6 +2445,11 @@ static int __init btrfs_print_mod_info(void)
 			", fsverity=yes"
 #else
 			", fsverity=no"
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+			", fscrypt=yes"
+#else
+			", fscrypt=no"
 #endif
 			;
 	pr_info("Btrfs loaded%s\n", options);
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index e6b51fb3ddc1..4ece703d9d5f 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -304,6 +304,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(raid_stripe_tree, RAID_STRIPE_TREE);
 #ifdef CONFIG_FS_VERITY
 BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY);
 #endif
+#ifdef CONFIG_FS_ENCRYPTION
+BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPT);
+#endif /* CONFIG_FS_ENCRYPTION */
 
 /*
  * Features which depend on feature bits and may differ between each fs.
@@ -336,6 +339,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = {
 #ifdef CONFIG_FS_VERITY
 	BTRFS_FEAT_ATTR_PTR(verity),
 #endif
+#ifdef CONFIG_FS_ENCRYPTION
+	BTRFS_FEAT_ATTR_PTR(encryption),
+#endif /* CONFIG_FS_ENCRYPTION */
 	NULL
 };
 
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 7c29d82db9ee..6a0f4c0e4096 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -334,6 +334,7 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_ZONED		(1ULL << 12)
 #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2	(1ULL << 13)
 #define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE	(1ULL << 14)
+#define BTRFS_FEATURE_INCOMPAT_ENCRYPT		(1ULL << 15)
 #define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA	(1ULL << 16)
 
 struct btrfs_ioctl_feature_flags {
-- 
2.41.0


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

* [PATCH v4 14/46] btrfs: adapt readdir for encrypted and nokey names
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (12 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 13/46] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 15/46] btrfs: handle " Josef Bacik
                   ` (34 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

Deleting an encrypted file must always be permitted, even if the user
does not have the appropriate key. Therefore, for listing an encrypted
directory, so-called 'nokey' names are provided, and these nokey names
must be sufficient to look up and delete the appropriate encrypted
files. See 'struct fscrypt_nokey_name' for more information on the
format of these names.

The first part of supporting nokey names is allowing lookups by nokey
name. Only a few entry points need to support these: deleting a
directory, file, or subvolume -- each of these call
fscrypt_setup_filename() with a '1' argument, indicating that the key is
not required and therefore a nokey name may be provided. If a nokey name
is provided, the fscrypt_name returned by fscrypt_setup_filename() will
not have its disk_name field populated, but will have various other
fields set.

This change alters the relevant codepaths to pass a complete
fscrypt_name anywhere that it might contain a nokey name. When it does
contain a nokey name, the first time the name is successfully matched to
a stored name populates the disk name field of the fscrypt_name,
allowing the caller to use the normal disk name codepaths afterward.
Otherwise, the matching functionality is in close analogue to the
function fscrypt_match_name().

Functions where most callers are providing a fscrypt_str are duplicated
and adapted for a fscrypt_name, and functions where most callers are
providing a fscrypt_name are changed to so require at all callsites.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/btrfs_inode.h   |   2 +-
 fs/btrfs/delayed-inode.c |  29 ++++++-
 fs/btrfs/delayed-inode.h |   6 +-
 fs/btrfs/dir-item.c      |  77 ++++++++++++++++---
 fs/btrfs/dir-item.h      |  11 ++-
 fs/btrfs/extent_io.c     |  38 ++++++++++
 fs/btrfs/extent_io.h     |   3 +
 fs/btrfs/fscrypt.c       |  34 +++++++++
 fs/btrfs/fscrypt.h       |  19 +++++
 fs/btrfs/inode.c         | 158 ++++++++++++++++++++++++++-------------
 fs/btrfs/root-tree.c     |   8 +-
 fs/btrfs/root-tree.h     |   2 +-
 fs/btrfs/tree-log.c      |   3 +-
 13 files changed, 317 insertions(+), 73 deletions(-)

diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 048eb211b54a..a6a5b3146777 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -430,7 +430,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
 int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index);
 int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 		       struct btrfs_inode *dir, struct btrfs_inode *inode,
-		       const struct fscrypt_str *name);
+		       struct fscrypt_name *name);
 int btrfs_add_link(struct btrfs_trans_handle *trans,
 		   struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
 		   const struct fscrypt_str *name, int add_backref, u64 index);
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 91159dd7355b..0c36a0e06c12 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1765,7 +1765,9 @@ int btrfs_should_delete_dir_index(struct list_head *del_list,
 /*
  * Read dir info stored in the delayed tree.
  */
-int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
+int btrfs_readdir_delayed_dir_index(struct inode *inode,
+				    struct fscrypt_str *fstr,
+				    struct dir_context *ctx,
 				    struct list_head *ins_list)
 {
 	struct btrfs_dir_item *di;
@@ -1775,6 +1777,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
 	int name_len;
 	int over = 0;
 	unsigned char d_type;
+	size_t fstr_len = fstr->len;
 
 	/*
 	 * Changing the data of the delayed item is impossible. So
@@ -1799,8 +1802,28 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
 		d_type = fs_ftype_to_dtype(btrfs_dir_flags_to_ftype(di->type));
 		btrfs_disk_key_to_cpu(&location, &di->location);
 
-		over = !dir_emit(ctx, name, name_len,
-			       location.objectid, d_type);
+		if (di->type & BTRFS_FT_ENCRYPTED) {
+			int ret;
+			struct fscrypt_str iname = FSTR_INIT(name, name_len);
+
+			fstr->len = fstr_len;
+			/*
+			 * The hash is only used when the encryption key is not
+			 * available. But if we have delayed insertions, then we
+			 * must have the encryption key available or we wouldn't
+			 * have been able to create entries in the directory.
+			 * So, we don't calculate the hash.
+			 */
+			ret = fscrypt_fname_disk_to_usr(inode, 0, 0, &iname,
+							fstr);
+			if (ret)
+				return ret;
+			over = !dir_emit(ctx, fstr->name, fstr->len,
+					 location.objectid, d_type);
+		} else {
+			over = !dir_emit(ctx, name, name_len, location.objectid,
+					 d_type);
+		}
 
 		if (refcount_dec_and_test(&curr->refs))
 			kfree(curr);
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index 5cceb31bbd16..d369f6997df7 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -16,6 +16,8 @@
 #include <linux/refcount.h>
 #include "ctree.h"
 
+struct fscrypt_str;
+
 enum btrfs_delayed_item_type {
 	BTRFS_DELAYED_INSERTION_ITEM,
 	BTRFS_DELAYED_DELETION_ITEM
@@ -155,7 +157,9 @@ void btrfs_readdir_put_delayed_items(struct inode *inode,
 				     struct list_head *del_list);
 int btrfs_should_delete_dir_index(struct list_head *del_list,
 				  u64 index);
-int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
+int btrfs_readdir_delayed_dir_index(struct inode *inode,
+				    struct fscrypt_str *fstr,
+				    struct dir_context *ctx,
 				    struct list_head *ins_list);
 
 /* Used during directory logging. */
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 9c07d5c3e5ad..a64cfddff7f0 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -6,6 +6,7 @@
 #include "messages.h"
 #include "ctree.h"
 #include "disk-io.h"
+#include "fscrypt.h"
 #include "transaction.h"
 #include "accessors.h"
 #include "dir-item.h"
@@ -230,6 +231,47 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
 	return di;
 }
 
+/*
+ * Lookup for a directory item by fscrypt_name.
+ *
+ * @trans:	The transaction handle to use.
+ * @root:	The root of the target tree.
+ * @path:	Path to use for the search.
+ * @dir:	The inode number (objectid) of the directory.
+ * @name:	The fscrypt_name associated to the directory entry
+ * @mod:	Used to indicate if the tree search is meant for a read only
+ *		lookup or for a deletion lookup, so its value should be 0 or
+ *		-1, respectively.
+ *
+ * Returns: NULL if the dir item does not exists, an error pointer if an error
+ * happened, or a pointer to a dir item if a dir item exists for the given name.
+ */
+struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *trans,
+						   struct btrfs_root *root,
+						   struct btrfs_path *path, u64 dir,
+						   struct fscrypt_name *name, int mod)
+{
+	struct btrfs_key key;
+	struct btrfs_dir_item *di = NULL;
+	int ret = 0;
+
+	key.objectid = dir;
+	key.type = BTRFS_DIR_ITEM_KEY;
+	key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
+	/* XXX get the right hash for no-key names */
+
+	ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
+	if (ret == 0)
+		di = btrfs_match_dir_item_fname(root->fs_info, path, name);
+
+	if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT))
+		return NULL;
+	if (ret < 0)
+		di = ERR_PTR(ret);
+
+	return di;
+}
+
 int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
 				   const struct fscrypt_str *name)
 {
@@ -287,9 +329,9 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
 }
 
 /*
- * Lookup for a directory index item by name and index number.
+ * Lookup for a directory index item by fscrypt_name and index number.
  *
- * @trans:	The transaction handle to use. Can be NULL if @mod is 0.
+ * @trans:	The transaction handle to use.
  * @root:	The root of the target tree.
  * @path:	Path to use for the search.
  * @dir:	The inode number (objectid) of the directory.
@@ -327,7 +369,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
 
 struct btrfs_dir_item *
 btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
-			    u64 dirid, const struct fscrypt_str *name)
+			    u64 dirid, struct fscrypt_name *name)
 {
 	struct btrfs_dir_item *di;
 	struct btrfs_key key;
@@ -340,9 +382,7 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
 	btrfs_for_each_slot(root, &key, &key, path, ret) {
 		if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY)
 			break;
-
-		di = btrfs_match_dir_item_name(root->fs_info, path,
-					       name->name, name->len);
+		di = btrfs_match_dir_item_fname(root->fs_info, path, name);
 		if (di)
 			return di;
 	}
@@ -378,9 +418,9 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
  * this walks through all the entries in a dir item and finds one
  * for a specific name.
  */
-struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
-						 struct btrfs_path *path,
-						 const char *name, int name_len)
+struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info,
+						  struct btrfs_path *path,
+						  struct fscrypt_name *name)
 {
 	struct btrfs_dir_item *dir_item;
 	unsigned long name_ptr;
@@ -399,8 +439,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
 			btrfs_dir_data_len(leaf, dir_item);
 		name_ptr = (unsigned long)(dir_item + 1);
 
-		if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
-		    memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
+		if (btrfs_fscrypt_match_name(name, leaf, name_ptr,
+					     btrfs_dir_name_len(leaf, dir_item)))
 			return dir_item;
 
 		cur += this_len;
@@ -410,6 +450,21 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
 	return NULL;
 }
 
+/*
+ * helper function to look at the directory item pointed to by 'path'
+ * this walks through all the entries in a dir item and finds one
+ * for a specific name.
+ */
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
+						 struct btrfs_path *path,
+						 const char *name, int name_len)
+{
+	struct fscrypt_name fname = {
+		.disk_name = FSTR_INIT((char *) name, name_len)
+	};
+	return btrfs_match_dir_item_fname(fs_info, path, &fname);
+}
+
 /*
  * given a pointer into a directory item, delete it.  This
  * handles items that have more than one entry in them.
diff --git a/fs/btrfs/dir-item.h b/fs/btrfs/dir-item.h
index e40a226373d7..1d6ab8c3b879 100644
--- a/fs/btrfs/dir-item.h
+++ b/fs/btrfs/dir-item.h
@@ -6,6 +6,7 @@
 #include <linux/crc32c.h>
 
 struct fscrypt_str;
+struct fscrypt_name;
 
 int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
 			  const struct fscrypt_str *name);
@@ -16,6 +17,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
 					     struct btrfs_root *root,
 					     struct btrfs_path *path, u64 dir,
 					     const struct fscrypt_str *name, int mod);
+struct btrfs_dir_item *btrfs_lookup_dir_item_fname(
+	struct btrfs_trans_handle *trans,
+	struct btrfs_root *root,
+	struct btrfs_path *path, u64 dir,
+	struct fscrypt_name *name, int mod);
 struct btrfs_dir_item *btrfs_lookup_dir_index_item(
 			struct btrfs_trans_handle *trans,
 			struct btrfs_root *root,
@@ -23,7 +29,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index_item(
 			u64 index, const struct fscrypt_str *name, int mod);
 struct btrfs_dir_item *btrfs_search_dir_index_item(struct btrfs_root *root,
 			    struct btrfs_path *path, u64 dirid,
-			    const struct fscrypt_str *name);
+			    struct fscrypt_name *name);
 int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
 			      struct btrfs_root *root,
 			      struct btrfs_path *path,
@@ -42,6 +48,9 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
 						 struct btrfs_path *path,
 						 const char *name,
 						 int name_len);
+struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info,
+						  struct btrfs_path *path,
+						  struct fscrypt_name *name);
 
 static inline u64 btrfs_name_hash(const char *name, int len)
 {
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index f91a688abd3b..42544c0d9ee1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -4159,6 +4159,44 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb,
 	}
 }
 
+/* Take a sha256 of a portion of an extent buffer. */
+void extent_buffer_sha256(const struct extent_buffer *eb,
+			  unsigned long start,
+			  unsigned long len, u8 *out)
+{
+	size_t cur;
+	size_t offset;
+	struct page *page;
+	char *kaddr;
+	unsigned long i = get_eb_page_index(start);
+	struct sha256_state sctx;
+
+	if (check_eb_range(eb, start, len))
+		return;
+
+	offset = get_eb_offset_in_page(eb, start);
+
+	/*
+	 * TODO: This should maybe be using the crypto API, not the fallback,
+	 * but fscrypt uses the fallback and this is only used in emulation of
+	 * fscrypt's buffer sha256 method.
+	 */
+	sha256_init(&sctx);
+	while (len > 0) {
+		page = eb->pages[i];
+		assert_eb_page_uptodate(eb, page);
+
+		cur = min(len, PAGE_SIZE - offset);
+		kaddr = page_address(page);
+		sha256_update(&sctx, (u8 *)(kaddr + offset), cur);
+
+		len -= cur;
+		offset = 0;
+		i++;
+	}
+	sha256_final(&sctx, out);
+}
+
 static void __write_extent_buffer(const struct extent_buffer *eb,
 				  const void *srcv, unsigned long start,
 				  unsigned long len, bool use_memmove)
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 785b0d07d741..7d35e7344386 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -244,6 +244,9 @@ static inline int extent_buffer_uptodate(const struct extent_buffer *eb)
 
 int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
 			 unsigned long start, unsigned long len);
+void extent_buffer_sha256(const struct extent_buffer *eb,
+			  unsigned long start,
+			  unsigned long len, u8 *out);
 void read_extent_buffer(const struct extent_buffer *eb, void *dst,
 			unsigned long start,
 			unsigned long len);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 0e4011d6b1cd..6a4e4f63a660 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -5,13 +5,47 @@
 #include "accessors.h"
 #include "btrfs_inode.h"
 #include "disk-io.h"
+#include "ioctl.h"
 #include "fs.h"
 #include "fscrypt.h"
 #include "ioctl.h"
 #include "messages.h"
+#include "root-tree.h"
 #include "transaction.h"
 #include "xattr.h"
 
+/*
+ * This function is extremely similar to fscrypt_match_name() but uses an
+ * extent_buffer.
+ */
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+			      struct extent_buffer *leaf, unsigned long de_name,
+			      u32 de_name_len)
+{
+	const struct fscrypt_nokey_name *nokey_name =
+		(const struct fscrypt_nokey_name *)fname->crypto_buf.name;
+	u8 digest[SHA256_DIGEST_SIZE];
+
+	if (likely(fname->disk_name.name)) {
+		if (de_name_len != fname->disk_name.len)
+			return false;
+		return !memcmp_extent_buffer(leaf, fname->disk_name.name,
+					     de_name, de_name_len);
+	}
+
+	if (de_name_len <= sizeof(nokey_name->bytes))
+		return false;
+
+	if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name,
+				 sizeof(nokey_name->bytes)))
+		return false;
+
+	extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes),
+			     de_name_len - sizeof(nokey_name->bytes), digest);
+
+	return !memcmp(digest, nokey_name->sha256, sizeof(digest));
+}
+
 static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
 {
 	struct btrfs_key key = {
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 80adb7e56826..1647bbbcd609 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -4,9 +4,28 @@
 #define BTRFS_FSCRYPT_H
 
 #include <linux/fscrypt.h>
+#include "extent_io.h"
 
 #include "fs.h"
 
+#ifdef CONFIG_FS_ENCRYPTION
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+			      struct extent_buffer *leaf,
+			      unsigned long de_name, u32 de_name_len);
+
+#else
+static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+					    struct extent_buffer *leaf,
+					    unsigned long de_name,
+					    u32 de_name_len)
+{
+	if (de_name_len != fname_len(fname))
+		return false;
+	return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
+				     de_name_len);
+}
+#endif /* CONFIG_FS_ENCRYPTION */
+
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
 
 #endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index cc52af10a520..60d1e93c2567 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4053,7 +4053,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,
 static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 				struct btrfs_inode *dir,
 				struct btrfs_inode *inode,
-				const struct fscrypt_str *name,
+				struct fscrypt_name *name,
 				struct btrfs_rename_ctx *rename_ctx)
 {
 	struct btrfs_root *root = dir->root;
@@ -4071,7 +4071,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 		goto out;
 	}
 
-	di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1);
+	di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, name, -1);
 	if (IS_ERR_OR_NULL(di)) {
 		ret = di ? PTR_ERR(di) : -ENOENT;
 		goto err;
@@ -4099,11 +4099,14 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 		}
 	}
 
-	ret = btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index);
+	ret = btrfs_del_inode_ref(trans, root, &name->disk_name, ino, dir_ino,
+				  &index);
 	if (ret) {
+		/* This should print a base-64 encoded name if relevant? */
 		btrfs_info(fs_info,
 			"failed to delete reference to %.*s, inode %llu parent %llu",
-			name->len, name->name, ino, dir_ino);
+			name->disk_name.len, name->disk_name.name, ino,
+			dir_ino);
 		btrfs_abort_transaction(trans, ret);
 		goto err;
 	}
@@ -4124,8 +4127,10 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 	 * operations on the log tree, increasing latency for applications.
 	 */
 	if (!rename_ctx) {
-		btrfs_del_inode_ref_in_log(trans, root, name, inode, dir_ino);
-		btrfs_del_dir_entries_in_log(trans, root, name, dir, index);
+		btrfs_del_inode_ref_in_log(trans, root, &name->disk_name,
+					   inode, dir_ino);
+		btrfs_del_dir_entries_in_log(trans, root, &name->disk_name,
+					     dir, index);
 	}
 
 	/*
@@ -4143,7 +4148,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 	if (ret)
 		goto out;
 
-	btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2);
+	btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->disk_name.len * 2);
 	inode_inc_iversion(&inode->vfs_inode);
 	inode_inc_iversion(&dir->vfs_inode);
  	inode_set_mtime_to_ts(&dir->vfs_inode, inode_set_ctime_current(&dir->vfs_inode));
@@ -4154,7 +4159,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 
 int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 		       struct btrfs_inode *dir, struct btrfs_inode *inode,
-		       const struct fscrypt_str *name)
+		       struct fscrypt_name *name)
 {
 	int ret;
 
@@ -4205,7 +4210,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
 				false);
 
 	ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
-				 &fname.disk_name);
+				 &fname);
 	if (ret)
 		goto end_trans;
 
@@ -4242,8 +4247,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
 	if (ret)
 		return ret;
 
-	/* This needs to handle no-key deletions later on */
-
 	if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
 		objectid = inode->root->root_key.objectid;
 	} else if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
@@ -4260,8 +4263,8 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
 		goto out;
 	}
 
-	di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
-				   &fname.disk_name, -1);
+	di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino,
+					 &fname, -1);
 	if (IS_ERR_OR_NULL(di)) {
 		ret = di ? PTR_ERR(di) : -ENOENT;
 		goto out;
@@ -4287,7 +4290,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
 	 * call btrfs_del_root_ref, and it _shouldn't_ fail.
 	 */
 	if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
-		di = btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name);
+		di = btrfs_search_dir_index_item(root, path, dir_ino, &fname);
 		if (IS_ERR_OR_NULL(di)) {
 			if (!di)
 				ret = -ENOENT;
@@ -4304,7 +4307,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
 	} else {
 		ret = btrfs_del_root_ref(trans, objectid,
 					 root->root_key.objectid, dir_ino,
-					 &index, &fname.disk_name);
+					 &index, &fname);
 		if (ret) {
 			btrfs_abort_transaction(trans, ret);
 			goto out;
@@ -4627,7 +4630,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
 
 	/* now the directory is empty */
 	err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
-				 &fname.disk_name);
+				 &fname);
 	if (!err) {
 		btrfs_i_size_write(BTRFS_I(inode), 0);
 		/*
@@ -5332,32 +5335,23 @@ void btrfs_evict_inode(struct inode *inode)
  * If no dir entries were found, returns -ENOENT.
  * If found a corrupted location in dir entry, returns -EUCLEAN.
  */
-static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry,
+static int btrfs_inode_by_name(struct btrfs_inode *dir,
+			       struct fscrypt_name *fname,
 			       struct btrfs_key *location, u8 *type)
 {
 	struct btrfs_dir_item *di;
 	struct btrfs_path *path;
 	struct btrfs_root *root = dir->root;
 	int ret = 0;
-	struct fscrypt_name fname;
 
 	path = btrfs_alloc_path();
 	if (!path)
 		return -ENOMEM;
 
-	ret = fscrypt_setup_filename(&dir->vfs_inode, &dentry->d_name, 1, &fname);
-	if (ret < 0)
-		goto out;
-	/*
-	 * fscrypt_setup_filename() should never return a positive value, but
-	 * gcc on sparc/parisc thinks it can, so assert that doesn't happen.
-	 */
-	ASSERT(ret == 0);
-
 	/* This needs to handle no-key deletions later on */
 
-	di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir),
-				   &fname.disk_name, 0);
+	di = btrfs_lookup_dir_item_fname(NULL, root, path, btrfs_ino(dir),
+				   fname, 0);
 	if (IS_ERR_OR_NULL(di)) {
 		ret = di ? PTR_ERR(di) : -ENOENT;
 		goto out;
@@ -5369,13 +5363,13 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry,
 		ret = -EUCLEAN;
 		btrfs_warn(root->fs_info,
 "%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
-			   __func__, fname.disk_name.name, btrfs_ino(dir),
-			   location->objectid, location->type, location->offset);
+			   __func__, fname->usr_fname->name,
+			   btrfs_ino(dir), location->objectid,
+			   location->type, location->offset);
 	}
 	if (!ret)
 		*type = btrfs_dir_ftype(path->nodes[0], di);
 out:
-	fscrypt_free_filename(&fname);
 	btrfs_free_path(path);
 	return ret;
 }
@@ -5653,20 +5647,27 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 	struct btrfs_root *root = BTRFS_I(dir)->root;
 	struct btrfs_root *sub_root = root;
 	struct btrfs_key location;
+	struct fscrypt_name fname;
 	u8 di_type = 0;
 	int ret = 0;
 
 	if (dentry->d_name.len > BTRFS_NAME_LEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
-	ret = btrfs_inode_by_name(BTRFS_I(dir), dentry, &location, &di_type);
-	if (ret < 0)
+	ret = fscrypt_prepare_lookup(dir, dentry, &fname);
+	if (ret)
 		return ERR_PTR(ret);
 
+	ret = btrfs_inode_by_name(BTRFS_I(dir), &fname, &location, &di_type);
+	if (ret < 0) {
+		inode = ERR_PTR(ret);
+		goto cleanup;
+	}
+
 	if (location.type == BTRFS_INODE_ITEM_KEY) {
 		inode = btrfs_iget(dir->i_sb, location.objectid, root);
 		if (IS_ERR(inode))
-			return inode;
+			goto cleanup;
 
 		/* Do extra check against inode mode with di_type */
 		if (btrfs_inode_type(inode) != di_type) {
@@ -5675,9 +5676,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 				  inode->i_mode, btrfs_inode_type(inode),
 				  di_type);
 			iput(inode);
-			return ERR_PTR(-EUCLEAN);
+			inode = ERR_PTR(-EUCLEAN);
+			goto cleanup;
 		}
-		return inode;
+		goto cleanup;
 	}
 
 	ret = fixup_tree_root_location(fs_info, BTRFS_I(dir), dentry,
@@ -5692,7 +5694,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 		btrfs_put_root(sub_root);
 
 		if (IS_ERR(inode))
-			return inode;
+			goto cleanup;
 
 		down_read(&fs_info->cleanup_work_sem);
 		if (!sb_rdonly(inode->i_sb))
@@ -5704,6 +5706,8 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 		}
 	}
 
+cleanup:
+	fscrypt_free_filename(&fname);
 	return inode;
 }
 
@@ -5892,18 +5896,32 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 	LIST_HEAD(del_list);
 	int ret;
 	char *name_ptr;
-	int name_len;
+	u32 name_len;
 	int entries = 0;
 	int total_len = 0;
 	bool put = false;
 	struct btrfs_key location;
+	struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
+	u32 fstr_len = 0;
 
 	if (!dir_emit_dots(file, ctx))
 		return 0;
 
+	if (BTRFS_I(inode)->flags & BTRFS_INODE_ENCRYPT) {
+		ret = fscrypt_prepare_readdir(inode);
+		if (ret)
+			return ret;
+		ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fstr);
+		if (ret)
+			return ret;
+		fstr_len = fstr.len;
+	}
+
 	path = btrfs_alloc_path();
-	if (!path)
-		return -ENOMEM;
+	if (!path) {
+		ret = -ENOMEM;
+		goto err_fstr;
+	}
 
 	addr = private->filldir_buf;
 	path->reada = READA_FORWARD;
@@ -5920,6 +5938,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 		struct dir_entry *entry;
 		struct extent_buffer *leaf = path->nodes[0];
 		u8 ftype;
+		u32 nokey_len;
 
 		if (found_key.objectid != key.objectid)
 			break;
@@ -5933,8 +5952,13 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 			continue;
 		di = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
 		name_len = btrfs_dir_name_len(leaf, di);
-		if ((total_len + sizeof(struct dir_entry) + name_len) >=
-		    PAGE_SIZE) {
+		nokey_len = DIV_ROUND_UP(name_len * 4, 3);
+		/*
+		 * If name is encrypted, and we don't have the key, we could
+		 * need up to 4/3rds the bytes to print it.
+		 */
+		if ((total_len + sizeof(struct dir_entry) + nokey_len)
+		    >= PAGE_SIZE) {
 			btrfs_release_path(path);
 			ret = btrfs_filldir(private->filldir_buf, entries, ctx);
 			if (ret)
@@ -5948,8 +5972,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 		ftype = btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di));
 		entry = addr;
 		name_ptr = (char *)(entry + 1);
-		read_extent_buffer(leaf, name_ptr,
-				   (unsigned long)(di + 1), name_len);
+		if (btrfs_dir_flags(leaf, di) & BTRFS_FT_ENCRYPTED) {
+			struct fscrypt_str oname = FSTR_INIT(name_ptr,
+							     nokey_len);
+			u32 hash = 0, minor_hash = 0;
+
+			read_extent_buffer(leaf, fstr.name,
+					   (unsigned long)(di + 1), name_len);
+			fstr.len = name_len;
+			/*
+			 * We're iterating through DIR_INDEX items, so we don't
+			 * have the DIR_ITEM hash handy. Only compute it if
+			 * we'll need it -- the nokey name stores it, so that
+			 * we can look up the appropriate item by nokey name
+			 * later on.
+			 */
+			if (!fscrypt_has_encryption_key(inode)) {
+				u64 name_hash = btrfs_name_hash(fstr.name,
+								fstr.len);
+				hash = name_hash;
+				minor_hash = name_hash >> 32;
+			}
+			ret = fscrypt_fname_disk_to_usr(inode, hash, minor_hash,
+							&fstr, &oname);
+			if (ret)
+				goto err;
+			name_len = oname.len;
+		} else {
+			read_extent_buffer(leaf, name_ptr,
+					   (unsigned long)(di + 1), name_len);
+		}
 		put_unaligned(name_len, &entry->name_len);
 		put_unaligned(fs_ftype_to_dtype(ftype), &entry->type);
 		btrfs_dir_item_key_to_cpu(leaf, di, &location);
@@ -5969,7 +6021,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 	if (ret)
 		goto nopos;
 
-	ret = btrfs_readdir_delayed_dir_index(ctx, &ins_list);
+	fstr.len = fstr_len;
+	ret = btrfs_readdir_delayed_dir_index(inode, &fstr, ctx, &ins_list);
 	if (ret)
 		goto nopos;
 
@@ -6000,6 +6053,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 	if (put)
 		btrfs_readdir_put_delayed_items(inode, &ins_list, &del_list);
 	btrfs_free_path(path);
+err_fstr:
+	fscrypt_fname_free_buffer(&fstr);
 	return ret;
 }
 
@@ -6458,6 +6513,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
 	struct btrfs_root *root = parent_inode->root;
 	u64 ino = btrfs_ino(inode);
 	u64 parent_ino = btrfs_ino(parent_inode);
+	struct fscrypt_name fname = { .disk_name = *name };
 
 	if (unlikely(ino == BTRFS_FIRST_FREE_OBJECTID)) {
 		memcpy(&key, &inode->root->root_key, sizeof(key));
@@ -6513,7 +6569,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
 		int err;
 		err = btrfs_del_root_ref(trans, key.objectid,
 					 root->root_key.objectid, parent_ino,
-					 &local_index, name);
+					 &local_index, &fname);
 		if (err)
 			btrfs_abort_transaction(trans, err);
 	} else if (add_backref) {
@@ -8894,7 +8950,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
 	} else { /* src is an inode */
 		ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
 					   BTRFS_I(old_dentry->d_inode),
-					   old_name, &old_rename_ctx);
+					   &old_fname, &old_rename_ctx);
 		if (!ret)
 			ret = btrfs_update_inode(trans, BTRFS_I(old_inode));
 	}
@@ -8909,7 +8965,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
 	} else { /* dest is an inode */
 		ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
 					   BTRFS_I(new_dentry->d_inode),
-					   new_name, &new_rename_ctx);
+					   &new_fname, &new_rename_ctx);
 		if (!ret)
 			ret = btrfs_update_inode(trans, BTRFS_I(new_inode));
 	}
@@ -9154,7 +9210,7 @@ static int btrfs_rename(struct mnt_idmap *idmap,
 	} else {
 		ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
 					   BTRFS_I(d_inode(old_dentry)),
-					   &old_fname.disk_name, &rename_ctx);
+					   &old_fname, &rename_ctx);
 		if (!ret)
 			ret = btrfs_update_inode(trans, BTRFS_I(old_inode));
 	}
@@ -9172,7 +9228,7 @@ static int btrfs_rename(struct mnt_idmap *idmap,
 		} else {
 			ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir),
 						 BTRFS_I(d_inode(new_dentry)),
-						 &new_fname.disk_name);
+						 &new_fname);
 		}
 		if (!ret && new_inode->i_nlink == 0)
 			ret = btrfs_orphan_add(trans,
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index 603ad1459368..d131934123f5 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -10,6 +10,7 @@
 #include "messages.h"
 #include "transaction.h"
 #include "disk-io.h"
+#include "fscrypt.h"
 #include "print-tree.h"
 #include "qgroup.h"
 #include "space-info.h"
@@ -334,7 +335,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans,
 
 int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
 		       u64 ref_id, u64 dirid, u64 *sequence,
-		       const struct fscrypt_str *name)
+		       struct fscrypt_name *name)
 {
 	struct btrfs_root *tree_root = trans->fs_info->tree_root;
 	struct btrfs_path *path;
@@ -356,13 +357,14 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
 	if (ret < 0) {
 		goto out;
 	} else if (ret == 0) {
+		u32 name_len;
 		leaf = path->nodes[0];
 		ref = btrfs_item_ptr(leaf, path->slots[0],
 				     struct btrfs_root_ref);
 		ptr = (unsigned long)(ref + 1);
+		name_len = btrfs_root_ref_name_len(leaf, ref);
 		if ((btrfs_root_ref_dirid(leaf, ref) != dirid) ||
-		    (btrfs_root_ref_name_len(leaf, ref) != name->len) ||
-		    memcmp_extent_buffer(leaf, name->name, ptr, name->len)) {
+		    !btrfs_fscrypt_match_name(name, leaf, ptr, name_len)) {
 			ret = -ENOENT;
 			goto out;
 		}
diff --git a/fs/btrfs/root-tree.h b/fs/btrfs/root-tree.h
index 8b2c3859e464..5ba7b36cf826 100644
--- a/fs/btrfs/root-tree.h
+++ b/fs/btrfs/root-tree.h
@@ -15,7 +15,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
 		       const struct fscrypt_str *name);
 int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
 		       u64 ref_id, u64 dirid, u64 *sequence,
-		       const struct fscrypt_str *name);
+		       struct fscrypt_name *name);
 int btrfs_del_root(struct btrfs_trans_handle *trans, const struct btrfs_key *key);
 int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 		      const struct btrfs_key *key,
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 00b6f0c71e1d..89d3dc769b15 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -901,9 +901,10 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
 				       struct btrfs_inode *inode,
 				       const struct fscrypt_str *name)
 {
+	struct fscrypt_name fname = { .disk_name = *name, };
 	int ret;
 
-	ret = btrfs_unlink_inode(trans, dir, inode, name);
+	ret = btrfs_unlink_inode(trans, dir, inode, &fname);
 	if (ret)
 		return ret;
 	/*
-- 
2.41.0


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

* [PATCH v4 15/46] btrfs: handle nokey names.
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (13 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 14/46] btrfs: adapt readdir for encrypted and nokey names Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:29   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 16/46] btrfs: implement fscrypt ioctls Josef Bacik
                   ` (33 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

For encrypted or unencrypted names, we calculate the offset for the dir
item by hashing the name for the dir item. However, this doesn't work
for a long nokey name, where we do not have the complete ciphertext.
Instead, fscrypt stores the filesystem-provided hash in the nokey name,
and we can extract it from the fscrypt_name structure in such a case.

Additionally, for nokey names, if we find the nokey name on disk we can
update the fscrypt_name with the disk name, so add that to searching for
diritems.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/dir-item.c | 37 +++++++++++++++++++++++++++++++++++--
 fs/btrfs/fscrypt.c  | 27 +++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h  | 11 +++++++++++
 3 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index a64cfddff7f0..897fb5477369 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -231,6 +231,28 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
 	return di;
 }
 
+/*
+ * If appropriate, populate the disk name for a fscrypt_name looked up without
+ * a key.
+ *
+ * @path:	The path to the extent buffer in which the name was found.
+ * @di:		The dir item corresponding.
+ * @fname:	The fscrypt_name to perhaps populate.
+ *
+ * Returns: 0 if the name is already populated or the dir item doesn't exist
+ * or the name was successfully populated, else an error code.
+ */
+static int ensure_disk_name_from_dir_item(struct btrfs_path *path,
+					  struct btrfs_dir_item *di,
+					  struct fscrypt_name *name)
+{
+	if (name->disk_name.name || !di)
+		return 0;
+
+	return btrfs_fscrypt_get_disk_name(path->nodes[0], di,
+					   &name->disk_name);
+}
+
 /*
  * Lookup for a directory item by fscrypt_name.
  *
@@ -257,8 +279,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
 
 	key.objectid = dir;
 	key.type = BTRFS_DIR_ITEM_KEY;
-	key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
-	/* XXX get the right hash for no-key names */
+
+	if (!name->disk_name.name)
+		key.offset = name->hash | ((u64)name->minor_hash << 32);
+	else
+		key.offset = btrfs_name_hash(name->disk_name.name,
+					     name->disk_name.len);
 
 	ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
 	if (ret == 0)
@@ -266,6 +292,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
 
 	if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT))
 		return NULL;
+	if (ret == 0)
+		ret = ensure_disk_name_from_dir_item(path, di, name);
 	if (ret < 0)
 		di = ERR_PTR(ret);
 
@@ -382,7 +410,12 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
 	btrfs_for_each_slot(root, &key, &key, path, ret) {
 		if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY)
 			break;
+
 		di = btrfs_match_dir_item_fname(root->fs_info, path, name);
+		if (di)
+			ret = ensure_disk_name_from_dir_item(path, di, name);
+		if (ret)
+			break;
 		if (di)
 			return di;
 	}
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 6a4e4f63a660..9103da28af7e 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -14,6 +14,33 @@
 #include "transaction.h"
 #include "xattr.h"
 
+/*
+ * From a given location in a leaf, read a name into a qstr (usually a
+ * fscrypt_name's disk_name), allocating the required buffer. Used for
+ * nokey names.
+ */
+int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+				struct btrfs_dir_item *dir_item,
+				struct fscrypt_str *name)
+{
+	unsigned long de_name_len = btrfs_dir_name_len(leaf, dir_item);
+	unsigned long de_name = (unsigned long)(dir_item + 1);
+	/*
+	 * For no-key names, we use this opportunity to find the disk
+	 * name, so future searches don't need to deal with nokey names
+	 * and we know what the encrypted size is.
+	 */
+	name->name = kmalloc(de_name_len, GFP_NOFS);
+
+	if (!name->name)
+		return -ENOMEM;
+
+	read_extent_buffer(leaf, name->name, de_name, de_name_len);
+
+	name->len = de_name_len;
+	return 0;
+}
+
 /*
  * This function is extremely similar to fscrypt_match_name() but uses an
  * extent_buffer.
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 1647bbbcd609..c08fd52c99b4 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -9,11 +9,22 @@
 #include "fs.h"
 
 #ifdef CONFIG_FS_ENCRYPTION
+int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+				struct btrfs_dir_item *di,
+				struct fscrypt_str *qstr);
+
 bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 			      struct extent_buffer *leaf,
 			      unsigned long de_name, u32 de_name_len);
 
 #else
+static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+					      struct btrfs_dir_item *di,
+					      struct fscrypt_str *qstr)
+{
+	return 0;
+}
+
 static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 					    struct extent_buffer *leaf,
 					    unsigned long de_name,
-- 
2.41.0


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

* [PATCH v4 16/46] btrfs: implement fscrypt ioctls
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (14 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 15/46] btrfs: handle " Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG Josef Bacik
                   ` (32 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

These ioctls allow encryption to actually be used.

The set_encryption_policy ioctl is the thing which actually turns on
encryption, and therefore sets the ENCRYPT flag in the superblock. This
prevents the filesystem from being loaded on older kernels.

fscrypt provides CONFIG_FS_ENCRYPTION-disabled versions of all these
functions which just return -EOPNOTSUPP, so the ioctls don't need to be
compiled out if CONFIG_FS_ENCRYPTION isn't enabled.

We could instead gate this ioctl on the superblock having the flag set,
if we wanted to require mkfs with the encrypt flag in order to have a
filesystem with any encryption.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ioctl.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6acf898f693e..9968a36079c4 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4573,6 +4573,34 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_fslabel(fs_info, argp);
 	case FS_IOC_SETFSLABEL:
 		return btrfs_ioctl_set_fslabel(file, argp);
+	case FS_IOC_SET_ENCRYPTION_POLICY: {
+		if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
+			return -EOPNOTSUPP;
+		if (sb_rdonly(fs_info->sb))
+			return -EROFS;
+		/*
+		 *  If we crash before we commit, nothing encrypted could have
+		 * been written so it doesn't matter whether the encrypted
+		 * state persists.
+		 */
+		btrfs_set_fs_incompat(fs_info, ENCRYPT);
+		return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
+	}
+	case FS_IOC_GET_ENCRYPTION_POLICY:
+		return fscrypt_ioctl_get_policy(file, (void __user *)arg);
+	case FS_IOC_GET_ENCRYPTION_POLICY_EX:
+		return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
+	case FS_IOC_ADD_ENCRYPTION_KEY:
+		return fscrypt_ioctl_add_key(file, (void __user *)arg);
+	case FS_IOC_REMOVE_ENCRYPTION_KEY:
+		return fscrypt_ioctl_remove_key(file, (void __user *)arg);
+	case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
+		return fscrypt_ioctl_remove_key_all_users(file,
+							  (void __user *)arg);
+	case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
+		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
+	case FS_IOC_GET_ENCRYPTION_NONCE:
+		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
 	case FITRIM:
 		return btrfs_ioctl_fitrim(fs_info, argp);
 	case BTRFS_IOC_SNAP_CREATE:
-- 
2.41.0


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

* [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (15 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 16/46] btrfs: implement fscrypt ioctls Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:11   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 18/46] btrfs: add get_devices hook for fscrypt Josef Bacik
                   ` (31 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

Since encryption is currently under BTRFS_DEBUG, this adds its
dependencies: inline encryption from fscrypt, and the inline encryption
fallback path from the block layer.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ioctl.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9968a36079c4..0e8e2ca48a2e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4573,6 +4573,7 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_get_fslabel(fs_info, argp);
 	case FS_IOC_SETFSLABEL:
 		return btrfs_ioctl_set_fslabel(file, argp);
+#ifdef CONFIG_BTRFS_DEBUG
 	case FS_IOC_SET_ENCRYPTION_POLICY: {
 		if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
 			return -EOPNOTSUPP;
@@ -4601,6 +4602,7 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
 	case FS_IOC_GET_ENCRYPTION_NONCE:
 		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
+#endif /* CONFIG_BTRFS_DEBUG */
 	case FITRIM:
 		return btrfs_ioctl_fitrim(fs_info, argp);
 	case BTRFS_IOC_SNAP_CREATE:
-- 
2.41.0


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

* [PATCH v4 18/46] btrfs: add get_devices hook for fscrypt
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (16 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt Josef Bacik
                   ` (30 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

Since extent encryption requires inline encryption, even though we
expect to use the inlinecrypt software fallback most of the time, we
need to enumerate all the devices in use by btrfs.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/fscrypt.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 9103da28af7e..2d037b105b5f 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -11,7 +11,9 @@
 #include "ioctl.h"
 #include "messages.h"
 #include "root-tree.h"
+#include "super.h"
 #include "transaction.h"
+#include "volumes.h"
 #include "xattr.h"
 
 /*
@@ -178,8 +180,43 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
 	return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
 }
 
+static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
+						       unsigned int *num_devs)
+{
+	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+	int nr_devices = fs_devices->open_devices;
+	struct block_device **devs;
+	struct btrfs_device *device;
+	int i = 0;
+
+	devs = kmalloc_array(nr_devices, sizeof(*devs), GFP_NOFS | GFP_NOWAIT);
+	if (!devs)
+		return ERR_PTR(-ENOMEM);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) {
+		if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA,
+						&device->dev_state) ||
+		    !device->bdev ||
+		    test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state))
+			continue;
+
+		devs[i++] = device->bdev;
+
+		if (i >= nr_devices)
+			break;
+
+	}
+	rcu_read_unlock();
+
+	*num_devs = i;
+	return devs;
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.get_context = btrfs_fscrypt_get_context,
 	.set_context = btrfs_fscrypt_set_context,
 	.empty_dir = btrfs_fscrypt_empty_dir,
+	.get_devices = btrfs_fscrypt_get_devices,
 };
-- 
2.41.0


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

* [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (17 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 18/46] btrfs: add get_devices hook for fscrypt Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:41   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 20/46] btrfs: set file extent encryption excplicitly Josef Bacik
                   ` (29 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

fscrypt's extent encryption requires the use of inline encryption or the
software fallback that the block layer provides; it is rather
complicated to allow software encryption with extent encryption due to
the timing of memory allocations. Thus, if btrfs has ever had a
encrypted file, or when encryption is enabled on a directory, update the
mount flags to include inlinecrypt.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ioctl.c |  3 +++
 fs/btrfs/super.c | 10 ++++++++++
 2 files changed, 13 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 0e8e2ca48a2e..48d751011d07 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4585,6 +4585,9 @@ long btrfs_ioctl(struct file *file, unsigned int
 		 * state persists.
 		 */
 		btrfs_set_fs_incompat(fs_info, ENCRYPT);
+		if (!(inode->i_sb->s_flags & SB_INLINECRYPT)) {
+			inode->i_sb->s_flags |= SB_INLINECRYPT;
+		}
 		return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
 	}
 	case FS_IOC_GET_ENCRYPTION_POLICY:
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 39a338e92115..462a23db26af 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -946,6 +946,16 @@ static int btrfs_fill_super(struct super_block *sb,
 		return err;
 	}
 
+	if (btrfs_fs_incompat(fs_info, ENCRYPT)) {
+		if (IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT)) {
+			sb->s_flags |= SB_INLINECRYPT;
+		} else {
+			btrfs_err(fs_info, "encryption not supported");
+			err = -EINVAL;
+			goto fail_close;
+		}
+	}
+
 	inode = btrfs_iget(sb, BTRFS_FIRST_FREE_OBJECTID, fs_info->fs_root);
 	if (IS_ERR(inode)) {
 		err = PTR_ERR(inode);
-- 
2.41.0


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

* [PATCH v4 20/46] btrfs: set file extent encryption excplicitly
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (18 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 21/46] btrfs: add fscrypt_info and encryption_type to extent_map Josef Bacik
                   ` (28 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

This puts the long-preserved 1-byte encryption field to work, storing
whether the extent is encrypted.  Update the tree-checker to allow for
the encryption bit to be set to our valid types.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/accessors.h            | 2 ++
 fs/btrfs/inode.c                | 8 ++++++--
 fs/btrfs/tree-checker.c         | 8 +++++---
 fs/btrfs/tree-log.c             | 2 ++
 include/uapi/linux/btrfs_tree.h | 8 +++++++-
 5 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index aa0844535644..5aaf204fa55f 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -949,6 +949,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
 			 struct btrfs_file_extent_item, disk_num_bytes, 64);
 BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
 			 struct btrfs_file_extent_item, compression, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
+			 struct btrfs_file_extent_item, encryption, 8);
 
 
 BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 60d1e93c2567..f894f5b5e786 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3001,7 +3001,9 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans,
 	btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes);
 	btrfs_set_stack_file_extent_ram_bytes(&stack_fi, ram_bytes);
 	btrfs_set_stack_file_extent_compression(&stack_fi, oe->compress_type);
-	/* Encryption and other encoding is reserved and all 0 */
+	btrfs_set_stack_file_extent_encryption(&stack_fi,
+					       BTRFS_ENCRYPTION_NONE);
+	/* Other encoding is reserved and always 0 */
 
 	/*
 	 * For delalloc, when completing an ordered extent we update the inode's
@@ -9671,7 +9673,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 	btrfs_set_stack_file_extent_num_bytes(&stack_fi, len);
 	btrfs_set_stack_file_extent_ram_bytes(&stack_fi, len);
 	btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE);
-	/* Encryption and other encoding is reserved and all 0 */
+	btrfs_set_stack_file_extent_encryption(&stack_fi,
+					       BTRFS_ENCRYPTION_NONE);
+	/* Other encoding is reserved and always 0 */
 
 	qgroup_released = btrfs_qgroup_release_data(inode, file_offset, len);
 	if (qgroup_released < 0)
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 50fdc69fdddf..1af2c967436b 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -211,6 +211,7 @@ static int check_extent_data_item(struct extent_buffer *leaf,
 	u32 sectorsize = fs_info->sectorsize;
 	u32 item_size = btrfs_item_size(leaf, slot);
 	u64 extent_end;
+	u8 policy;
 
 	if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) {
 		file_extent_err(leaf, slot,
@@ -262,10 +263,11 @@ static int check_extent_data_item(struct extent_buffer *leaf,
 			BTRFS_NR_COMPRESS_TYPES - 1);
 		return -EUCLEAN;
 	}
-	if (unlikely(btrfs_file_extent_encryption(leaf, fi))) {
+	policy = btrfs_file_extent_encryption(leaf, fi);
+	if (unlikely(policy >= BTRFS_NR_ENCRYPTION_TYPES)) {
 		file_extent_err(leaf, slot,
-			"invalid encryption for file extent, have %u expect 0",
-			btrfs_file_extent_encryption(leaf, fi));
+			"invalid encryption for file extent, have %u expect range [0, %u]",
+			policy, BTRFS_NR_ENCRYPTION_TYPES - 1);
 		return -EUCLEAN;
 	}
 	if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 89d3dc769b15..36063dc39443 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4627,6 +4627,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	u64 extent_offset = em->start - em->orig_start;
 	u64 block_len;
 	int ret;
+	u8 encryption = BTRFS_ENCRYPTION_NONE;
 
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
 	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4648,6 +4649,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	btrfs_set_stack_file_extent_num_bytes(&fi, em->len);
 	btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes);
 	btrfs_set_stack_file_extent_compression(&fi, em->compress_type);
+	btrfs_set_stack_file_extent_encryption(&fi, encryption);
 
 	ret = log_extent_csums(trans, inode, log, em, ctx);
 	if (ret)
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index 08f561da33cd..af56a5eb3a00 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1101,8 +1101,14 @@ struct btrfs_file_extent_item {
 	 * but not for stat.
 	 */
 	__u8 compression;
+
+	/*
+	 * Type of encryption in use. Unencrypted value is 0.
+	 */
 	__u8 encryption;
-	__le16 other_encoding; /* spare for later use */
+
+	/* spare for later use */
+	__le16 other_encoding;
 
 	/* are we inline data or a real extent? */
 	__u8 type;
-- 
2.41.0


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

* [PATCH v4 21/46] btrfs: add fscrypt_info and encryption_type to extent_map
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (19 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 20/46] btrfs: set file extent encryption excplicitly Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 22/46] btrfs: add fscrypt_info and encryption_type to ordered_extent Josef Bacik
                   ` (27 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

Each extent_map will end up with a pointer to its associated
fscrypt_info if any, which should have the same lifetime as the
extent_map. We are also going to need to track the encryption_type for
the file extent items.  Add the fscrypt_info to the extent_map, and the
subsequent code for transferring it in the split and merge cases, as
well as the code necessary to free them.  A future patch will add the
code to load them as appropriate.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/extent_map.c | 39 +++++++++++++++++++++++++++++++++++----
 fs/btrfs/extent_map.h |  2 ++
 fs/btrfs/file-item.c  |  1 +
 fs/btrfs/inode.c      |  1 +
 4 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 20d347fa6f8a..b87d26fe6785 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -59,6 +59,12 @@ struct extent_map *alloc_extent_map(void)
 	return em;
 }
 
+static void __free_extent_map(struct extent_map *em)
+{
+	fscrypt_put_extent_info(em->fscrypt_info);
+	kmem_cache_free(extent_map_cache, em);
+}
+
 /*
  * Drop the reference out on @em by one and free the structure if the reference
  * count hits zero.
@@ -70,7 +76,7 @@ void free_extent_map(struct extent_map *em)
 	if (refcount_dec_and_test(&em->refs)) {
 		WARN_ON(extent_map_in_tree(em));
 		WARN_ON(!list_empty(&em->list));
-		kmem_cache_free(extent_map_cache, em);
+		__free_extent_map(em);
 	}
 }
 
@@ -96,12 +102,24 @@ void free_extent_map_safe(struct extent_map_tree *tree,
 	if (!em)
 		return;
 
-	if (refcount_dec_and_test(&em->refs)) {
-		WARN_ON(extent_map_in_tree(em));
-		WARN_ON(!list_empty(&em->list));
+	if (!refcount_dec_and_test(&em->refs))
+		return;
+
+	WARN_ON(extent_map_in_tree(em));
+	WARN_ON(!list_empty(&em->list));
+
+	/*
+	 * We could take a lock freeing the fscrypt_info, so add this to the
+	 * list of freed_extents to be freed later.
+	 */
+	if (em->fscrypt_info) {
 		list_add_tail(&em->free_list, &tree->freed_extents);
 		set_bit(EXTENT_MAP_TREE_PENDING_FREES, &tree->flags);
+		return;
 	}
+
+	/* Nothing scary here, just free the object. */
+	__free_extent_map(em);
 }
 
 /*
@@ -274,6 +292,12 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
 	if (!list_empty(&prev->list) || !list_empty(&next->list))
 		return 0;
 
+	/*
+	 * Don't merge adjacent encrypted maps.
+	 */
+	if (prev->fscrypt_info || next->fscrypt_info)
+		return 0;
+
 	ASSERT(next->block_start != EXTENT_MAP_DELALLOC &&
 	       prev->block_start != EXTENT_MAP_DELALLOC);
 
@@ -840,6 +864,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
 			split->generation = gen;
 			split->flags = flags;
 			split->compress_type = em->compress_type;
+			split->fscrypt_info =
+				fscrypt_get_extent_info(em->fscrypt_info);
 			replace_extent_mapping(em_tree, em, split, modified);
 			free_extent_map(split);
 			split = split2;
@@ -881,6 +907,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
 				split->orig_block_len = 0;
 			}
 
+			split->fscrypt_info =
+				fscrypt_get_extent_info(em->fscrypt_info);
 			if (extent_map_in_tree(em)) {
 				replace_extent_mapping(em_tree, em, split,
 						       modified);
@@ -1043,6 +1071,7 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre,
 	split_pre->flags = flags;
 	split_pre->compress_type = em->compress_type;
 	split_pre->generation = em->generation;
+	split_pre->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info);
 
 	replace_extent_mapping(em_tree, em, split_pre, 1);
 
@@ -1062,6 +1091,8 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre,
 	split_mid->flags = flags;
 	split_mid->compress_type = em->compress_type;
 	split_mid->generation = em->generation;
+	split_mid->fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info);
+
 	add_extent_mapping(em_tree, split_mid, 1);
 
 	/* Once for us */
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index fb8905f88f7c..e1075cadfd94 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -48,8 +48,10 @@ struct extent_map {
 	 */
 	u64 generation;
 	unsigned long flags;
+	struct fscrypt_extent_info *fscrypt_info;
 	refcount_t refs;
 	unsigned int compress_type;
+	unsigned int encryption_type;
 	struct list_head list;
 	struct list_head free_list;
 };
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 45cae356e89b..26f35c1baedc 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1305,6 +1305,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 			if (type == BTRFS_FILE_EXTENT_PREALLOC)
 				set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
 		}
+		em->encryption_type = btrfs_file_extent_encryption(leaf, fi);
 	} else if (type == BTRFS_FILE_EXTENT_INLINE) {
 		em->block_start = EXTENT_MAP_INLINE;
 		em->start = extent_start;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f894f5b5e786..5b1aea9f5f46 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7373,6 +7373,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
 		set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
 		em->compress_type = compress_type;
 	}
+	em->encryption_type = BTRFS_ENCRYPTION_NONE;
 
 	ret = btrfs_replace_extent_map_range(inode, em, true);
 	if (ret) {
-- 
2.41.0


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

* [PATCH v4 22/46] btrfs: add fscrypt_info and encryption_type to ordered_extent
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (20 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 21/46] btrfs: add fscrypt_info and encryption_type to extent_map Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 23/46] btrfs: plumb through setting the fscrypt_info for ordered extents Josef Bacik
                   ` (26 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We're going to need these to update the file extent items once the
writes are complete.  Add them and add the pieces necessary to assign
them and free everything.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ordered-data.c | 2 ++
 fs/btrfs/ordered-data.h | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 574e8a55e24a..27350dd50828 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -181,6 +181,7 @@ static struct btrfs_ordered_extent *alloc_ordered_extent(
 	entry->bytes_left = num_bytes;
 	entry->inode = igrab(&inode->vfs_inode);
 	entry->compress_type = compress_type;
+	entry->encryption_type = BTRFS_ENCRYPTION_NONE;
 	entry->truncated_len = (u64)-1;
 	entry->qgroup_rsv = ret;
 	entry->flags = flags;
@@ -564,6 +565,7 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
 			list_del(&sum->list);
 			kvfree(sum);
 		}
+		fscrypt_put_extent_info(entry->fscrypt_info);
 		kmem_cache_free(btrfs_ordered_extent_cache, entry);
 	}
 }
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 127ef8bf0ffd..85ba9a381880 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -108,6 +108,9 @@ struct btrfs_ordered_extent {
 	/* compression algorithm */
 	int compress_type;
 
+	/* encryption mode */
+	int encryption_type;
+
 	/* Qgroup reserved space */
 	int qgroup_rsv;
 
@@ -117,6 +120,9 @@ struct btrfs_ordered_extent {
 	/* the inode we belong to */
 	struct inode *inode;
 
+	/* the fscrypt_info for this extent, if necessary */
+	struct fscrypt_extent_info *fscrypt_info;
+
 	/* list of checksums for insertion when the extent io is done */
 	struct list_head list;
 
-- 
2.41.0


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

* [PATCH v4 23/46] btrfs: plumb through setting the fscrypt_info for ordered extents
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (21 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 22/46] btrfs: add fscrypt_info and encryption_type to ordered_extent Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 24/46] btrfs: plumb the fscrypt extent context through create_io_em Josef Bacik
                   ` (25 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We're going to be getting fscrypt_info from the extent maps, update the
helpers to take an fscrypt_info argument and use that to set the
encryption type on the ordered extent.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c        | 20 +++++++++++---------
 fs/btrfs/ordered-data.c | 32 ++++++++++++++++++++------------
 fs/btrfs/ordered-data.h |  9 +++++----
 3 files changed, 36 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5b1aea9f5f46..83d653376deb 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1171,7 +1171,8 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
 	}
 	free_extent_map(em);
 
-	ordered = btrfs_alloc_ordered_extent(inode, start,	/* file_offset */
+	ordered = btrfs_alloc_ordered_extent(inode, NULL,
+				       start,			/* file_offset */
 				       async_extent->ram_size,	/* num_bytes */
 				       async_extent->ram_size,	/* ram_bytes */
 				       ins.objectid,		/* disk_bytenr */
@@ -1434,9 +1435,10 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
 		}
 		free_extent_map(em);
 
-		ordered = btrfs_alloc_ordered_extent(inode, start, ram_size,
-					ram_size, ins.objectid, cur_alloc_size,
-					0, 1 << BTRFS_ORDERED_REGULAR,
+		ordered = btrfs_alloc_ordered_extent(inode, NULL,
+					start, ram_size, ram_size, ins.objectid,
+					cur_alloc_size, 0,
+					1 << BTRFS_ORDERED_REGULAR,
 					BTRFS_COMPRESS_NONE);
 		if (IS_ERR(ordered)) {
 			ret = PTR_ERR(ordered);
@@ -2167,7 +2169,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 			free_extent_map(em);
 		}
 
-		ordered = btrfs_alloc_ordered_extent(inode, cur_offset,
+		ordered = btrfs_alloc_ordered_extent(inode, NULL, cur_offset,
 				nocow_args.num_bytes, nocow_args.num_bytes,
 				nocow_args.disk_bytenr, nocow_args.num_bytes, 0,
 				is_prealloc
@@ -7058,7 +7060,7 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
 		if (IS_ERR(em))
 			goto out;
 	}
-	ordered = btrfs_alloc_ordered_extent(inode, start, len, len,
+	ordered = btrfs_alloc_ordered_extent(inode, NULL, start, len, len,
 					     block_start, block_len, 0,
 					     (1 << type) |
 					     (1 << BTRFS_ORDERED_DIRECT),
@@ -10543,9 +10545,9 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
 	}
 	free_extent_map(em);
 
-	ordered = btrfs_alloc_ordered_extent(inode, start, num_bytes, ram_bytes,
-				       ins.objectid, ins.offset,
-				       encoded->unencoded_offset,
+	ordered = btrfs_alloc_ordered_extent(inode, NULL, start,
+				       num_bytes, ram_bytes, ins.objectid,
+				       ins.offset, encoded->unencoded_offset,
 				       (1 << BTRFS_ORDERED_ENCODED) |
 				       (1 << BTRFS_ORDERED_COMPRESSED),
 				       compression);
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 27350dd50828..ee3138a6d11e 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -146,9 +146,11 @@ static inline struct rb_node *ordered_tree_search(struct btrfs_inode *inode,
 }
 
 static struct btrfs_ordered_extent *alloc_ordered_extent(
-			struct btrfs_inode *inode, u64 file_offset, u64 num_bytes,
-			u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
-			u64 offset, unsigned long flags, int compress_type)
+			struct btrfs_inode *inode,
+			struct fscrypt_extent_info *fscrypt_info,
+			u64 file_offset, u64 num_bytes, u64 ram_bytes,
+			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
+			unsigned long flags, int compress_type)
 {
 	struct btrfs_ordered_extent *entry;
 	int ret;
@@ -181,10 +183,12 @@ static struct btrfs_ordered_extent *alloc_ordered_extent(
 	entry->bytes_left = num_bytes;
 	entry->inode = igrab(&inode->vfs_inode);
 	entry->compress_type = compress_type;
-	entry->encryption_type = BTRFS_ENCRYPTION_NONE;
 	entry->truncated_len = (u64)-1;
 	entry->qgroup_rsv = ret;
 	entry->flags = flags;
+	entry->fscrypt_info = fscrypt_get_extent_info(fscrypt_info);
+	entry->encryption_type = entry->fscrypt_info ?
+		BTRFS_ENCRYPTION_FSCRYPT : BTRFS_ENCRYPTION_NONE;
 	refcount_set(&entry->refs, 1);
 	init_waitqueue_head(&entry->wait);
 	INIT_LIST_HEAD(&entry->list);
@@ -247,6 +251,7 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry)
  * Add an ordered extent to the per-inode tree.
  *
  * @inode:           Inode that this extent is for.
+ * @fscrypt_info:    The fscrypt_extent_info for this extent, if necessary.
  * @file_offset:     Logical offset in file where the extent starts.
  * @num_bytes:       Logical length of extent in file.
  * @ram_bytes:       Full length of unencoded data.
@@ -263,17 +268,19 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry)
  * Return: the new ordered extent or error pointer.
  */
 struct btrfs_ordered_extent *btrfs_alloc_ordered_extent(
-			struct btrfs_inode *inode, u64 file_offset,
-			u64 num_bytes, u64 ram_bytes, u64 disk_bytenr,
-			u64 disk_num_bytes, u64 offset, unsigned long flags,
-			int compress_type)
+			struct btrfs_inode *inode,
+			struct fscrypt_extent_info *fscrypt_info,
+			u64 file_offset, u64 num_bytes, u64 ram_bytes,
+			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
+			unsigned long flags, int compress_type)
 {
 	struct btrfs_ordered_extent *entry;
 
 	ASSERT((flags & ~BTRFS_ORDERED_TYPE_FLAGS) == 0);
 
-	entry = alloc_ordered_extent(inode, file_offset, num_bytes, ram_bytes,
-				     disk_bytenr, disk_num_bytes, offset, flags,
+	entry = alloc_ordered_extent(inode, fscrypt_info, file_offset,
+				     num_bytes, ram_bytes, disk_bytenr,
+				     disk_num_bytes, offset, flags,
 				     compress_type);
 	if (!IS_ERR(entry))
 		insert_ordered_extent(entry);
@@ -1166,8 +1173,9 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
 	if (WARN_ON_ONCE(ordered->disk_num_bytes != ordered->num_bytes))
 		return ERR_PTR(-EINVAL);
 
-	new = alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr,
-				   len, 0, flags, ordered->compress_type);
+	new = alloc_ordered_extent(inode, ordered->fscrypt_info, file_offset,
+				   len, len, disk_bytenr, len, 0, flags,
+				   ordered->compress_type);
 	if (IS_ERR(new))
 		return new;
 
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 85ba9a381880..57ca8ce6eb6d 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -163,10 +163,11 @@ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
 				    struct btrfs_ordered_extent **cached,
 				    u64 file_offset, u64 io_size);
 struct btrfs_ordered_extent *btrfs_alloc_ordered_extent(
-			struct btrfs_inode *inode, u64 file_offset,
-			u64 num_bytes, u64 ram_bytes, u64 disk_bytenr,
-			u64 disk_num_bytes, u64 offset, unsigned long flags,
-			int compress_type);
+			struct btrfs_inode *inode,
+			struct fscrypt_extent_info *fscrypt_info,
+			u64 file_offset, u64 num_bytes, u64 ram_bytes,
+			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
+			unsigned long flags, int compress_type);
 void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry,
 			   struct btrfs_ordered_sum *sum);
 struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *inode,
-- 
2.41.0


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

* [PATCH v4 24/46] btrfs: plumb the fscrypt extent context through create_io_em
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (22 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 23/46] btrfs: plumb through setting the fscrypt_info for ordered extents Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 25/46] btrfs: populate the ordered_extent with the fscrypt context Josef Bacik
                   ` (24 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

For prealloc extents we create an em, since we already have the context
loaded from the original prealloc extent creation we need to
pre-populate the extent map fscrypt info so it can be read properly
later if the pages are evicted.  Add the argument for create_io_em and
set it to NULL until we're ready to populate it properly.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 83d653376deb..837d7a3969d1 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -140,11 +140,12 @@ static noinline int run_delalloc_cow(struct btrfs_inode *inode,
 				     struct page *locked_page, u64 start,
 				     u64 end, struct writeback_control *wbc,
 				     bool pages_dirty);
-static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
-				       u64 len, u64 orig_start, u64 block_start,
-				       u64 block_len, u64 orig_block_len,
-				       u64 ram_bytes, int compress_type,
-				       int type);
+static struct extent_map *create_io_em(struct btrfs_inode *inode,
+				       struct fscrypt_extent_info *fscrypt_info,
+				       u64 start, u64 len, u64 orig_start,
+				       u64 block_start, u64 block_len,
+				       u64 orig_block_len, u64 ram_bytes,
+				       int compress_type, int type);
 
 static int data_reloc_print_warning_inode(u64 inum, u64 offset, u64 num_bytes,
 					  u64 root, void *warn_ctx)
@@ -1156,7 +1157,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
 	}
 
 	/* Here we're doing allocation and writeback of the compressed pages */
-	em = create_io_em(inode, start,
+	em = create_io_em(inode, NULL, start,
 			  async_extent->ram_size,	/* len */
 			  start,			/* orig_start */
 			  ins.objectid,			/* block_start */
@@ -1421,7 +1422,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
 		extent_reserved = true;
 
 		ram_size = ins.offset;
-		em = create_io_em(inode, start, ins.offset, /* len */
+		em = create_io_em(inode, NULL, start, ins.offset, /* len */
 				  start, /* orig_start */
 				  ins.objectid, /* block_start */
 				  ins.offset, /* block_len */
@@ -2154,7 +2155,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 			u64 orig_start = found_key.offset - nocow_args.extent_offset;
 			struct extent_map *em;
 
-			em = create_io_em(inode, cur_offset, nocow_args.num_bytes,
+			em = create_io_em(inode, NULL, cur_offset,
+					  nocow_args.num_bytes,
 					  orig_start,
 					  nocow_args.disk_bytenr, /* block_start */
 					  nocow_args.num_bytes, /* block_len */
@@ -7053,8 +7055,9 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
 	struct btrfs_ordered_extent *ordered;
 
 	if (type != BTRFS_ORDERED_NOCOW) {
-		em = create_io_em(inode, start, len, orig_start, block_start,
-				  block_len, orig_block_len, ram_bytes,
+		em = create_io_em(inode, NULL, start, len, orig_start,
+				  block_start, block_len, orig_block_len,
+				  ram_bytes,
 				  BTRFS_COMPRESS_NONE, /* compress_type */
 				  type);
 		if (IS_ERR(em))
@@ -7342,11 +7345,12 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
 }
 
 /* The callers of this must take lock_extent() */
-static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
-				       u64 len, u64 orig_start, u64 block_start,
-				       u64 block_len, u64 orig_block_len,
-				       u64 ram_bytes, int compress_type,
-				       int type)
+static struct extent_map *create_io_em(struct btrfs_inode *inode,
+				       struct fscrypt_extent_info *fscrypt_info,
+				       u64 start, u64 len, u64 orig_start,
+				       u64 block_start, u64 block_len,
+				       u64 orig_block_len, u64 ram_bytes,
+				       int compress_type, int type)
 {
 	struct extent_map *em;
 	int ret;
@@ -10535,7 +10539,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
 		goto out_delalloc_release;
 	extent_reserved = true;
 
-	em = create_io_em(inode, start, num_bytes,
+	em = create_io_em(inode, NULL, start, num_bytes,
 			  start - encoded->unencoded_offset, ins.objectid,
 			  ins.offset, ins.offset, ram_bytes, compression,
 			  BTRFS_ORDERED_COMPRESSED);
-- 
2.41.0


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

* [PATCH v4 25/46] btrfs: populate the ordered_extent with the fscrypt context
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (23 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 24/46] btrfs: plumb the fscrypt extent context through create_io_em Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads Josef Bacik
                   ` (23 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

The fscrypt_extent_info will be tied to the extent_map lifetime, so it
will be created when we create the IO em, or it'll already exist in the
NOCOW case.  Use this fscrypt_info when creating the ordered extent to
make sure everything is passed through properly.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c | 77 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 56 insertions(+), 21 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 837d7a3969d1..9c704b1cfe05 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1170,9 +1170,8 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
 		ret = PTR_ERR(em);
 		goto out_free_reserve;
 	}
-	free_extent_map(em);
 
-	ordered = btrfs_alloc_ordered_extent(inode, NULL,
+	ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info,
 				       start,			/* file_offset */
 				       async_extent->ram_size,	/* num_bytes */
 				       async_extent->ram_size,	/* ram_bytes */
@@ -1181,6 +1180,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
 				       0,			/* offset */
 				       1 << BTRFS_ORDERED_COMPRESSED,
 				       async_extent->compress_type);
+	free_extent_map(em);
 	if (IS_ERR(ordered)) {
 		btrfs_drop_extent_map_range(inode, start, end, false);
 		ret = PTR_ERR(ordered);
@@ -1434,13 +1434,13 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
 			ret = PTR_ERR(em);
 			goto out_reserve;
 		}
-		free_extent_map(em);
 
-		ordered = btrfs_alloc_ordered_extent(inode, NULL,
+		ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info,
 					start, ram_size, ram_size, ins.objectid,
 					cur_alloc_size, 0,
 					1 << BTRFS_ORDERED_REGULAR,
 					BTRFS_COMPRESS_NONE);
+		free_extent_map(em);
 		if (IS_ERR(ordered)) {
 			ret = PTR_ERR(ordered);
 			goto out_drop_extent_cache;
@@ -2013,6 +2013,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 		struct btrfs_key found_key;
 		struct btrfs_file_extent_item *fi;
 		struct extent_buffer *leaf;
+		struct extent_map *em = NULL;
+		struct fscrypt_extent_info *fscrypt_info = NULL;
 		u64 extent_end;
 		u64 ram_bytes;
 		u64 nocow_end;
@@ -2149,13 +2151,29 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 			}
 		}
 
+		/*
+		 * We only want to do this lookup if we're encrypted, otherwise
+		 * fsrypt_info will be null and we can avoid this lookup.
+		 */
+		if (IS_ENCRYPTED(&inode->vfs_inode)) {
+			em = btrfs_get_extent(inode, NULL, 0, cur_offset,
+					      nocow_args.num_bytes);
+			if (IS_ERR(em)) {
+				btrfs_dec_nocow_writers(nocow_bg);
+				ret = PTR_ERR(em);
+				goto error;
+			}
+			fscrypt_info = fscrypt_get_extent_info(em->fscrypt_info);
+			free_extent_map(em);
+			em = NULL;
+		}
+
 		nocow_end = cur_offset + nocow_args.num_bytes - 1;
 		is_prealloc = extent_type == BTRFS_FILE_EXTENT_PREALLOC;
 		if (is_prealloc) {
 			u64 orig_start = found_key.offset - nocow_args.extent_offset;
-			struct extent_map *em;
 
-			em = create_io_em(inode, NULL, cur_offset,
+			em = create_io_em(inode, fscrypt_info, cur_offset,
 					  nocow_args.num_bytes,
 					  orig_start,
 					  nocow_args.disk_bytenr, /* block_start */
@@ -2164,6 +2182,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 					  ram_bytes, BTRFS_COMPRESS_NONE,
 					  BTRFS_ORDERED_PREALLOC);
 			if (IS_ERR(em)) {
+				fscrypt_put_extent_info(fscrypt_info);
 				btrfs_dec_nocow_writers(nocow_bg);
 				ret = PTR_ERR(em);
 				goto error;
@@ -2171,13 +2190,15 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 			free_extent_map(em);
 		}
 
-		ordered = btrfs_alloc_ordered_extent(inode, NULL, cur_offset,
-				nocow_args.num_bytes, nocow_args.num_bytes,
-				nocow_args.disk_bytenr, nocow_args.num_bytes, 0,
+		ordered = btrfs_alloc_ordered_extent(inode, fscrypt_info,
+				cur_offset, nocow_args.num_bytes,
+				nocow_args.num_bytes, nocow_args.disk_bytenr,
+				nocow_args.num_bytes, 0,
 				is_prealloc
 				? (1 << BTRFS_ORDERED_PREALLOC)
 				: (1 << BTRFS_ORDERED_NOCOW),
 				BTRFS_COMPRESS_NONE);
+		fscrypt_put_extent_info(fscrypt_info);
 		btrfs_dec_nocow_writers(nocow_bg);
 		if (IS_ERR(ordered)) {
 			if (is_prealloc) {
@@ -7042,6 +7063,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 
 static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
 						  struct btrfs_dio_data *dio_data,
+						  struct extent_map *orig_em,
 						  const u64 start,
 						  const u64 len,
 						  const u64 orig_start,
@@ -7053,18 +7075,24 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
 {
 	struct extent_map *em = NULL;
 	struct btrfs_ordered_extent *ordered;
+	struct fscrypt_extent_info *fscrypt_info = NULL;
+
+	if (orig_em)
+		fscrypt_info = orig_em->fscrypt_info;
 
 	if (type != BTRFS_ORDERED_NOCOW) {
-		em = create_io_em(inode, NULL, start, len, orig_start,
+		em = create_io_em(inode, fscrypt_info, start, len, orig_start,
 				  block_start, block_len, orig_block_len,
 				  ram_bytes,
 				  BTRFS_COMPRESS_NONE, /* compress_type */
 				  type);
 		if (IS_ERR(em))
 			goto out;
+		fscrypt_info = em->fscrypt_info;
 	}
-	ordered = btrfs_alloc_ordered_extent(inode, NULL, start, len, len,
-					     block_start, block_len, 0,
+
+	ordered = btrfs_alloc_ordered_extent(inode, fscrypt_info, start, len,
+					     len, block_start, block_len, 0,
 					     (1 << type) |
 					     (1 << BTRFS_ORDERED_DIRECT),
 					     BTRFS_COMPRESS_NONE);
@@ -7108,9 +7136,10 @@ static struct extent_map *btrfs_new_extent_direct(struct btrfs_inode *inode,
 	if (ret)
 		return ERR_PTR(ret);
 
-	em = btrfs_create_dio_extent(inode, dio_data, start, ins.offset, start,
-				     ins.objectid, ins.offset, ins.offset,
-				     ins.offset, BTRFS_ORDERED_REGULAR);
+	em = btrfs_create_dio_extent(inode, dio_data, NULL, start, ins.offset,
+				     start, ins.objectid, ins.offset,
+				     ins.offset, ins.offset,
+				     BTRFS_ORDERED_REGULAR);
 	btrfs_dec_block_group_reservations(fs_info, ins.objectid);
 	if (IS_ERR(em))
 		btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset,
@@ -7379,7 +7408,13 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode,
 		set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
 		em->compress_type = compress_type;
 	}
-	em->encryption_type = BTRFS_ENCRYPTION_NONE;
+
+	if (fscrypt_info) {
+		em->encryption_type = BTRFS_ENCRYPTION_FSCRYPT;
+		em->fscrypt_info = fscrypt_get_extent_info(fscrypt_info);
+	} else {
+		em->encryption_type = BTRFS_ENCRYPTION_NONE;
+	}
 
 	ret = btrfs_replace_extent_map_range(inode, em, true);
 	if (ret) {
@@ -7455,9 +7490,9 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
 		}
 		space_reserved = true;
 
-		em2 = btrfs_create_dio_extent(BTRFS_I(inode), dio_data, start, len,
-					      orig_start, block_start,
-					      len, orig_block_len,
+		em2 = btrfs_create_dio_extent(BTRFS_I(inode), dio_data, em,
+					      start, len, orig_start,
+					      block_start, len, orig_block_len,
 					      ram_bytes, type);
 		btrfs_dec_nocow_writers(bg);
 		if (type == BTRFS_ORDERED_PREALLOC) {
@@ -10547,14 +10582,14 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
 		ret = PTR_ERR(em);
 		goto out_free_reserved;
 	}
-	free_extent_map(em);
 
-	ordered = btrfs_alloc_ordered_extent(inode, NULL, start,
+	ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info, start,
 				       num_bytes, ram_bytes, ins.objectid,
 				       ins.offset, encoded->unencoded_offset,
 				       (1 << BTRFS_ORDERED_ENCODED) |
 				       (1 << BTRFS_ORDERED_COMPRESSED),
 				       compression);
+	free_extent_map(em);
 	if (IS_ERR(ordered)) {
 		btrfs_drop_extent_map_range(inode, start, end, false);
 		ret = PTR_ERR(ordered);
-- 
2.41.0


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

* [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (24 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 25/46] btrfs: populate the ordered_extent with the fscrypt context Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:44   ` Eric Biggers
  2023-12-01 22:11 ` [PATCH v4 27/46] btrfs: add an optional encryption context to the end of file extents Josef Bacik
                   ` (22 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We keep track of this information in the ordered extent for writes, but
we need it for reads as well.  Add fscrypt_extent_info and orig_start to
the dio_data so we can populate this on reads.  This will be used later
when we attach the fscrypt context to the bios.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9c704b1cfe05..dbdc01f25215 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -83,6 +83,8 @@ struct btrfs_dio_data {
 	ssize_t submitted;
 	struct extent_changeset *data_reserved;
 	struct btrfs_ordered_extent *ordered;
+	struct fscrypt_extent_info *fscrypt_info;
+	u64 orig_start;
 	bool data_space_reserved;
 	bool nocow_done;
 };
@@ -7767,6 +7769,10 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start,
 							       release_len);
 		}
 	} else {
+		dio_data->fscrypt_info =
+			fscrypt_get_extent_info(em->fscrypt_info);
+		dio_data->orig_start = em->orig_start;
+
 		/*
 		 * We need to unlock only the end area that we aren't using.
 		 * The rest is going to be unlocked by the endio routine.
@@ -7848,6 +7854,11 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length,
 		dio_data->ordered = NULL;
 	}
 
+	if (dio_data->fscrypt_info) {
+		fscrypt_put_extent_info(dio_data->fscrypt_info);
+		dio_data->fscrypt_info = NULL;
+	}
+
 	if (write)
 		extent_changeset_free(dio_data->data_reserved);
 	return ret;
-- 
2.41.0


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

* [PATCH v4 27/46] btrfs: add an optional encryption context to the end of file extents
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (25 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 28/46] btrfs: explicitly track file extent length for replace and drop Josef Bacik
                   ` (21 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

The fscrypt encryption context can be extended to include different
things in the future.  To facilitate future expansion add an optional
btrfs_encryption_info to the end of the file extent.  This will hold the
size of the context and then will have the binary context tacked onto
the end of the extent item.

Add the appropriate accessors to make it easy to read this information
if we have encryption set, and then update the tree-checker to validate
that if this is indeed set properly that the size matches properly.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/accessors.h            | 48 +++++++++++++++++++++++++++
 fs/btrfs/tree-checker.c         | 58 ++++++++++++++++++++++++++++-----
 include/uapi/linux/btrfs_tree.h | 17 +++++++++-
 3 files changed, 113 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index 5aaf204fa55f..a54a4671bd15 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -932,6 +932,10 @@ BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
 BTRFS_SETGET_STACK_FUNCS(super_nr_global_roots, struct btrfs_super_block,
 			 nr_global_roots, 64);
 
+/* struct btrfs_file_extent_encryption_info */
+BTRFS_SETGET_FUNCS(encryption_info_size, struct btrfs_encryption_info, size,
+		   32);
+
 /* struct btrfs_file_extent_item */
 BTRFS_SETGET_STACK_FUNCS(stack_file_extent_type, struct btrfs_file_extent_item,
 			 type, 8);
@@ -973,6 +977,50 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
 BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
 		   other_encoding, 16);
 
+static inline struct btrfs_encryption_info *btrfs_file_extent_encryption_info(
+					const struct btrfs_file_extent_item *ei)
+{
+	unsigned long offset = (unsigned long)ei;
+
+	offset += offsetof(struct btrfs_file_extent_item, encryption_info);
+	return (struct btrfs_encryption_info *)offset;
+}
+
+static inline unsigned long btrfs_file_extent_encryption_ctx_offset(
+					const struct btrfs_file_extent_item *ei)
+{
+	unsigned long offset = (unsigned long)ei;
+
+	offset += offsetof(struct btrfs_file_extent_item, encryption_info);
+	return offset + offsetof(struct btrfs_encryption_info, context);
+}
+
+static inline u32 btrfs_file_extent_encryption_ctx_size(
+					const struct extent_buffer *eb,
+					const struct btrfs_file_extent_item *ei)
+{
+	return btrfs_encryption_info_size(eb,
+					  btrfs_file_extent_encryption_info(ei));
+}
+
+static inline void btrfs_set_file_extent_encryption_ctx_size(
+						const struct extent_buffer *eb,
+						struct btrfs_file_extent_item *ei,
+						u32 val)
+{
+	btrfs_set_encryption_info_size(eb,
+				       btrfs_file_extent_encryption_info(ei),
+				       val);
+}
+
+static inline u32 btrfs_file_extent_encryption_info_size(
+					const struct extent_buffer *eb,
+					const struct btrfs_file_extent_item *ei)
+{
+	return btrfs_encryption_info_size(eb,
+					  btrfs_file_extent_encryption_info(ei));
+}
+
 /* btrfs_qgroup_status_item */
 BTRFS_SETGET_FUNCS(qgroup_status_generation, struct btrfs_qgroup_status_item,
 		   generation, 64);
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 1af2c967436b..ccf1bf59d7e0 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -212,6 +212,7 @@ static int check_extent_data_item(struct extent_buffer *leaf,
 	u32 item_size = btrfs_item_size(leaf, slot);
 	u64 extent_end;
 	u8 policy;
+	u8 fe_type;
 
 	if (unlikely(!IS_ALIGNED(key->offset, sectorsize))) {
 		file_extent_err(leaf, slot,
@@ -242,12 +243,12 @@ static int check_extent_data_item(struct extent_buffer *leaf,
 				SZ_4K);
 		return -EUCLEAN;
 	}
-	if (unlikely(btrfs_file_extent_type(leaf, fi) >=
-		     BTRFS_NR_FILE_EXTENT_TYPES)) {
+
+	fe_type = btrfs_file_extent_type(leaf, fi);
+	if (unlikely(fe_type >= BTRFS_NR_FILE_EXTENT_TYPES)) {
 		file_extent_err(leaf, slot,
 		"invalid type for file extent, have %u expect range [0, %u]",
-			btrfs_file_extent_type(leaf, fi),
-			BTRFS_NR_FILE_EXTENT_TYPES - 1);
+			fe_type, BTRFS_NR_FILE_EXTENT_TYPES - 1);
 		return -EUCLEAN;
 	}
 
@@ -296,12 +297,51 @@ static int check_extent_data_item(struct extent_buffer *leaf,
 		return 0;
 	}
 
-	/* Regular or preallocated extent has fixed item size */
-	if (unlikely(item_size != sizeof(*fi))) {
-		file_extent_err(leaf, slot,
+	if (policy == BTRFS_ENCRYPTION_FSCRYPT) {
+		size_t fe_size = sizeof(*fi) +
+			sizeof(struct btrfs_encryption_info);
+		u32 ctxsize;
+
+		if (unlikely(item_size < fe_size)) {
+			file_extent_err(leaf, slot,
+	"invalid item size for encrypted file extent, have %u expect = %zu + size of u32",
+					item_size, sizeof(*fi));
+			return -EUCLEAN;
+		}
+
+		ctxsize = btrfs_file_extent_encryption_info_size(leaf, fi);
+		if (unlikely(item_size != (fe_size + ctxsize))) {
+			file_extent_err(leaf, slot,
+	"invalid item size for encrypted file extent, have %u expect = %zu + context of size %u",
+					item_size, fe_size, ctxsize);
+			return -EUCLEAN;
+		}
+
+		if (unlikely(ctxsize > BTRFS_MAX_EXTENT_CTX_SIZE)) {
+			file_extent_err(leaf, slot,
+	"invalid file extent context size, have %u expect a maximum of %u",
+					ctxsize, BTRFS_MAX_EXTENT_CTX_SIZE);
+			return -EUCLEAN;
+		}
+
+		/*
+		 * Only regular and prealloc extents should have an encryption
+		 * context.
+		 */
+		if (unlikely(fe_type != BTRFS_FILE_EXTENT_REG &&
+			     fe_type != BTRFS_FILE_EXTENT_PREALLOC)) {
+			file_extent_err(leaf, slot,
+		"invalid type for encrypted file extent, have %u",
+					btrfs_file_extent_type(leaf, fi));
+			return -EUCLEAN;
+		}
+	} else {
+		if (unlikely(item_size != sizeof(*fi))) {
+			file_extent_err(leaf, slot,
 	"invalid item size for reg/prealloc file extent, have %u expect %zu",
-			item_size, sizeof(*fi));
-		return -EUCLEAN;
+					item_size, sizeof(*fi));
+			return -EUCLEAN;
+		}
 	}
 	if (unlikely(CHECK_FE_ALIGNED(leaf, slot, fi, ram_bytes, sectorsize) ||
 		     CHECK_FE_ALIGNED(leaf, slot, fi, disk_bytenr, sectorsize) ||
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index af56a5eb3a00..4d352666b393 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1073,12 +1073,24 @@ enum {
 	BTRFS_NR_FILE_EXTENT_TYPES = 3,
 };
 
+/*
+ * Currently just the FSCRYPT_SET_CONTEXT_MAX_SIZE, which is larger than the
+ * current extent context size from fscrypt, so this should give us plenty of
+ * breathing room for expansion later.
+ */
+#define BTRFS_MAX_EXTENT_CTX_SIZE 40
+
 enum {
 	BTRFS_ENCRYPTION_NONE,
 	BTRFS_ENCRYPTION_FSCRYPT,
 	BTRFS_NR_ENCRYPTION_TYPES,
 };
 
+struct btrfs_encryption_info {
+	__le32 size;
+	__u8 context[0];
+};
+
 struct btrfs_file_extent_item {
 	/*
 	 * transaction id that created this extent
@@ -1134,7 +1146,10 @@ struct btrfs_file_extent_item {
 	 * always reflects the size uncompressed and without encoding.
 	 */
 	__le64 num_bytes;
-
+	/*
+	 * the encryption info, if any
+	 */
+	struct btrfs_encryption_info encryption_info[0];
 } __attribute__ ((__packed__));
 
 struct btrfs_csum_item {
-- 
2.41.0


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

* [PATCH v4 28/46] btrfs: explicitly track file extent length for replace and drop
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (26 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 27/46] btrfs: add an optional encryption context to the end of file extents Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 29/46] btrfs: pass through fscrypt_extent_info to the file extent helpers Josef Bacik
                   ` (20 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: Sweet Tea Dorminy

From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>

With the advent of storing fscrypt contexts with each encrypted extent,
extents will have a variable length depending on encryption status.
Make sure the replace and drop file extent item helpers encode this
information so that everything gets updated properly.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ctree.h    | 2 ++
 fs/btrfs/file.c     | 4 ++--
 fs/btrfs/inode.c    | 7 +++++--
 fs/btrfs/reflink.c  | 1 +
 fs/btrfs/tree-log.c | 5 +++--
 5 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 54fd4eb92745..ba73171fb8b0 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -367,6 +367,8 @@ struct btrfs_replace_extent_info {
 	u64 file_offset;
 	/* Pointer to a file extent item of type regular or prealloc. */
 	char *extent_buf;
+	/* The length of @extent_buf */
+	u32 extent_buf_size;
 	/*
 	 * Set to true when attempting to replace a file range with a new extent
 	 * described by this structure, set to false when attempting to clone an
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index b4a503731e79..87d07d7985ec 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2262,14 +2262,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
 	key.type = BTRFS_EXTENT_DATA_KEY;
 	key.offset = extent_info->file_offset;
 	ret = btrfs_insert_empty_item(trans, root, path, &key,
-				      sizeof(struct btrfs_file_extent_item));
+				      extent_info->extent_buf_size);
 	if (ret)
 		return ret;
 	leaf = path->nodes[0];
 	slot = path->slots[0];
 	write_extent_buffer(leaf, extent_info->extent_buf,
 			    btrfs_item_ptr_offset(leaf, slot),
-			    sizeof(struct btrfs_file_extent_item));
+			    extent_info->extent_buf_size);
 	extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
 	ASSERT(btrfs_file_extent_type(leaf, extent) != BTRFS_FILE_EXTENT_INLINE);
 	btrfs_set_file_extent_offset(leaf, extent, extent_info->data_offset);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dbdc01f25215..150ec68783c3 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2913,6 +2913,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
 	u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi);
 	u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi);
 	struct btrfs_drop_extents_args drop_args = { 0 };
+	size_t fscrypt_context_size = 0;
 	int ret;
 
 	path = btrfs_alloc_path();
@@ -2932,7 +2933,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
 	drop_args.start = file_pos;
 	drop_args.end = file_pos + num_bytes;
 	drop_args.replace_extent = true;
-	drop_args.extent_item_size = sizeof(*stack_fi);
+	drop_args.extent_item_size = sizeof(*stack_fi) + fscrypt_context_size;
 	ret = btrfs_drop_extents(trans, root, inode, &drop_args);
 	if (ret)
 		goto out;
@@ -2943,7 +2944,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
 		ins.type = BTRFS_EXTENT_DATA_KEY;
 
 		ret = btrfs_insert_empty_item(trans, root, path, &ins,
-					      sizeof(*stack_fi));
+					      sizeof(*stack_fi) + fscrypt_context_size);
 		if (ret)
 			goto out;
 	}
@@ -9717,6 +9718,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 	u64 len = ins->offset;
 	int qgroup_released;
 	int ret;
+	size_t fscrypt_context_size = 0;
 
 	memset(&stack_fi, 0, sizeof(stack_fi));
 
@@ -9749,6 +9751,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 	extent_info.data_len = len;
 	extent_info.file_offset = file_offset;
 	extent_info.extent_buf = (char *)&stack_fi;
+	extent_info.extent_buf_size = sizeof(stack_fi) + fscrypt_context_size;
 	extent_info.is_new_extent = true;
 	extent_info.update_times = true;
 	extent_info.qgroup_reserved = qgroup_released;
diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index c39de2a43a00..d01499d8bcf0 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -500,6 +500,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 			clone_info.data_len = datal;
 			clone_info.file_offset = new_key.offset;
 			clone_info.extent_buf = buf;
+			clone_info.extent_buf_size = size;
 			clone_info.is_new_extent = false;
 			clone_info.update_times = !no_time_update;
 			ret = btrfs_replace_file_extents(BTRFS_I(inode), path,
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 36063dc39443..580974153649 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4627,6 +4627,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	u64 extent_offset = em->start - em->orig_start;
 	u64 block_len;
 	int ret;
+	size_t fscrypt_context_size = 0;
 	u8 encryption = BTRFS_ENCRYPTION_NONE;
 
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
@@ -4669,7 +4670,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 		drop_args.start = em->start;
 		drop_args.end = em->start + em->len;
 		drop_args.replace_extent = true;
-		drop_args.extent_item_size = sizeof(fi);
+		drop_args.extent_item_size = sizeof(fi) + fscrypt_context_size;
 		ret = btrfs_drop_extents(trans, log, inode, &drop_args);
 		if (ret)
 			return ret;
@@ -4681,7 +4682,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 		key.offset = em->start;
 
 		ret = btrfs_insert_empty_item(trans, log, path, &key,
-					      sizeof(fi));
+					      sizeof(fi) + fscrypt_context_size);
 		if (ret)
 			return ret;
 	}
-- 
2.41.0


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

* [PATCH v4 29/46] btrfs: pass through fscrypt_extent_info to the file extent helpers
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (27 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 28/46] btrfs: explicitly track file extent length for replace and drop Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 30/46] btrfs: pass the fscrypt_info through the replace extent infrastructure Josef Bacik
                   ` (19 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Now that we have the fscrypt_extnet_info in all of the supporting
structures, pass this through and set the file extent encryption bit
accordingly from the supporting structures.  In subsequent patches code
will be added to populate these appropriately.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c    | 18 +++++++++++-------
 fs/btrfs/tree-log.c |  2 +-
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 150ec68783c3..8bd5c7488055 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2897,7 +2897,9 @@ int btrfs_writepage_cow_fixup(struct page *page)
 }
 
 static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
-				       struct btrfs_inode *inode, u64 file_pos,
+				       struct btrfs_inode *inode,
+				       struct fscrypt_extent_info *fscrypt_info,
+				       u64 file_pos,
 				       struct btrfs_file_extent_item *stack_fi,
 				       const bool update_inode_bytes,
 				       u64 qgroup_reserved)
@@ -3029,8 +3031,7 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans,
 	btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes);
 	btrfs_set_stack_file_extent_ram_bytes(&stack_fi, ram_bytes);
 	btrfs_set_stack_file_extent_compression(&stack_fi, oe->compress_type);
-	btrfs_set_stack_file_extent_encryption(&stack_fi,
-					       BTRFS_ENCRYPTION_NONE);
+	btrfs_set_stack_file_extent_encryption(&stack_fi, oe->encryption_type);
 	/* Other encoding is reserved and always 0 */
 
 	/*
@@ -3044,8 +3045,9 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans,
 			     test_bit(BTRFS_ORDERED_TRUNCATED, &oe->flags);
 
 	return insert_reserved_file_extent(trans, BTRFS_I(oe->inode),
-					   oe->file_offset, &stack_fi,
-					   update_inode_bytes, oe->qgroup_rsv);
+					   oe->fscrypt_info, oe->file_offset,
+					   &stack_fi, update_inode_bytes,
+					   oe->qgroup_rsv);
 }
 
 /*
@@ -9708,6 +9710,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 				       struct btrfs_trans_handle *trans_in,
 				       struct btrfs_inode *inode,
 				       struct btrfs_key *ins,
+				       struct fscrypt_extent_info *fscrypt_info,
 				       u64 file_offset)
 {
 	struct btrfs_file_extent_item stack_fi;
@@ -9729,6 +9732,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 	btrfs_set_stack_file_extent_ram_bytes(&stack_fi, len);
 	btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE);
 	btrfs_set_stack_file_extent_encryption(&stack_fi,
+					       fscrypt_info ? BTRFS_ENCRYPTION_FSCRYPT :
 					       BTRFS_ENCRYPTION_NONE);
 	/* Other encoding is reserved and always 0 */
 
@@ -9737,7 +9741,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 		return ERR_PTR(qgroup_released);
 
 	if (trans) {
-		ret = insert_reserved_file_extent(trans, inode,
+		ret = insert_reserved_file_extent(trans, inode, fscrypt_info,
 						  file_offset, &stack_fi,
 						  true, qgroup_released);
 		if (ret)
@@ -9831,7 +9835,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 
 		last_alloc = ins.offset;
 		trans = insert_prealloc_file_extent(trans, BTRFS_I(inode),
-						    &ins, cur_offset);
+						    &ins, NULL, cur_offset);
 		/*
 		 * Now that we inserted the prealloc extent we can finally
 		 * decrement the number of reservations in the block group.
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 580974153649..36cbc4d176c5 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4628,7 +4628,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	u64 block_len;
 	int ret;
 	size_t fscrypt_context_size = 0;
-	u8 encryption = BTRFS_ENCRYPTION_NONE;
+	u8 encryption = em->encryption_type;
 
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
 	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
-- 
2.41.0


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

* [PATCH v4 30/46] btrfs: pass the fscrypt_info through the replace extent infrastructure
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (28 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 29/46] btrfs: pass through fscrypt_extent_info to the file extent helpers Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 31/46] btrfs: implement the fscrypt extent encryption hooks Josef Bacik
                   ` (18 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Prealloc uses the btrfs_replace_file_extents() infrastructure to insert
its new extents.  We need to set the fscrypt context on these extents,
so pass this through the btrfs_replace_extent_info so it can be used in
a later patch when we hook in this infrastructure.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ctree.h | 2 ++
 fs/btrfs/inode.c | 1 +
 2 files changed, 3 insertions(+)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index ba73171fb8b0..9eb14b067e1f 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -369,6 +369,8 @@ struct btrfs_replace_extent_info {
 	char *extent_buf;
 	/* The length of @extent_buf */
 	u32 extent_buf_size;
+	/* The fscrypt_extent_info for a new extent. */
+	struct fscrypt_extent_info *fscrypt_info;
 	/*
 	 * Set to true when attempting to replace a file range with a new extent
 	 * described by this structure, set to false when attempting to clone an
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8bd5c7488055..231d9fe6ff8a 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9760,6 +9760,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 	extent_info.update_times = true;
 	extent_info.qgroup_reserved = qgroup_released;
 	extent_info.insertions = 0;
+	extent_info.fscrypt_info = fscrypt_info;
 
 	path = btrfs_alloc_path();
 	if (!path) {
-- 
2.41.0


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

* [PATCH v4 31/46] btrfs: implement the fscrypt extent encryption hooks
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (29 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 30/46] btrfs: pass the fscrypt_info through the replace extent infrastructure Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 32/46] btrfs: setup fscrypt_extent_info for new extents Josef Bacik
                   ` (17 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

This patch implements the necessary hooks from fscrypt to support
per-extent encryption.  There's two main entry points

btrfs_fscrypt_load_extent_info
btrfs_fscrypt_save_extent_info

btrfs_fscrypt_load_extent_info gets called when we create the extent
maps from the file extent item at btrfs_get_extent() time.  We read the
extent context, and pass it into fscrypt to create the appropriate
fscrypt_extent_info structure.  This is then used on the bio's to make
sure the encryption is done properly.

btrfs_fscrypt_save_extent_info is used to generate the fscrypt context
from fscrypt and save it into the file extent item when we create a new
file extent item.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/defrag.c    | 10 ++++++++-
 fs/btrfs/file-item.c | 11 +++++++++-
 fs/btrfs/file-item.h |  5 ++++-
 fs/btrfs/file.c      |  9 ++++++++
 fs/btrfs/fscrypt.c   | 49 ++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h   | 31 ++++++++++++++++++++++++++++
 fs/btrfs/inode.c     | 22 +++++++++++++++++++-
 fs/btrfs/tree-log.c  | 10 +++++++++
 8 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c
index 5244561e2016..f3b7438ddbc7 100644
--- a/fs/btrfs/defrag.c
+++ b/fs/btrfs/defrag.c
@@ -16,6 +16,7 @@
 #include "defrag.h"
 #include "file-item.h"
 #include "super.h"
+#include "fscrypt.h"
 
 static struct kmem_cache *btrfs_inode_defrag_cachep;
 
@@ -631,9 +632,12 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode,
 	struct btrfs_path path = { 0 };
 	struct extent_map *em;
 	struct btrfs_key key;
+	struct btrfs_fscrypt_ctx ctx;
 	u64 ino = btrfs_ino(inode);
 	int ret;
 
+	ctx.size = 0;
+
 	em = alloc_extent_map();
 	if (!em) {
 		ret = -ENOMEM;
@@ -728,7 +732,7 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode,
 			goto next;
 
 		/* Now this extent covers @start, convert it to em */
-		btrfs_extent_item_to_extent_map(inode, &path, fi, em);
+		btrfs_extent_item_to_extent_map(inode, &path, fi, em, &ctx);
 		break;
 next:
 		ret = btrfs_next_item(root, &path);
@@ -738,6 +742,10 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode,
 			goto not_found;
 	}
 	btrfs_release_path(&path);
+
+	ret = btrfs_fscrypt_load_extent_info(inode, em, &ctx);
+	if (ret)
+		goto err;
 	return em;
 
 not_found:
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 26f35c1baedc..35036fab58c4 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -21,6 +21,7 @@
 #include "accessors.h"
 #include "file-item.h"
 #include "super.h"
+#include "fscrypt.h"
 
 #define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
 				   sizeof(struct btrfs_item) * 2) / \
@@ -1264,7 +1265,8 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
 void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 				     const struct btrfs_path *path,
 				     struct btrfs_file_extent_item *fi,
-				     struct extent_map *em)
+				     struct extent_map *em,
+				     struct btrfs_fscrypt_ctx *ctx)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_root *root = inode->root;
@@ -1306,6 +1308,13 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 				set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
 		}
 		em->encryption_type = btrfs_file_extent_encryption(leaf, fi);
+		if (em->encryption_type != BTRFS_ENCRYPTION_NONE) {
+			ctx->size =
+				btrfs_file_extent_encryption_ctx_size(leaf, fi);
+			read_extent_buffer(leaf, ctx->ctx,
+				btrfs_file_extent_encryption_ctx_offset(fi),
+				ctx->size);
+		}
 	} else if (type == BTRFS_FILE_EXTENT_INLINE) {
 		em->block_start = EXTENT_MAP_INLINE;
 		em->start = extent_start;
diff --git a/fs/btrfs/file-item.h b/fs/btrfs/file-item.h
index 04bd2d34efb1..bb79014024bd 100644
--- a/fs/btrfs/file-item.h
+++ b/fs/btrfs/file-item.h
@@ -5,6 +5,8 @@
 
 #include "accessors.h"
 
+struct btrfs_fscrypt_ctx;
+
 #define BTRFS_FILE_EXTENT_INLINE_DATA_START		\
 		(offsetof(struct btrfs_file_extent_item, disk_bytenr))
 
@@ -63,7 +65,8 @@ int btrfs_lookup_csums_bitmap(struct btrfs_root *root, struct btrfs_path *path,
 void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 				     const struct btrfs_path *path,
 				     struct btrfs_file_extent_item *fi,
-				     struct extent_map *em);
+				     struct extent_map *em,
+				     struct btrfs_fscrypt_ctx *ctx);
 int btrfs_inode_clear_file_extent_range(struct btrfs_inode *inode, u64 start,
 					u64 len);
 int btrfs_inode_set_file_extent_range(struct btrfs_inode *inode, u64 start, u64 len);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 87d07d7985ec..30d509259922 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -38,6 +38,7 @@
 #include "ioctl.h"
 #include "file.h"
 #include "super.h"
+#include "fscrypt.h"
 
 /* simple helper to fault in pages and copy.  This should go away
  * and be replaced with calls into generic code.
@@ -2276,6 +2277,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
 	btrfs_set_file_extent_num_bytes(leaf, extent, replace_len);
 	if (extent_info->is_new_extent)
 		btrfs_set_file_extent_generation(leaf, extent, trans->transid);
+	if (extent_info->fscrypt_info) {
+		ret = btrfs_fscrypt_save_extent_info(inode, path,
+						     extent_info->fscrypt_info);
+		if (ret) {
+			btrfs_release_path(path);
+			return ret;
+		}
+	}
 	btrfs_mark_buffer_dirty(trans, leaf);
 	btrfs_release_path(path);
 
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 2d037b105b5f..7a7272cb83ec 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -214,7 +214,56 @@ static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
 	return devs;
 }
 
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+				   struct extent_map *em,
+				   struct btrfs_fscrypt_ctx *ctx)
+{
+	struct fscrypt_extent_info *info;
+	unsigned long nofs_flag;
+
+	if (ctx->size == 0)
+		return 0;
+
+	nofs_flag = memalloc_nofs_save();
+	info = fscrypt_load_extent_info(&inode->vfs_inode, ctx->ctx, ctx->size);
+	memalloc_nofs_restore(nofs_flag);
+	if (IS_ERR(info))
+		return PTR_ERR(info);
+	em->fscrypt_info = info;
+	return 0;
+}
+
+int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
+				   struct btrfs_path *path,
+				   struct fscrypt_extent_info *info)
+{
+	struct btrfs_file_extent_item *ei;
+	u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];
+	ssize_t ctx_size;
+
+	ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			    struct btrfs_file_extent_item);
+
+	ctx_size = fscrypt_set_extent_context(&inode->vfs_inode, info, ctx);
+	if (ctx_size < 0) {
+		btrfs_err_rl(inode->root->fs_info, "invalid encrypt context\n");
+		return (int)ctx_size;
+	}
+	write_extent_buffer(path->nodes[0], ctx,
+			    btrfs_file_extent_encryption_ctx_offset(ei),
+			    ctx_size);
+	btrfs_set_file_extent_encryption_ctx_size(path->nodes[0], ei, ctx_size);
+	return 0;
+}
+
+size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode)
+{
+	return sizeof(struct btrfs_encryption_info) +
+		fscrypt_extent_context_size(&inode->vfs_inode);
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
+	.has_per_extent_encryption = 1,
 	.get_context = btrfs_fscrypt_get_context,
 	.set_context = btrfs_fscrypt_set_context,
 	.empty_dir = btrfs_fscrypt_empty_dir,
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index c08fd52c99b4..2882a4a9d978 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -8,6 +8,11 @@
 
 #include "fs.h"
 
+struct btrfs_fscrypt_ctx {
+	u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];
+	size_t size;
+};
+
 #ifdef CONFIG_FS_ENCRYPTION
 int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
 				struct btrfs_dir_item *di,
@@ -16,8 +21,29 @@ int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
 bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 			      struct extent_buffer *leaf,
 			      unsigned long de_name, u32 de_name_len);
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+				   struct extent_map *em,
+				   struct btrfs_fscrypt_ctx *ctx);
+int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
+				   struct btrfs_path *path,
+				   struct fscrypt_extent_info *fi);
+size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode);
 
 #else
+static inline int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
+						 struct btrfs_path *path,
+						 struct fscrypt_extent_info *fi)
+{
+	return 0;
+}
+
+static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+						 struct extent_map *em,
+						 struct btrfs_fscrypt_ctx *ctx)
+{
+	return 0;
+}
+
 static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
 					      struct btrfs_dir_item *di,
 					      struct fscrypt_str *qstr)
@@ -35,6 +61,11 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 	return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
 				     de_name_len);
 }
+
+static inline size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode)
+{
+	return 0;
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 231d9fe6ff8a..dc24f11016d9 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2918,6 +2918,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
 	size_t fscrypt_context_size = 0;
 	int ret;
 
+	if (btrfs_stack_file_extent_encryption(stack_fi))
+		fscrypt_context_size = btrfs_fscrypt_extent_context_size(inode);
+
 	path = btrfs_alloc_path();
 	if (!path)
 		return -ENOMEM;
@@ -2956,6 +2959,12 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
 			btrfs_item_ptr_offset(leaf, path->slots[0]),
 			sizeof(struct btrfs_file_extent_item));
 
+	if (fscrypt_context_size) {
+		ret = btrfs_fscrypt_save_extent_info(inode, path, fscrypt_info);
+		if (ret)
+			goto out;
+	}
+
 	btrfs_mark_buffer_dirty(trans, leaf);
 	btrfs_release_path(path);
 
@@ -6889,6 +6898,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 	struct btrfs_key found_key;
 	struct extent_map *em = NULL;
 	struct extent_map_tree *em_tree = &inode->extent_tree;
+	struct btrfs_fscrypt_ctx ctx;
 
 	read_lock(&em_tree->lock);
 	em = lookup_extent_mapping(em_tree, start, len);
@@ -6902,6 +6912,9 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 		else
 			goto out;
 	}
+
+	ctx.size = 0;
+
 	em = alloc_extent_map();
 	if (!em) {
 		ret = -ENOMEM;
@@ -7006,7 +7019,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 		goto insert;
 	}
 
-	btrfs_extent_item_to_extent_map(inode, path, item, em);
+	btrfs_extent_item_to_extent_map(inode, path, item, em, &ctx);
 
 	if (extent_type == BTRFS_FILE_EXTENT_REG ||
 	    extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
@@ -7051,6 +7064,10 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 		goto out;
 	}
 
+	ret = btrfs_fscrypt_load_extent_info(inode, em, &ctx);
+	if (ret)
+		goto out;
+
 	write_lock(&em_tree->lock);
 	ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len);
 	write_unlock(&em_tree->lock);
@@ -9749,6 +9766,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 		return trans;
 	}
 
+	if (fscrypt_info)
+		fscrypt_context_size = btrfs_fscrypt_extent_context_size(inode);
+
 	extent_info.disk_offset = start;
 	extent_info.disk_len = len;
 	extent_info.data_offset = 0;
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 36cbc4d176c5..53c2c58d0002 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -30,6 +30,7 @@
 #include "file.h"
 #include "orphan.h"
 #include "tree-checker.h"
+#include "fscrypt.h"
 
 #define MAX_CONFLICT_INODES 10
 
@@ -4630,6 +4631,9 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	size_t fscrypt_context_size = 0;
 	u8 encryption = em->encryption_type;
 
+	if (encryption)
+		fscrypt_context_size = btrfs_fscrypt_extent_context_size(inode);
+
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
 	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
 		btrfs_set_stack_file_extent_type(&fi, BTRFS_FILE_EXTENT_PREALLOC);
@@ -4690,6 +4694,12 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
 	write_extent_buffer(leaf, &fi,
 			    btrfs_item_ptr_offset(leaf, path->slots[0]),
 			    sizeof(fi));
+	if (fscrypt_context_size) {
+		ret = btrfs_fscrypt_save_extent_info(inode, path,
+						     em->fscrypt_info);
+		if (ret)
+			return ret;
+	}
 	btrfs_mark_buffer_dirty(trans, leaf);
 
 	btrfs_release_path(path);
-- 
2.41.0


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

* [PATCH v4 32/46] btrfs: setup fscrypt_extent_info for new extents
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (30 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 31/46] btrfs: implement the fscrypt extent encryption hooks Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 33/46] btrfs: populate ordered_extent with the orig offset Josef Bacik
                   ` (16 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

New extents for encrypted inodes must have a fscrypt_extent_info, which
has the necessary keys and does all the registration at the block layer
for them.  This is passed through all of the infrastructure we've
previously added to make sure the context gets saved properly with the
file extents.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dc24f11016d9..60d016da1fce 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7434,6 +7434,16 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode,
 	if (fscrypt_info) {
 		em->encryption_type = BTRFS_ENCRYPTION_FSCRYPT;
 		em->fscrypt_info = fscrypt_get_extent_info(fscrypt_info);
+	} else if (IS_ENCRYPTED(&inode->vfs_inode)) {
+		struct fscrypt_extent_info *fscrypt_info;
+
+		em->encryption_type = BTRFS_ENCRYPTION_FSCRYPT;
+		fscrypt_info = fscrypt_prepare_new_extent(&inode->vfs_inode);
+		if (IS_ERR(fscrypt_info)) {
+			free_extent_map(em);
+			return ERR_CAST(fscrypt_info);
+		}
+		em->fscrypt_info = fscrypt_info;
 	} else {
 		em->encryption_type = BTRFS_ENCRYPTION_NONE;
 	}
@@ -9831,6 +9841,9 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 	if (trans)
 		own_trans = false;
 	while (num_bytes > 0) {
+		struct fscrypt_extent_info *fscrypt_info = NULL;
+		int encryption_type = BTRFS_ENCRYPTION_NONE;
+
 		cur_bytes = min_t(u64, num_bytes, SZ_256M);
 		cur_bytes = max(cur_bytes, min_size);
 		/*
@@ -9845,6 +9858,20 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 		if (ret)
 			break;
 
+		if (IS_ENCRYPTED(inode)) {
+			fscrypt_info = fscrypt_prepare_new_extent(inode);
+			if (IS_ERR(fscrypt_info)) {
+				btrfs_dec_block_group_reservations(fs_info,
+								   ins.objectid);
+				btrfs_free_reserved_extent(fs_info,
+							   ins.objectid,
+							   ins.offset, 0);
+				ret = PTR_ERR(fscrypt_info);
+				break;
+			}
+			encryption_type = BTRFS_ENCRYPTION_FSCRYPT;
+		}
+
 		/*
 		 * We've reserved this space, and thus converted it from
 		 * ->bytes_may_use to ->bytes_reserved.  Any error that happens
@@ -9856,7 +9883,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 
 		last_alloc = ins.offset;
 		trans = insert_prealloc_file_extent(trans, BTRFS_I(inode),
-						    &ins, NULL, cur_offset);
+						    &ins, fscrypt_info,
+						    cur_offset);
 		/*
 		 * Now that we inserted the prealloc extent we can finally
 		 * decrement the number of reservations in the block group.
@@ -9866,6 +9894,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 		btrfs_dec_block_group_reservations(fs_info, ins.objectid);
 		if (IS_ERR(trans)) {
 			ret = PTR_ERR(trans);
+			fscrypt_put_extent_info(fscrypt_info);
 			btrfs_free_reserved_extent(fs_info, ins.objectid,
 						   ins.offset, 0);
 			break;
@@ -9873,6 +9902,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 
 		em = alloc_extent_map();
 		if (!em) {
+			fscrypt_put_extent_info(fscrypt_info);
 			btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
 					    cur_offset + ins.offset - 1, false);
 			btrfs_set_inode_full_sync(BTRFS_I(inode));
@@ -9888,6 +9918,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
 		em->ram_bytes = ins.offset;
 		set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
 		em->generation = trans->transid;
+		em->fscrypt_info = fscrypt_info;
+		em->encryption_type = encryption_type;
 
 		ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, true);
 		free_extent_map(em);
-- 
2.41.0


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

* [PATCH v4 33/46] btrfs: populate ordered_extent with the orig offset
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (31 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 32/46] btrfs: setup fscrypt_extent_info for new extents Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 34/46] btrfs: set the bio fscrypt context when applicable Josef Bacik
                   ` (15 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

For extent encryption we have to use a logical block nr as input for the
IV.  For btrfs we're using the offset into the extent we're operating
on.  For most ordered extents this is the same as the file_offset,
however for prealloc and NOCOW we have to use the original offset.

Add this as an argument and plumb it through everywhere, this will be
used when setting up the bio.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/inode.c        | 15 ++++++++++-----
 fs/btrfs/ordered-data.c | 32 ++++++++++++++++++++++----------
 fs/btrfs/ordered-data.h | 12 +++++++++---
 3 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 60d016da1fce..3dce53601915 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1175,6 +1175,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
 
 	ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info,
 				       start,			/* file_offset */
+				       start,			/* orig_start */
 				       async_extent->ram_size,	/* num_bytes */
 				       async_extent->ram_size,	/* ram_bytes */
 				       ins.objectid,		/* disk_bytenr */
@@ -1438,8 +1439,8 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
 		}
 
 		ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info,
-					start, ram_size, ram_size, ins.objectid,
-					cur_alloc_size, 0,
+					start, start, ram_size, ram_size,
+					ins.objectid, cur_alloc_size, 0,
 					1 << BTRFS_ORDERED_REGULAR,
 					BTRFS_COMPRESS_NONE);
 		free_extent_map(em);
@@ -2193,7 +2194,9 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
 		}
 
 		ordered = btrfs_alloc_ordered_extent(inode, fscrypt_info,
-				cur_offset, nocow_args.num_bytes,
+				cur_offset,
+				found_key.offset - nocow_args.extent_offset,
+				nocow_args.num_bytes,
 				nocow_args.num_bytes, nocow_args.disk_bytenr,
 				nocow_args.num_bytes, 0,
 				is_prealloc
@@ -7113,8 +7116,9 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
 		fscrypt_info = em->fscrypt_info;
 	}
 
-	ordered = btrfs_alloc_ordered_extent(inode, fscrypt_info, start, len,
-					     len, block_start, block_len, 0,
+	ordered = btrfs_alloc_ordered_extent(inode, fscrypt_info, start,
+					     orig_start, len, len, block_start,
+					     block_len, 0,
 					     (1 << type) |
 					     (1 << BTRFS_ORDERED_DIRECT),
 					     BTRFS_COMPRESS_NONE);
@@ -10655,6 +10659,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
 	}
 
 	ordered = btrfs_alloc_ordered_extent(inode, em->fscrypt_info, start,
+				       start - encoded->unencoded_offset,
 				       num_bytes, ram_bytes, ins.objectid,
 				       ins.offset, encoded->unencoded_offset,
 				       (1 << BTRFS_ORDERED_ENCODED) |
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index ee3138a6d11e..a9c9d9cc6ee3 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -148,9 +148,9 @@ static inline struct rb_node *ordered_tree_search(struct btrfs_inode *inode,
 static struct btrfs_ordered_extent *alloc_ordered_extent(
 			struct btrfs_inode *inode,
 			struct fscrypt_extent_info *fscrypt_info,
-			u64 file_offset, u64 num_bytes, u64 ram_bytes,
-			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
-			unsigned long flags, int compress_type)
+			u64 file_offset, u64 orig_offset, u64 num_bytes,
+			u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
+			u64 offset, unsigned long flags, int compress_type)
 {
 	struct btrfs_ordered_extent *entry;
 	int ret;
@@ -175,6 +175,7 @@ static struct btrfs_ordered_extent *alloc_ordered_extent(
 		return ERR_PTR(-ENOMEM);
 
 	entry->file_offset = file_offset;
+	entry->orig_offset = orig_offset;
 	entry->num_bytes = num_bytes;
 	entry->ram_bytes = ram_bytes;
 	entry->disk_bytenr = disk_bytenr;
@@ -253,6 +254,7 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry)
  * @inode:           Inode that this extent is for.
  * @fscrypt_info:    The fscrypt_extent_info for this extent, if necessary.
  * @file_offset:     Logical offset in file where the extent starts.
+ * @orig_offset:     Logical offset of the original extent (PREALLOC or NOCOW)
  * @num_bytes:       Logical length of extent in file.
  * @ram_bytes:       Full length of unencoded data.
  * @disk_bytenr:     Offset of extent on disk.
@@ -270,17 +272,17 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry)
 struct btrfs_ordered_extent *btrfs_alloc_ordered_extent(
 			struct btrfs_inode *inode,
 			struct fscrypt_extent_info *fscrypt_info,
-			u64 file_offset, u64 num_bytes, u64 ram_bytes,
-			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
-			unsigned long flags, int compress_type)
+			u64 file_offset, u64 orig_offset, u64 num_bytes,
+			u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
+			u64 offset, unsigned long flags, int compress_type)
 {
 	struct btrfs_ordered_extent *entry;
 
 	ASSERT((flags & ~BTRFS_ORDERED_TYPE_FLAGS) == 0);
 
 	entry = alloc_ordered_extent(inode, fscrypt_info, file_offset,
-				     num_bytes, ram_bytes, disk_bytenr,
-				     disk_num_bytes, offset, flags,
+				     orig_offset, num_bytes, ram_bytes,
+				     disk_bytenr, disk_num_bytes, offset, flags,
 				     compress_type);
 	if (!IS_ERR(entry))
 		insert_ordered_extent(entry);
@@ -1174,8 +1176,8 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
 		return ERR_PTR(-EINVAL);
 
 	new = alloc_ordered_extent(inode, ordered->fscrypt_info, file_offset,
-				   len, len, disk_bytenr, len, 0, flags,
-				   ordered->compress_type);
+				   ordered->orig_offset, len, len, disk_bytenr,
+				   len, 0, flags, ordered->compress_type);
 	if (IS_ERR(new))
 		return new;
 
@@ -1196,6 +1198,16 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
 	ordered->num_bytes -= len;
 	ordered->disk_num_bytes -= len;
 
+	/*
+	 * ->orig_offset is the original offset of the original extent, which
+	 * for PREALLOC or NOCOW stays the same, but if we're a regular extent
+	 * that means this is a new extent and thus ->orig_offset must equal
+	 * ->file_offset.  This is only important for encryption as we only use
+	 * it for setting the offset for the bio encryption context.
+	 */
+	if (test_bit(BTRFS_ORDERED_REGULAR, &ordered->flags))
+		ordered->orig_offset = ordered->file_offset;
+
 	if (test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)) {
 		ASSERT(ordered->bytes_left == 0);
 		new->bytes_left = 0;
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 57ca8ce6eb6d..a8ce181288f7 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -83,6 +83,12 @@ struct btrfs_ordered_extent {
 	/* logical offset in the file */
 	u64 file_offset;
 
+	/*
+	 * The original logical offset of the extent, this is for NOCOW and
+	 * PREALLOC extents, otherwise it'll be the same as file_offset.
+	 */
+	u64 orig_offset;
+
 	/*
 	 * These fields directly correspond to the same fields in
 	 * btrfs_file_extent_item.
@@ -165,9 +171,9 @@ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
 struct btrfs_ordered_extent *btrfs_alloc_ordered_extent(
 			struct btrfs_inode *inode,
 			struct fscrypt_extent_info *fscrypt_info,
-			u64 file_offset, u64 num_bytes, u64 ram_bytes,
-			u64 disk_bytenr, u64 disk_num_bytes, u64 offset,
-			unsigned long flags, int compress_type);
+			u64 file_offset, u64 orig_offset, u64 num_bytes,
+			u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
+			u64 offset, unsigned long flags, int compress_type);
 void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry,
 			   struct btrfs_ordered_sum *sum);
 struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *inode,
-- 
2.41.0


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

* [PATCH v4 34/46] btrfs: set the bio fscrypt context when applicable
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (32 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 33/46] btrfs: populate ordered_extent with the orig offset Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 35/46] btrfs: add a bio argument to btrfs_csum_one_bio Josef Bacik
                   ` (14 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Now that we have the fscrypt_info plumbed through everywhere, add the
code to setup the bio encryption context from the extent context.

We use the per-extent fscrypt_extent_info for encryption/decryption.
We use the offset into the extent as the lblk for fscrypt.  So the start
of the extent has the lblk of 0, 4k into the extent has the lblk of 4k,
etc.  This is done to allow things like relocation to continue to work
properly.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/compression.c |  6 ++++
 fs/btrfs/extent_io.c   | 76 +++++++++++++++++++++++++++++++++++++++++-
 fs/btrfs/fscrypt.c     | 36 ++++++++++++++++++++
 fs/btrfs/fscrypt.h     | 22 ++++++++++++
 fs/btrfs/inode.c       | 10 ++++++
 5 files changed, 149 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 05595d113ff8..a71614359c33 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -37,6 +37,7 @@
 #include "zoned.h"
 #include "file-item.h"
 #include "super.h"
+#include "fscrypt.h"
 
 static struct bio_set btrfs_compressed_bioset;
 
@@ -396,6 +397,9 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
 	cb->bbio.ordered = ordered;
 	btrfs_add_compressed_bio_pages(cb);
 
+	btrfs_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, inode,
+					    ordered->fscrypt_info, 0);
+
 	btrfs_submit_bio(&cb->bbio, 0);
 }
 
@@ -599,6 +603,8 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio)
 	cb->compress_type = em->compress_type;
 	cb->orig_bbio = bbio;
 
+	btrfs_set_bio_crypt_ctx_from_extent(&cb->bbio.bio, inode,
+					    em->fscrypt_info, 0);
 	free_extent_map(em);
 
 	cb->nr_pages = DIV_ROUND_UP(compressed_len, PAGE_SIZE);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 42544c0d9ee1..9824dd356e3c 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -36,6 +36,7 @@
 #include "dev-replace.h"
 #include "super.h"
 #include "transaction.h"
+#include "fscrypt.h"
 
 static struct kmem_cache *extent_buffer_cache;
 
@@ -102,6 +103,10 @@ struct btrfs_bio_ctrl {
 	blk_opf_t opf;
 	btrfs_bio_end_io_t end_io_func;
 	struct writeback_control *wbc;
+
+	/* This is set for reads and we have encryption. */
+	struct fscrypt_extent_info *fscrypt_info;
+	u64 orig_start;
 };
 
 static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
@@ -708,10 +713,31 @@ static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl,
 				struct page *page, u64 disk_bytenr,
 				unsigned int pg_offset)
 {
-	struct bio *bio = &bio_ctrl->bbio->bio;
+	struct inode *inode = page->mapping->host;
+	struct btrfs_bio *bbio = bio_ctrl->bbio;
+	struct bio *bio = &bbio->bio;
 	struct bio_vec *bvec = bio_last_bvec_all(bio);
 	const sector_t sector = disk_bytenr >> SECTOR_SHIFT;
 
+	if (IS_ENCRYPTED(inode)) {
+		u64 file_offset = page_offset(page) + pg_offset;
+		u64 offset = 0;
+		struct fscrypt_extent_info *fscrypt_info = NULL;
+
+		/* bio_ctrl->fscrypt_info is only set in the READ case. */
+		if (bio_ctrl->fscrypt_info) {
+			offset = file_offset - bio_ctrl->orig_start;
+			fscrypt_info = bio_ctrl->fscrypt_info;
+		} else if (bbio->ordered) {
+			fscrypt_info = bbio->ordered->fscrypt_info;
+			offset = file_offset - bbio->ordered->orig_offset;
+		}
+
+		if (!btrfs_mergeable_encrypted_bio(bio, inode, fscrypt_info,
+						   offset))
+			return false;
+	}
+
 	if (bio_ctrl->compress_type != BTRFS_COMPRESS_NONE) {
 		/*
 		 * For compression, all IO should have its logical bytenr set
@@ -742,6 +768,8 @@ static void alloc_new_bio(struct btrfs_inode *inode,
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_bio *bbio;
+	struct fscrypt_extent_info *fscrypt_info = NULL;
+	u64 offset = 0;
 
 	bbio = btrfs_bio_alloc(BIO_MAX_VECS, bio_ctrl->opf, fs_info,
 			       bio_ctrl->end_io_func, NULL);
@@ -761,6 +789,8 @@ static void alloc_new_bio(struct btrfs_inode *inode,
 					ordered->file_offset +
 					ordered->disk_num_bytes - file_offset);
 			bbio->ordered = ordered;
+			fscrypt_info = ordered->fscrypt_info;
+			offset = file_offset - ordered->orig_offset;
 		}
 
 		/*
@@ -771,7 +801,13 @@ static void alloc_new_bio(struct btrfs_inode *inode,
 		 */
 		bio_set_dev(&bbio->bio, fs_info->fs_devices->latest_dev->bdev);
 		wbc_init_bio(bio_ctrl->wbc, &bbio->bio);
+	} else {
+		fscrypt_info = bio_ctrl->fscrypt_info;
+		offset = file_offset - bio_ctrl->orig_start;
 	}
+
+	btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, inode, fscrypt_info,
+					    offset);
 }
 
 /*
@@ -815,6 +851,19 @@ static void submit_extent_page(struct btrfs_bio_ctrl *bio_ctrl,
 			len = bio_ctrl->len_to_oe_boundary;
 		}
 
+		/*
+		 * Encryption has to allocate bounce buffers to encrypt the bio,
+		 * and we need to make sure that it doesn't split the bio so we
+		 * retain all of our special info in the btrfs_bio, so submit
+		 * any bio that gets up to BIO_MAX_VECS worth of segments.
+		 */
+		if (IS_ENCRYPTED(&inode->vfs_inode) &&
+		    bio_data_dir(&bio_ctrl->bbio->bio) == WRITE &&
+		    bio_segments(&bio_ctrl->bbio->bio) == BIO_MAX_VECS) {
+			submit_one_bio(bio_ctrl);
+			continue;
+		}
+
 		if (bio_add_page(&bio_ctrl->bbio->bio, page, len, pg_offset) != len) {
 			/* bio full: move on to a new one */
 			submit_one_bio(bio_ctrl);
@@ -1008,6 +1057,8 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
 		bool force_bio_submit = false;
 		u64 disk_bytenr;
 
+		bio_ctrl->fscrypt_info = NULL;
+
 		ASSERT(IS_ALIGNED(cur, fs_info->sectorsize));
 		if (cur >= last_byte) {
 			iosize = PAGE_SIZE - pg_offset;
@@ -1082,6 +1133,22 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
 		if (prev_em_start)
 			*prev_em_start = em->start;
 
+		/*
+		 * We use the extent offset for the IV when decrypting the page,
+		 * so we have to set the extent_offset based on the orig_start
+		 * for this extent.  Also save the fscrypt_info so the bio ctx
+		 * can be set properly.  If this inode isn't encrypted this
+		 * won't do anything.
+		 *
+		 * If we're compressed we'll handle all of this in
+		 * btrfs_submit_compressed_read.
+		 */
+		if (compress_type == BTRFS_COMPRESS_NONE) {
+			bio_ctrl->orig_start = em->orig_start;
+			bio_ctrl->fscrypt_info =
+				fscrypt_get_extent_info(em->fscrypt_info);
+		}
+
 		free_extent_map(em);
 		em = NULL;
 
@@ -1093,6 +1160,9 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
 			end_page_read(page, true, cur, iosize);
 			cur = cur + iosize;
 			pg_offset += iosize;
+
+			/* This shouldn't be set, but clear it just in case. */
+			fscrypt_put_extent_info(bio_ctrl->fscrypt_info);
 			continue;
 		}
 		/* the get_extent function already copied into the page */
@@ -1101,6 +1171,9 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
 			end_page_read(page, true, cur, iosize);
 			cur = cur + iosize;
 			pg_offset += iosize;
+
+			/* This shouldn't be set, but clear it just in case. */
+			fscrypt_put_extent_info(bio_ctrl->fscrypt_info);
 			continue;
 		}
 
@@ -1113,6 +1186,7 @@ static int btrfs_do_readpage(struct page *page, struct extent_map **em_cached,
 			submit_one_bio(bio_ctrl);
 		submit_extent_page(bio_ctrl, disk_bytenr, page, iosize,
 				   pg_offset);
+		fscrypt_put_extent_info(bio_ctrl->fscrypt_info);
 		cur = cur + iosize;
 		pg_offset += iosize;
 	}
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 7a7272cb83ec..726cb6121934 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -262,6 +262,42 @@ size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode)
 		fscrypt_extent_context_size(&inode->vfs_inode);
 }
 
+void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio,
+					 struct btrfs_inode *inode,
+					 struct fscrypt_extent_info *fi,
+					 u64 logical_offset)
+{
+	if (!fi)
+		return;
+
+	/*
+	 * fscrypt uses bytes >> s_blocksize_bits for the block numbers, so we
+	 * have to adjust everything based on our sectorsize so that the DUN
+	 * calculations are correct.
+	 */
+	logical_offset = div64_u64(logical_offset,
+				   inode->root->fs_info->sectorsize);
+	fscrypt_set_bio_crypt_ctx_from_extent(bio, &inode->vfs_inode, fi,
+					      logical_offset, GFP_NOFS);
+}
+
+bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode,
+				   struct fscrypt_extent_info *fi,
+				   u64 logical_offset)
+{
+	if (!fi)
+		return true;
+
+	/*
+	 * fscrypt uses bytes >> s_blocksize_bits for the block numbers, so we
+	 * have to adjust everything based on our sectorsize so that the DUN
+	 * calculations are correct.
+	 */
+	logical_offset = div64_u64(logical_offset,
+				   BTRFS_I(inode)->root->fs_info->sectorsize);
+	return fscrypt_mergeable_extent_bio(bio, inode, fi, logical_offset);
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.has_per_extent_encryption = 1,
 	.get_context = btrfs_fscrypt_get_context,
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 2882a4a9d978..756375ade0b6 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -28,6 +28,13 @@ int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
 				   struct btrfs_path *path,
 				   struct fscrypt_extent_info *fi);
 size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode);
+void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio,
+					 struct btrfs_inode *inode,
+					 struct fscrypt_extent_info *fi,
+					 u64 logical_offset);
+bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode,
+				   struct fscrypt_extent_info *fi,
+				   u64 logical_offset);
 
 #else
 static inline int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
@@ -66,6 +73,21 @@ static inline size_t btrfs_fscrypt_extent_context_size(struct btrfs_inode *inode
 {
 	return 0;
 }
+
+static inline void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio,
+						       struct btrfs_inode *inode,
+						       struct fscrypt_extent_info *fi,
+						       u64 logical_offset)
+{
+}
+
+static inline bool btrfs_mergeable_encrypted_bio(struct bio *bio,
+						 struct inode *inode,
+						 struct fscrypt_extent_info *fi,
+						 u64 logical_offset)
+{
+	return true;
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 3dce53601915..c5878da937d3 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7932,6 +7932,8 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
 	struct btrfs_dio_private *dip =
 		container_of(bbio, struct btrfs_dio_private, bbio);
 	struct btrfs_dio_data *dio_data = iter->private;
+	struct fscrypt_extent_info *fscrypt_info = NULL;
+	u64 offset = 0;
 
 	btrfs_bio_init(bbio, BTRFS_I(iter->inode)->root->fs_info,
 		       btrfs_dio_end_io, bio->bi_private);
@@ -7953,6 +7955,9 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
 	if (iter->flags & IOMAP_WRITE) {
 		int ret;
 
+		offset = file_offset - dio_data->ordered->orig_offset;
+		fscrypt_info = dio_data->ordered->fscrypt_info;
+
 		ret = btrfs_extract_ordered_extent(bbio, dio_data->ordered);
 		if (ret) {
 			btrfs_finish_ordered_extent(dio_data->ordered, NULL,
@@ -7962,8 +7967,13 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
 			iomap_dio_bio_end_io(bio);
 			return;
 		}
+	} else {
+		fscrypt_info = dio_data->fscrypt_info;
+		offset = file_offset - dio_data->orig_start;
 	}
 
+	btrfs_set_bio_crypt_ctx_from_extent(&bbio->bio, bbio->inode,
+					    fscrypt_info, offset);
 	btrfs_submit_bio(bbio, 0);
 }
 
-- 
2.41.0


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

* [PATCH v4 35/46] btrfs: add a bio argument to btrfs_csum_one_bio
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (33 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 34/46] btrfs: set the bio fscrypt context when applicable Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 36/46] btrfs: add orig_logical to btrfs_bio Josef Bacik
                   ` (13 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We only ever needed the bbio in btrfs_csum_one_bio, since that has the
bio embedded in it.  However with encryption we'll have a different bio
with the encrypted data in it, and the original bbio.  Update
btrfs_csum_one_bio to take the bio we're going to csum as an argument,
which will allow us to csum the encrypted bio and stuff the csums into
the corresponding bbio to be used later when the IO completes.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/bio.c       | 2 +-
 fs/btrfs/file-item.c | 3 +--
 fs/btrfs/file-item.h | 2 +-
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 4f3b693a16b1..90e4d4709fa3 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -533,7 +533,7 @@ static blk_status_t btrfs_bio_csum(struct btrfs_bio *bbio)
 {
 	if (bbio->bio.bi_opf & REQ_META)
 		return btree_csum_one_bio(bbio);
-	return btrfs_csum_one_bio(bbio);
+	return btrfs_csum_one_bio(bbio, &bbio->bio);
 }
 
 /*
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 35036fab58c4..d925d6d98bf4 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -730,13 +730,12 @@ int btrfs_lookup_csums_bitmap(struct btrfs_root *root, struct btrfs_path *path,
 /*
  * Calculate checksums of the data contained inside a bio.
  */
-blk_status_t btrfs_csum_one_bio(struct btrfs_bio *bbio)
+blk_status_t btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio)
 {
 	struct btrfs_ordered_extent *ordered = bbio->ordered;
 	struct btrfs_inode *inode = bbio->inode;
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
-	struct bio *bio = &bbio->bio;
 	struct btrfs_ordered_sum *sums;
 	char *data;
 	struct bvec_iter iter;
diff --git a/fs/btrfs/file-item.h b/fs/btrfs/file-item.h
index bb79014024bd..e52d5d71d533 100644
--- a/fs/btrfs/file-item.h
+++ b/fs/btrfs/file-item.h
@@ -51,7 +51,7 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
 int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
 			   struct btrfs_root *root,
 			   struct btrfs_ordered_sum *sums);
-blk_status_t btrfs_csum_one_bio(struct btrfs_bio *bbio);
+blk_status_t btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio);
 blk_status_t btrfs_alloc_dummy_sum(struct btrfs_bio *bbio);
 int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
 			     struct list_head *list, int search_commit,
-- 
2.41.0


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

* [PATCH v4 36/46] btrfs: add orig_logical to btrfs_bio
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (34 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 35/46] btrfs: add a bio argument to btrfs_csum_one_bio Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 37/46] btrfs: limit encrypted writes to 256 segments Josef Bacik
                   ` (12 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

When checksumming the encrypted bio on writes we need to know which
logical address this checksum is for.  At the point where we get the
encrypted bio the bi_sector is the physical location on the target disk,
so we need to save the original logical offset in the btrfs_bio.  Then
we can use this when csum'ing the bio instead of the
bio->iter.bi_sector.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/bio.c       | 10 ++++++++++
 fs/btrfs/bio.h       |  3 +++
 fs/btrfs/file-item.c |  2 +-
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 90e4d4709fa3..52f027877aaa 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -96,6 +96,8 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info,
 	if (bbio_has_ordered_extent(bbio)) {
 		refcount_inc(&orig_bbio->ordered->refs);
 		bbio->ordered = orig_bbio->ordered;
+		bbio->orig_logical = orig_bbio->orig_logical;
+		orig_bbio->orig_logical += map_length;
 	}
 	atomic_inc(&orig_bbio->pending_ios);
 	return bbio;
@@ -674,6 +676,14 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
 		goto fail;
 	}
 
+	/*
+	 * For fscrypt writes we will get the encrypted bio after we've remapped
+	 * our bio to the physical disk location, so we need to save the
+	 * original bytenr so we know what we're checksumming.
+	 */
+	if (bio_op(bio) == REQ_OP_WRITE && is_data_bbio(bbio))
+		bbio->orig_logical = logical;
+
 	map_length = min(map_length, length);
 	if (use_append)
 		map_length = min(map_length, fs_info->max_zone_append_size);
diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
index ca79decee060..5d3f53dcd6d5 100644
--- a/fs/btrfs/bio.h
+++ b/fs/btrfs/bio.h
@@ -54,11 +54,14 @@ struct btrfs_bio {
 		 * - pointer to the checksums for this bio
 		 * - original physical address from the allocator
 		 *   (for zone append only)
+		 * - original logical address, used for checksumming fscrypt
+		 *   bios.
 		 */
 		struct {
 			struct btrfs_ordered_extent *ordered;
 			struct btrfs_ordered_sum *sums;
 			u64 orig_physical;
+			u64 orig_logical;
 		};
 
 		/* For metadata reads: parentness verification. */
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index d925d6d98bf4..26e3bc602655 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -756,7 +756,7 @@ blk_status_t btrfs_csum_one_bio(struct btrfs_bio *bbio, struct bio *bio)
 	sums->len = bio->bi_iter.bi_size;
 	INIT_LIST_HEAD(&sums->list);
 
-	sums->logical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
+	sums->logical = bbio->orig_logical;
 	index = 0;
 
 	shash->tfm = fs_info->csum_shash;
-- 
2.41.0


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

* [PATCH v4 37/46] btrfs: limit encrypted writes to 256 segments
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (35 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 36/46] btrfs: add orig_logical to btrfs_bio Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 38/46] btrfs: implement process_bio cb for fscrypt Josef Bacik
                   ` (11 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

For the fallback encrypted writes it allocates a bounce buffer to
encrypt, and if the bio is larger than 256 segments it splits the bio we
send down.  This wreaks havoc on us because we need our actual original
bio to be passed into the process_cb callback in order to get at our
ordered extent.  With the split we'll get some cloned bio that has none
of our information.  Handle this by returning the length we need to be
truncated to and then splitting ourselves, which will handle giving us
the correct btrfs_bio that we need.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/bio.c     | 29 ++++++++++++++++++++++++++++-
 fs/btrfs/fscrypt.c | 24 ++++++++++++++++++++++++
 fs/btrfs/fscrypt.h |  6 ++++++
 3 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 52f027877aaa..ef3f98b3ec3f 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -15,6 +15,7 @@
 #include "zoned.h"
 #include "file-item.h"
 #include "raid-stripe-tree.h"
+#include "fscrypt.h"
 
 static struct bio_set btrfs_bioset;
 static struct bio_set btrfs_clone_bioset;
@@ -660,6 +661,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
 	u64 logical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
 	u64 length = bio->bi_iter.bi_size;
 	u64 map_length = length;
+	u64 max_bio_len = length;
 	bool use_append = btrfs_use_zone_append(bbio);
 	struct btrfs_io_context *bioc = NULL;
 	struct btrfs_io_stripe smap;
@@ -669,6 +671,31 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
 	smap.is_scrub = !bbio->inode;
 
 	btrfs_bio_counter_inc_blocked(fs_info);
+
+	/*
+	 * The blk-crypto-fallback limits bio sizes to 256 segments, because it
+	 * has no way of controlling the pages it gets for the bounce bio it
+	 * submits.
+	 *
+	 * If we don't pre-split our bio blk-crypto-fallback will do it for us,
+	 * and then call into our checksum callback with a random cloned bio
+	 * that isn't a btrfs_bio.
+	 *
+	 * To account for this we must truncate the bio ourselves, so we need to
+	 * get our length at 256 segments and return that.  We must do this
+	 * before btrfs_map_block because the RAID5/6 code relies on having
+	 * properly stripe aligned things, so we return the truncated length
+	 * here so that btrfs_map_block() does the correct thing.  Further down
+	 * we actually truncate map_length to map_bio_len because map_length
+	 * will be set to whatever the mapping length is for the underlying
+	 * geometry.  This will work properly with RAID5/6 as it will have
+	 * already setup everything for the expected length, and everything else
+	 * can handle with a truncated map_length.
+	 */
+	if (bio_has_crypt_ctx(bio))
+		max_bio_len = btrfs_fscrypt_bio_length(bio, map_length);
+
+	map_length = max_bio_len;
 	error = btrfs_map_block(fs_info, btrfs_op(bio), logical, &map_length,
 				&bioc, &smap, &mirror_num);
 	if (error) {
@@ -684,7 +711,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
 	if (bio_op(bio) == REQ_OP_WRITE && is_data_bbio(bbio))
 		bbio->orig_logical = logical;
 
-	map_length = min(map_length, length);
+	map_length = min(map_length, max_bio_len);
 	if (use_append)
 		map_length = min(map_length, fs_info->max_zone_append_size);
 
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 726cb6121934..419b0f6d8629 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -298,6 +298,30 @@ bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode,
 	return fscrypt_mergeable_extent_bio(bio, inode, fi, logical_offset);
 }
 
+/*
+ * The block crypto stuff allocates bounce buffers for encryption, so splits at
+ * BIO_MAX_VECS worth of segments.  If we are larger than that number of
+ * segments then we need to limit the size to the size that BIO_MAX_VECS covers.
+ */
+int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length)
+{
+	unsigned int i = 0;
+	struct bio_vec bv;
+	struct bvec_iter iter;
+	u64 segments_length = 0;
+
+	if (bio_op(bio) != REQ_OP_WRITE)
+		return map_length;
+
+	bio_for_each_segment(bv, bio, iter) {
+		segments_length += bv.bv_len;
+		if (++i == BIO_MAX_VECS)
+			return segments_length;
+	}
+
+	return map_length;
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.has_per_extent_encryption = 1,
 	.get_context = btrfs_fscrypt_get_context,
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 756375ade0b6..c4a327be0eeb 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -35,6 +35,7 @@ void btrfs_set_bio_crypt_ctx_from_extent(struct bio *bio,
 bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode,
 				   struct fscrypt_extent_info *fi,
 				   u64 logical_offset);
+int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length);
 
 #else
 static inline int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
@@ -88,6 +89,11 @@ static inline bool btrfs_mergeable_encrypted_bio(struct bio *bio,
 {
 	return true;
 }
+
+static inline u64 btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length)
+{
+	return map_length;
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
-- 
2.41.0


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

* [PATCH v4 38/46] btrfs: implement process_bio cb for fscrypt
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (36 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 37/46] btrfs: limit encrypted writes to 256 segments Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 39/46] btrfs: add test_dummy_encryption support Josef Bacik
                   ` (10 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We are going to be checksumming the encrypted data, so we have to
implement the ->process_bio fscrypt callback.  This will provide us with
the original bio and the encrypted bio to do work on.  For WRITE's this
will happen after the encrypted bio has been encrypted.  For READ's this
will happen after the read has completed and before the decryption step
is done.

For write's this is straightforward, we can just pass in the encrypted
bio to btrfs_csum_one_bio and then the csums will be added to the bbio
as normal.

For read's this is relatively straightforward, but requires some care.
We assume (because that's how it works currently) that the encrypted bio
match the original bio, this is important because we save the iter of
the bio before we submit.  If this changes in the future we'll need a
hook to give us the bi_iter of the decryption bio before it's submitted.
We check the csums before decryption.  If it doesn't match we simply
error out and we let the normal path handle the repair work.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/bio.c     | 34 +++++++++++++++++++++++++++++++++-
 fs/btrfs/bio.h     |  3 +++
 fs/btrfs/fscrypt.c | 29 +++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index ef3f98b3ec3f..494b3ba05f7a 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -282,6 +282,34 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
 	return fbio;
 }
 
+blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio,
+					    struct bio *enc_bio)
+{
+	struct btrfs_inode *inode = bbio->inode;
+	struct btrfs_fs_info *fs_info = inode->root->fs_info;
+	u32 sectorsize = fs_info->sectorsize;
+	struct bvec_iter iter = bbio->saved_iter;
+	struct btrfs_device *dev = bbio->bio.bi_private;
+	u32 offset = 0;
+
+	/*
+	 * We have to use a copy of iter in case there's an error,
+	 * btrfs_check_read_bio will handle submitting the repair bios.
+	 */
+	while (iter.bi_size) {
+		struct bio_vec bv = bio_iter_iovec(enc_bio, iter);
+
+		bv.bv_len = min(bv.bv_len, sectorsize);
+		if (!btrfs_data_csum_ok(bbio, dev, offset, &bv))
+			return BLK_STS_IOERR;
+		bio_advance_iter_single(enc_bio, &iter, sectorsize);
+		offset += sectorsize;
+	}
+
+	bbio->csum_done = true;
+	return BLK_STS_OK;
+}
+
 static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *dev)
 {
 	struct btrfs_inode *inode = bbio->inode;
@@ -307,6 +335,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
 	/* Clear the I/O error. A failed repair will reset it. */
 	bbio->bio.bi_status = BLK_STS_OK;
 
+	/* This was an encrypted bio and we've already done the csum check. */
+	if (status == BLK_STS_OK && bbio->csum_done)
+		goto out;
+
 	while (iter->bi_size) {
 		struct bio_vec bv = bio_iter_iovec(&bbio->bio, *iter);
 
@@ -317,7 +349,7 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
 		bio_advance_iter_single(&bbio->bio, iter, sectorsize);
 		offset += sectorsize;
 	}
-
+out:
 	if (bbio->csum != bbio->csum_inline)
 		kfree(bbio->csum);
 
diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
index 5d3f53dcd6d5..393ef32f5321 100644
--- a/fs/btrfs/bio.h
+++ b/fs/btrfs/bio.h
@@ -45,6 +45,7 @@ struct btrfs_bio {
 		struct {
 			u8 *csum;
 			u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE];
+			bool csum_done;
 			struct bvec_iter saved_iter;
 		};
 
@@ -110,5 +111,7 @@ void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_
 int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
 			    u64 length, u64 logical, struct page *page,
 			    unsigned int pg_offset, int mirror_num);
+blk_status_t btrfs_check_encrypted_read_bio(struct btrfs_bio *bbio,
+					    struct bio *enc_bio);
 
 #endif
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 419b0f6d8629..85b596711371 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -15,6 +15,7 @@
 #include "transaction.h"
 #include "volumes.h"
 #include "xattr.h"
+#include "file-item.h"
 
 /*
  * From a given location in a leaf, read a name into a qstr (usually a
@@ -214,6 +215,33 @@ static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
 	return devs;
 }
 
+static blk_status_t btrfs_process_encrypted_bio(struct bio *orig_bio,
+						struct bio *enc_bio)
+{
+	struct btrfs_bio *bbio;
+
+	/*
+	 * If our bio is from the normal fs_bio_set then we know this is a
+	 * mirror split and we can skip it, we'll get the real bio on the last
+	 * mirror and we can process that one.
+	 */
+	if (orig_bio->bi_pool == &fs_bio_set)
+		return BLK_STS_OK;
+
+	bbio = btrfs_bio(orig_bio);
+
+	if (bio_op(orig_bio) == REQ_OP_READ) {
+		/*
+		 * We have ->saved_iter based on the orig_bio, so if the block
+		 * layer changes we need to notice this asap so we can update
+		 * our code to handle the new world order.
+		 */
+		ASSERT(orig_bio == enc_bio);
+		return btrfs_check_encrypted_read_bio(bbio, enc_bio);
+	}
+	return btrfs_csum_one_bio(bbio, enc_bio);
+}
+
 int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
 				   struct extent_map *em,
 				   struct btrfs_fscrypt_ctx *ctx)
@@ -328,4 +356,5 @@ const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.set_context = btrfs_fscrypt_set_context,
 	.empty_dir = btrfs_fscrypt_empty_dir,
 	.get_devices = btrfs_fscrypt_get_devices,
+	.process_bio = btrfs_process_encrypted_bio,
 };
-- 
2.41.0


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

* [PATCH v4 39/46] btrfs: add test_dummy_encryption support
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (37 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 38/46] btrfs: implement process_bio cb for fscrypt Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 40/46] btrfs: don't rewrite ret from inode_permission Josef Bacik
                   ` (9 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

In order to enable more thorough testing of fscrypt enable the
test_dummy_encryption mount option.  This is used by fscrypt users to
easily enable fscrypt on the file system for testing without needing to
do the key setup and everything.

The only deviation from other file systems we make is we only support
the fsparam_flag version of this mount option, as it defaults to v2.  We
don't want to have to bother with rejecting v1 related mount options.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/disk-io.c |  1 +
 fs/btrfs/fs.h      |  3 +++
 fs/btrfs/fscrypt.c |  6 +++++
 fs/btrfs/super.c   | 58 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+)

diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 719245d73b99..aedd8a0e4962 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1272,6 +1272,7 @@ void btrfs_free_fs_info(struct btrfs_fs_info *fs_info)
 	kfree(fs_info->super_copy);
 	kfree(fs_info->super_for_commit);
 	kfree(fs_info->subpage_info);
+	fscrypt_free_dummy_policy(&fs_info->dummy_enc_policy);
 	kvfree(fs_info);
 }
 
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 1340e71d026c..74752204f3ab 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -7,6 +7,7 @@
 #include <linux/fs.h>
 #include <linux/btrfs_tree.h>
 #include <linux/sizes.h>
+#include <linux/fscrypt.h>
 #include "extent-io-tree.h"
 #include "extent_map.h"
 #include "async-thread.h"
@@ -189,6 +190,7 @@ enum {
 	BTRFS_MOUNT_IGNOREDATACSUMS		= (1UL << 28),
 	BTRFS_MOUNT_NODISCARD			= (1UL << 29),
 	BTRFS_MOUNT_NOSPACECACHE		= (1UL << 30),
+	BTRFS_MOUNT_TEST_DUMMY_ENCRYPTION	= (1UL << 31),
 };
 
 /*
@@ -828,6 +830,7 @@ struct btrfs_fs_info {
 	spinlock_t eb_leak_lock;
 	struct list_head allocated_ebs;
 #endif
+	struct fscrypt_dummy_policy dummy_enc_policy;
 };
 
 static inline u64 btrfs_get_fs_generation(const struct btrfs_fs_info *fs_info)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 85b596711371..2fbceec62dc7 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -242,6 +242,11 @@ static blk_status_t btrfs_process_encrypted_bio(struct bio *orig_bio,
 	return btrfs_csum_one_bio(bbio, enc_bio);
 }
 
+static const union fscrypt_policy *btrfs_get_dummy_policy(struct super_block *sb)
+{
+	return btrfs_sb(sb)->dummy_enc_policy.policy;
+}
+
 int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
 				   struct extent_map *em,
 				   struct btrfs_fscrypt_ctx *ctx)
@@ -357,4 +362,5 @@ const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.empty_dir = btrfs_fscrypt_empty_dir,
 	.get_devices = btrfs_fscrypt_get_devices,
 	.process_bio = btrfs_process_encrypted_bio,
+	.get_dummy_policy = btrfs_get_dummy_policy,
 };
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 462a23db26af..2a7789519a6c 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -88,6 +88,7 @@ struct btrfs_fs_context {
 	unsigned long compress_type:4;
 	unsigned int compress_level;
 	refcount_t refs;
+	struct fscrypt_dummy_policy dummy_enc_policy;
 };
 
 enum {
@@ -122,6 +123,7 @@ enum {
 	Opt_thread_pool,
 	Opt_treelog,
 	Opt_user_subvol_rm_allowed,
+	Opt_test_dummy_encryption,
 
 	/* Rescue options */
 	Opt_rescue,
@@ -255,6 +257,13 @@ static const struct fs_parameter_spec btrfs_fs_parameters[] = {
 	fsparam_flag_no("enospc_debug", Opt_enospc_debug),
 #ifdef CONFIG_BTRFS_DEBUG
 	fsparam_enum("fragment", Opt_fragment, btrfs_parameter_fragment),
+
+	/*
+	 * With -o test_dummy_encryption we default to v2, only allow the flag
+	 * version, since we don't support v1.
+	 */
+	fsparam_flag("test_dummy_encryption", Opt_test_dummy_encryption),
+
 #endif
 #ifdef CONFIG_BTRFS_FS_REF_VERIFY
 	fsparam_flag("ref_verify", Opt_ref_verify),
@@ -268,6 +277,7 @@ static int btrfs_parse_param(struct fs_context *fc,
 	struct btrfs_fs_context *ctx = fc->fs_private;
 	struct fs_parse_result result;
 	int opt;
+	int ret;
 
 	opt = fs_parse(fc, btrfs_fs_parameters, param, &result);
 	if (opt < 0)
@@ -598,6 +608,13 @@ static int btrfs_parse_param(struct fs_context *fc,
 			return -EINVAL;
 		}
 		break;
+	case Opt_test_dummy_encryption:
+		btrfs_set_opt(ctx->mount_opt, TEST_DUMMY_ENCRYPTION);
+		ret = fscrypt_parse_test_dummy_encryption(param,
+							  &ctx->dummy_enc_policy);
+		if (ret)
+			return ret;
+		break;
 #endif
 #ifdef CONFIG_BTRFS_FS_REF_VERIFY
 	case Opt_ref_verify:
@@ -946,6 +963,9 @@ static int btrfs_fill_super(struct super_block *sb,
 		return err;
 	}
 
+	if (fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy))
+		btrfs_set_fs_incompat(fs_info, ENCRYPT);
+
 	if (btrfs_fs_incompat(fs_info, ENCRYPT)) {
 		if (IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT)) {
 			sb->s_flags |= SB_INLINECRYPT;
@@ -1112,6 +1132,9 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
 #endif
 	if (btrfs_test_opt(info, REF_VERIFY))
 		seq_puts(seq, ",ref_verify");
+	if (btrfs_test_opt(info, TEST_DUMMY_ENCRYPTION))
+		fscrypt_show_test_dummy_encryption(seq, ',', dentry->d_sb);
+
 	seq_printf(seq, ",subvolid=%llu",
 		  BTRFS_I(d_inode(dentry))->root->root_key.objectid);
 	subvol_name = btrfs_get_subvol_name_from_objectid(info,
@@ -1384,6 +1407,18 @@ static void btrfs_ctx_to_info(struct btrfs_fs_info *fs_info,
 	fs_info->mount_opt = ctx->mount_opt;
 	fs_info->compress_type = ctx->compress_type;
 	fs_info->compress_level = ctx->compress_level;
+
+	/*
+	 * If there's nothing set, or if the fs_info already has one set, don't
+	 * do anything.  If the fs_info is set we'll free the dummy one when we
+	 * free the ctx.
+	 */
+	if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy) ||
+	    fscrypt_is_dummy_policy_set(&fs_info->dummy_enc_policy))
+		return;
+
+	fs_info->dummy_enc_policy = ctx->dummy_enc_policy;
+	memset(&ctx->dummy_enc_policy, 0, sizeof(ctx->dummy_enc_policy));
 }
 
 static void btrfs_info_to_ctx(struct btrfs_fs_info *fs_info,
@@ -1436,6 +1471,7 @@ static void btrfs_emit_options(struct btrfs_fs_info *info,
 	btrfs_info_if_set(info, old, USEBACKUPROOT, "trying to use backup root at mount time");
 	btrfs_info_if_set(info, old, IGNOREBADROOTS, "ignoring bad roots");
 	btrfs_info_if_set(info, old, IGNOREDATACSUMS, "ignoring data csums");
+	btrfs_info_if_set(info, old, TEST_DUMMY_ENCRYPTION, "test dummy encryption mode enabled");
 
 	btrfs_info_if_unset(info, old, NODATACOW, "setting datacow");
 	btrfs_info_if_unset(info, old, SSD, "not using ssd optimizations");
@@ -1467,6 +1503,23 @@ static void btrfs_emit_options(struct btrfs_fs_info *info,
 			   info->max_inline);
 }
 
+static bool btrfs_check_test_dummy_encryption(struct fs_context *fc)
+{
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	struct btrfs_fs_info *fs_info = btrfs_sb(fc->root->d_sb);
+
+	if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy))
+		return true;
+
+	if (fscrypt_dummy_policies_equal(&fs_info->dummy_enc_policy,
+					 &ctx->dummy_enc_policy))
+		return true;
+
+	btrfs_warn(fs_info,
+		   "Can't set or change test_dummy_encryption on remount");
+	return false;
+}
+
 static int btrfs_reconfigure(struct fs_context *fc)
 {
 	struct super_block *sb = fc->root->d_sb;
@@ -1485,6 +1538,10 @@ static int btrfs_reconfigure(struct fs_context *fc)
 	    !btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags))
 		return -EINVAL;
 
+	if (!mount_reconfigure &&
+	    !btrfs_check_test_dummy_encryption(fc))
+		return -EINVAL;
+
 	ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY));
 	if (ret < 0)
 		return ret;
@@ -2121,6 +2178,7 @@ static void btrfs_free_fs_context(struct fs_context *fc)
 		btrfs_free_fs_info(fs_info);
 
 	if (ctx && refcount_dec_and_test(&ctx->refs)) {
+		fscrypt_free_dummy_policy(&ctx->dummy_enc_policy);
 		kfree(ctx->subvol_name);
 		kfree(ctx);
 	}
-- 
2.41.0


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

* [PATCH v4 40/46] btrfs: don't rewrite ret from inode_permission
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (38 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 39/46] btrfs: add test_dummy_encryption support Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 41/46] btrfs: move inode_to_path higher in backref.c Josef Bacik
                   ` (8 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

In our user safe ino resolve ioctl we'll just turn any ret into -EACCES
from inode_permission.  This is redundant, and could potentially be
wrong if we had an ENOMEM in the security layer or some such other
error, so simply return the actual return value.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ioctl.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 48d751011d07..2f4f9f812616 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1977,10 +1977,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
 			ret = inode_permission(idmap, temp_inode,
 					       MAY_READ | MAY_EXEC);
 			iput(temp_inode);
-			if (ret) {
-				ret = -EACCES;
+			if (ret)
 				goto out_put;
-			}
 
 			if (key.offset == upper_limit.objectid)
 				break;
-- 
2.41.0


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

* [PATCH v4 41/46] btrfs: move inode_to_path higher in backref.c
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (39 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 40/46] btrfs: don't rewrite ret from inode_permission Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 42/46] btrfs: make btrfs_ref_to_path handle encrypted filenames Josef Bacik
                   ` (7 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We have a prototype and then the definition lower below, we don't need
to do this, simply move the function to where the prototype is.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/backref.c | 69 ++++++++++++++++++++++------------------------
 1 file changed, 33 insertions(+), 36 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index beed7e459dab..f58fe7c745c2 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -2578,8 +2578,40 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
 				     build_ino_list, ctx);
 }
 
+/*
+ * returns 0 if the path could be dumped (probably truncated)
+ * returns <0 in case of an error
+ */
 static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
-			 struct extent_buffer *eb, struct inode_fs_paths *ipath);
+			 struct extent_buffer *eb, struct inode_fs_paths *ipath)
+{
+	char *fspath;
+	char *fspath_min;
+	int i = ipath->fspath->elem_cnt;
+	const int s_ptr = sizeof(char *);
+	u32 bytes_left;
+
+	bytes_left = ipath->fspath->bytes_left > s_ptr ?
+					ipath->fspath->bytes_left - s_ptr : 0;
+
+	fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
+	fspath = btrfs_ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
+				   name_off, eb, inum, fspath_min, bytes_left);
+	if (IS_ERR(fspath))
+		return PTR_ERR(fspath);
+
+	if (fspath > fspath_min) {
+		ipath->fspath->val[i] = (u64)(unsigned long)fspath;
+		++ipath->fspath->elem_cnt;
+		ipath->fspath->bytes_left = fspath - fspath_min;
+	} else {
+		++ipath->fspath->elem_missed;
+		ipath->fspath->bytes_missing += fspath_min - fspath;
+		ipath->fspath->bytes_left = 0;
+	}
+
+	return 0;
+}
 
 static int iterate_inode_refs(u64 inum, struct inode_fs_paths *ipath)
 {
@@ -2704,41 +2736,6 @@ static int iterate_inode_extrefs(u64 inum, struct inode_fs_paths *ipath)
 	return ret;
 }
 
-/*
- * returns 0 if the path could be dumped (probably truncated)
- * returns <0 in case of an error
- */
-static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
-			 struct extent_buffer *eb, struct inode_fs_paths *ipath)
-{
-	char *fspath;
-	char *fspath_min;
-	int i = ipath->fspath->elem_cnt;
-	const int s_ptr = sizeof(char *);
-	u32 bytes_left;
-
-	bytes_left = ipath->fspath->bytes_left > s_ptr ?
-					ipath->fspath->bytes_left - s_ptr : 0;
-
-	fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
-	fspath = btrfs_ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len,
-				   name_off, eb, inum, fspath_min, bytes_left);
-	if (IS_ERR(fspath))
-		return PTR_ERR(fspath);
-
-	if (fspath > fspath_min) {
-		ipath->fspath->val[i] = (u64)(unsigned long)fspath;
-		++ipath->fspath->elem_cnt;
-		ipath->fspath->bytes_left = fspath - fspath_min;
-	} else {
-		++ipath->fspath->elem_missed;
-		ipath->fspath->bytes_missing += fspath_min - fspath;
-		ipath->fspath->bytes_left = 0;
-	}
-
-	return 0;
-}
-
 /*
  * this dumps all file system paths to the inode into the ipath struct, provided
  * is has been created large enough. each path is zero-terminated and accessed
-- 
2.41.0


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

* [PATCH v4 42/46] btrfs: make btrfs_ref_to_path handle encrypted filenames
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (40 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 41/46] btrfs: move inode_to_path higher in backref.c Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 43/46] btrfs: don't search back for dir inode item in INO_LOOKUP_USER Josef Bacik
                   ` (6 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We use this helper for inode-resolve and path resolution in send, so
update this helper to properly decrypt any encrypted names it finds.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/backref.c | 45 +++++++++++++++++++++++++++++++++++++++++----
 fs/btrfs/fscrypt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h | 11 +++++++++++
 3 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index f58fe7c745c2..9ed854b9f3fc 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -20,6 +20,7 @@
 #include "extent-tree.h"
 #include "relocation.h"
 #include "tree-checker.h"
+#include "fscrypt.h"
 
 /* Just arbitrary numbers so we can be sure one of these happened. */
 #define BACKREF_FOUND_SHARED     6
@@ -2117,6 +2118,42 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
 	return ret;
 }
 
+static int copy_resolved_iref_to_buf(struct btrfs_root *fs_root,
+				     struct extent_buffer *eb,
+				     char *dest, u64 parent,
+				     unsigned long name_off, u32 name_len,
+				     s64 *bytes_left)
+{
+	struct btrfs_fs_info *fs_info = fs_root->fs_info;
+	struct fscrypt_str fname = FSTR_INIT(NULL, 0);
+	int ret;
+
+	/* No encryption, just copy the name in. */
+	if (!btrfs_fs_incompat(fs_info, ENCRYPT)) {
+		*bytes_left -= name_len;
+		if (*bytes_left >= 0)
+			read_extent_buffer(eb, dest + *bytes_left,
+					   name_off, name_len);
+		return 0;
+	}
+
+	ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname);
+	if (ret)
+		return ret;
+
+	ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent,
+				 &fname);
+	if (ret)
+		goto out;
+
+	*bytes_left -= fname.len;
+	if (*bytes_left >= 0)
+		memcpy(dest + *bytes_left, fname.name, fname.len);
+out:
+	fscrypt_fname_free_buffer(&fname);
+	return ret;
+}
+
 /*
  * this iterates to turn a name (from iref/extref) into a full filesystem path.
  * Elements of the path are separated by '/' and the path is guaranteed to be
@@ -2148,10 +2185,10 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
 		dest[bytes_left] = '\0';
 
 	while (1) {
-		bytes_left -= name_len;
-		if (bytes_left >= 0)
-			read_extent_buffer(eb, dest + bytes_left,
-					   name_off, name_len);
+		ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent,
+						name_off, name_len, &bytes_left);
+		if (ret)
+			break;
 		if (eb != eb_in) {
 			if (!path->skip_locking)
 				btrfs_tree_read_unlock(eb);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 2fbceec62dc7..6211fa17bf79 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -355,6 +355,52 @@ int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length)
 	return map_length;
 }
 
+int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb,
+		       unsigned long name_off, u32 name_len,
+		       u64 parent_ino, struct fscrypt_str *name)
+{
+	struct btrfs_fs_info *fs_info = root->fs_info;
+	struct inode *dir;
+	struct fscrypt_str iname = FSTR_INIT(NULL, 0);
+	int ret;
+
+	ASSERT(name_len <= BTRFS_NAME_LEN);
+
+	ret = fscrypt_fname_alloc_buffer(name_len, &iname);
+	if (ret)
+		return ret;
+
+	dir = btrfs_iget(fs_info->sb, parent_ino, root);
+	if (IS_ERR(dir)) {
+		ret = PTR_ERR(dir);
+		goto out;
+	}
+
+	/*
+	 * Directory isn't encrypted, the name isn't encrypted, we can just copy
+	 * it into the buffer.
+	 */
+	if (!IS_ENCRYPTED(dir)) {
+		read_extent_buffer(eb, name->name, name_off, name_len);
+		name->len = name_len;
+		goto out_inode;
+	}
+
+	read_extent_buffer(eb, iname.name, name_off, name_len);
+
+	ret = fscrypt_prepare_readdir(dir);
+	if (ret)
+		goto out_inode;
+
+	ASSERT(dir->i_crypt_info);
+	ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, name);
+out_inode:
+	iput(dir);
+out:
+	fscrypt_fname_free_buffer(&iname);
+	return ret;
+}
+
 const struct fscrypt_operations btrfs_fscrypt_ops = {
 	.has_per_extent_encryption = 1,
 	.get_context = btrfs_fscrypt_get_context,
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index c4a327be0eeb..aba01bacc165 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -36,6 +36,9 @@ bool btrfs_mergeable_encrypted_bio(struct bio *bio, struct inode *inode,
 				   struct fscrypt_extent_info *fi,
 				   u64 logical_offset);
 int btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length);
+int btrfs_decrypt_name(struct btrfs_root *root, struct extent_buffer *eb,
+		       unsigned long name_off, u32 name_len,
+		       u64 parent_ino, struct fscrypt_str *name);
 
 #else
 static inline int btrfs_fscrypt_save_extent_info(struct btrfs_inode *inode,
@@ -94,6 +97,14 @@ static inline u64 btrfs_fscrypt_bio_length(struct bio *bio, u64 map_length)
 {
 	return map_length;
 }
+
+static inline int btrfs_decrypt_name(struct btrfs_root *root,
+				     struct extent_buffer *eb,
+				     unsigned long name_off, u32 name_len,
+				     u64 parent_ino, struct fscrypt_str *name)
+{
+	return -EINVAL;
+}
 #endif /* CONFIG_FS_ENCRYPTION */
 
 extern const struct fscrypt_operations btrfs_fscrypt_ops;
-- 
2.41.0


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

* [PATCH v4 43/46] btrfs: don't search back for dir inode item in INO_LOOKUP_USER
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (41 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 42/46] btrfs: make btrfs_ref_to_path handle encrypted filenames Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 44/46] btrfs: deal with encrypted symlinks in send Josef Bacik
                   ` (5 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

We don't need to search back to the inode item, the directory inode
number is in key.offset, so simply use that.  If we can't find the
directory we'll get an ENOENT at the iget.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ioctl.c | 23 +++--------------------
 1 file changed, 3 insertions(+), 20 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 2f4f9f812616..7634ba23d046 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1890,7 +1890,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
 	struct btrfs_root_ref *rref;
 	struct btrfs_root *root = NULL;
 	struct btrfs_path *path;
-	struct btrfs_key key, key2;
+	struct btrfs_key key;
 	struct extent_buffer *leaf;
 	struct inode *temp_inode;
 	char *ptr;
@@ -1944,24 +1944,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
 			read_extent_buffer(leaf, ptr,
 					(unsigned long)(iref + 1), len);
 
-			/* Check the read+exec permission of this directory */
-			ret = btrfs_previous_item(root, path, dirid,
-						  BTRFS_INODE_ITEM_KEY);
-			if (ret < 0) {
-				goto out_put;
-			} else if (ret > 0) {
-				ret = -ENOENT;
-				goto out_put;
-			}
-
-			leaf = path->nodes[0];
-			slot = path->slots[0];
-			btrfs_item_key_to_cpu(leaf, &key2, slot);
-			if (key2.objectid != dirid) {
-				ret = -ENOENT;
-				goto out_put;
-			}
-
 			/*
 			 * We don't need the path anymore, so release it and
 			 * avoid deadlocks and lockdep warnings in case
@@ -1969,11 +1951,12 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
 			 * btree and lock the same leaf.
 			 */
 			btrfs_release_path(path);
-			temp_inode = btrfs_iget(sb, key2.objectid, root);
+			temp_inode = btrfs_iget(sb, key.offset, root);
 			if (IS_ERR(temp_inode)) {
 				ret = PTR_ERR(temp_inode);
 				goto out_put;
 			}
+			/* Check the read+exec permission of this directory */
 			ret = inode_permission(idmap, temp_inode,
 					       MAY_READ | MAY_EXEC);
 			iput(temp_inode);
-- 
2.41.0


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

* [PATCH v4 44/46] btrfs: deal with encrypted symlinks in send
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (42 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 43/46] btrfs: don't search back for dir inode item in INO_LOOKUP_USER Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 45/46] btrfs: decrypt file names for send Josef Bacik
                   ` (4 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

Send needs to send the decrypted value of the symlinks, handle the case
where the inode is encrypted and decrypt the symlink name into a buffer
and copy this buffer into our fs_path struct.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/send.c | 47 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 44 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 3b929f0e8f04..ee5ea16423bb 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1732,9 +1732,8 @@ static int find_extent_clone(struct send_ctx *sctx,
 	return ret;
 }
 
-static int read_symlink(struct btrfs_root *root,
-			u64 ino,
-			struct fs_path *dest)
+static int read_symlink_unencrypted(struct btrfs_root *root, u64 ino,
+				    struct fs_path *dest)
 {
 	int ret;
 	struct btrfs_path *path;
@@ -1800,6 +1799,48 @@ static int read_symlink(struct btrfs_root *root,
 	return ret;
 }
 
+static int read_symlink_encrypted(struct btrfs_root *root, u64 ino,
+				  struct fs_path *dest)
+{
+	DEFINE_DELAYED_CALL(done);
+	const char *buf;
+	struct page *page;
+	struct inode *inode;
+	int ret = 0;
+
+	inode = btrfs_iget(root->fs_info->sb, ino, root);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	page = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(page)) {
+		ret = PTR_ERR(page);
+		goto out;
+	}
+
+	buf = fscrypt_get_symlink(inode, page_address(page),
+				  BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info),
+				  &done);
+	if (IS_ERR(buf))
+		goto out_page;
+	ret = fs_path_add(dest, buf, strlen(buf));
+out_page:
+	put_page(page);
+	do_delayed_call(&done);
+out:
+	iput(inode);
+	return ret;
+}
+
+
+static int read_symlink(struct btrfs_root *root, u64 ino,
+			struct fs_path *dest)
+{
+	if (btrfs_fs_incompat(root->fs_info, ENCRYPT))
+		return read_symlink_encrypted(root, ino, dest);
+	return read_symlink_unencrypted(root, ino, dest);
+}
+
 /*
  * Helper function to generate a file name that is unique in the root of
  * send_root and parent_root. This is used to generate names for orphan inodes.
-- 
2.41.0


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

* [PATCH v4 45/46] btrfs: decrypt file names for send
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (43 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 44/46] btrfs: deal with encrypted symlinks in send Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-01 22:11 ` [PATCH v4 46/46] btrfs: load the inode context before sending writes Josef Bacik
                   ` (3 subsequent siblings)
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

In send we're going to be looking up file names from back references and
putting them into the send stream.  If we are encrypted use the helper
for decrypting names and copy the decrypted name into the buffer.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/send.c | 51 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index ee5ea16423bb..de77321777f4 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -33,6 +33,7 @@
 #include "ioctl.h"
 #include "verity.h"
 #include "lru_cache.h"
+#include "fscrypt.h"
 
 /*
  * Maximum number of references an extent can have in order for us to attempt to
@@ -585,13 +586,42 @@ static int fs_path_add_path(struct fs_path *p, struct fs_path *p2)
 	return ret;
 }
 
-static int fs_path_add_from_extent_buffer(struct fs_path *p,
+static int fs_path_add_from_encrypted(struct btrfs_root *root,
+				      struct fs_path *p,
+				      struct extent_buffer *eb,
+				      unsigned long off, int len,
+				      u64 parent_ino)
+{
+	struct fscrypt_str fname = FSTR_INIT(NULL, 0);
+	int ret;
+
+	ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname);
+	if (ret)
+		return ret;
+
+	ret = btrfs_decrypt_name(root, eb, off, len, parent_ino, &fname);
+	if (ret)
+		goto out;
+
+	ret = fs_path_add(p, fname.name, fname.len);
+out:
+	fscrypt_fname_free_buffer(&fname);
+	return ret;
+}
+
+static int fs_path_add_from_extent_buffer(struct btrfs_root *root,
+					  struct fs_path *p,
 					  struct extent_buffer *eb,
-					  unsigned long off, int len)
+					  unsigned long off, int len,
+					  u64 parent_ino)
 {
 	int ret;
 	char *prepared;
 
+	if (root && btrfs_fs_incompat(root->fs_info, ENCRYPT))
+		return fs_path_add_from_encrypted(root, p, eb, off, len,
+						  parent_ino);
+
 	ret = fs_path_prepare_for_add(p, len, &prepared);
 	if (ret < 0)
 		goto out;
@@ -1074,8 +1104,8 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path,
 			}
 			p->start = start;
 		} else {
-			ret = fs_path_add_from_extent_buffer(p, eb, name_off,
-							     name_len);
+			ret = fs_path_add_from_extent_buffer(root, p, eb, name_off,
+							     name_len, dir);
 			if (ret < 0)
 				goto out;
 		}
@@ -1792,7 +1822,7 @@ static int read_symlink_unencrypted(struct btrfs_root *root, u64 ino,
 	off = btrfs_file_extent_inline_start(ei);
 	len = btrfs_file_extent_ram_bytes(path->nodes[0], ei);
 
-	ret = fs_path_add_from_extent_buffer(dest, path->nodes[0], off, len);
+	ret = fs_path_add_from_extent_buffer(NULL, dest, path->nodes[0], off, len, 0);
 
 out:
 	btrfs_free_path(path);
@@ -2090,18 +2120,19 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
 		iref = btrfs_item_ptr(path->nodes[0], path->slots[0],
 				      struct btrfs_inode_ref);
 		len = btrfs_inode_ref_name_len(path->nodes[0], iref);
-		ret = fs_path_add_from_extent_buffer(name, path->nodes[0],
-						     (unsigned long)(iref + 1),
-						     len);
 		parent_dir = found_key.offset;
+		ret = fs_path_add_from_extent_buffer(root, name, path->nodes[0],
+						     (unsigned long)(iref + 1),
+						     len, parent_dir);
 	} else {
 		struct btrfs_inode_extref *extref;
 		extref = btrfs_item_ptr(path->nodes[0], path->slots[0],
 					struct btrfs_inode_extref);
 		len = btrfs_inode_extref_name_len(path->nodes[0], extref);
-		ret = fs_path_add_from_extent_buffer(name, path->nodes[0],
-					(unsigned long)&extref->name, len);
 		parent_dir = btrfs_inode_extref_parent(path->nodes[0], extref);
+		ret = fs_path_add_from_extent_buffer(root, name, path->nodes[0],
+					(unsigned long)&extref->name, len,
+					parent_dir);
 	}
 	if (ret < 0)
 		goto out;
-- 
2.41.0


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

* [PATCH v4 46/46] btrfs: load the inode context before sending writes
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (44 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 45/46] btrfs: decrypt file names for send Josef Bacik
@ 2023-12-01 22:11 ` Josef Bacik
  2023-12-05  5:54   ` Eric Biggers
  2023-12-01 22:15 ` [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (2 subsequent siblings)
  48 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:11 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel

For send we will read the pages and copy them into our buffer.  Use the
fscrypt_inode_open helper to make sure the key is loaded properly before
trying to read from the inode so the contents are properly decrypted.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/send.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index de77321777f4..3475b4cea09d 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -5392,6 +5392,37 @@ static int put_file_data(struct send_ctx *sctx, u64 offset, u32 len)
 	return ret;
 }
 
+static int load_fscrypt_context(struct send_ctx *sctx)
+{
+	struct btrfs_root *root = sctx->send_root;
+	struct name_cache_entry *nce;
+	struct inode *dir;
+	int ret;
+
+	if (!IS_ENCRYPTED(sctx->cur_inode))
+		return 0;
+
+	/*
+	 * We're encrypted, we need to load the parent inode in order to make
+	 * sure the encryption context is loaded, we use this after calling
+	 * get_cur_path() so our nce for the current inode should be here.  If
+	 * not handle it, but ASSERT() for developers.
+	 */
+	nce = name_cache_search(sctx, sctx->cur_ino, sctx->cur_inode_gen);
+	if (!nce) {
+		ASSERT(nce);
+		return -EINVAL;
+	}
+
+	dir = btrfs_iget(root->fs_info->sb, nce->parent_ino, root);
+	if (IS_ERR(dir))
+		return PTR_ERR(dir);
+
+	ret = fscrypt_inode_open(dir, sctx->cur_inode);
+	iput(dir);
+	return ret;
+}
+
 /*
  * Read some bytes from the current inode/file and send a write command to
  * user space.
@@ -5415,7 +5446,9 @@ static int send_write(struct send_ctx *sctx, u64 offset, u32 len)
 	ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
 	if (ret < 0)
 		goto out;
-
+	ret = load_fscrypt_context(sctx);
+	if (ret < 0)
+		goto out;
 	TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
 	TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset);
 	ret = put_file_data(sctx, offset, len);
-- 
2.41.0


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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (45 preceding siblings ...)
  2023-12-01 22:11 ` [PATCH v4 46/46] btrfs: load the inode context before sending writes Josef Bacik
@ 2023-12-01 22:15 ` Josef Bacik
  2023-12-05  1:49 ` Eric Biggers
  2024-04-09 23:42 ` Eric Biggers
  48 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2023-12-01 22:15 UTC (permalink / raw)
  To: linux-btrfs, kernel-team, linux-fsdevel; +Cc: ebiggers, linux-fscrypt

On Fri, Dec 01, 2023 at 05:10:57PM -0500, Josef Bacik wrote:
> Hello,
> 
> v3 can be found here
> 
> https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/

Sorry Eric, it's been a long week and I forgot how to use email, didn't cc you
or linux-fscrypt on this series.  It's on fsdevel and the btrfs list.  Thanks,

Josef

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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (46 preceding siblings ...)
  2023-12-01 22:15 ` [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
@ 2023-12-05  1:49 ` Eric Biggers
  2023-12-05 14:16   ` David Sterba
  2024-04-09 23:42 ` Eric Biggers
  48 siblings, 1 reply; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  1:49 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:10:57PM -0500, Josef Bacik wrote:
> Hello,
> 
> v3 can be found here
> 
> https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/
> 
> There's been a longer delay between versions than I'd like, this was mostly due
> to Plumbers, Holidays, and then uncovering a bunch of new issues with '-o
> test_dummy_encryption'.  I'm still working through some of the btrfs specific
> failures, but the fscrypt side appears to be stable.  I had to add a few changes
> to fscrypt since the last time, but nothing earth shattering, just moving the
> keyring destruction and adding a helper we need for btrfs send to work properly.
> 
> This is passing a good chunk of the fstests, at this point the majority appear
> to be cases where I need to exclude the test when using test_dummy_encryption
> because of various limitations of our tools or other infrastructure related
> things.
> 
> I likely will have a follow-up series with more fixes, but the bulk of this is
> unchanged since the last posting.  There were some bug fixes and such but the
> overall design remains the same.  Thanks,
> 

Well, it wouldn't be Linux kernel development without patchsets that don't
mention what they apply to...  I managed to apply this for reviewing after
spending a while choosing the base that seemed to work best (6d3880a76eedd from
kdave/for-next) and resolving conflicts.  It would save a lot of time if proper
information was included, though.

- Eric

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

* Re: [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super
  2023-12-01 22:10 ` [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super Josef Bacik
@ 2023-12-05  1:58   ` Eric Biggers
  2023-12-05 22:48     ` Josef Bacik
  0 siblings, 1 reply; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  1:58 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:10:58PM -0500, Josef Bacik wrote:
> btrfs has a variety of asynchronous things we do with inodes that can
> potentially last until ->put_super, when we shut everything down and
> clean up all of our async work.  Due to this we need to move
> fscrypt_destroy_keyring() to after ->put_super, otherwise we get
> warnings about still having active references on the master key.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/super.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/super.c b/fs/super.c
> index 076392396e72..faf7d248145d 100644
> --- a/fs/super.c
> +++ b/fs/super.c
> @@ -681,12 +681,6 @@ void generic_shutdown_super(struct super_block *sb)
>  		fsnotify_sb_delete(sb);
>  		security_sb_delete(sb);
>  
> -		/*
> -		 * Now that all potentially-encrypted inodes have been evicted,
> -		 * the fscrypt keyring can be destroyed.
> -		 */
> -		fscrypt_destroy_keyring(sb);
> -
>  		if (sb->s_dio_done_wq) {
>  			destroy_workqueue(sb->s_dio_done_wq);
>  			sb->s_dio_done_wq = NULL;
> @@ -695,6 +689,12 @@ void generic_shutdown_super(struct super_block *sb)
>  		if (sop->put_super)
>  			sop->put_super(sb);
>  
> +		/*
> +		 * Now that all potentially-encrypted inodes have been evicted,
> +		 * the fscrypt keyring can be destroyed.
> +		 */
> +		fscrypt_destroy_keyring(sb);
> +

This patch will cause a NULL dereference on f2fs, since f2fs_put_super() frees
->s_fs_info, and then fscrypt_destroy_keyring() can call f2fs_get_devices() (via
fscrypt_operations::get_devices) which dereferences it.  (ext4 also frees
->s_fs_info in its ->put_super, but ext4 doesn't implement ->get_devices.)

I think we need to move the fscrypt keyring destruction into ->put_super for
each filesystem.

- Eric

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

* Re: [PATCH v4 02/46] fscrypt: add per-extent encryption support
  2023-12-01 22:10 ` [PATCH v4 02/46] fscrypt: add per-extent encryption support Josef Bacik
@ 2023-12-05  3:58   ` Eric Biggers
  2023-12-05 22:48     ` Josef Bacik
  2023-12-13  4:16     ` Eric Biggers
  0 siblings, 2 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  3:58 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:10:59PM -0500, Josef Bacik wrote:
> The file system indicates it wants to use per-extent encryption by
> setting s_cop->set_extent_context.

It's actually s_cop->has_per_extent_encryption.

> diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
> index 1892356cf924..03c006e5c47d 100644
> --- a/fs/crypto/fscrypt_private.h
> +++ b/fs/crypto/fscrypt_private.h
[...]
> @@ -30,6 +30,8 @@
>  #define FSCRYPT_CONTEXT_V1	1
>  #define FSCRYPT_CONTEXT_V2	2
>  
> +#define FSCRYPT_EXTENT_CONTEXT_V1	1
> +
>  /* Keep this in sync with include/uapi/linux/fscrypt.h */
>  #define FSCRYPT_MODE_MAX	FSCRYPT_MODE_AES_256_HCTR2
>  
> @@ -53,6 +55,28 @@ struct fscrypt_context_v2 {
>  	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
>  };

Hmm, I think we're going to want fscrypt_context => fscrypt_inode_context too...
Also FSCRYPT_FILE_NONCE_SIZE => FSCRYPT_NONCE_SIZE.  I guess don't worry about
it for now, though.

> +/*
> + * fscrypt_extent_context - the encryption context of an extent
> + *
> + * This is the on-disk information stored for an extent.  The policy and
> + * relevante information is stored in the inode, the per-extent information is
> + * simply the nonce that's used in as KDF input in conjunction with the inode
> + * context to derive a per-extent key for encryption.
> + *
> + * At this point the master_key_identifier exists only for possible future
> + * expansion.  This will allow for an inode to have extents with multiple master
> + * keys, although sharing the same encryption mode.  This would be for re-keying
> + * or for reflinking between two differently encrypted inodes.  For now the
> + * master_key_descriptor must match the inode's, and we'll be using the inode's
> + * for all key derivation.
> + */
> +struct fscrypt_extent_context {
> +	u8 version; /* FSCRYPT_EXTENT_CONTEXT_V2 */
> +	u8 encryption_mode;
> +	u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
> +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> +};

s/relevante/relevant/
s/FSCRYPT_EXTENT_CONTEXT_V2/FSCRYPT_EXTENT_CONTEXT_V1/
s/master_key_descriptor/master_key_identifier/

The second paragraph also contradicts the first, which says that the context
"is simply the nonce".

Also, encryption_mode is there for the same reason as master_key_identifier,
right?  The comment only mentions master_key_identifier.

> +/*
> + * fscrypt_extent_info - the "encryption key" for an extent.
> + *
> + * This contains the dervied key for the given extent and the nonce for the
> + * extent.
> + */

s/dervied/derived/

> +struct fscrypt_extent_info {
> +	refcount_t refs;
> +	/* This is the extents nonce, loaded from the fscrypt_extent_context */
> +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];

s/extents/extent's/

> diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
> index b4002aea7cdb..4eeb75410ba8 100644
> --- a/fs/crypto/inline_crypt.c
> +++ b/fs/crypto/inline_crypt.c
[...]
> +/**
> + * fscrypt_set_bio_crypt_ctx() - prepare a file contents bio for inline crypto
> + * @bio: a bio which will eventually be submitted to the file
> + * @inode: the file's inode
> + * @ei: the extent's crypto info
> + * @first_lblk: the first file logical block number in the I/O
> + * @gfp_mask: memory allocation flags - these must be a waiting mask so that
> + *					bio_crypt_set_ctx can't fail.
> + *
> + * If the contents of the file should be encrypted (or decrypted) with inline
> + * encryption, then assign the appropriate encryption context to the bio.
> + *
> + * Normally the bio should be newly allocated (i.e. no pages added yet), as
> + * otherwise fscrypt_mergeable_bio() won't work as intended.
> + *
> + * The encryption context will be freed automatically when the bio is freed.
> + */
> +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
> +					   const struct inode *inode,
> +					   const struct fscrypt_extent_info *ei,
> +					   u64 first_lblk, gfp_t gfp_mask)
> +{
> +	const struct fscrypt_inode_info *ci;
> +	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> +
> +	if (!fscrypt_inode_uses_inline_crypto(inode))
> +		return;
> +	ci = inode->i_crypt_info;
> +
> +	fscrypt_generate_dun(ci, first_lblk, dun);
> +	bio_crypt_set_ctx(bio, ei->prep_key.blk_key, dun, gfp_mask);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_from_extent);

Silently leaving the data unencrypted if
!fscrypt_inode_uses_inline_crypto(inode) is dangerous.  I think you should just
leave that part out.  It was already checked in fscrypt_load_extent_info().

Also, I think that for now we should keep things simple by doing the following
instead of fscrypt_generate_dun():

	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk };

Note that these two changes would eliminate the need for the inode parameter to
the function.

Please also make sure to update the function comment.  Note that the first line
gives the function name incorrectly.

> +/**
> + * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio
> + * @bio: the bio being built up
> + * @inode: the inode for the next part of the I/O
> + * @ei: the fscrypt_extent_info for this extent
> + * @next_lblk: the next file logical block number in the I/O
> + *
> + * When building a bio which may contain data which should undergo inline
> + * encryption (or decryption) via fscrypt, filesystems should call this function
> + * to ensure that the resulting bio contains only contiguous data unit numbers.
> + * This will return false if the next part of the I/O cannot be merged with the
> + * bio because either the encryption key would be different or the encryption
> + * data unit numbers would be discontiguous.
> + *
> + * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called on the
> + * bio.
> + *
> + * This function isn't required in cases where crypto-mergeability is ensured in
> + * another way, such as I/O targeting only a single file (and thus a single key)
> + * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity.
> + *
> + * Return: true iff the I/O is mergeable
> + */
> +bool fscrypt_mergeable_extent_bio(struct bio *bio, const struct inode *inode,
> +				  const struct fscrypt_extent_info *ei,
> +				  u64 next_lblk)
> +{
> +	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
> +	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> +
> +	if (!ei)
> +		return true;
> +	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
> +		return false;
> +	if (!bc)
> +		return true;
> +
> +	/*
> +	 * 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 != ei->prep_key.blk_key)
> +		return false;
> +
> +	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
> +	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio);

Similarly, I think we should keep things simple here.  With extent-based
encryption not supporting any "weird" IV generation methods, the filesystem just
needs to ensure that bios target a logically contiguous range in one file only.

It looks like btrfs already ensures that, as per the following code:

	/*
	 * The contig check requires the following conditions to be met:
	 *
	 * 1) The pages are belonging to the same inode
	 *    This is implied by the call chain.
	 *
	 * 2) The range has adjacent logical bytenr
	 *
	 * 3) The range has adjacent file offset
	 *    This is required for the usage of btrfs_bio->file_offset.
	 */
	return bio_end_sector(bio) == sector &&
		page_offset(bvec->bv_page) + bvec->bv_offset + bvec->bv_len ==
		page_offset(page) + pg_offset;


So I think you could just delete fscrypt_mergeable_extent_bio() and the
IS_ENCRYPTED() special case in btrfs_bio_is_contig(), and everything would still
work fine.

The existing fully general mergeability check is mainly for
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, where the DUN can wrap around at random
places, and for f2fs which can build bios containing data of many different
files.  Without those, just "same file + contiguous logical blocks" suffices.

> +static struct fscrypt_extent_info *
> +setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_SIZE])
> +{
> +	struct fscrypt_extent_info *ei;
> +	struct fscrypt_inode_info *ci;
> +	struct fscrypt_master_key *mk;
> +	u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
> +	int err;
> +
> +	ci = inode->i_crypt_info;
> +	mk = ci->ci_master_key;
> +	if (!mk)
> +		return ERR_PTR(-ENOKEY);

mk being NULL here would be a bug, right?  Maybe WARN_ON_ONCE() on it?

> +
> +	ei = kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL);
> +	if (!ei)
> +		return ERR_PTR(-ENOMEM);
> +
> +	refcount_set(&ei->refs, 1);
> +	memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
> +	ei->sb = inode->i_sb;
> +
> +	down_read(&mk->mk_sem);
> +	/*
> +	 * We specifically don't do is_master_key_secret_present() here because
> +	 * if the inode is open and has a reference on the master key then it
> +	 * should be available for us to use.
> +	 */

->mk_secret, not is_master_key_secret_present()

> +
> +	err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
> +				  HKDF_CONTEXT_PER_FILE_ENC_KEY, ei->nonce,
> +				  FSCRYPT_FILE_NONCE_SIZE, derived_key,
> +				  ci->ci_mode->keysize);
> +	if (err)
> +		goto out_free;

Reusing HKDF_CONTEXT_PER_FILE_ENC_KEY here is misleading, as this isn't deriving
a per-file key.  Cryptographically it's okay, since it's still following the
same pattern with the 16-byte nonce, but I'm thinking it would be best to
introduce a new HKDF context byte as follows:

	#define HKDF_CONTEXT_PER_EXTENT_ENC_KEY 8 /* info=extent_nonce          */

> +/**
> + * fscrypt_prepare_new_extent() - prepare to create a new extent for a file
> + * @inode: the possibly-encrypted inode
> + *
> + * If the inode is encrypted, setup the fscrypt_extent_info for a new extent.
> + * This will include the nonce and the derived key necessary for the extent to
> + * be encrypted.  This is only meant to be used with inline crypto.
> + *
> + * This doesn't persist the new extents encryption context, this is done later
> + * by calling fscrypt_set_extent_context().
> + *
> + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> + *	   we're not encrypted, or another -errno code
> + */
> +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode)
> +{
> +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> +
> +	if (!fscrypt_inode_uses_inline_crypto(inode))
> +		return ERR_PTR(-EOPNOTSUPP);
> +
> +	get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
> +	return setup_extent_info(inode, nonce);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);

The behavior of this on failure is not clear.  AFAICS it actually does:

	inode is not an encryted regular file, -EOPNOTSUPP
	else if inode does not have key set up, NULL dereference
	else if inode does not use inline crypto, -EOPNOTSUPP

But it's a bug for any of those three cases to be reached at all, right?  I.e.,
this function is only for inodes for encrypted regular files that have already
had their key set up, on filesystems that force the use of inline crypto?

I'm wondering if we should make all those cases trigger a WARN_ON_ONCE() and
document the preconditions for this function appropriately.

> +/**
> + * fscrypt_load_extent_info() - create an fscrypt_extent_info from the context
> + * @inode: the inode
> + * @ctx: the context buffer
> + * @ctx_size: the size of the context buffer
> + *
> + * Create the file_extent_info and derive the key based on the
> + * fscrypt_extent_context buffer that is probided.

s/file_extent_info/fscrypt_extent_info/
s/probided/provided/

> + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> + *	   we're not encrypted, or another -errno code
> + */
> +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
> +						     u8 *ctx, size_t ctx_size)
> +{
> +	struct fscrypt_extent_context extent_ctx;
> +	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
> +	const struct fscrypt_policy_v2 *policy = &ci->ci_policy.v2;
> +
> +	if (!fscrypt_inode_uses_inline_crypto(inode))
> +		return ERR_PTR(-EOPNOTSUPP);

Same comment as fscrypt_prepare_new_extent().  Should there be a WARN_ON_ONCE()
here, and also an explicit check for the inode's key being set up?

> +	if (ctx_size < sizeof(extent_ctx))
> +		return ERR_PTR(-EINVAL);
> +
> +	memcpy(&extent_ctx, ctx, sizeof(extent_ctx));
> +
> +	/*
> +	 * For now we need to validate that the master key for the inode matches
> +	 * the extent.
> +	 */
> +	if (memcmp(extent_ctx.master_key_identifier,
> +		   policy->master_key_identifier,
> +		   sizeof(extent_ctx.master_key_identifier)))
> +		return ERR_PTR(-EINVAL);

extent_ctx.version and extent_ctx.encryption_mode both need to be validated.

Also, it would be helpful if there were appropriate warning messages (using
fscrypt_warn()) upon seeing an unsupported extent context, similar to the
messages that get printed when seeing an unsupported inode context.

> +EXPORT_SYMBOL(fscrypt_load_extent_info);

EXPORT_SYMBOL_GPL for any new exported symbols in fs/crypto/, please.

> +/**
> + * fscrypt_put_extent_info() - free the extent_info fscrypt data

fscrypt_put_extent_info() - put a reference to an fscrypt extent info

> + * @ei: the extent_info being evicted

@ei: the extent info being evicted or NULL

(BTW, is the NULL support really needed?)

> + * Note this will unload the key from the block layer, which takes the crypto
> + * profile semaphore to unload the key.  Make sure you're not dropping this in a
> + * context that can't sleep.
> + */

"Might sleep, since this may call blk_crypto_evict_key() which can sleep."

> +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei)
> +{
> +	if (!ei)
> +		return;
> +
> +	if (!refcount_dec_and_test(&ei->refs))
> +		return;
> +
> +	fscrypt_destroy_prepared_key(ei->sb, &ei->prep_key);
> +	memzero_explicit(ei, sizeof(*ei));
> +	kmem_cache_free(fscrypt_extent_info_cachep, ei);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_put_extent_info);

This would look cleaner as:

	if (ei && refcount_dec_and_test(&ei->refs)) {
		...
	}

> +
> +/**
> + * fscrypt_get_extent_info() - get a ref on the fscrypt extent info

fscrypt_get_extent_info() - get a reference to an fscrypt extent info

> + * @ei: the extent_info to get.
> + *
> + * Get a reference on the fscrypt_extent_info.
> + *

An explanation here about why filesystems would need to use this would be
useful.

> + * Return: the ei with an extra ref, NULL if there was no ei passed in.

Return: the ei with an extra ref, or NULL if ei was NULL.

> + */
> +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei)
> +{
> +	if (!ei)
> +		return NULL;
> +	refcount_inc(&ei->refs);
> +	return ei;
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_get_extent_info);

Similarly, this would look cleaner as:

	if (ei)
		refcount_inc(&ei->refs);
	return ei;

> diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
> index 701259991277..75a69f02f11d 100644
> --- a/fs/crypto/policy.c
> +++ b/fs/crypto/policy.c
> @@ -209,6 +209,12 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
>  		return false;
>  	}
>  
> +	if (inode->i_sb->s_cop->has_per_extent_encryption) {
> +		fscrypt_warn(inode,
> +			     "v1 policies can't be used on file systems that use extent encryption");
> +		return false;

Maybe "can't be used" => "aren't supported", to be consistent with the other
message you're adding.

> +	}
> +
>  	return true;
>  }
>  
> @@ -269,6 +275,12 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
>  		}
>  	}
>  
> +	if ((inode->i_sb->s_cop->has_per_extent_encryption) && count) {
> +		fscrypt_warn(inode,
> +			     "Encryption flags aren't supported on file systems that use extent encryption");
> +		return false;
> +	}

Maybe move this up to where count is being checked already, so that the code
looks like:

	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY);
	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64);
	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);
	if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) {
		fscrypt_warn(inode,
				"Encryption flags aren't supported on file systems that use extent encryption");
		return false;
	}
	if (count > 1) {

> +/**
> + * fscrypt_set_extent_context() - Set the fscrypt extent context of a new extent
> + * @inode: the inode this extent belongs to
> + * @ei: the fscrypt_extent_info for the given extent
> + * @buf: the buffer to copy the fscrypt extent context into
> + *
> + * This should be called after fscrypt_prepare_new_extent(), using the
> + * fscrypt_extent_info that was created at that point.
> + *
> + * Return: the size of the fscrypt_extent_context, errno if the inode has the
> + *	   wrong policy version.
> + */
> +ssize_t fscrypt_set_extent_context(struct inode *inode,
> +				   struct fscrypt_extent_info *ei, u8 *buf)
> +{
> +	struct fscrypt_extent_context *ctx = (struct fscrypt_extent_context *)buf;
> +	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
> +
> +	if (ci->ci_policy.version != 2)
> +		return -EINVAL;
> +
> +	ctx->version = 1;
> +	memcpy(ctx->master_key_identifier,
> +	       ci->ci_policy.v2.master_key_identifier,
> +	       sizeof(ctx->master_key_identifier));
> +	memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE);
> +	return sizeof(struct fscrypt_extent_context);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);

Well, this doesn't *set* an extent context.  It generates one.  Basically it's
the extent equivalent of fscrypt_context_for_new_inode().  Maybe call this
fscrypt_context_for_new_extent()?

Also, 'ci->ci_policy.version != 2' indicates a bug, so it should be a
WARN_ON_ONCE().

Also, the literal 1 should be FSCRYPT_EXTENT_CONTEXT_V1.

Also, the expected size of the buffer needs to be documented (as it is for
fscrypt_context_for_new_inode()).  It's FSCRYPT_SET_CONTEXT_MAX_SIZE, right?

It might be a good idea to also add the following:

	BUILD_BUG_ON(sizeof(struct fscrypt_extent_context) >
                     FSCRYPT_SET_CONTEXT_MAX_SIZE);
> +/**
> + * fscrypt_extent_context_size() - Return the size of the on-disk extent context
> + * @inode: the inode this extent belongs to.
> + *
> + * Return the size of the extent context for this inode.  Since there is only
> + * one extent context version currently this is just the size of the extent
> + * context if the inode is encrypted.
> + */
> +size_t fscrypt_extent_context_size(struct inode *inode)
> +{
> +	if (!IS_ENCRYPTED(inode))
> +		return 0;
> +
> +	return sizeof(struct fscrypt_extent_context);
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_extent_context_size);

Huh, shouldn't the filesystem use the size from fscrypt_set_extent_context() (or
fscrypt_context_for_new_extent() as I recommend calling it)?  Why is the above
function necessary?

> @@ -308,6 +317,11 @@ 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);
> +ssize_t fscrypt_set_extent_context(struct inode *inode,
> +				   struct fscrypt_extent_info *ei, u8 *buf);
> +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
> +						     u8 *ctx, size_t ctx_size);
> +size_t fscrypt_extent_context_size(struct inode *inode);

The declarations in fscrypt.h are organized by where they're defined, so the
declaration of fscrypt_load_extent_info() is in the wrong place.

- Eric

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

* Re: [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper
  2023-12-01 22:11 ` [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper Josef Bacik
@ 2023-12-05  4:14   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  4:14 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:11:00PM -0500, Josef Bacik wrote:
> We have fscrypt_file_open() which is meant to be called on files being
> opened so that their key is loaded when we start reading data from them.
> 
> However for btrfs send we are opening the inode directly without a filp,
> so we need a different helper to make sure we can load the fscrypt
> context for the inode before reading its contents.
> 
> We need a different helper as opposed to simply using
> fscrypt_has_permitted_context() directly because of '-o
> test_dummy_encryption', which allows for encrypted files to be created
> with !IS_ENCRYPTED set on the directory (the root directory in this
> case).  fscrypt_file_open() already does the appropriate check where it
> simply doesn't call fscrypt_has_permitted_context() if the parent
> directory isn't marked with IS_ENCRYPTED in order to facilitate this
> invariant when using '-o test_dummy_encryption'.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/crypto/hooks.c       | 42 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/fscrypt.h |  8 ++++++++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c
> index 52504dd478d3..a391a987c58f 100644
> --- a/fs/crypto/hooks.c
> +++ b/fs/crypto/hooks.c
> @@ -49,6 +49,48 @@ int fscrypt_file_open(struct inode *inode, struct file *filp)
>  }
>  EXPORT_SYMBOL_GPL(fscrypt_file_open);
>  
> +/**
> + * fscrypt_inode_open() - prepare to open a possibly-encrypted regular file
> + * @dir: the directory that contains this inode
> + * @inode: the inode being opened
> + *
> + * Currently, an encrypted regular file can only be opened if its encryption key
> + * is available; access to the raw encrypted contents is not supported.
> + * Therefore, we first set up the inode's encryption key (if not already done)
> + * and return an error if it's unavailable.
> + *
> + * We also verify that if the parent directory is encrypted, then the inode
> + * being opened uses the same encryption policy.  This is needed as part of the
> + * enforcement that all files in an encrypted directory tree use the same
> + * encryption policy, as a protection against certain types of offline attacks.
> + * Note that this check is needed even when opening an *unencrypted* file, since
> + * it's forbidden to have an unencrypted file in an encrypted directory.
> + *
> + * File systems should be using fscrypt_file_open in their open callback.  This
> + * is for file systems that may need to open inodes outside of the normal file
> + * open path, btrfs send for example.
> + *
> + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code
> + */
> +int fscrypt_inode_open(struct inode *dir, struct inode *inode)
> +{
> +	int err;
> +
> +	err = fscrypt_require_key(inode);
> +	if (err)
> +		return err;
> +
> +	if (IS_ENCRYPTED(dir) &&
> +	    !fscrypt_has_permitted_context(dir, inode)) {
> +		fscrypt_warn(inode,
> +			     "Inconsistent encryption context (parent directory: %lu)",
> +			     dir->i_ino);
> +		err = -EPERM;
> +	}
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(fscrypt_inode_open);

The comment and code is heavily copy+pasted from fscrypt_file_open(), which is
not great.  How about naming the new function __fscrypt_file_open(),
implementing fscrypt_file_open() on top of it, and making the comment describe
the differences vs. fscrypt_file_open()?

So fscrypt_file_open() would do:

	struct inode *dir;
	int err;

	dir = dget_parent(file_dentry(filp));

	err = __fscrypt_file_open(dir, filp);

	dput(dir);
	return err;

... and the comment for the new function would be something like:

/**
 * __fscrypt_file_open() - prepare for filesystem-internal access to a
 *			   possibly-encrypted regular file
 * @dir: the inode for the directory via which the file is being accessed
 * @inode: the inode being "opened"
 *
 * This is like fscrypt_file_open(), but instead of taking the 'struct file'
 * being opened it takes the parent directory explicitly.  This is intended for
 * use cases such as "send/receive" which involve the filesystem accessing file
 * contents without setting up a 'struct file'.
 *
 * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code
 */
int __fscrypt_file_open(struct inode *dir, struct inode *inode)


Note, we need to be careful when describing "@dir".  It's not simply "the
directory that contains this inode".  An inode can be in multiple directories.

- Eric

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

* Re: [PATCH v4 05/46] blk-crypto: add a process bio callback
  2023-12-01 22:11 ` [PATCH v4 05/46] blk-crypto: add a process bio callback Josef Bacik
@ 2023-12-05  4:54   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  4:54 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:11:02PM -0500, Josef Bacik wrote:
> +	/* Process the encrypted bio before we submit it. */
> +	if (bc->bc_key->crypto_cfg.process_bio) {
> +		blk_st = bc->bc_key->crypto_cfg.process_bio(src_bio, enc_bio);
> +		if (blk_st != BLK_STS_OK) {
> +			src_bio->bi_status = blk_st;
> +			goto out_free_bounce_pages;
> +		}
> +	}
> +

How does this interact with the splitting that can happen at the beginning of
blk_crypto_fallback_encrypt_bio()?  Won't src_bio differ from the original bio
in that case?

> +	/*
> +	 * Process the bio first before trying to decrypt.
> +	 *
> +	 * NOTE: btrfs expects that this bio is the same that was submitted.  If
> +	 * at any point this changes we will need to update process_bio to take
> +	 * f_ctx->crypt_iter in order to make sure we can iterate the pages for
> +	 * checksumming.  We're currently saving this in our btrfs_bio, so this
> +	 * works, but if at any point in the future we start allocating a bounce
> +	 * bio or something we need to update this callback.
> +	 */
> +	if (bc->bc_key->crypto_cfg.process_bio) {
> +		blk_st = bc->bc_key->crypto_cfg.process_bio(bio, bio);
> +		if (blk_st != BLK_STS_OK) {
> +			bio->bi_status = blk_st;
> +			goto out_no_keyslot;
> +		}
> +	}

The NOTE above feels a bit out of place.  It doesn't make sense to use a bounce
bio for decryption, so the described concern doesn't seem too realistic.
Specific filesystems also shouldn't really be mentioned here.  Maybe the comment
should just say that the contract of blk_crypto_process_bio_t requires
orig_bio == enc_bio for reads?  Maybe it should be a comment on
blk_crypto_process_bio_t itself.

> +/**
> + * blk_crypto_cfg_supports_process_bio - check if this config supports
> + *					 process_bio
> + * @profile: the profile we're checking
> + *
> + * This is just a quick check to make sure @profile is the fallback profile, as
> + * no other offload implementations support process_bio.
> + */
> +bool blk_crypto_cfg_supports_process_bio(struct blk_crypto_profile *profile)
> +{
> +	return profile == blk_crypto_fallback_profile;
> +}

How about calling this blk_crypto_profile_is_fallback()?

> diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h
> index 5e5822c18ee4..194c1d727013 100644
> --- a/include/linux/blk-crypto.h
> +++ b/include/linux/blk-crypto.h
> @@ -6,7 +6,7 @@
>  #ifndef __LINUX_BLK_CRYPTO_H
>  #define __LINUX_BLK_CRYPTO_H
>  
> -#include <linux/types.h>
> +#include <linux/blk_types.h>
>  
>  enum blk_crypto_mode_num {
>  	BLK_ENCRYPTION_MODE_INVALID,
> @@ -17,6 +17,9 @@ enum blk_crypto_mode_num {
>  	BLK_ENCRYPTION_MODE_MAX,
>  };
>  
> +typedef blk_status_t (blk_crypto_process_bio_t)(struct bio *orig_bio,
> +						struct bio *enc_bio);

Usually people include a '*' in function pointer typedefs.

> +
>  #define BLK_CRYPTO_MAX_KEY_SIZE		64
>  /**
>   * struct blk_crypto_config - an inline encryption key's crypto configuration

This kerneldoc comment is missing documentation for process_bio.

> @@ -31,6 +34,7 @@ struct blk_crypto_config {
>  	enum blk_crypto_mode_num crypto_mode;
>  	unsigned int data_unit_size;
>  	unsigned int dun_bytes;
> +	blk_crypto_process_bio_t *process_bio;
>  };

*process_bio => process_bio.

> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
> index 756f23fc3e83..5f5efb472fc9 100644
> --- a/include/linux/fscrypt.h
> +++ b/include/linux/fscrypt.h
> @@ -16,6 +16,7 @@
>  #include <linux/fs.h>
>  #include <linux/mm.h>
>  #include <linux/slab.h>
> +#include <linux/blk-crypto.h>
>  #include <uapi/linux/fscrypt.h>
>  
>  /*
> @@ -199,6 +200,19 @@ struct fscrypt_operations {
>  	 */
>  	struct block_device **(*get_devices)(struct super_block *sb,
>  					     unsigned int *num_devs);
> +
> +	/*
> +	 * A callback if the file system requires the ability to process the
> +	 * encrypted bio.
> +	 *
> +	 * @orig_bio: the original bio submitted.
> +	 * @enc_bio: the encrypted bio.
> +	 *
> +	 * For writes the enc_bio will be different from the orig_bio, for reads
> +	 * they will be the same.  For reads we get the bio before it is
> +	 * decrypted, for writes we get the bio before it is submitted.
> +	 */
> +	blk_crypto_process_bio_t *process_bio;

Adding fscrypt support for process_bio should be a separate patch.

Also, the documentation for fscrypt_operations::process_bio should make it clear
that it only applies to inline encryption.

- Eric

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

* Re: [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name
  2023-12-01 22:11 ` [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name Josef Bacik
@ 2023-12-05  5:03   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:03 UTC (permalink / raw)
  To: Josef Bacik
  Cc: linux-btrfs, kernel-team, linux-fsdevel, Omar Sandoval,
	Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:03PM -0500, Josef Bacik wrote:
> -/*
> - * Decoded size of max-size no-key name, i.e. a name that was abbreviated using
> + * Decoded size of max-size nokey name, i.e. a name that was abbreviated using
>   * the strong hash and thus includes the 'sha256' field.  This isn't simply
>   * sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included.
>   */

The above change seems accidental?  Note that while the C identifiers use
"nokey", in text I've been writing it as "no-key".

> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
> index 5f5efb472fc9..f57601b40e18 100644
> --- a/include/linux/fscrypt.h
> +++ b/include/linux/fscrypt.h
> @@ -17,6 +17,7 @@
>  #include <linux/mm.h>
>  #include <linux/slab.h>
>  #include <linux/blk-crypto.h>
> +#include <crypto/sha2.h>
>  #include <uapi/linux/fscrypt.h>
>  
>  /*
> @@ -56,6 +57,42 @@ struct fscrypt_name {
>  #define fname_name(p)		((p)->disk_name.name)
>  #define fname_len(p)		((p)->disk_name.len)
>  
[...]
> +struct fscrypt_nokey_name {
> +	u32 dirhash[2];
> +	u8 bytes[149];
> +	u8 sha256[SHA256_DIGEST_SIZE];
> +}; /* 189 bytes => 252 bytes base64url-encoded, which is <= NAME_MAX (255) */

I'd be tempted to just change SHA256_DIGEST_SIZE to 32, which would avoid
needing to include crypto/sha2.h.  The size is effectively hardcoded anyway, via
the 'u8 bytes[149];' field.  And it's not like SHA-256 will stop being 256 bits.

- Eric

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

* Re: [PATCH v4 10/46] btrfs: disable verity on encrypted inodes
  2023-12-01 22:11 ` [PATCH v4 10/46] btrfs: disable verity " Josef Bacik
@ 2023-12-05  5:07   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:07 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel, Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:07PM -0500, Josef Bacik wrote:
> From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> 
> Right now there isn't a way to encrypt things that aren't either
> filenames in directories or data on blocks on disk with extent
> encryption, so for now, disable verity usage with encryption on btrfs.
> 
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/btrfs/verity.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
> index 66e2270b0dae..92536913df04 100644
> --- a/fs/btrfs/verity.c
> +++ b/fs/btrfs/verity.c
> @@ -588,6 +588,9 @@ static int btrfs_begin_enable_verity(struct file *filp)
>  
>  	ASSERT(inode_is_locked(file_inode(filp)));
>  
> +	if (IS_ENCRYPTED(&inode->vfs_inode))
> +		return -EINVAL;

As per the documentation for FS_IOC_ENABLE_VERITY
(https://docs.kernel.org/filesystems/fsverity.html#fs-ioc-enable-verity), the
error code for the case of "the filesystem does not support fs-verity on this
file" should be EOPNOTSUPP, not EINVAL.  That's what ext4 returns if you try to
enable verity on a file that doesn't use extents, for example.

- Eric

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

* Re: [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG
  2023-12-01 22:11 ` [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG Josef Bacik
@ 2023-12-05  5:11   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:11 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel, Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:14PM -0500, Josef Bacik wrote:
> From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> 
> Since encryption is currently under BTRFS_DEBUG, this adds its
> dependencies: inline encryption from fscrypt, and the inline encryption
> fallback path from the block layer.
> 
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/btrfs/ioctl.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 9968a36079c4..0e8e2ca48a2e 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -4573,6 +4573,7 @@ long btrfs_ioctl(struct file *file, unsigned int
>  		return btrfs_ioctl_get_fslabel(fs_info, argp);
>  	case FS_IOC_SETFSLABEL:
>  		return btrfs_ioctl_set_fslabel(file, argp);
> +#ifdef CONFIG_BTRFS_DEBUG
>  	case FS_IOC_SET_ENCRYPTION_POLICY: {
>  		if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
>  			return -EOPNOTSUPP;
> @@ -4601,6 +4602,7 @@ long btrfs_ioctl(struct file *file, unsigned int
>  		return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
>  	case FS_IOC_GET_ENCRYPTION_NONCE:
>  		return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
> +#endif /* CONFIG_BTRFS_DEBUG */

This diff doesn't seem to match the commit message.

BTW, only semi-related, but BTRFS_FS should select FS_ENCRYPTION_ALGS if
FS_ENCRYPTION.

- Eric

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

* Re: [PATCH v4 12/46] btrfs: add inode encryption contexts
  2023-12-01 22:11 ` [PATCH v4 12/46] btrfs: add inode encryption contexts Josef Bacik
@ 2023-12-05  5:22   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:22 UTC (permalink / raw)
  To: Josef Bacik
  Cc: linux-btrfs, kernel-team, linux-fsdevel, Omar Sandoval,
	Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:09PM -0500, Josef Bacik wrote:
> From: Omar Sandoval <osandov@osandov.com>
> 
> In order to store encryption information for directories, symlinks,
> etc., fscrypt stores a context item with each encrypted non-regular
> inode. fscrypt provides an arbitrary blob for the filesystem to store,
> and it does not clearly fit into an existing structure, so this goes in
> a new item type.
> 

It's actually regular files too, right?

> diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
> index d24e8e121507..08f561da33cd 100644
> --- a/include/uapi/linux/btrfs_tree.h
> +++ b/include/uapi/linux/btrfs_tree.h
> @@ -164,6 +164,8 @@
>  #define BTRFS_VERITY_DESC_ITEM_KEY	36
>  #define BTRFS_VERITY_MERKLE_ITEM_KEY	37
>  
> +#define BTRFS_FSCRYPT_CTX_ITEM_KEY	41

Maybe call this BTRFS_FSCRYPT_INODE_CTX_ITEM_KEY?

- Eric

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

* Re: [PATCH v4 15/46] btrfs: handle nokey names.
  2023-12-01 22:11 ` [PATCH v4 15/46] btrfs: handle " Josef Bacik
@ 2023-12-05  5:29   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:29 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel, Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:12PM -0500, Josef Bacik wrote:
> +/*
> + * From a given location in a leaf, read a name into a qstr (usually a
> + * fscrypt_name's disk_name), allocating the required buffer. Used for
> + * nokey names.
> + */
> +int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
> +				struct btrfs_dir_item *dir_item,
> +				struct fscrypt_str *name)
> +{
> +	unsigned long de_name_len = btrfs_dir_name_len(leaf, dir_item);
> +	unsigned long de_name = (unsigned long)(dir_item + 1);
> +	/*
> +	 * For no-key names, we use this opportunity to find the disk
> +	 * name, so future searches don't need to deal with nokey names
> +	 * and we know what the encrypted size is.
> +	 */
> +	name->name = kmalloc(de_name_len, GFP_NOFS);
> +
> +	if (!name->name)
> +		return -ENOMEM;

Where is the kfree() that matches the above kmalloc()?

- Eric

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

* Re: [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt
  2023-12-01 22:11 ` [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt Josef Bacik
@ 2023-12-05  5:41   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:41 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel, Sweet Tea Dorminy

On Fri, Dec 01, 2023 at 05:11:16PM -0500, Josef Bacik wrote:
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 0e8e2ca48a2e..48d751011d07 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -4585,6 +4585,9 @@ long btrfs_ioctl(struct file *file, unsigned int
>  		 * state persists.
>  		 */
>  		btrfs_set_fs_incompat(fs_info, ENCRYPT);
> +		if (!(inode->i_sb->s_flags & SB_INLINECRYPT)) {
> +			inode->i_sb->s_flags |= SB_INLINECRYPT;
> +		}
>  		return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
>  	}

Multiple tasks can execute ioctls at the same time, so isn't the lockless
modification of 's_flags' above a data race?

Maybe you should just set SB_INLINECRYPT at mount time only, regardless of the
ENCRYPT feature, so that it doesn't have to be enabled later.

> +	if (btrfs_fs_incompat(fs_info, ENCRYPT)) {
> +		if (IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT)) {
> +			sb->s_flags |= SB_INLINECRYPT;
> +		} else {
> +			btrfs_err(fs_info, "encryption not supported");
> +			err = -EINVAL;
> +			goto fail_close;
> +		}
> +	}

Why CONFIG_FS_ENCRYPTION_INLINE_CRYPT instead of CONFIG_FS_ENCRYPTION?  I think
you need to make CONFIG_FS_ENCRYPTION select CONFIG_FS_ENCRYPTION_INLINE_CRYPT
when btrfs is enabled anyway, right?

Also, should the error message be clearer?  Like "filesystem has encrypt feature
but kernel doesn't support encryption", or something like that.  Actually,
should that case even be an error?  ext4, for example, allows a filesystem with
the encrypt feature to be mounted even when the kernel doesn't support
encryption.  (It doesn't allow access to encrypted files, of course.)

- Eric

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

* Re: [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads
  2023-12-01 22:11 ` [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads Josef Bacik
@ 2023-12-05  5:44   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:44 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:11:23PM -0500, Josef Bacik wrote:
> We keep track of this information in the ordered extent for writes, but
> we need it for reads as well.  Add fscrypt_extent_info and orig_start to
> the dio_data so we can populate this on reads.  This will be used later
> when we attach the fscrypt context to the bios.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>

Does this mean that btrfs will support direct I/O on encrypted files from the
beginning?  Are you enforcing FS block alignment, as is required for encrypted
direct I/O?

- Eric

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

* Re: [PATCH v4 46/46] btrfs: load the inode context before sending writes
  2023-12-01 22:11 ` [PATCH v4 46/46] btrfs: load the inode context before sending writes Josef Bacik
@ 2023-12-05  5:54   ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05  5:54 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Fri, Dec 01, 2023 at 05:11:43PM -0500, Josef Bacik wrote:
> For send we will read the pages and copy them into our buffer.  Use the
> fscrypt_inode_open helper to make sure the key is loaded properly before
> trying to read from the inode so the contents are properly decrypted.
> 
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> ---
>  fs/btrfs/send.c | 35 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index de77321777f4..3475b4cea09d 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -5392,6 +5392,37 @@ static int put_file_data(struct send_ctx *sctx, u64 offset, u32 len)
>  	return ret;
>  }
>  
> +static int load_fscrypt_context(struct send_ctx *sctx)
> +{
> +	struct btrfs_root *root = sctx->send_root;
> +	struct name_cache_entry *nce;
> +	struct inode *dir;
> +	int ret;
> +
> +	if (!IS_ENCRYPTED(sctx->cur_inode))
> +		return 0;
> +
> +	/*
> +	 * We're encrypted, we need to load the parent inode in order to make
> +	 * sure the encryption context is loaded, we use this after calling
> +	 * get_cur_path() so our nce for the current inode should be here.  If
> +	 * not handle it, but ASSERT() for developers.
> +	 */
> +	nce = name_cache_search(sctx, sctx->cur_ino, sctx->cur_inode_gen);
> +	if (!nce) {
> +		ASSERT(nce);
> +		return -EINVAL;
> +	}
> +
> +	dir = btrfs_iget(root->fs_info->sb, nce->parent_ino, root);
> +	if (IS_ERR(dir))
> +		return PTR_ERR(dir);
> +
> +	ret = fscrypt_inode_open(dir, sctx->cur_inode);
> +	iput(dir);
> +	return ret;

fscrypt_file_open() is called even on unencrypted files, which results in strong
enforcement that encrypted directories don't contain unencrypted files.

The code above doesn't do that with fscrypt_inode_open().  That seems like a
bug; the rules for "send" internally "opening" a file should be the same as a
standard open, right?  Or did you do it this way intentionally?

- Eric

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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2023-12-05  1:49 ` Eric Biggers
@ 2023-12-05 14:16   ` David Sterba
  2023-12-05 20:02     ` Eric Biggers
  0 siblings, 1 reply; 70+ messages in thread
From: David Sterba @ 2023-12-05 14:16 UTC (permalink / raw)
  To: Eric Biggers; +Cc: Josef Bacik, linux-btrfs, kernel-team, linux-fsdevel

On Mon, Dec 04, 2023 at 05:49:17PM -0800, Eric Biggers wrote:
> On Fri, Dec 01, 2023 at 05:10:57PM -0500, Josef Bacik wrote:
> > Hello,
> > 
> > v3 can be found here
> > 
> > https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/
> > 
> > There's been a longer delay between versions than I'd like, this was mostly due
> > to Plumbers, Holidays, and then uncovering a bunch of new issues with '-o
> > test_dummy_encryption'.  I'm still working through some of the btrfs specific
> > failures, but the fscrypt side appears to be stable.  I had to add a few changes
> > to fscrypt since the last time, but nothing earth shattering, just moving the
> > keyring destruction and adding a helper we need for btrfs send to work properly.
> > 
> > This is passing a good chunk of the fstests, at this point the majority appear
> > to be cases where I need to exclude the test when using test_dummy_encryption
> > because of various limitations of our tools or other infrastructure related
> > things.
> > 
> > I likely will have a follow-up series with more fixes, but the bulk of this is
> > unchanged since the last posting.  There were some bug fixes and such but the
> > overall design remains the same.  Thanks,
> > 
> 
> Well, it wouldn't be Linux kernel development without patchsets that don't
> mention what they apply to...  I managed to apply this for reviewing after
> spending a while choosing the base that seemed to work best (6d3880a76eedd from
> kdave/for-next) and resolving conflicts.  It would save a lot of time if proper
> information was included, though.

Right, the base branch should have been mentioned, we have the development
git tree on github and not on kernel.org.

For future reference:
https://btrfs.readthedocs.io/en/latest/Source-repositories.html#kernel-module

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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2023-12-05 14:16   ` David Sterba
@ 2023-12-05 20:02     ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05 20:02 UTC (permalink / raw)
  To: David Sterba; +Cc: Josef Bacik, linux-btrfs, kernel-team, linux-fsdevel

On Tue, Dec 05, 2023 at 03:16:55PM +0100, David Sterba wrote:
> 
> we have the development git tree on github and not on kernel.org.

Then the MAINTAINERS entry for btrfs needs to be updated, as it points to
kernel.org.  I already had to update it for you guys the last time it changed
(https://git.kernel.org/linus/eb91db63a90d8f8e).  Not sure why you guys can't
keep it up to date.

BTW, https://github.com/btrfs/linux exists but it's the wrong one too, which
makes it extra confusing.

- Eric

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

* Re: [PATCH v4 02/46] fscrypt: add per-extent encryption support
  2023-12-05  3:58   ` Eric Biggers
@ 2023-12-05 22:48     ` Josef Bacik
  2023-12-05 23:57       ` Eric Biggers
  2023-12-13  4:16     ` Eric Biggers
  1 sibling, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-05 22:48 UTC (permalink / raw)
  To: Eric Biggers; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Mon, Dec 04, 2023 at 07:58:20PM -0800, Eric Biggers wrote:
> On Fri, Dec 01, 2023 at 05:10:59PM -0500, Josef Bacik wrote:
> > The file system indicates it wants to use per-extent encryption by
> > setting s_cop->set_extent_context.
> 
> It's actually s_cop->has_per_extent_encryption.
> 
> > diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
> > index 1892356cf924..03c006e5c47d 100644
> > --- a/fs/crypto/fscrypt_private.h
> > +++ b/fs/crypto/fscrypt_private.h
> [...]
> > @@ -30,6 +30,8 @@
> >  #define FSCRYPT_CONTEXT_V1	1
> >  #define FSCRYPT_CONTEXT_V2	2
> >  
> > +#define FSCRYPT_EXTENT_CONTEXT_V1	1
> > +
> >  /* Keep this in sync with include/uapi/linux/fscrypt.h */
> >  #define FSCRYPT_MODE_MAX	FSCRYPT_MODE_AES_256_HCTR2
> >  
> > @@ -53,6 +55,28 @@ struct fscrypt_context_v2 {
> >  	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> >  };
> 
> Hmm, I think we're going to want fscrypt_context => fscrypt_inode_context too...
> Also FSCRYPT_FILE_NONCE_SIZE => FSCRYPT_NONCE_SIZE.  I guess don't worry about
> it for now, though.
> 
> > +/*
> > + * fscrypt_extent_context - the encryption context of an extent
> > + *
> > + * This is the on-disk information stored for an extent.  The policy and
> > + * relevante information is stored in the inode, the per-extent information is
> > + * simply the nonce that's used in as KDF input in conjunction with the inode
> > + * context to derive a per-extent key for encryption.
> > + *
> > + * At this point the master_key_identifier exists only for possible future
> > + * expansion.  This will allow for an inode to have extents with multiple master
> > + * keys, although sharing the same encryption mode.  This would be for re-keying
> > + * or for reflinking between two differently encrypted inodes.  For now the
> > + * master_key_descriptor must match the inode's, and we'll be using the inode's
> > + * for all key derivation.
> > + */
> > +struct fscrypt_extent_context {
> > +	u8 version; /* FSCRYPT_EXTENT_CONTEXT_V2 */
> > +	u8 encryption_mode;
> > +	u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
> > +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> > +};
> 
> s/relevante/relevant/
> s/FSCRYPT_EXTENT_CONTEXT_V2/FSCRYPT_EXTENT_CONTEXT_V1/
> s/master_key_descriptor/master_key_identifier/
> 
> The second paragraph also contradicts the first, which says that the context
> "is simply the nonce".
> 
> Also, encryption_mode is there for the same reason as master_key_identifier,
> right?  The comment only mentions master_key_identifier.
> 
> > +/*
> > + * fscrypt_extent_info - the "encryption key" for an extent.
> > + *
> > + * This contains the dervied key for the given extent and the nonce for the
> > + * extent.
> > + */
> 
> s/dervied/derived/
> 
> > +struct fscrypt_extent_info {
> > +	refcount_t refs;
> > +	/* This is the extents nonce, loaded from the fscrypt_extent_context */
> > +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> 
> s/extents/extent's/
> 
> > diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
> > index b4002aea7cdb..4eeb75410ba8 100644
> > --- a/fs/crypto/inline_crypt.c
> > +++ b/fs/crypto/inline_crypt.c
> [...]
> > +/**
> > + * fscrypt_set_bio_crypt_ctx() - prepare a file contents bio for inline crypto
> > + * @bio: a bio which will eventually be submitted to the file
> > + * @inode: the file's inode
> > + * @ei: the extent's crypto info
> > + * @first_lblk: the first file logical block number in the I/O
> > + * @gfp_mask: memory allocation flags - these must be a waiting mask so that
> > + *					bio_crypt_set_ctx can't fail.
> > + *
> > + * If the contents of the file should be encrypted (or decrypted) with inline
> > + * encryption, then assign the appropriate encryption context to the bio.
> > + *
> > + * Normally the bio should be newly allocated (i.e. no pages added yet), as
> > + * otherwise fscrypt_mergeable_bio() won't work as intended.
> > + *
> > + * The encryption context will be freed automatically when the bio is freed.
> > + */
> > +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
> > +					   const struct inode *inode,
> > +					   const struct fscrypt_extent_info *ei,
> > +					   u64 first_lblk, gfp_t gfp_mask)
> > +{
> > +	const struct fscrypt_inode_info *ci;
> > +	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> > +
> > +	if (!fscrypt_inode_uses_inline_crypto(inode))
> > +		return;
> > +	ci = inode->i_crypt_info;
> > +
> > +	fscrypt_generate_dun(ci, first_lblk, dun);
> > +	bio_crypt_set_ctx(bio, ei->prep_key.blk_key, dun, gfp_mask);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_from_extent);
> 
> Silently leaving the data unencrypted if
> !fscrypt_inode_uses_inline_crypto(inode) is dangerous.  I think you should just
> leave that part out.  It was already checked in fscrypt_load_extent_info().
> 
> Also, I think that for now we should keep things simple by doing the following
> instead of fscrypt_generate_dun():
> 
> 	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk };
> 
> Note that these two changes would eliminate the need for the inode parameter to
> the function.
> 
> Please also make sure to update the function comment.  Note that the first line
> gives the function name incorrectly.
> 

First off thanks for the review, I'm currently going through it one by one, so
I've come to this bit and I've got a question.

The DUN has to be le64, so this strictly isn't ok.  So would you be ok with

__le64 first_lblk_le = le64_to_cpu(first_lblk;
u64 dun [BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk_le };

or do you want something different?  Additionally fscrypt_generate_dun() also
takes into account the data units per block bits, which as I'm typing this out
I'm realizing doesn't really matter for us even if we have different sectorsize
per pagesize.  I guess I'll put a big comment about why we're not using that in
there to make sure future Josef isn't confused.

> > +/**
> > + * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio
> > + * @bio: the bio being built up
> > + * @inode: the inode for the next part of the I/O
> > + * @ei: the fscrypt_extent_info for this extent
> > + * @next_lblk: the next file logical block number in the I/O
> > + *
> > + * When building a bio which may contain data which should undergo inline
> > + * encryption (or decryption) via fscrypt, filesystems should call this function
> > + * to ensure that the resulting bio contains only contiguous data unit numbers.
> > + * This will return false if the next part of the I/O cannot be merged with the
> > + * bio because either the encryption key would be different or the encryption
> > + * data unit numbers would be discontiguous.
> > + *
> > + * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called on the
> > + * bio.
> > + *
> > + * This function isn't required in cases where crypto-mergeability is ensured in
> > + * another way, such as I/O targeting only a single file (and thus a single key)
> > + * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity.
> > + *
> > + * Return: true iff the I/O is mergeable
> > + */
> > +bool fscrypt_mergeable_extent_bio(struct bio *bio, const struct inode *inode,
> > +				  const struct fscrypt_extent_info *ei,
> > +				  u64 next_lblk)
> > +{
> > +	const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
> > +	u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];
> > +
> > +	if (!ei)
> > +		return true;
> > +	if (!!bc != fscrypt_inode_uses_inline_crypto(inode))
> > +		return false;
> > +	if (!bc)
> > +		return true;
> > +
> > +	/*
> > +	 * 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 != ei->prep_key.blk_key)
> > +		return false;
> > +
> > +	fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);
> > +	return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio);
> 
> Similarly, I think we should keep things simple here.  With extent-based
> encryption not supporting any "weird" IV generation methods, the filesystem just
> needs to ensure that bios target a logically contiguous range in one file only.
> 
> It looks like btrfs already ensures that, as per the following code:
> 
> 	/*
> 	 * The contig check requires the following conditions to be met:
> 	 *
> 	 * 1) The pages are belonging to the same inode
> 	 *    This is implied by the call chain.
> 	 *
> 	 * 2) The range has adjacent logical bytenr
> 	 *
> 	 * 3) The range has adjacent file offset
> 	 *    This is required for the usage of btrfs_bio->file_offset.
> 	 */
> 	return bio_end_sector(bio) == sector &&
> 		page_offset(bvec->bv_page) + bvec->bv_offset + bvec->bv_len ==
> 		page_offset(page) + pg_offset;
> 
> 
> So I think you could just delete fscrypt_mergeable_extent_bio() and the
> IS_ENCRYPTED() special case in btrfs_bio_is_contig(), and everything would still
> work fine.
> 
> The existing fully general mergeability check is mainly for
> FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32, where the DUN can wrap around at random
> places, and for f2fs which can build bios containing data of many different
> files.  Without those, just "same file + contiguous logical blocks" suffices.
> 
> > +static struct fscrypt_extent_info *
> > +setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_SIZE])
> > +{
> > +	struct fscrypt_extent_info *ei;
> > +	struct fscrypt_inode_info *ci;
> > +	struct fscrypt_master_key *mk;
> > +	u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
> > +	int err;
> > +
> > +	ci = inode->i_crypt_info;
> > +	mk = ci->ci_master_key;
> > +	if (!mk)
> > +		return ERR_PTR(-ENOKEY);
> 
> mk being NULL here would be a bug, right?  Maybe WARN_ON_ONCE() on it?
> 
> > +
> > +	ei = kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL);
> > +	if (!ei)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	refcount_set(&ei->refs, 1);
> > +	memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
> > +	ei->sb = inode->i_sb;
> > +
> > +	down_read(&mk->mk_sem);
> > +	/*
> > +	 * We specifically don't do is_master_key_secret_present() here because
> > +	 * if the inode is open and has a reference on the master key then it
> > +	 * should be available for us to use.
> > +	 */
> 
> ->mk_secret, not is_master_key_secret_present()
> 
> > +
> > +	err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
> > +				  HKDF_CONTEXT_PER_FILE_ENC_KEY, ei->nonce,
> > +				  FSCRYPT_FILE_NONCE_SIZE, derived_key,
> > +				  ci->ci_mode->keysize);
> > +	if (err)
> > +		goto out_free;
> 
> Reusing HKDF_CONTEXT_PER_FILE_ENC_KEY here is misleading, as this isn't deriving
> a per-file key.  Cryptographically it's okay, since it's still following the
> same pattern with the 16-byte nonce, but I'm thinking it would be best to
> introduce a new HKDF context byte as follows:
> 
> 	#define HKDF_CONTEXT_PER_EXTENT_ENC_KEY 8 /* info=extent_nonce          */
> 
> > +/**
> > + * fscrypt_prepare_new_extent() - prepare to create a new extent for a file
> > + * @inode: the possibly-encrypted inode
> > + *
> > + * If the inode is encrypted, setup the fscrypt_extent_info for a new extent.
> > + * This will include the nonce and the derived key necessary for the extent to
> > + * be encrypted.  This is only meant to be used with inline crypto.
> > + *
> > + * This doesn't persist the new extents encryption context, this is done later
> > + * by calling fscrypt_set_extent_context().
> > + *
> > + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> > + *	   we're not encrypted, or another -errno code
> > + */
> > +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode)
> > +{
> > +	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> > +
> > +	if (!fscrypt_inode_uses_inline_crypto(inode))
> > +		return ERR_PTR(-EOPNOTSUPP);
> > +
> > +	get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
> > +	return setup_extent_info(inode, nonce);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
> 
> The behavior of this on failure is not clear.  AFAICS it actually does:
> 
> 	inode is not an encryted regular file, -EOPNOTSUPP
> 	else if inode does not have key set up, NULL dereference
> 	else if inode does not use inline crypto, -EOPNOTSUPP
> 
> But it's a bug for any of those three cases to be reached at all, right?  I.e.,
> this function is only for inodes for encrypted regular files that have already
> had their key set up, on filesystems that force the use of inline crypto?
> 
> I'm wondering if we should make all those cases trigger a WARN_ON_ONCE() and
> document the preconditions for this function appropriately.
> 

Agreed, I was actually using the NULL dereference case to find bugs where I
wasn't loading the context properly lol.

> > +/**
> > + * fscrypt_load_extent_info() - create an fscrypt_extent_info from the context
> > + * @inode: the inode
> > + * @ctx: the context buffer
> > + * @ctx_size: the size of the context buffer
> > + *
> > + * Create the file_extent_info and derive the key based on the
> > + * fscrypt_extent_context buffer that is probided.
> 
> s/file_extent_info/fscrypt_extent_info/
> s/probided/provided/
> 
> > + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> > + *	   we're not encrypted, or another -errno code
> > + */
> > +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
> > +						     u8 *ctx, size_t ctx_size)
> > +{
> > +	struct fscrypt_extent_context extent_ctx;
> > +	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
> > +	const struct fscrypt_policy_v2 *policy = &ci->ci_policy.v2;
> > +
> > +	if (!fscrypt_inode_uses_inline_crypto(inode))
> > +		return ERR_PTR(-EOPNOTSUPP);
> 
> Same comment as fscrypt_prepare_new_extent().  Should there be a WARN_ON_ONCE()
> here, and also an explicit check for the inode's key being set up?
> 
> > +	if (ctx_size < sizeof(extent_ctx))
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	memcpy(&extent_ctx, ctx, sizeof(extent_ctx));
> > +
> > +	/*
> > +	 * For now we need to validate that the master key for the inode matches
> > +	 * the extent.
> > +	 */
> > +	if (memcmp(extent_ctx.master_key_identifier,
> > +		   policy->master_key_identifier,
> > +		   sizeof(extent_ctx.master_key_identifier)))
> > +		return ERR_PTR(-EINVAL);
> 
> extent_ctx.version and extent_ctx.encryption_mode both need to be validated.
> 
> Also, it would be helpful if there were appropriate warning messages (using
> fscrypt_warn()) upon seeing an unsupported extent context, similar to the
> messages that get printed when seeing an unsupported inode context.
> 
> > +EXPORT_SYMBOL(fscrypt_load_extent_info);
> 
> EXPORT_SYMBOL_GPL for any new exported symbols in fs/crypto/, please.
> 
> > +/**
> > + * fscrypt_put_extent_info() - free the extent_info fscrypt data
> 
> fscrypt_put_extent_info() - put a reference to an fscrypt extent info
> 
> > + * @ei: the extent_info being evicted
> 
> @ei: the extent info being evicted or NULL
> 
> (BTW, is the NULL support really needed?)
> 
> > + * Note this will unload the key from the block layer, which takes the crypto
> > + * profile semaphore to unload the key.  Make sure you're not dropping this in a
> > + * context that can't sleep.
> > + */
> 
> "Might sleep, since this may call blk_crypto_evict_key() which can sleep."
> 
> > +void fscrypt_put_extent_info(struct fscrypt_extent_info *ei)
> > +{
> > +	if (!ei)
> > +		return;
> > +
> > +	if (!refcount_dec_and_test(&ei->refs))
> > +		return;
> > +
> > +	fscrypt_destroy_prepared_key(ei->sb, &ei->prep_key);
> > +	memzero_explicit(ei, sizeof(*ei));
> > +	kmem_cache_free(fscrypt_extent_info_cachep, ei);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_put_extent_info);
> 
> This would look cleaner as:
> 
> 	if (ei && refcount_dec_and_test(&ei->refs)) {
> 		...
> 	}
> 
> > +
> > +/**
> > + * fscrypt_get_extent_info() - get a ref on the fscrypt extent info
> 
> fscrypt_get_extent_info() - get a reference to an fscrypt extent info
> 
> > + * @ei: the extent_info to get.
> > + *
> > + * Get a reference on the fscrypt_extent_info.
> > + *
> 
> An explanation here about why filesystems would need to use this would be
> useful.
> 
> > + * Return: the ei with an extra ref, NULL if there was no ei passed in.
> 
> Return: the ei with an extra ref, or NULL if ei was NULL.
> 
> > + */
> > +struct fscrypt_extent_info *fscrypt_get_extent_info(struct fscrypt_extent_info *ei)
> > +{
> > +	if (!ei)
> > +		return NULL;
> > +	refcount_inc(&ei->refs);
> > +	return ei;
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_get_extent_info);
> 
> Similarly, this would look cleaner as:
> 
> 	if (ei)
> 		refcount_inc(&ei->refs);
> 	return ei;
> 
> > diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
> > index 701259991277..75a69f02f11d 100644
> > --- a/fs/crypto/policy.c
> > +++ b/fs/crypto/policy.c
> > @@ -209,6 +209,12 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
> >  		return false;
> >  	}
> >  
> > +	if (inode->i_sb->s_cop->has_per_extent_encryption) {
> > +		fscrypt_warn(inode,
> > +			     "v1 policies can't be used on file systems that use extent encryption");
> > +		return false;
> 
> Maybe "can't be used" => "aren't supported", to be consistent with the other
> message you're adding.
> 
> > +	}
> > +
> >  	return true;
> >  }
> >  
> > @@ -269,6 +275,12 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
> >  		}
> >  	}
> >  
> > +	if ((inode->i_sb->s_cop->has_per_extent_encryption) && count) {
> > +		fscrypt_warn(inode,
> > +			     "Encryption flags aren't supported on file systems that use extent encryption");
> > +		return false;
> > +	}
> 
> Maybe move this up to where count is being checked already, so that the code
> looks like:
> 
> 	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY);
> 	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64);
> 	count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);
> 	if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) {
> 		fscrypt_warn(inode,
> 				"Encryption flags aren't supported on file systems that use extent encryption");
> 		return false;
> 	}
> 	if (count > 1) {
> 
> > +/**
> > + * fscrypt_set_extent_context() - Set the fscrypt extent context of a new extent
> > + * @inode: the inode this extent belongs to
> > + * @ei: the fscrypt_extent_info for the given extent
> > + * @buf: the buffer to copy the fscrypt extent context into
> > + *
> > + * This should be called after fscrypt_prepare_new_extent(), using the
> > + * fscrypt_extent_info that was created at that point.
> > + *
> > + * Return: the size of the fscrypt_extent_context, errno if the inode has the
> > + *	   wrong policy version.
> > + */
> > +ssize_t fscrypt_set_extent_context(struct inode *inode,
> > +				   struct fscrypt_extent_info *ei, u8 *buf)
> > +{
> > +	struct fscrypt_extent_context *ctx = (struct fscrypt_extent_context *)buf;
> > +	const struct fscrypt_inode_info *ci = inode->i_crypt_info;
> > +
> > +	if (ci->ci_policy.version != 2)
> > +		return -EINVAL;
> > +
> > +	ctx->version = 1;
> > +	memcpy(ctx->master_key_identifier,
> > +	       ci->ci_policy.v2.master_key_identifier,
> > +	       sizeof(ctx->master_key_identifier));
> > +	memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE);
> > +	return sizeof(struct fscrypt_extent_context);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);
> 
> Well, this doesn't *set* an extent context.  It generates one.  Basically it's
> the extent equivalent of fscrypt_context_for_new_inode().  Maybe call this
> fscrypt_context_for_new_extent()?
> 
> Also, 'ci->ci_policy.version != 2' indicates a bug, so it should be a
> WARN_ON_ONCE().
> 
> Also, the literal 1 should be FSCRYPT_EXTENT_CONTEXT_V1.
> 
> Also, the expected size of the buffer needs to be documented (as it is for
> fscrypt_context_for_new_inode()).  It's FSCRYPT_SET_CONTEXT_MAX_SIZE, right?
> 
> It might be a good idea to also add the following:
> 
> 	BUILD_BUG_ON(sizeof(struct fscrypt_extent_context) >
>                      FSCRYPT_SET_CONTEXT_MAX_SIZE);
> > +/**
> > + * fscrypt_extent_context_size() - Return the size of the on-disk extent context
> > + * @inode: the inode this extent belongs to.
> > + *
> > + * Return the size of the extent context for this inode.  Since there is only
> > + * one extent context version currently this is just the size of the extent
> > + * context if the inode is encrypted.
> > + */
> > +size_t fscrypt_extent_context_size(struct inode *inode)
> > +{
> > +	if (!IS_ENCRYPTED(inode))
> > +		return 0;
> > +
> > +	return sizeof(struct fscrypt_extent_context);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_extent_context_size);
> 
> Huh, shouldn't the filesystem use the size from fscrypt_set_extent_context() (or
> fscrypt_context_for_new_extent() as I recommend calling it)?  Why is the above
> function necessary?

This is because we need to know how big the context is ahead of time before
searching down the b-tree to insert the file extent item, and we have to add
this size to the file extent item ahead of time.  The disconnect between when we
need to know how big it'll be and when we actually generate the context to save
on disk is the reason for there being two helpers.

The main reason for this is code separation.  I would have to have the context
on stack for the !fscrypt case if I were to only have one helper, because I
would need it there to generate the extent context to save on disk to pass it
around to all the things that add file extents, and I could use the length from
that.  Not a huge deal, but this way is a little cleaner.  You can see how I've
used the two helpers in later patches, but the general flow is

total_size = sizeof(our file extent);
total_size += fscrypt_extent_context_size(inode);

btrfs_insert_empty_item(path, total_size);

btrfs_fscrypt_save_extent_info(path, inode);

and then in btrfs_fscrypt_save_extent_info() we do

u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];

size = fscrypt_set_extent_context(inode, ctx);

Now the above case does seem like we could just uconditionally do something like

u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];

total_size = sizeof(our file extent);
ctx_size = fscrypt_set_extent_context(inode, ctx);
if (ctx_size < 0)
	goto error;
total_size += ctx_size;
btrfs_insert_empty_item(path, total_size);
copy_ctx_into_file_extent(path, ctx);

But there's some other file extent manipulation cases that aren't this clear
cut, so I'd have to pass this ctx around to be copied in.  And in the !encrypted
part we're paying a higher stack memory cost.

If you still don't like this I can rework it, but I wanted to explain my
reasoning before I went changing a bunch of stuff to make the new paradigm fit.

The rest of the comments make sense, I'll fix everything up.  Thanks,

Josef

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

* Re: [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super
  2023-12-05  1:58   ` Eric Biggers
@ 2023-12-05 22:48     ` Josef Bacik
  2023-12-06  0:01       ` Eric Biggers
  0 siblings, 1 reply; 70+ messages in thread
From: Josef Bacik @ 2023-12-05 22:48 UTC (permalink / raw)
  To: Eric Biggers; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Mon, Dec 04, 2023 at 05:58:00PM -0800, Eric Biggers wrote:
> On Fri, Dec 01, 2023 at 05:10:58PM -0500, Josef Bacik wrote:
> > btrfs has a variety of asynchronous things we do with inodes that can
> > potentially last until ->put_super, when we shut everything down and
> > clean up all of our async work.  Due to this we need to move
> > fscrypt_destroy_keyring() to after ->put_super, otherwise we get
> > warnings about still having active references on the master key.
> > 
> > Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> > ---
> >  fs/super.c | 12 ++++++------
> >  1 file changed, 6 insertions(+), 6 deletions(-)
> > 
> > diff --git a/fs/super.c b/fs/super.c
> > index 076392396e72..faf7d248145d 100644
> > --- a/fs/super.c
> > +++ b/fs/super.c
> > @@ -681,12 +681,6 @@ void generic_shutdown_super(struct super_block *sb)
> >  		fsnotify_sb_delete(sb);
> >  		security_sb_delete(sb);
> >  
> > -		/*
> > -		 * Now that all potentially-encrypted inodes have been evicted,
> > -		 * the fscrypt keyring can be destroyed.
> > -		 */
> > -		fscrypt_destroy_keyring(sb);
> > -
> >  		if (sb->s_dio_done_wq) {
> >  			destroy_workqueue(sb->s_dio_done_wq);
> >  			sb->s_dio_done_wq = NULL;
> > @@ -695,6 +689,12 @@ void generic_shutdown_super(struct super_block *sb)
> >  		if (sop->put_super)
> >  			sop->put_super(sb);
> >  
> > +		/*
> > +		 * Now that all potentially-encrypted inodes have been evicted,
> > +		 * the fscrypt keyring can be destroyed.
> > +		 */
> > +		fscrypt_destroy_keyring(sb);
> > +
> 
> This patch will cause a NULL dereference on f2fs, since f2fs_put_super() frees
> ->s_fs_info, and then fscrypt_destroy_keyring() can call f2fs_get_devices() (via
> fscrypt_operations::get_devices) which dereferences it.  (ext4 also frees
> ->s_fs_info in its ->put_super, but ext4 doesn't implement ->get_devices.)
> 
> I think we need to move the fscrypt keyring destruction into ->put_super for
> each filesystem.

I can do this, I'll send a separate series for this since this should be
straightforward and we can get that part done.  Thanks,

Josef

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

* Re: [PATCH v4 02/46] fscrypt: add per-extent encryption support
  2023-12-05 22:48     ` Josef Bacik
@ 2023-12-05 23:57       ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-05 23:57 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Tue, Dec 05, 2023 at 05:48:09PM -0500, Josef Bacik wrote:
> > Also, I think that for now we should keep things simple by doing the following
> > instead of fscrypt_generate_dun():
> > 
> > 	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk };
> > 
> > Note that these two changes would eliminate the need for the inode parameter to
> > the function.
> > 
> > Please also make sure to update the function comment.  Note that the first line
> > gives the function name incorrectly.
> > 
> 
> First off thanks for the review, I'm currently going through it one by one, so
> I've come to this bit and I've got a question.
> 
> The DUN has to be le64, so this strictly isn't ok.  So would you be ok with
> 
> __le64 first_lblk_le = le64_to_cpu(first_lblk;
> u64 dun [BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk_le };
> 
> or do you want something different?  Additionally fscrypt_generate_dun() also
> takes into account the data units per block bits, which as I'm typing this out
> I'm realizing doesn't really matter for us even if we have different sectorsize
> per pagesize.  I guess I'll put a big comment about why we're not using that in
> there to make sure future Josef isn't confused.

The blk-crypto DUN is CPU endian, as indicated by the type (array of u64).  Note
that fscrypt_generate_dun() converts u64 => __le64 => u64.  (And then
blk-crypto-fallback converts u64 => __le64.)  Getting rid of that round trip is
nice, which is why I'm recommending it.  The only reason that
fscrypt_generate_dun() does the round trip is because it allows it to reuse
fscrypt_generate_iv(), and I wanted to be 100% sure that all of the IV methods
were implemented the same as in FS layer encryption.  With the extent-based
encryption we're just supporting one method, so things are simpler.

Yeah, it should be 'first_lblk << ci_data_units_per_block_bits'.
That would just be for future-proofing, since btrfs isn't setting
supports_subblock_data_units anyway.

> > > +/**
> > > + * fscrypt_extent_context_size() - Return the size of the on-disk extent context
> > > + * @inode: the inode this extent belongs to.
> > > + *
> > > + * Return the size of the extent context for this inode.  Since there is only
> > > + * one extent context version currently this is just the size of the extent
> > > + * context if the inode is encrypted.
> > > + */
> > > +size_t fscrypt_extent_context_size(struct inode *inode)
> > > +{
> > > +	if (!IS_ENCRYPTED(inode))
> > > +		return 0;
> > > +
> > > +	return sizeof(struct fscrypt_extent_context);
> > > +}
> > > +EXPORT_SYMBOL_GPL(fscrypt_extent_context_size);
> > 
> > Huh, shouldn't the filesystem use the size from fscrypt_set_extent_context() (or
> > fscrypt_context_for_new_extent() as I recommend calling it)?  Why is the above
> > function necessary?
> 
> This is because we need to know how big the context is ahead of time before
> searching down the b-tree to insert the file extent item, and we have to add
> this size to the file extent item ahead of time.  The disconnect between when we
> need to know how big it'll be and when we actually generate the context to save
> on disk is the reason for there being two helpers.
> 
> The main reason for this is code separation.  I would have to have the context
> on stack for the !fscrypt case if I were to only have one helper, because I
> would need it there to generate the extent context to save on disk to pass it
> around to all the things that add file extents, and I could use the length from
> that.  Not a huge deal, but this way is a little cleaner.  You can see how I've
> used the two helpers in later patches, but the general flow is
> 
> total_size = sizeof(our file extent);
> total_size += fscrypt_extent_context_size(inode);
> 
> btrfs_insert_empty_item(path, total_size);
> 
> btrfs_fscrypt_save_extent_info(path, inode);
> 
> and then in btrfs_fscrypt_save_extent_info() we do
> 
> u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];
> 
> size = fscrypt_set_extent_context(inode, ctx);
> 
> Now the above case does seem like we could just uconditionally do something like
> 
> u8 ctx[BTRFS_MAX_EXTENT_CTX_SIZE];
> 
> total_size = sizeof(our file extent);
> ctx_size = fscrypt_set_extent_context(inode, ctx);
> if (ctx_size < 0)
> 	goto error;
> total_size += ctx_size;
> btrfs_insert_empty_item(path, total_size);
> copy_ctx_into_file_extent(path, ctx);
> 
> But there's some other file extent manipulation cases that aren't this clear
> cut, so I'd have to pass this ctx around to be copied in.  And in the !encrypted
> part we're paying a higher stack memory cost.
> 
> If you still don't like this I can rework it, but I wanted to explain my
> reasoning before I went changing a bunch of stuff to make the new paradigm fit.
> 
> The rest of the comments make sense, I'll fix everything up.  Thanks,

Maybe fscrypt_set_extent_context() (or fscrypt_context_for_new_extent()?) should
return the size if it's passed a NULL pointer?  It just seems error-prone have
the size of the thing being generated be returned by a different function.

- Eric

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

* Re: [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super
  2023-12-05 22:48     ` Josef Bacik
@ 2023-12-06  0:01       ` Eric Biggers
  0 siblings, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-06  0:01 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Tue, Dec 05, 2023 at 05:48:48PM -0500, Josef Bacik wrote:
> On Mon, Dec 04, 2023 at 05:58:00PM -0800, Eric Biggers wrote:
> > On Fri, Dec 01, 2023 at 05:10:58PM -0500, Josef Bacik wrote:
> > > btrfs has a variety of asynchronous things we do with inodes that can
> > > potentially last until ->put_super, when we shut everything down and
> > > clean up all of our async work.  Due to this we need to move
> > > fscrypt_destroy_keyring() to after ->put_super, otherwise we get
> > > warnings about still having active references on the master key.
> > > 
> > > Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> > > ---
> > >  fs/super.c | 12 ++++++------
> > >  1 file changed, 6 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/fs/super.c b/fs/super.c
> > > index 076392396e72..faf7d248145d 100644
> > > --- a/fs/super.c
> > > +++ b/fs/super.c
> > > @@ -681,12 +681,6 @@ void generic_shutdown_super(struct super_block *sb)
> > >  		fsnotify_sb_delete(sb);
> > >  		security_sb_delete(sb);
> > >  
> > > -		/*
> > > -		 * Now that all potentially-encrypted inodes have been evicted,
> > > -		 * the fscrypt keyring can be destroyed.
> > > -		 */
> > > -		fscrypt_destroy_keyring(sb);
> > > -
> > >  		if (sb->s_dio_done_wq) {
> > >  			destroy_workqueue(sb->s_dio_done_wq);
> > >  			sb->s_dio_done_wq = NULL;
> > > @@ -695,6 +689,12 @@ void generic_shutdown_super(struct super_block *sb)
> > >  		if (sop->put_super)
> > >  			sop->put_super(sb);
> > >  
> > > +		/*
> > > +		 * Now that all potentially-encrypted inodes have been evicted,
> > > +		 * the fscrypt keyring can be destroyed.
> > > +		 */
> > > +		fscrypt_destroy_keyring(sb);
> > > +
> > 
> > This patch will cause a NULL dereference on f2fs, since f2fs_put_super() frees
> > ->s_fs_info, and then fscrypt_destroy_keyring() can call f2fs_get_devices() (via
> > fscrypt_operations::get_devices) which dereferences it.  (ext4 also frees
> > ->s_fs_info in its ->put_super, but ext4 doesn't implement ->get_devices.)
> > 
> > I think we need to move the fscrypt keyring destruction into ->put_super for
> > each filesystem.
> 
> I can do this, I'll send a separate series for this since this should be
> straightforward and we can get that part done.  Thanks,
> 

I actually started a patch for this earlier today, just haven't had a chance to
send it out yet.  I'll do so in a minute.  Thanks,

- Eric

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

* Re: [PATCH v4 02/46] fscrypt: add per-extent encryption support
  2023-12-05  3:58   ` Eric Biggers
  2023-12-05 22:48     ` Josef Bacik
@ 2023-12-13  4:16     ` Eric Biggers
  1 sibling, 0 replies; 70+ messages in thread
From: Eric Biggers @ 2023-12-13  4:16 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team, linux-fsdevel

On Mon, Dec 04, 2023 at 07:58:20PM -0800, Eric Biggers wrote:
> > @@ -53,6 +55,28 @@ struct fscrypt_context_v2 {
> >  	u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> >  };
> 
> Hmm, I think we're going to want fscrypt_context => fscrypt_inode_context too...
> Also FSCRYPT_FILE_NONCE_SIZE => FSCRYPT_NONCE_SIZE.  I guess don't worry about
> it for now, though.

Actually for the nonce size, I'm now thinking you should add a new constant
named FSCRYPT_EXTENT_NONCE_SIZE.  It will have the same value as the file one,
but it's a logically separate thing.

- Eric

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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
                   ` (47 preceding siblings ...)
  2023-12-05  1:49 ` Eric Biggers
@ 2024-04-09 23:42 ` Eric Biggers
  2024-04-11 18:45   ` Josef Bacik
  48 siblings, 1 reply; 70+ messages in thread
From: Eric Biggers @ 2024-04-09 23:42 UTC (permalink / raw)
  To: Josef Bacik, Sweet Tea Dorminy
  Cc: linux-btrfs, kernel-team, linux-fsdevel, linux-fscrypt

Hi Josef and Sweet Tea,

On Fri, Dec 01, 2023 at 05:10:57PM -0500, Josef Bacik wrote:
> Hello,
> 
> v3 can be found here
> 
> https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/
> 
> There's been a longer delay between versions than I'd like, this was mostly due
> to Plumbers, Holidays, and then uncovering a bunch of new issues with '-o
> test_dummy_encryption'.  I'm still working through some of the btrfs specific
> failures, but the fscrypt side appears to be stable.  I had to add a few changes
> to fscrypt since the last time, but nothing earth shattering, just moving the
> keyring destruction and adding a helper we need for btrfs send to work properly.
> 
> This is passing a good chunk of the fstests, at this point the majority appear
> to be cases where I need to exclude the test when using test_dummy_encryption
> because of various limitations of our tools or other infrastructure related
> things.
> 
> I likely will have a follow-up series with more fixes, but the bulk of this is
> unchanged since the last posting.  There were some bug fixes and such but the
> overall design remains the same.  Thanks,
> 

Is there a plan for someone to keep working on this?  I think it was finally
getting somewhere, but the work on it seems to have stopped.

Thanks,

- Eric

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

* Re: [PATCH v4 00/46] btrfs: add fscrypt support
  2024-04-09 23:42 ` Eric Biggers
@ 2024-04-11 18:45   ` Josef Bacik
  0 siblings, 0 replies; 70+ messages in thread
From: Josef Bacik @ 2024-04-11 18:45 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Sweet Tea Dorminy, linux-btrfs, kernel-team, linux-fsdevel,
	linux-fscrypt

On Tue, Apr 09, 2024 at 07:42:22PM -0400, Eric Biggers wrote:
> Hi Josef and Sweet Tea,
> 
> On Fri, Dec 01, 2023 at 05:10:57PM -0500, Josef Bacik wrote:
> > Hello,
> > 
> > v3 can be found here
> > 
> > https://lore.kernel.org/linux-btrfs/cover.1697480198.git.josef@toxicpanda.com/
> > 
> > There's been a longer delay between versions than I'd like, this was mostly due
> > to Plumbers, Holidays, and then uncovering a bunch of new issues with '-o
> > test_dummy_encryption'.  I'm still working through some of the btrfs specific
> > failures, but the fscrypt side appears to be stable.  I had to add a few changes
> > to fscrypt since the last time, but nothing earth shattering, just moving the
> > keyring destruction and adding a helper we need for btrfs send to work properly.
> > 
> > This is passing a good chunk of the fstests, at this point the majority appear
> > to be cases where I need to exclude the test when using test_dummy_encryption
> > because of various limitations of our tools or other infrastructure related
> > things.
> > 
> > I likely will have a follow-up series with more fixes, but the bulk of this is
> > unchanged since the last posting.  There were some bug fixes and such but the
> > overall design remains the same.  Thanks,
> > 
> 
> Is there a plan for someone to keep working on this?  I think it was finally
> getting somewhere, but the work on it seems to have stopped.
> 

I fixed up all your review comments, but yes we don't care about this internally
anymore so it's been de-prioritized.  I have to rebase onto the new stuff,
re-run tests, fix any bugs that may have creeped in, but the current code
addressed all of your comments.  Once I get time to get back to this you'll have
a new version in your inbox, but that may be some time.  Thanks,

Josef

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

end of thread, other threads:[~2024-04-11 18:45 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-01 22:10 [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
2023-12-01 22:10 ` [PATCH v4 01/46] fs: move fscrypt keyring destruction to after ->put_super Josef Bacik
2023-12-05  1:58   ` Eric Biggers
2023-12-05 22:48     ` Josef Bacik
2023-12-06  0:01       ` Eric Biggers
2023-12-01 22:10 ` [PATCH v4 02/46] fscrypt: add per-extent encryption support Josef Bacik
2023-12-05  3:58   ` Eric Biggers
2023-12-05 22:48     ` Josef Bacik
2023-12-05 23:57       ` Eric Biggers
2023-12-13  4:16     ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 03/46] fscrypt: add a fscrypt_inode_open helper Josef Bacik
2023-12-05  4:14   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 04/46] fscrypt: conditionally don't wipe mk secret until the last active user is done Josef Bacik
2023-12-01 22:11 ` [PATCH v4 05/46] blk-crypto: add a process bio callback Josef Bacik
2023-12-05  4:54   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 06/46] fscrypt: expose fscrypt_nokey_name Josef Bacik
2023-12-05  5:03   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 07/46] fscrypt: add documentation about extent encryption Josef Bacik
2023-12-01 22:11 ` [PATCH v4 08/46] btrfs: add infrastructure for safe em freeing Josef Bacik
2023-12-01 22:11 ` [PATCH v4 09/46] btrfs: disable various operations on encrypted inodes Josef Bacik
2023-12-01 22:11 ` [PATCH v4 10/46] btrfs: disable verity " Josef Bacik
2023-12-05  5:07   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 11/46] btrfs: start using fscrypt hooks Josef Bacik
2023-12-01 22:11 ` [PATCH v4 12/46] btrfs: add inode encryption contexts Josef Bacik
2023-12-05  5:22   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 13/46] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Josef Bacik
2023-12-01 22:11 ` [PATCH v4 14/46] btrfs: adapt readdir for encrypted and nokey names Josef Bacik
2023-12-01 22:11 ` [PATCH v4 15/46] btrfs: handle " Josef Bacik
2023-12-05  5:29   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 16/46] btrfs: implement fscrypt ioctls Josef Bacik
2023-12-01 22:11 ` [PATCH v4 17/46] btrfs: add encryption to CONFIG_BTRFS_DEBUG Josef Bacik
2023-12-05  5:11   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 18/46] btrfs: add get_devices hook for fscrypt Josef Bacik
2023-12-01 22:11 ` [PATCH v4 19/46] btrfs: turn on inlinecrypt mount option for encrypt Josef Bacik
2023-12-05  5:41   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 20/46] btrfs: set file extent encryption excplicitly Josef Bacik
2023-12-01 22:11 ` [PATCH v4 21/46] btrfs: add fscrypt_info and encryption_type to extent_map Josef Bacik
2023-12-01 22:11 ` [PATCH v4 22/46] btrfs: add fscrypt_info and encryption_type to ordered_extent Josef Bacik
2023-12-01 22:11 ` [PATCH v4 23/46] btrfs: plumb through setting the fscrypt_info for ordered extents Josef Bacik
2023-12-01 22:11 ` [PATCH v4 24/46] btrfs: plumb the fscrypt extent context through create_io_em Josef Bacik
2023-12-01 22:11 ` [PATCH v4 25/46] btrfs: populate the ordered_extent with the fscrypt context Josef Bacik
2023-12-01 22:11 ` [PATCH v4 26/46] btrfs: keep track of fscrypt info and orig_start for dio reads Josef Bacik
2023-12-05  5:44   ` Eric Biggers
2023-12-01 22:11 ` [PATCH v4 27/46] btrfs: add an optional encryption context to the end of file extents Josef Bacik
2023-12-01 22:11 ` [PATCH v4 28/46] btrfs: explicitly track file extent length for replace and drop Josef Bacik
2023-12-01 22:11 ` [PATCH v4 29/46] btrfs: pass through fscrypt_extent_info to the file extent helpers Josef Bacik
2023-12-01 22:11 ` [PATCH v4 30/46] btrfs: pass the fscrypt_info through the replace extent infrastructure Josef Bacik
2023-12-01 22:11 ` [PATCH v4 31/46] btrfs: implement the fscrypt extent encryption hooks Josef Bacik
2023-12-01 22:11 ` [PATCH v4 32/46] btrfs: setup fscrypt_extent_info for new extents Josef Bacik
2023-12-01 22:11 ` [PATCH v4 33/46] btrfs: populate ordered_extent with the orig offset Josef Bacik
2023-12-01 22:11 ` [PATCH v4 34/46] btrfs: set the bio fscrypt context when applicable Josef Bacik
2023-12-01 22:11 ` [PATCH v4 35/46] btrfs: add a bio argument to btrfs_csum_one_bio Josef Bacik
2023-12-01 22:11 ` [PATCH v4 36/46] btrfs: add orig_logical to btrfs_bio Josef Bacik
2023-12-01 22:11 ` [PATCH v4 37/46] btrfs: limit encrypted writes to 256 segments Josef Bacik
2023-12-01 22:11 ` [PATCH v4 38/46] btrfs: implement process_bio cb for fscrypt Josef Bacik
2023-12-01 22:11 ` [PATCH v4 39/46] btrfs: add test_dummy_encryption support Josef Bacik
2023-12-01 22:11 ` [PATCH v4 40/46] btrfs: don't rewrite ret from inode_permission Josef Bacik
2023-12-01 22:11 ` [PATCH v4 41/46] btrfs: move inode_to_path higher in backref.c Josef Bacik
2023-12-01 22:11 ` [PATCH v4 42/46] btrfs: make btrfs_ref_to_path handle encrypted filenames Josef Bacik
2023-12-01 22:11 ` [PATCH v4 43/46] btrfs: don't search back for dir inode item in INO_LOOKUP_USER Josef Bacik
2023-12-01 22:11 ` [PATCH v4 44/46] btrfs: deal with encrypted symlinks in send Josef Bacik
2023-12-01 22:11 ` [PATCH v4 45/46] btrfs: decrypt file names for send Josef Bacik
2023-12-01 22:11 ` [PATCH v4 46/46] btrfs: load the inode context before sending writes Josef Bacik
2023-12-05  5:54   ` Eric Biggers
2023-12-01 22:15 ` [PATCH v4 00/46] btrfs: add fscrypt support Josef Bacik
2023-12-05  1:49 ` Eric Biggers
2023-12-05 14:16   ` David Sterba
2023-12-05 20:02     ` Eric Biggers
2024-04-09 23:42 ` Eric Biggers
2024-04-11 18:45   ` Josef Bacik

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.