linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 00/17] btrfs: add encryption feature
@ 2023-06-29  0:35 Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
                   ` (16 more replies)
  0 siblings, 17 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

This is a changeset adding encryption to btrfs. It is not complete; it
does not support inline data or verity or authenticated encryption. It
is primarily intended as a proof that the fscrypt extent encryption
changeset it builds on work. 

As per the design doc refined in the fall of last year [1], btrfs
encryption has several steps: first, adding extent encryption to fscrypt
and then btrfs; second, adding authenticated encryption support to the
block layer, fscrypt, and then btrfs; and later adding potentially the
ability to change the key used by a directory (either for all data or
just newly written data) and/or allowing use of inline extents and
verity items in combination with encryption and/or enabling send/receive
of encrypted volumes. As such, this change is only the first step and is
unsafe.

This change does not pass a couple of encryption xfstests, because of
different properties of extent encryption. It hasn't been tested with
direct IO or RAID. Because currently extent encryption always uses inline
encryption (i.e. IO-block-only) for data encryption, it does not support
encryption of inline extents; similarly, since btrfs stores verity items
in the tree instead of in inline encryptable blocks on disk as other
filesystems do, btrfs cannot currently encrypt verity items. Finally,
this is insecure; the checksums are calculated on the unencrypted data
and stored unencrypted, which is a potential information leak. (This
will be addressed by authenticated encryption).

This changeset is built on two prior changesets to fscrypt: [2] and [3]
and should have no effect on unencrypted usage.

[1] https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
[2]
https://lore.kernel.org/linux-fscrypt/cover.1687988119.git.sweettea-kernel@dorminy.me/
[3]
https://lore.kernel.org/linux-fscrypt/cover.1687988246.git.sweettea-kernel@dorminy.me

Omar Sandoval (7):
  btrfs: disable various operations on encrypted inodes
  fscrypt: expose fscrypt_nokey_name
  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 (10):
  btrfs: disable verity on encrypted inodes
  btrfs: use correct name hash for 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: turn on the encryption ioctls
  btrfs: create and free extent fscrypt_infos
  btrfs: start tracking extent encryption context info
  btrfs: explicitly track file extent length and encryption
  btrfs: save and load fscrypt extent contexts

 fs/btrfs/Kconfig                |   2 +-
 fs/btrfs/Makefile               |   1 +
 fs/btrfs/accessors.h            |  31 +++
 fs/btrfs/btrfs_inode.h          |   3 +-
 fs/btrfs/ctree.h                |   2 +
 fs/btrfs/delayed-inode.c        |  30 ++-
 fs/btrfs/delayed-inode.h        |   4 +-
 fs/btrfs/dir-item.c             |  81 ++++++--
 fs/btrfs/dir-item.h             |  13 +-
 fs/btrfs/extent_io.c            |  49 +++++
 fs/btrfs/extent_io.h            |   3 +
 fs/btrfs/extent_map.c           |   9 +
 fs/btrfs/extent_map.h           |   3 +
 fs/btrfs/file-item.c            |  29 +++
 fs/btrfs/file.c                 |  11 +-
 fs/btrfs/fs.h                   |   7 +-
 fs/btrfs/fscrypt.c              | 236 ++++++++++++++++++++++
 fs/btrfs/fscrypt.h              |  61 ++++++
 fs/btrfs/inode.c                | 333 +++++++++++++++++++++++++-------
 fs/btrfs/ioctl.c                |  42 +++-
 fs/btrfs/reflink.c              |   8 +
 fs/btrfs/root-tree.c            |   8 +-
 fs/btrfs/root-tree.h            |   2 +-
 fs/btrfs/super.c                |  17 ++
 fs/btrfs/tree-checker.c         |  37 +++-
 fs/btrfs/tree-log.c             |  28 ++-
 fs/btrfs/verity.c               |   3 +
 fs/crypto/fname.c               |  39 +---
 include/linux/fscrypt.h         |  37 ++++
 include/uapi/linux/btrfs.h      |   1 +
 include/uapi/linux/btrfs_tree.h |  20 ++
 31 files changed, 1004 insertions(+), 146 deletions(-)
 create mode 100644 fs/btrfs/fscrypt.c
 create mode 100644 fs/btrfs/fscrypt.h


base-commit: 212cb3d0b8f4abf657671f05dbe0b3d9d858211d
-- 
2.40.1


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

* [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-07-07 23:36   ` Boris Burkov
  2023-06-29  0:35 ` [PATCH v1 02/17] btrfs: disable verity " Sweet Tea Dorminy
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Omar Sandoval, Sweet Tea Dorminy

From: Omar Sandoval <osandov@osandov.com>

Initially, only normal data extents, using the normal (non-direct) IO
path, will be encrypted. This change forbids various other bits:
- allows reflinking only if both inodes have the same encryption status
- disables direct IO on encrypted inodes
- disable inline data on encrypted inodes

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/file.c    | 4 ++--
 fs/btrfs/inode.c   | 3 ++-
 fs/btrfs/reflink.c | 7 +++++++
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 392bc7d512a0..354962a7dd72 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1502,7 +1502,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
 		goto relock;
 	}
 
-	if (check_direct_IO(fs_info, from, pos)) {
+	if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) {
 		btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
 		goto buffered;
 	}
@@ -3741,7 +3741,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to)
 	ssize_t read = 0;
 	ssize_t ret;
 
-	if (fsverity_active(inode))
+	if (IS_ENCRYPTED(inode) || fsverity_active(inode))
 		return 0;
 
 	if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos))
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dbbb67293e34..48eadc4f187f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -630,7 +630,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 0474bbe39da7..ad722f495c9b 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"
@@ -811,6 +812,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.40.1


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

* [PATCH v1 02/17] btrfs: disable verity on encrypted inodes
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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>
---
 fs/btrfs/verity.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index c5ff16f9e9fa..cda969c6cb0c 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.40.1


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

* [PATCH v1 03/17] fscrypt: expose fscrypt_nokey_name
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 02/17] btrfs: disable verity " Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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>
Reviewed-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 edb78cd1b0e7..fb6bc46302c1 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 9c7255b48f1d..74bfb91a0f6b 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 <crypto/sha2.h>
 #include <uapi/linux/fscrypt.h>
 
 /*
@@ -54,6 +55,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.40.1


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

* [PATCH v1 04/17] btrfs: start using fscrypt hooks
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (2 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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. Also add the key prefix for fscrypt v1
keys.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/Makefile      |  1 +
 fs/btrfs/btrfs_inode.h |  1 +
 fs/btrfs/file.c        |  3 ++
 fs/btrfs/fscrypt.c     |  8 ++++
 fs/btrfs/fscrypt.h     | 10 +++++
 fs/btrfs/inode.c       | 87 +++++++++++++++++++++++++++++++++++-------
 fs/btrfs/super.c       |  2 +
 7 files changed, 99 insertions(+), 13 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 90d53209755b..90d30b1e044a 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -40,6 +40,7 @@ btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.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 d47a927b3504..ec4a06a78aff 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -448,6 +448,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 354962a7dd72..0de95c8375a1 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3702,6 +3702,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..3a53dc59c1e4
--- /dev/null
+++ b/fs/btrfs/fscrypt.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "ctree.h"
+#include "fscrypt.h"
+
+const struct fscrypt_operations btrfs_fscrypt_ops = {
+	.key_prefix = "btrfs:"
+};
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 48eadc4f187f..5ce167cfa1dc 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5337,6 +5337,7 @@ void btrfs_evict_inode(struct inode *inode)
 	trace_btrfs_inode_evict(inode);
 
 	if (!root) {
+		fscrypt_put_encryption_info(inode);
 		fsverity_cleanup_inode(inode);
 		clear_inode(inode);
 		return;
@@ -5440,6 +5441,7 @@ void btrfs_evict_inode(struct inode *inode)
 	 * to retry these periodically in the future.
 	 */
 	btrfs_remove_delayed_node(BTRFS_I(inode));
+	fscrypt_put_encryption_info(inode);
 	fsverity_cleanup_inode(inode);
 	clear_inode(inode);
 }
@@ -6190,6 +6192,10 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
 		return ret;
 	}
 
+	ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt);
+	if (ret)
+		return ret;
+
 	/* 1 to add inode item */
 	*trans_num_items = 1;
 	/* 1 to add compression property */
@@ -6666,9 +6672,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)
@@ -8612,6 +8622,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));
 }
 
@@ -8682,8 +8693,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)
@@ -9274,6 +9284,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);
@@ -9493,15 +9508,22 @@ 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;
+	u32 name_len = strlen(symname);
 
-	name_len = strlen(symname);
-	if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(fs_info))
-		return -ENAMETOOLONG;
+	/*
+	 * 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,
+				      BTRFS_MAX_INLINE_DATA_SIZE(fs_info) + 1,
+				      &disk_link);
+	if (err)
+		return err;
 
 	inode = new_inode(dir->i_sb);
 	if (!inode)
@@ -9510,8 +9532,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);
@@ -9538,10 +9560,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) {
@@ -9560,10 +9595,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(leaf);
 	btrfs_free_path(path);
 
@@ -9580,6 +9615,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,
@@ -11058,7 +11116,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,
@@ -11068,4 +11126,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 8b1c1225245e..d0df46370c5d 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -48,6 +48,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"
@@ -1140,6 +1141,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;
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
 	sb->s_flags |= SB_POSIXACL;
-- 
2.40.1


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

* [PATCH v1 05/17] btrfs: add inode encryption contexts
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (3 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-07-07 23:32   ` Boris Burkov
  2023-06-29  0:35 ` [PATCH v1 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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>
---
 fs/btrfs/fscrypt.c              | 116 ++++++++++++++++++++++++++++++++
 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, 153 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 3a53dc59c1e4..235f65e43d96 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -1,8 +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_CTXT_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 = -EINVAL;
+		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 void btrfs_fscrypt_update_context(struct btrfs_path *path,
+					 const void *ctx, size_t len)
+{
+	struct extent_buffer *leaf = path->nodes[0];
+	unsigned long 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(leaf);
+}
+
+static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
+				     size_t len, void *fs_data)
+{
+	struct btrfs_path *path;
+	int ret;
+	struct btrfs_trans_handle *trans = fs_data;
+	struct btrfs_key key = {
+		.objectid = btrfs_ino(BTRFS_I(inode)),
+		.type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
+		.offset = 0,
+	};
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	if (!trans)
+		trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
+	if (IS_ERR(trans))
+		return PTR_ERR(trans);
+
+	ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
+	if (ret == 0) {
+		btrfs_fscrypt_update_context(path, ctx, len);
+		btrfs_free_path(path);
+		return ret;
+	}
+
+	btrfs_free_path(path);
+	if (ret < 0) {
+		btrfs_abort_transaction(trans, ret);
+		return ret;
+	}
+
+	ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
+	if (ret) {
+		btrfs_abort_transaction(trans, ret);
+		return ret;
+	}
+
+	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+	btrfs_sync_inode_flags_to_i_flags(inode);
+	inode_inc_iversion(inode);
+	inode->i_ctime = current_time(inode);
+	ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode));
+	if (!ret) {
+		if (!fs_data)
+			btrfs_end_transaction(trans);
+		return ret;
+	}
+
+	btrfs_abort_transaction(trans, ret);
+	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,
 	.key_prefix = "btrfs:"
 };
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 5ce167cfa1dc..9dc47f769641 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"
@@ -6179,6 +6180,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);
@@ -6212,6 +6216,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, but only for encrypted non-regular files */
+	if (args->encrypt && !S_ISREG(inode->i_mode))
+		(*trans_num_items)++;
 	if (args->orphan) {
 		/* 1 to add orphan item */
 		(*trans_num_items)++;
@@ -6390,6 +6397,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
 	inode->i_ctime = inode->i_mtime;
 	BTRFS_I(inode)->i_otime = inode->i_mtime;
 
+	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.
@@ -6464,6 +6476,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
 			goto discard;
 		}
 	}
+	if (args->encrypt && !S_ISREG(inode->i_mode)) {
+		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 edbbd5cf23fc..11564e48d736 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 ab38d0f411fa..ea88dd69957f 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -161,6 +161,8 @@
 #define BTRFS_VERITY_DESC_ITEM_KEY	36
 #define BTRFS_VERITY_MERKLE_ITEM_KEY	37
 
+#define BTRFS_FSCRYPT_CTXT_ITEM_KEY	41
+
 #define BTRFS_ORPHAN_ITEM_KEY		48
 /* reserve 2-15 close to the inode for later flexibility */
 
@@ -399,6 +401,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)
 
@@ -415,6 +418,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)
@@ -1016,6 +1020,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.40.1


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

* [PATCH v1 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (4 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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>
---
 fs/btrfs/fs.h              | 7 ++++---
 fs/btrfs/super.c           | 5 +++++
 include/uapi/linux/btrfs.h | 1 +
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 203d2a267828..578874fbc98f 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -225,9 +225,10 @@ enum {
 	 * Features under developmen like Extent tree v2 support is enabled
 	 * only under CONFIG_BTRFS_DEBUG.
 	 */
-#define BTRFS_FEATURE_INCOMPAT_SUPP		\
-	(BTRFS_FEATURE_INCOMPAT_SUPP_STABLE |	\
-	 BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2)
+#define BTRFS_FEATURE_INCOMPAT_SUPP		 \
+	(BTRFS_FEATURE_INCOMPAT_SUPP_STABLE |	 \
+	 BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \
+	 BTRFS_FEATURE_INCOMPAT_ENCRYPT)
 
 #else
 
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index d0df46370c5d..ad18127cee10 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2422,6 +2422,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/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index dbb8b96da50d..98d1fa51a351 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -333,6 +333,7 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_RAID1C34		(1ULL << 11)
 #define BTRFS_FEATURE_INCOMPAT_ZONED		(1ULL << 12)
 #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2	(1ULL << 13)
+#define BTRFS_FEATURE_INCOMPAT_ENCRYPT		(1ULL << 14)
 
 struct btrfs_ioctl_feature_flags {
 	__u64 compat_flags;
-- 
2.40.1


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

* [PATCH v1 07/17] btrfs: adapt readdir for encrypted and nokey names
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (5 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 08/17] btrfs: use correct name hash for " Sweet Tea Dorminy
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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>
---
 fs/btrfs/btrfs_inode.h   |   2 +-
 fs/btrfs/delayed-inode.c |  30 +++++++-
 fs/btrfs/delayed-inode.h |   4 +-
 fs/btrfs/dir-item.c      |  77 ++++++++++++++++++---
 fs/btrfs/dir-item.h      |  13 +++-
 fs/btrfs/extent_io.c     |  38 +++++++++++
 fs/btrfs/extent_io.h     |   3 +
 fs/btrfs/fscrypt.c       |  46 +++++++++++++
 fs/btrfs/fscrypt.h       |  19 ++++++
 fs/btrfs/inode.c         | 143 ++++++++++++++++++++++++++-------------
 fs/btrfs/root-tree.c     |   8 ++-
 fs/btrfs/root-tree.h     |   2 +-
 fs/btrfs/tree-log.c      |   3 +-
 13 files changed, 320 insertions(+), 68 deletions(-)

diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index ec4a06a78aff..464059674ae5 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -421,7 +421,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 6b457b010cbc..919303d29b76 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1497,6 +1497,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
 
 	ret = __btrfs_add_delayed_item(delayed_node, delayed_item);
 	if (unlikely(ret)) {
+		// TODO: It would be nice to print the base64encoded name here maybe?
 		btrfs_err(trans->fs_info,
 			  "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)",
 			  name_len, name, delayed_node->root->root_key.objectid,
@@ -1724,7 +1725,9 @@ int btrfs_should_delete_dir_index(struct list_head *del_list,
  * btrfs_readdir_delayed_dir_index - 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;
@@ -1734,6 +1737,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;
 
 	if (list_empty(ins_list))
 		return 0;
@@ -1761,8 +1765,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 4f21daa3dbc7..a4f9fa27b126 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -155,7 +155,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 082eb0e19598..da95ae411d72 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 aab4b7cc7fa0..4f42a4f6a4ec 100644
--- a/fs/btrfs/dir-item.h
+++ b/fs/btrfs/dir-item.h
@@ -3,6 +3,8 @@
 #ifndef BTRFS_DIR_ITEM_H
 #define BTRFS_DIR_ITEM_H
 
+#include <linux/fscrypt.h>
+
 int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
 			  const struct fscrypt_str *name);
 int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
@@ -12,6 +14,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,
@@ -19,7 +26,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,
@@ -39,4 +46,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
 						 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);
+
 #endif
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index a91d5ad27984..10a06719811c 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -4180,6 +4180,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);
+}
+
 void write_extent_buffer_chunk_tree_uuid(const struct extent_buffer *eb,
 		const void *srcv)
 {
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index c5fae3a7d911..9dc8c6f3af0f 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -230,6 +230,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 235f65e43d96..20727d920841 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -5,13 +5,59 @@
 #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. Also, it edits the provided argument to populate the disk_name
+ * if we successfully match and previously were using a nokey name.
+ */
+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 void *)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);
+	if (!memcmp(digest, nokey_name->sha256, sizeof(digest))) {
+		/*
+		 * 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.
+		 */
+		fname->disk_name.name = kmalloc(de_name_len, GFP_KERNEL | GFP_NOFS);
+		if (!fname->disk_name.name)
+			fname->disk_name.name = ERR_PTR(-ENOMEM);
+		else
+			read_extent_buffer(leaf, fname->disk_name.name,
+					   de_name, de_name_len);
+		fname->disk_name.len = de_name_len;
+		return true;
+	}
+	return false;
+}
+
 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 9dc47f769641..7fa74693e154 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4182,7 +4182,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;
@@ -4200,7 +4200,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;
@@ -4228,11 +4228,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;
 	}
@@ -4253,8 +4256,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);
 	}
 
 	/*
@@ -4272,7 +4277,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->vfs_inode.i_ctime = current_time(&inode->vfs_inode);
@@ -4285,7 +4290,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;
 
@@ -4336,7 +4341,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;
 
@@ -4373,8 +4378,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) {
@@ -4391,8 +4394,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;
@@ -4418,7 +4421,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;
@@ -4435,7 +4438,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;
@@ -4759,7 +4762,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);
 		/*
@@ -5454,32 +5457,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;
@@ -5491,13 +5485,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;
 }
@@ -5769,13 +5763,18 @@ 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);
+	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)
 		return ERR_PTR(ret);
 
@@ -5916,18 +5915,32 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
 	struct 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;
@@ -5945,6 +5958,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;
@@ -5956,8 +5970,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)
@@ -5971,8 +5990,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);
@@ -5992,7 +6039,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;
 
@@ -6023,6 +6071,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;
 }
 
@@ -6531,6 +6581,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));
@@ -6588,7 +6639,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) {
@@ -8958,7 +9009,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, root, BTRFS_I(old_inode));
 	}
@@ -8973,7 +9024,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, dest, BTRFS_I(new_inode));
 	}
@@ -9222,7 +9273,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, root, BTRFS_I(old_inode));
 	}
@@ -9241,7 +9292,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 859874579456..5fa416ef54ad 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"
@@ -333,7 +334,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;
@@ -355,13 +356,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 cbbaca32126e..a57bbf7b0180 100644
--- a/fs/btrfs/root-tree.h
+++ b/fs/btrfs/root-tree.h
@@ -13,7 +13,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 365a1cc0a3c3..f32f961adc7b 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.40.1


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

* [PATCH v1 08/17] btrfs: use correct name hash for nokey names
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (6 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/dir-item.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index da95ae411d72..87993b19cd9d 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -257,8 +257,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)
-- 
2.40.1


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

* [PATCH v1 09/17] btrfs: implement fscrypt ioctls
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (7 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 08/17] btrfs: use correct name hash for " Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  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>
---
 fs/btrfs/ioctl.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 11564e48d736..71d1610444b4 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4562,6 +4562,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.40.1


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

* [PATCH v1 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (8 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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>
---
 fs/btrfs/Kconfig | 2 +-
 fs/btrfs/ioctl.c | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index 66fa9ab2c046..c7861e579d72 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -80,7 +80,7 @@ config BTRFS_FS_RUN_SANITY_TESTS
 
 config BTRFS_DEBUG
 	bool "Btrfs debugging support"
-	depends on BTRFS_FS
+	depends on BTRFS_FS && FS_ENCRYPTION_INLINE_CRYPT && BLK_INLINE_ENCRYPTION_FALLBACK
 	help
 	  Enable run-time debugging support for the btrfs filesystem. This may
 	  enable additional and expensive checks with negative impact on
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 71d1610444b4..7c4360b59aae 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4562,6 +4562,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;
@@ -4590,6 +4591,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.40.1


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

* [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (9 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29 13:20   ` Luís Henriques
  2023-06-29  0:35 ` [PATCH v1 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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.

I'm not sure this is the correct list of active devices and it isn't
tested with any multi-device test yet.

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

diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 20727d920841..658a67856de6 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"
 
 /*
@@ -162,9 +164,37 @@ 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;
+	struct block_device **devs;
+	struct btrfs_device *device;
+	int i;
+
+	devs = kmalloc_array(fs_devices->num_devices, sizeof(*devs),
+		GFP_KERNEL);
+	if (!devs)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&fs_devices->device_list_mutex);
+	i = 0;
+	list_for_each_entry(device, &fs_devices->devices, dev_list) {
+		if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state))
+			continue;
+
+		devs[i++] = device->bdev;
+	}
+	mutex_unlock(&fs_devices->device_list_mutex);
+	*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,
 	.key_prefix = "btrfs:"
 };
-- 
2.40.1


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

* [PATCH v1 12/17] btrfs: turn on inlinecrypt mount option for encrypt
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (10 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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.
---
 fs/btrfs/ioctl.c |  4 ++++
 fs/btrfs/super.c | 10 ++++++++++
 2 files changed, 14 insertions(+)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 7c4360b59aae..384bad37fa9d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4574,6 +4574,10 @@ 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;
+			mb();
+		}
 		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 ad18127cee10..d1c192dcd800 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1161,6 +1161,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.40.1


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

* [PATCH v1 13/17] btrfs: turn on the encryption ioctls
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (11 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

This allows the use of encryption with btrfs. Since the extent
encryption interfaces are not currently defined, this is using the
normal inode encryption, and that is not the long-term plan. But it
allows verifying by test that the steps for inode encryption are correct
and pass fstests.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/extent_io.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 10a06719811c..3abf2b64e92e 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -787,6 +787,14 @@ static bool btrfs_bio_is_contig(struct btrfs_bio_ctrl *bio_ctrl,
 	struct bio_vec *bvec = bio_last_bvec_all(bio);
 	const sector_t sector = disk_bytenr >> SECTOR_SHIFT;
 
+	if (IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
+		struct inode *inode = page->mapping->host;
+		u64 lblk = pg_offset / inode->i_sb->s_blocksize;
+
+		if (!fscrypt_mergeable_bio(bio, inode, lblk))
+			return false;
+	}
+
 	if (bio_ctrl->compress_type != BTRFS_COMPRESS_NONE) {
 		/*
 		 * For compression, all IO should have its logical bytenr set
@@ -825,6 +833,9 @@ static void alloc_new_bio(struct btrfs_inode *inode,
 	bbio->file_offset = file_offset;
 	bio_ctrl->bbio = bbio;
 	bio_ctrl->len_to_oe_boundary = U32_MAX;
+	fscrypt_set_bio_crypt_ctx(&bbio->bio, &inode->vfs_inode,
+				  file_offset >> fs_info->sectorsize_bits,
+				  GFP_NOIO);
 
 	/* Limit data write bios to the ordered boundary. */
 	if (bio_ctrl->wbc) {
-- 
2.40.1


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

* [PATCH v1 14/17] btrfs: create and free extent fscrypt_infos
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (12 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

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. Add creation of fscrypt_infos for new extent_maps, and
freeing as appropriate.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/extent_map.c | 9 +++++++++
 fs/btrfs/extent_map.h | 3 +++
 fs/btrfs/inode.c      | 8 ++++++++
 3 files changed, 20 insertions(+)

diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 0cdb3e86f29b..876ecd317c8a 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -65,6 +65,8 @@ void free_extent_map(struct extent_map *em)
 	if (!em)
 		return;
 	if (refcount_dec_and_test(&em->refs)) {
+		if (em->fscrypt_info)
+			fscrypt_free_extent_info(&em->fscrypt_info);
 		WARN_ON(extent_map_in_tree(em));
 		WARN_ON(!list_empty(&em->list));
 		if (test_bit(EXTENT_FLAG_FS_MAPPING, &em->flags))
@@ -207,6 +209,13 @@ 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 maps with different fscrypt_contexts.
+	 */
+	if (!memcmp(&prev->fscrypt_info, &next->fscrypt_info,
+		    sizeof(next->fscrypt_info)))
+		return 0;
+
 	ASSERT(next->block_start != EXTENT_MAP_DELALLOC &&
 	       prev->block_start != EXTENT_MAP_DELALLOC);
 
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 35d27c756e08..3a1b66b1cedf 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -27,6 +27,8 @@ enum {
 	EXTENT_FLAG_FS_MAPPING,
 	/* This em is merged from two or more physically adjacent ems */
 	EXTENT_FLAG_MERGED,
+	/* This em has a fscrypt info */
+	EXTENT_FLAG_ENCRYPTED,
 };
 
 struct extent_map {
@@ -50,6 +52,7 @@ struct extent_map {
 	 */
 	u64 generation;
 	unsigned long flags;
+	struct fscrypt_info *fscrypt_info;
 	/* Used for chunk mappings, flag EXTENT_FLAG_FS_MAPPING must be set */
 	struct map_lookup *map_lookup;
 	refcount_t refs;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7fa74693e154..72da8f5e32af 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7436,6 +7436,14 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
 		em->compress_type = compress_type;
 	}
 
+	ret = fscrypt_prepare_new_extent(&inode->vfs_inode, &em->fscrypt_info);
+	if (ret < 0) {
+		free_extent_map(em);
+		return ERR_PTR(ret);
+	}
+	if (em->fscrypt_info)
+		set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+
 	ret = btrfs_replace_extent_map_range(inode, em, true);
 	if (ret) {
 		free_extent_map(em);
-- 
2.40.1


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

* [PATCH v1 15/17] btrfs: start tracking extent encryption context info
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (13 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

This puts the long-preserved 1-byte encryption field to work, storing
whether the extent is encrypted and the length of its fscrypt_context.
Since there is no way for a btrfs inode to be encrypted right now, we
set context length to be 0 in all locations. We then update all the
locations which check the size of an extent item to expect the size to
agree with the context length.

The 1-byte field packs the encryption type and context length together,
to avoid a format change. Right now we don't anticipate very many
encryption policies, so 2 bits should be ample; similarly right now we
can't imagine a context larger than fscrypt's current contexts, which
are 40 bytes, so 6 bits is ample to store the context's size; and
therefore we can pack these together into the one-byte encryption field
without touching other space reserved for future use.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/accessors.h    | 31 +++++++++++++++++++++++++++++++
 fs/btrfs/file-item.c    |  8 ++++++++
 fs/btrfs/fscrypt.h      | 24 ++++++++++++++++++++++++
 fs/btrfs/inode.c        | 26 ++++++++++++++++++++++----
 fs/btrfs/tree-checker.c | 37 +++++++++++++++++++++++++++++--------
 fs/btrfs/tree-log.c     |  3 +++
 6 files changed, 117 insertions(+), 12 deletions(-)

diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index ceadfc5d6c66..fbee45b9d9c2 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -3,6 +3,8 @@
 #ifndef BTRFS_ACCESSORS_H
 #define BTRFS_ACCESSORS_H
 
+#include "fscrypt.h"
+
 struct btrfs_map_token {
 	struct extent_buffer *eb;
 	char *kaddr;
@@ -936,6 +938,16 @@ 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);
+static inline u8 btrfs_stack_file_extent_encryption_ctxsize(
+	struct btrfs_file_extent_item *e)
+{
+	u8 ctxsize;
+
+	btrfs_unpack_encryption(e->encryption, NULL, &ctxsize);
+	return ctxsize;
+}
 
 
 BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
@@ -958,6 +970,25 @@ 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 u8
+btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
+				     struct btrfs_file_extent_item *e)
+{
+	u8 ctxsize;
+
+	btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e),
+				NULL, &ctxsize);
+	return ctxsize;
+}
+
+static inline u8
+btrfs_file_extent_ctxsize_from_item(const struct extent_buffer *leaf,
+				    const struct btrfs_path *path)
+{
+	return (btrfs_item_size(leaf, path->slots[0]) -
+		sizeof(struct btrfs_file_extent_item));
+}
+
 /* btrfs_qgroup_status_item */
 BTRFS_SETGET_FUNCS(qgroup_status_generation, struct btrfs_qgroup_status_item,
 		   generation, 64);
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 696bf695d8eb..8095fc2e7ca1 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1272,6 +1272,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 	em->generation = btrfs_file_extent_generation(leaf, fi);
 	if (type == BTRFS_FILE_EXTENT_REG ||
 	    type == BTRFS_FILE_EXTENT_PREALLOC) {
+		u8 ctxsize;
 		em->start = extent_start;
 		em->len = extent_end - extent_start;
 		em->orig_start = extent_start -
@@ -1287,6 +1288,10 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 			em->compress_type = compress_type;
 			em->block_start = bytenr;
 			em->block_len = em->orig_block_len;
+		} else if (btrfs_file_extent_encryption(leaf, fi)) {
+			set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+			em->block_start = bytenr;
+			em->block_len = em->orig_block_len;
 		} else {
 			bytenr += btrfs_file_extent_offset(leaf, fi);
 			em->block_start = bytenr;
@@ -1294,6 +1299,9 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 			if (type == BTRFS_FILE_EXTENT_PREALLOC)
 				set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
 		}
+
+		ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
+		ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi));
 	} else if (type == BTRFS_FILE_EXTENT_INLINE) {
 		em->block_start = EXTENT_MAP_INLINE;
 		em->start = extent_start;
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 1647bbbcd609..2d405d54cbc7 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -8,6 +8,30 @@
 
 #include "fs.h"
 
+#define BTRFS_ENCRYPTION_POLICY_BITS 2
+#define BTRFS_ENCRYPTION_CTXSIZE_BITS 6
+
+#define BTRFS_ENCRYPTION_POLICY_MASK ((1 << BTRFS_ENCRYPTION_POLICY_BITS) - 1)
+#define BTRFS_ENCRYPTION_CTXSIZE_MASK \
+	(((1 << BTRFS_ENCRYPTION_CTXSIZE_BITS) - 1) << \
+		BTRFS_ENCRYPTION_POLICY_BITS)
+
+static inline void btrfs_unpack_encryption(u8 encryption,
+					   u8 *policy,
+					   u8 *ctxsize)
+{
+	if (policy)
+		*policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK;
+	if (ctxsize)
+		*ctxsize = ((encryption & BTRFS_ENCRYPTION_CTXSIZE_MASK) >>
+			    BTRFS_ENCRYPTION_POLICY_BITS);
+}
+
+static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize)
+{
+	return policy | (ctxsize << BTRFS_ENCRYPTION_POLICY_BITS);
+}
+
 #ifdef CONFIG_FS_ENCRYPTION
 bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 			      struct extent_buffer *leaf,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 72da8f5e32af..931f948b42f7 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3151,7 +3151,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);
-	/* Encryption and other encoding is reserved and all 0 */
+	/* Other encoding is reserved and always 0 */
 
 	/*
 	 * For delalloc, when completing an ordered extent we update the inode's
@@ -7046,8 +7046,24 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 
 	btrfs_extent_item_to_extent_map(inode, path, item, em);
 
-	if (extent_type == BTRFS_FILE_EXTENT_REG ||
-	    extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
+	if (extent_type == BTRFS_FILE_EXTENT_REG) {
+		u8 item_ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
+		u8 encryption = btrfs_file_extent_encryption(leaf, item);
+		u8 policy, ctxsize;
+
+		btrfs_unpack_encryption(encryption, &policy, &ctxsize);
+
+		if (policy == BTRFS_ENCRYPTION_FSCRYPT) {
+			if (ctxsize != item_ctxsize) {
+				btrfs_crit(fs_info,
+					"invalid encryption context size for inode %llu: itemsize %d item %d",
+					btrfs_ino(inode), ctxsize, item_ctxsize);
+				ret = -EUCLEAN;
+				goto out;
+			}
+		}
+		goto insert;
+	} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
 		goto insert;
 	} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
 		/*
@@ -9739,7 +9755,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 351ba9e90675..c1244fd60d3f 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -208,6 +208,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,
@@ -259,10 +260,12 @@ 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))) {
+	btrfs_unpack_encryption(btrfs_file_extent_encryption(leaf, fi),
+				&policy, NULL);
+	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) {
@@ -291,12 +294,30 @@ 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) {
+		u8 ctxsize = btrfs_file_extent_encryption_ctxsize(leaf, fi);
+
+		if (unlikely(item_size != sizeof(*fi) + ctxsize)) {
+			file_extent_err(leaf, slot,
+	"invalid item size for encrypted file extent, have %u expect = %zu + context of size %u",
+					item_size, sizeof(*fi), ctxsize);
+			return -EUCLEAN;
+		}
+		/* Only regular extents should be encrypted. */
+		if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) {
+			file_extent_err(leaf, slot,
+		"invalid type for encrypted file extent, have %u expect %u",
+					btrfs_file_extent_type(leaf, fi),
+					BTRFS_FILE_EXTENT_REG);
+			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/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index f32f961adc7b..5a636a6b0d82 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4634,6 +4634,8 @@ 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_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ?
+					      BTRFS_ENCRYPTION_FSCRYPT : 0, 0);
 
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
 	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4655,6 +4657,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)
-- 
2.40.1


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

* [PATCH v1 16/17] btrfs: explicitly track file extent length and encryption
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (14 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  2023-06-29  0:35 ` [PATCH v1 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

With the advent of storing fscrypt contexts with each encrypted extent,
extents will have a variable length depending on encryption status.
Add accessors for the encryption field, and update all the checks for
file extents.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/ctree.h                | 2 ++
 fs/btrfs/file.c                 | 4 ++--
 fs/btrfs/inode.c                | 9 +++++++--
 fs/btrfs/reflink.c              | 1 +
 fs/btrfs/tree-log.c             | 2 +-
 include/uapi/linux/btrfs_tree.h | 5 +++++
 6 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index f2d2b313bde5..b1afcfc62f75 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -364,6 +364,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 0de95c8375a1..703033816876 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2250,14 +2250,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 931f948b42f7..2f709874124d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3036,6 +3036,9 @@ 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 =
+		btrfs_stack_file_extent_encryption(stack_fi) ?
+			FSCRYPT_SET_CONTEXT_MAX_SIZE : 0;
 	int ret;
 
 	path = btrfs_alloc_path();
@@ -3055,7 +3058,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;
@@ -3066,7 +3069,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;
 	}
@@ -9746,6 +9749,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));
 
@@ -9778,6 +9782,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 ad722f495c9b..9f3b5748f39b 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -502,6 +502,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 5a636a6b0d82..eaf014ab7cac 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4689,7 +4689,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;
 	}
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index ea88dd69957f..e780eee85e6e 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1048,6 +1048,11 @@ struct btrfs_file_extent_item {
 	 * but not for stat.
 	 */
 	__u8 compression;
+
+	/*
+	 * 2 bits of encryption type in the lower bits, 6 bits of context size
+	 * in the upper bits. Unencrypted value is 0.
+	 */
 	__u8 encryption;
 	__le16 other_encoding; /* spare for later use */
 
-- 
2.40.1


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

* [PATCH v1 17/17] btrfs: save and load fscrypt extent contexts
  2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
                   ` (15 preceding siblings ...)
  2023-06-29  0:35 ` [PATCH v1 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
@ 2023-06-29  0:35 ` Sweet Tea Dorminy
  16 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-06-29  0:35 UTC (permalink / raw)
  To: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt
  Cc: Sweet Tea Dorminy

This change actually saves and loads the extent contexts created and
freed by the last change.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
 fs/btrfs/file-item.c            | 21 ++++++++++++++++
 fs/btrfs/fscrypt.c              | 36 +++++++++++++++++++++++++++
 fs/btrfs/fscrypt.h              |  6 +++++
 fs/btrfs/inode.c                | 44 ++++++++++++++++++++++++++++++---
 fs/btrfs/tree-log.c             | 24 ++++++++++++++++--
 include/uapi/linux/btrfs_tree.h |  5 ++++
 6 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 8095fc2e7ca1..ccc2d12faba3 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1302,6 +1302,27 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 
 		ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
 		ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi));
+
+#ifdef CONFIG_FS_ENCRYPTION
+		if (ctxsize) {
+			u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE];
+			int res;
+			unsigned int nofs_flag;
+
+			read_extent_buffer(leaf, context,
+					   (unsigned long)fi->fscrypt_context,
+					   ctxsize);
+			nofs_flag = memalloc_nofs_save();
+			res = fscrypt_load_extent_info(&inode->vfs_inode,
+						       context, ctxsize,
+						       &em->fscrypt_info);
+			memalloc_nofs_restore(nofs_flag);
+			if (res)
+				btrfs_err(fs_info,
+					  "Unable to load fscrypt info: %d",
+					   res);
+		}
+#endif /* CONFIG_FS_ENCRYPTION */
 	} else if (type == BTRFS_FILE_EXTENT_INLINE) {
 		em->block_start = EXTENT_MAP_INLINE;
 		em->start = extent_start;
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 658a67856de6..e98b0c31f841 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -164,6 +164,41 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
 	return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
 }
 
+int btrfs_fscrypt_get_extent_info(const struct inode *inode,
+				  u64 lblk_num,
+				  struct fscrypt_info **info_ptr,
+				  u64 *extent_offset,
+				  u64 *extent_length)
+{
+	u64 offset = lblk_num << inode->i_blkbits;
+	struct extent_map *em;
+
+	/* Since IO must be in progress on this extent, this must succeed */
+	em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, PAGE_SIZE);
+	if (!em)
+		return -EINVAL;
+
+	if (em->block_start == EXTENT_MAP_HOLE) {
+		btrfs_info(BTRFS_I(inode)->root->fs_info,
+			   "extent context requested for block %llu of inode %lu without an extent",
+			   lblk_num, inode->i_ino);
+		free_extent_map(em);
+		return -ENOENT;
+	}
+
+	*info_ptr = em->fscrypt_info;
+
+	if (extent_offset)
+		*extent_offset
+			 = (offset - em->start) >> inode->i_blkbits;
+
+	if (extent_length)
+		*extent_length = em->len >> inode->i_blkbits;
+
+	free_extent_map(em);
+	return 0;
+}
+
 static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
 						       unsigned int *num_devs)
 {
@@ -195,6 +230,7 @@ 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_extent_info = btrfs_fscrypt_get_extent_info,
 	.get_devices = btrfs_fscrypt_get_devices,
 	.key_prefix = "btrfs:"
 };
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 2d405d54cbc7..1cab721a64e5 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -50,6 +50,12 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
 }
 #endif /* CONFIG_FS_ENCRYPTION */
 
+int btrfs_fscrypt_get_extent_info(const struct inode *inode,
+				  u64 lblk_num,
+				  struct fscrypt_info **info_ptr,
+				  u64 *extent_offset,
+				  u64 *extent_length);
+
 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 2f709874124d..b12b60120b13 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3036,17 +3036,46 @@ 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 =
-		btrfs_stack_file_extent_encryption(stack_fi) ?
-			FSCRYPT_SET_CONTEXT_MAX_SIZE : 0;
+	size_t fscrypt_context_size = 0;
+#ifdef CONFIG_FS_ENCRYPTION
+	u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE];
+#endif /* CONFIG_FS_ENCRYPTION */
+
 	int ret;
 
 	path = btrfs_alloc_path();
 	if (!path)
 		return -ENOMEM;
 
+#ifdef CONFIG_FS_ENCRYPTION
+	if (IS_ENCRYPTED(&inode->vfs_inode)) {
+		u8 encryption;
+		struct fscrypt_info *fscrypt_info;
+		u64 lblk_num = file_pos >> root->fs_info->sectorsize_bits;
+
+		ret = btrfs_fscrypt_get_extent_info(&inode->vfs_inode,
+						    lblk_num, &fscrypt_info,
+						    NULL, NULL);
+		if (ret) {
+			btrfs_err(root->fs_info, "No fscrypt context found");
+			goto out;
+		}
+
+		fscrypt_context_size =
+			fscrypt_set_extent_context(fscrypt_info, context,
+						   FSCRYPT_SET_CONTEXT_MAX_SIZE);
+		if (fscrypt_context_size < 0) {
+			ret = fscrypt_context_size;
+			goto out;
+		}
+		encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT,
+						   fscrypt_context_size);
+		btrfs_set_stack_file_extent_encryption(stack_fi, encryption);
+	}
+#endif /* CONFIG_FS_ENCRYPTION */
+
 	/*
-	 * we may be replacing one extent in the tree with another.
+	 * We may be replacing one extent in the tree with another.
 	 * The new extent is pinned in the extent map, and we don't want
 	 * to drop it from the cache until it is completely in the btree.
 	 *
@@ -3079,6 +3108,13 @@ 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));
 
+#ifdef CONFIG_FS_ENCRYPTION
+	write_extent_buffer(leaf, context,
+			    btrfs_item_ptr_offset(leaf, path->slots[0]) +
+			    sizeof(struct btrfs_file_extent_item),
+			    fscrypt_context_size);
+#endif /* CONFIG_FS_ENCRYPTION */
+
 	btrfs_mark_buffer_dirty(leaf);
 	btrfs_release_path(path);
 
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index eaf014ab7cac..25966ae95eea 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4634,8 +4634,22 @@ 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_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ?
-					      BTRFS_ENCRYPTION_FSCRYPT : 0, 0);
+	u8 encryption = 0;
+	size_t fscrypt_context_size = 0;
+#ifdef CONFIG_FS_ENCRYPTION
+	u8 context[FSCRYPT_SET_CONTEXT_MAX_SIZE];
+
+	if (em->fscrypt_info) {
+		fscrypt_context_size =
+			fscrypt_set_extent_context(em->fscrypt_info, context,
+						   FSCRYPT_SET_CONTEXT_MAX_SIZE);
+		if (fscrypt_context_size < 0)
+			return fscrypt_context_size;
+
+		encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT,
+						   fscrypt_context_size);
+	}
+#endif /* CONFIG_FS_ENCRYPTION */
 
 	btrfs_set_stack_file_extent_generation(&fi, trans->transid);
 	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4697,6 +4711,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));
+#ifdef CONFIG_FS_ENCRYPTION
+	write_extent_buffer(leaf, context,
+			    btrfs_item_ptr_offset(leaf, path->slots[0]) +
+			    sizeof(fi), fscrypt_context_size);
+#endif /* CONFIG_FS_ENCRYPTION */
+
 	btrfs_mark_buffer_dirty(leaf);
 
 	btrfs_release_path(path);
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index e780eee85e6e..787c9e67b491 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1080,6 +1080,11 @@ struct btrfs_file_extent_item {
 	 * always reflects the size uncompressed and without encoding.
 	 */
 	__le64 num_bytes;
+	/*
+	 * fscrypt extent encryption context. Only present if extent is
+	 * encrypted (as per the encryption field).
+	 */
+	__u8 fscrypt_context[0];
 
 } __attribute__ ((__packed__));
 
-- 
2.40.1


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

* Re: [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt
  2023-06-29  0:35 ` [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
@ 2023-06-29 13:20   ` Luís Henriques
  0 siblings, 0 replies; 23+ messages in thread
From: Luís Henriques @ 2023-06-29 13:20 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt

Hi!

On Wed, Jun 28, 2023 at 08:35:34PM -0400, Sweet Tea Dorminy wrote:
> 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.
> 
> I'm not sure this is the correct list of active devices and it isn't
> tested with any multi-device test yet.

To be honest, I can't say whether this is the correct list either (sorry,
I'm new here!).  However, a quick test shows me there's something wrong
with this code because I'm seeing the lockdep splat bellow: mutex_lock()
can't be called here.

It's very easy to reproduce with the fscrypt userspace tool, simply doing
an 'fscrypt lock' in an encrypted directory.

Cheers,
--
Luís

> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
>  fs/btrfs/fscrypt.c | 30 ++++++++++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
> 
> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
> index 20727d920841..658a67856de6 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"
>  
>  /*
> @@ -162,9 +164,37 @@ 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;
> +	struct block_device **devs;
> +	struct btrfs_device *device;
> +	int i;
> +
> +	devs = kmalloc_array(fs_devices->num_devices, sizeof(*devs),
> +		GFP_KERNEL);
> +	if (!devs)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_lock(&fs_devices->device_list_mutex);
> +	i = 0;
> +	list_for_each_entry(device, &fs_devices->devices, dev_list) {
> +		if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state))
> +			continue;
> +
> +		devs[i++] = device->bdev;
> +	}
> +	mutex_unlock(&fs_devices->device_list_mutex);
> +	*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,
>  	.key_prefix = "btrfs:"
>  };
> -- 
> 2.40.1
> 
[   79.809077] =============================
[   79.810268] [ BUG: Invalid wait context ]
[   79.811370] 6.4.0-rc7+ #6 Not tainted
[   79.812451] -----------------------------
[   79.813561] fscrypt/233 is trying to lock:
[   79.814576] ffff88812e7b78d8 (&fs_devs->device_list_mutex){+.+.}-{3:3}, at: btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.817937] other info that might help us debug this:
[   79.819436] context-{4:4}
[   79.820227] 1 lock held by fscrypt/233:
[   79.821335]  #0: ffff888111afb7b8 (&tree->lock){++++}-{2:2}, at: btrfs_drop_extent_map_range+0x9f9/0xac0 [btrfs]
[   79.824674] stack backtrace:
[   79.825478] CPU: 0 PID: 233 Comm: fscrypt Not tainted 6.4.0-rc7+ #6
[   79.827186] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014
[   79.829975] Call Trace:
[   79.830562]  <TASK>
[   79.831051]  dump_stack_lvl+0x46/0x80
[   79.831832]  __lock_acquire+0xd05/0x3320
[   79.832636]  ? _raw_spin_unlock_irqrestore+0x2b/0x50
[   79.833642]  ? __stack_depot_save+0x207/0x440
[   79.834537]  ? __pfx___lock_acquire+0x10/0x10
[   79.835442]  ? btrfs_fscrypt_get_devices+0x65/0x140 [btrfs]
[   79.836836]  ? kasan_save_stack+0x2b/0x40
[   79.837751]  ? kasan_save_stack+0x1c/0x40
[   79.838643]  lock_acquire+0x14f/0x390
[   79.839498]  ? btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.841052]  ? __pfx_lock_acquire+0x10/0x10
[   79.841965]  ? entry_SYSCALL_64_after_hwframe+0x72/0xdc
[   79.843106]  ? lock_acquire+0x14f/0x390
[   79.843928]  ? __create_object+0x250/0x620
[   79.844786]  ? __pfx_lock_acquire+0x10/0x10
[   79.845691]  __mutex_lock+0xf2/0x1210
[   79.846457]  ? btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.847883]  ? __kmem_cache_alloc_node+0x154/0x250
[   79.848822]  ? btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.850214]  ? mark_held_locks+0x65/0x90
[   79.851007]  ? __pfx___mutex_lock+0x10/0x10
[   79.851800]  ? lockdep_hardirqs_on_prepare+0x132/0x200
[   79.852746]  ? _raw_spin_unlock_irqrestore+0x2b/0x50
[   79.853625]  ? kasan_set_track+0x21/0x30
[   79.854366]  ? __kasan_kmalloc+0x8b/0x90
[   79.855109]  ? btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.856319]  btrfs_fscrypt_get_devices+0x8a/0x140 [btrfs]
[   79.857458]  fscrypt_get_devices+0x3a/0xc0
[   79.858137]  fscrypt_destroy_inline_crypt_key+0x6c/0xe0
[   79.859006]  ? __pfx_fscrypt_destroy_inline_crypt_key+0x10/0x10
[   79.860016]  ? __pfx_truncate_inode_pages_range+0x10/0x10
[   79.860849]  put_crypt_info+0x13b/0x180
[   79.861435]  fscrypt_free_extent_info+0x11/0x30
[   79.862175]  free_extent_map+0x5a/0xf0 [btrfs]
[   79.863145]  btrfs_drop_extent_map_range+0xa66/0xac0 [btrfs]
[   79.864221]  ? __lockdep_reset_lock+0x80/0xb0
[   79.864878]  ? truncate_inode_pages_final+0x42/0x60
[   79.865637]  ? __pfx_lock_release+0x10/0x10
[   79.866285]  ? do_raw_spin_lock+0x104/0x180
[   79.866935]  ? __pfx_btrfs_drop_extent_map_range+0x10/0x10 [btrfs]
[   79.868066]  ? lockdep_hardirqs_on_prepare+0x132/0x200
[   79.868798]  btrfs_evict_inode+0x14b/0x660 [btrfs]
[   79.869738]  ? __pfx_btrfs_evict_inode+0x10/0x10 [btrfs]
[   79.870707]  ? lock_release+0x1c5/0x380
[   79.871297]  ? evict+0x137/0x2a0
[   79.871793]  ? __pfx_lock_release+0x10/0x10
[   79.872386]  ? __pfx_wake_bit_function+0x10/0x10
[   79.873070]  evict+0x155/0x2a0
[   79.873532]  try_to_lock_encrypted_files+0x211/0x4b0
[   79.874244]  ? __pfx_try_to_lock_encrypted_files+0x10/0x10
[   79.874981]  ? do_remove_key.isra.0+0x2da/0x3e0
[   79.875636]  ? lockdep_hardirqs_on_prepare+0x132/0x200
[   79.876368]  ? up_write+0xfb/0x2d0
[   79.876833]  do_remove_key.isra.0+0x36a/0x3e0
[   79.877441]  ? __pfx_do_remove_key.isra.0+0x10/0x10
[   79.878111]  btrfs_ioctl+0x1cf1/0x4000 [btrfs]
[   79.878919]  ? __pfx_btrfs_ioctl+0x10/0x10 [btrfs]
[   79.879805]  ? __pfx___lock_acquire+0x10/0x10
[   79.880381]  ? mark_held_locks+0x24/0x90
[   79.880928]  ? do_sys_openat2+0xce/0x240
[   79.881441]  ? do_vfs_ioctl+0xb2f/0xca0
[   79.881948]  ? __pfx_do_vfs_ioctl+0x10/0x10
[   79.882483]  ? lock_acquire+0x14f/0x390
[   79.883007]  ? __pfx___fget_files+0x10/0x10
[   79.883566]  ? find_held_lock+0x83/0xa0
[   79.884062]  ? lock_release+0x1c5/0x380
[   79.884551]  ? __fget_files+0x129/0x200
[   79.885051]  ? __pfx_lock_release+0x10/0x10
[   79.885598]  ? map_id_up+0xd7/0x150
[   79.886037]  ? __pfx_map_id_up+0x10/0x10
[   79.886532]  ? __fget_files+0x141/0x200
[   79.887049]  __x64_sys_ioctl+0xc1/0xe0
[   79.887537]  do_syscall_64+0x42/0x90
[   79.887965]  entry_SYSCALL_64_after_hwframe+0x72/0xdc
[   79.888587] RIP: 0033:0x405e8e
[   79.888976] Code: 48 89 6c 24 38 48 8d 6c 24 38 e8 0d 00 00 00 48 8b 6c 24 38 48 83 c4 40 c3 cc cc cc 49 89 f2 48 89 fa 48 89 ce 48 89 df 0f 05 <48> 3d 01 f0 ff ff 76 158
[   79.891248] RSP: 002b:000000c0000ed3f8 EFLAGS: 00000206 ORIG_RAX: 0000000000000010
[   79.892146] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000000000405e8e
[   79.892967] RDX: 000000c0000ed540 RSI: 00000000c0406618 RDI: 0000000000000003
[   79.893829] RBP: 000000c0000ed438 R08: 0000000000000000 R09: 0000000000000000
[   79.894677] R10: 0000000000000000 R11: 0000000000000206 R12: 000000c0000b2b90
[   79.895503] R13: 0000000000000000 R14: 000000c0000061a0 R15: ffffffffffffffff
[   79.896329]  </TASK>

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

* Re: [PATCH v1 05/17] btrfs: add inode encryption contexts
  2023-06-29  0:35 ` [PATCH v1 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
@ 2023-07-07 23:32   ` Boris Burkov
  2023-07-17  1:43     ` Sweet Tea Dorminy
  0 siblings, 1 reply; 23+ messages in thread
From: Boris Burkov @ 2023-07-07 23:32 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt, Omar Sandoval

On Wed, Jun 28, 2023 at 08:35:28PM -0400, Sweet Tea Dorminy 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.
> 
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
>  fs/btrfs/fscrypt.c              | 116 ++++++++++++++++++++++++++++++++
>  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, 153 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
> index 3a53dc59c1e4..235f65e43d96 100644
> --- a/fs/btrfs/fscrypt.c
> +++ b/fs/btrfs/fscrypt.c
> @@ -1,8 +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_CTXT_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 = -EINVAL;

I'm a little wary about squishing the errors down like this. It could
be some error, in which case it might be interesting to get the real errno
or it could be ret > 1, in which case I think ENOENT is more useful than
EINVAL.

Also, having a ret variable and mashing that into len feels kinda weird.
Maybe that's the neatest way to write this logic, though.

> +		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 void btrfs_fscrypt_update_context(struct btrfs_path *path,
> +					 const void *ctx, size_t len)
> +{
> +	struct extent_buffer *leaf = path->nodes[0];
> +	unsigned long 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(leaf);
> +}
> +
> +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
> +				     size_t len, void *fs_data)
> +{
> +	struct btrfs_path *path;
> +	int ret;
> +	struct btrfs_trans_handle *trans = fs_data;
> +	struct btrfs_key key = {
> +		.objectid = btrfs_ino(BTRFS_I(inode)),
> +		.type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
> +		.offset = 0,
> +	};
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	if (!trans)
> +		trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
> +	if (IS_ERR(trans))
> +		return PTR_ERR(trans);
> +
> +	ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
> +	if (ret == 0) {
> +		btrfs_fscrypt_update_context(path, ctx, len);
> +		btrfs_free_path(path);
> +		return ret;
> +	}
> +
> +	btrfs_free_path(path);
> +	if (ret < 0) {
> +		btrfs_abort_transaction(trans, ret);
> +		return ret;
> +	}
> +
> +	ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
> +	if (ret) {
> +		btrfs_abort_transaction(trans, ret);
> +		return ret;
> +	}
> +
> +	BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
> +	btrfs_sync_inode_flags_to_i_flags(inode);
> +	inode_inc_iversion(inode);
> +	inode->i_ctime = current_time(inode);
> +	ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode));
> +	if (!ret) {
> +		if (!fs_data)
> +			btrfs_end_transaction(trans);
> +		return ret;
> +	}
> +
> +	btrfs_abort_transaction(trans, ret);
> +	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,
>  	.key_prefix = "btrfs:"
>  };
> 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 5ce167cfa1dc..9dc47f769641 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"
> @@ -6179,6 +6180,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);
> @@ -6212,6 +6216,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, but only for encrypted non-regular files */
> +	if (args->encrypt && !S_ISREG(inode->i_mode))
> +		(*trans_num_items)++;
>  	if (args->orphan) {
>  		/* 1 to add orphan item */
>  		(*trans_num_items)++;
> @@ -6390,6 +6397,11 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
>  	inode->i_ctime = inode->i_mtime;
>  	BTRFS_I(inode)->i_otime = inode->i_mtime;
>  
> +	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.
> @@ -6464,6 +6476,13 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
>  			goto discard;
>  		}
>  	}
> +	if (args->encrypt && !S_ISREG(inode->i_mode)) {
> +		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 edbbd5cf23fc..11564e48d736 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 ab38d0f411fa..ea88dd69957f 100644
> --- a/include/uapi/linux/btrfs_tree.h
> +++ b/include/uapi/linux/btrfs_tree.h
> @@ -161,6 +161,8 @@
>  #define BTRFS_VERITY_DESC_ITEM_KEY	36
>  #define BTRFS_VERITY_MERKLE_ITEM_KEY	37
>  
> +#define BTRFS_FSCRYPT_CTXT_ITEM_KEY	41
> +

Since the variables usually go by ctx, I lightly prefer CTX_ITEM_KEY.
Obviously not a big deal.

>  #define BTRFS_ORPHAN_ITEM_KEY		48
>  /* reserve 2-15 close to the inode for later flexibility */
>  
> @@ -399,6 +401,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)
>  
> @@ -415,6 +418,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)
> @@ -1016,6 +1020,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.40.1
> 

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

* Re: [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes
  2023-06-29  0:35 ` [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2023-07-07 23:36   ` Boris Burkov
  2023-07-17  1:42     ` Sweet Tea Dorminy
  0 siblings, 1 reply; 23+ messages in thread
From: Boris Burkov @ 2023-07-07 23:36 UTC (permalink / raw)
  To: Sweet Tea Dorminy
  Cc: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt, Omar Sandoval

On Wed, Jun 28, 2023 at 08:35:24PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
> 
> Initially, only normal data extents, using the normal (non-direct) IO
> path, will be encrypted. This change forbids various other bits:
> - allows reflinking only if both inodes have the same encryption status
> - disables direct IO on encrypted inodes
> - disable inline data on encrypted inodes
> 
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
>  fs/btrfs/file.c    | 4 ++--
>  fs/btrfs/inode.c   | 3 ++-
>  fs/btrfs/reflink.c | 7 +++++++
>  3 files changed, 11 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index 392bc7d512a0..354962a7dd72 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1502,7 +1502,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
>  		goto relock;
>  	}
>  
> -	if (check_direct_IO(fs_info, from, pos)) {
> +	if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) {
>  		btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
>  		goto buffered;
>  	}
> @@ -3741,7 +3741,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to)
>  	ssize_t read = 0;
>  	ssize_t ret;
>  
> -	if (fsverity_active(inode))
> +	if (IS_ENCRYPTED(inode) || fsverity_active(inode))

What's different about fscrypt vs fsverity that makes the inode flag a
good check for encryption while verity relies on the presence of the
extra context metadata?

Is the enable model not subject to the same race where S_VERITY gets set
ahead of actually storing the verity info/item?

I think it's fine for these "skip" cases, but I imagine if we have cases
of "I want a fully ready encrypted file" the verity-style check could be
better?

>  		return 0;
>  
>  	if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos))
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index dbbb67293e34..48eadc4f187f 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -630,7 +630,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 0474bbe39da7..ad722f495c9b 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"
> @@ -811,6 +812,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.40.1
> 

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

* Re: [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes
  2023-07-07 23:36   ` Boris Burkov
@ 2023-07-17  1:42     ` Sweet Tea Dorminy
  0 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-07-17  1:42 UTC (permalink / raw)
  To: Boris Burkov
  Cc: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt, Omar Sandoval



On 7/7/23 19:36, Boris Burkov wrote:
> On Wed, Jun 28, 2023 at 08:35:24PM -0400, Sweet Tea Dorminy wrote:
>> From: Omar Sandoval <osandov@osandov.com>
>>
>> Initially, only normal data extents, using the normal (non-direct) IO
>> path, will be encrypted. This change forbids various other bits:
>> - allows reflinking only if both inodes have the same encryption status
>> - disables direct IO on encrypted inodes
>> - disable inline data on encrypted inodes
>>
>> Signed-off-by: Omar Sandoval <osandov@osandov.com>
>> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
>> ---
>>   fs/btrfs/file.c    | 4 ++--
>>   fs/btrfs/inode.c   | 3 ++-
>>   fs/btrfs/reflink.c | 7 +++++++
>>   3 files changed, 11 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
>> index 392bc7d512a0..354962a7dd72 100644
>> --- a/fs/btrfs/file.c
>> +++ b/fs/btrfs/file.c
>> @@ -1502,7 +1502,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
>>   		goto relock;
>>   	}
>>   
>> -	if (check_direct_IO(fs_info, from, pos)) {
>> +	if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) {
>>   		btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);
>>   		goto buffered;
>>   	}
>> @@ -3741,7 +3741,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to)
>>   	ssize_t read = 0;
>>   	ssize_t ret;
>>   
>> -	if (fsverity_active(inode))
>> +	if (IS_ENCRYPTED(inode) || fsverity_active(inode))
> 
> What's different about fscrypt vs fsverity that makes the inode flag a
> good check for encryption while verity relies on the presence of the
> extra context metadata?
> 
> Is the enable model not subject to the same race where S_VERITY gets set
> ahead of actually storing the verity info/item?
> 
> I think it's fine for these "skip" cases, but I imagine if we have cases
> of "I want a fully ready encrypted file" the verity-style check could be
> better?
> 
Clear solution: enable direct encrypted IO :). I've removed this check 
altogether, thanks for the note.

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

* Re: [PATCH v1 05/17] btrfs: add inode encryption contexts
  2023-07-07 23:32   ` Boris Burkov
@ 2023-07-17  1:43     ` Sweet Tea Dorminy
  0 siblings, 0 replies; 23+ messages in thread
From: Sweet Tea Dorminy @ 2023-07-17  1:43 UTC (permalink / raw)
  To: Boris Burkov
  Cc: Chris Mason, Josef Bacik, David Sterba, Eric Biggers,
	Theodore Y. Ts'o, Jaegeuk Kim, kernel-team, linux-btrfs,
	linux-fscrypt, Omar Sandoval



On 7/7/23 19:32, Boris Burkov wrote:
> On Wed, Jun 28, 2023 at 08:35:28PM -0400, Sweet Tea Dorminy 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.
>>
>> Signed-off-by: Omar Sandoval <osandov@osandov.com>
>> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
>> ---
>>   fs/btrfs/fscrypt.c              | 116 ++++++++++++++++++++++++++++++++
>>   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, 153 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
>> index 3a53dc59c1e4..235f65e43d96 100644
>> --- a/fs/btrfs/fscrypt.c
>> +++ b/fs/btrfs/fscrypt.c
>> @@ -1,8 +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_CTXT_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 = -EINVAL;
> 
> I'm a little wary about squishing the errors down like this. It could
> be some error, in which case it might be interesting to get the real errno
> or it could be ret > 1, in which case I think ENOENT is more useful than
> EINVAL.

I'll make it ENOENT.

> Also, having a ret variable and mashing that into len feels kinda weird.
> Maybe that's the neatest way to write this logic, though.

It's the way the existing fscrypt interface does things, so it'd be hard 
to change.

> 
> Since the variables usually go by ctx, I lightly prefer CTX_ITEM_KEY.
> Obviously not a big deal.
Sure, will do.

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

end of thread, other threads:[~2023-07-17  1:43 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-29  0:35 [PATCH v1 00/17] btrfs: add encryption feature Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
2023-07-07 23:36   ` Boris Burkov
2023-07-17  1:42     ` Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 02/17] btrfs: disable verity " Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
2023-07-07 23:32   ` Boris Burkov
2023-07-17  1:43     ` Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 08/17] btrfs: use correct name hash for " Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
2023-06-29 13:20   ` Luís Henriques
2023-06-29  0:35 ` [PATCH v1 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
2023-06-29  0:35 ` [PATCH v1 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy

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