* [PATCH v3 00/17] btrfs: add encryption feature
@ 2023-08-08 17:12 Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
` (17 more replies)
0 siblings, 18 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
Cc: Sweet Tea Dorminy
Encryption has been desired for btrfs for a long time, in order to
provide some measure of security for data at rest. However, since btrfs
supports snapshots and reflinks, fscrypt encryption has previously been
incompatible since it relies on single inode ownership of data
locations. A design for fscrypt to support btrfs's requirements, and for
btrfs to use encryption, was constructed in October '21 [1] and refined
in November '22 [2].
This patch series builds on two fscrypt patch series adding extent-based
encryption to fscrypt, which allows using fscrypt in btrfs. The fscrypt
patchsets have no effect without a user, and this patchset makes btrfs
use the new extent encryption abilities of fscrypt.
These constitute the first of several steps laid out in the design
document [2]: the second step will be adding authenticated encryption
support to the block layer, fscrypt, and then btrfs. Other steps will
potentially add the ability to change the key used by a directory
(either for all data or just newly written data), allow use of inline
extents and verity items in combination with encryption, and enable
send/receive of encrypted volumes. This changeset is not suitable for
usage due to the lack of authenticated encryption.
In addition to the fscrypt patchsets, [3] [4], this changeset requires
the latest version of the btrfs-progs changeset, which is currently at
[5], and the latest version of the fstests changeset, [6]. It is based
on kdave/misc-next as of approximately now.
This changeset passes all encryption tests in fstests, and also survives
fsperf runs with lockdep turned on, including the previously failing
dbench test.
This version changes the format of extent contexts on disk as per
Josef's comment on v2: the encryption field in file extents now only
stores the fact of encryption with fscrypt, and the context stored at
the end of the file extent now stores the length of the fscrypt extent
as well as the fscrypt extent itself.
I remain really excited about Qu's work to make extent buffers
potentially be either folios or vmalloc'd memory -- this would allow
eliminating change 'fscrypt: expose fscrypt_nokey_name' and the code
using it.
[1] https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
[2] https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit
[3] https://lore.kernel.org/linux-fscrypt/cover.1691505830.git.sweettea-kernel@dorminy.me/
[4] https://lore.kernel.org/linux-fscrypt/cover.1691505882.git.sweettea-kernel@dorminy.me/
[5] https://lore.kernel.org/linux-btrfs/cover.1691520000.git.sweettea-kernel@dorminy.me/
[6] https://lore.kernel.org/linux-fscrypt/cover.1691530000.git.sweettea-kernel@dorminy.me/T/#t
Changelog:
v3:
- Fixed an incorrect length in 'explicitly track file extent length and
encryption' resulting in corrupted trees.
- Added missing handling of extent_map splitting.
- Changed format to store length with the fscrypt context instead of
packed into the file extent's encryption field, thanks Josef.
- Reworked hunting for encrypted names to not leak nokey names
hopefully.
- Added missing filename cleanup to btrfs_lookup_dentry(), thanks Luis.
- Hopefully handled all the miscellaneous review comments, thanks to all.
v2:
- https://lore.kernel.org/linux-fscrypt/cover.1689564024.git.sweettea-kernel@dorminy.me/
- Re-enabled direct IO on encrypted files.
- Renamed inode context item as per Boris' request.
- Fixed a return value in inode context getting, as per Boris' note.
- Fixed an lblk calculation in checking mergeable bios.
- Disabled all extent map merging if either is encrypted, instead of
comparing them, for now.
- Fixed getting the list of devices under btrfs, thanks to Luis for the
report and Josef for pointing me at the right way to do it.
v1:
- https://lore.kernel.org/linux-btrfs/cover.1687988380.git.sweettea-kernel@dorminy.me/T/#t
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: handle nokey names.
btrfs: add encryption to CONFIG_BTRFS_DEBUG
btrfs: add get_devices hook for fscrypt
btrfs: turn on inlinecrypt mount option for encrypt
btrfs: 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 | 3 +-
fs/btrfs/btrfs_inode.h | 3 +-
fs/btrfs/ctree.h | 2 +
fs/btrfs/delayed-inode.c | 29 ++-
fs/btrfs/delayed-inode.h | 4 +-
fs/btrfs/dir-item.c | 108 +++++++++-
fs/btrfs/dir-item.h | 13 +-
fs/btrfs/extent_io.c | 49 +++++
fs/btrfs/extent_io.h | 3 +
fs/btrfs/extent_map.c | 18 ++
fs/btrfs/extent_map.h | 1 +
fs/btrfs/file-item.c | 12 ++
fs/btrfs/file.c | 7 +-
fs/btrfs/fs.h | 7 +-
fs/btrfs/fscrypt.c | 341 ++++++++++++++++++++++++++++++++
fs/btrfs/fscrypt.h | 102 ++++++++++
fs/btrfs/inode.c | 333 ++++++++++++++++++++++++-------
fs/btrfs/ioctl.c | 41 +++-
fs/btrfs/reflink.c | 8 +
fs/btrfs/root-tree.c | 8 +-
fs/btrfs/root-tree.h | 2 +-
fs/btrfs/super.c | 17 ++
fs/btrfs/sysfs.c | 6 +
fs/btrfs/tree-checker.c | 37 +++-
fs/btrfs/tree-log.c | 23 ++-
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 | 22 ++-
32 files changed, 1131 insertions(+), 151 deletions(-)
create mode 100644 fs/btrfs/fscrypt.c
create mode 100644 fs/btrfs/fscrypt.h
base-commit: 54d2161835d828a9663f548f61d1d9c3d3482122
prerequisite-patch-id: 2f1424d04bb5a76abf0ecf2c9cd8426d300078ae
prerequisite-patch-id: ab342a72cf967dadfb8bec1320c5906fd3c6800f
prerequisite-patch-id: ced2a9dab36539f55c14cd74a28950087c475ff2
prerequisite-patch-id: d4f1a64c994c2fa0d2d4cab83f9ddff52f0622e9
prerequisite-patch-id: 1af0fc98277159b31c26bc5751663efc0d322d75
prerequisite-patch-id: 3b21b62208587486cf9b31618f7c3bc875362f1a
prerequisite-patch-id: c43d693f5b7c498a876d9ffcfc49c11a8ca93d80
prerequisite-patch-id: f120bde1cf47fbef1d9f8fd09cdcccc1408c3ff4
prerequisite-patch-id: c6a1f087d4a67b928b9c6af04e00310bfa74ace1
prerequisite-patch-id: 55ff7e03d98b9944c91b85974d6437a5ba3c353c
prerequisite-patch-id: adcb847e01bfe31f59b6c1710f3574a8c11c05f6
prerequisite-patch-id: 8ac189b6daaab42a03fdff4604ba49a60ec050da
prerequisite-patch-id: ca3622130a89edf2c8a3bffc3d3ee2e69f6d9fa3
prerequisite-patch-id: fee001b42c3d9d2025613ce76be03d7a94b1e5e2
prerequisite-patch-id: 2090ac664e0ce0b314af240d50ff99c3e8690979
prerequisite-patch-id: 5e676d9f7f31cdeefc8ea28c4127ebbae91c78c3
prerequisite-patch-id: 28f47e16193378c3187472c02f75673592daafd0
prerequisite-patch-id: b388d8c6b09884463cb156f28d48ba75acb73afd
prerequisite-patch-id: 5b7a3363907c503ccc3b650dea9f112e6d7e885e
prerequisite-patch-id: 879f43fe091d8d9c83a37f882a83c91bb9b7894f
prerequisite-patch-id: 21aeb9f14e1041576ae5a26714694e1e8dde0c21
prerequisite-patch-id: d0eeda13cb8063a357185e04557be4f4780bd3dc
prerequisite-patch-id: a10a13f19768707f4e47a3e1fb1b151dac0aa554
prerequisite-patch-id: 25495042ace3f4308bd958243dc3a4abc8ea53f6
prerequisite-patch-id: 94d153796d396cf62c2fec6e6766479d897e2cd3
--
2.41.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 02/17] btrfs: disable verity " Sweet Tea Dorminy
` (16 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Initially, only normal data extents will be encrypted. This change
forbids various other bits:
- allows reflinking only if both inodes have the same encryption status
- disable inline data on encrypted inodes
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/inode.c | 3 ++-
fs/btrfs/reflink.c | 7 +++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 6a68d5a3ed20..bb9e1e608488 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -629,7 +629,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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 02/17] btrfs: disable verity on encrypted inodes
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
` (15 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 03/17] fscrypt: expose fscrypt_nokey_name
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 02/17] btrfs: disable verity " Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
` (14 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 b67054a2c965..fd8cb413e718 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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 04/17] btrfs: start using fscrypt hooks
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (2 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
` (13 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 | 97 ++++++++++++++++++++++++++++++++++--------
fs/btrfs/super.c | 2 +
7 files changed, 104 insertions(+), 18 deletions(-)
create mode 100644 fs/btrfs/fscrypt.c
create mode 100644 fs/btrfs/fscrypt.h
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 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 bda1fdbba666..7abf98d56181 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 fd03e689a6be..73038908876a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3698,6 +3698,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 bb9e1e608488..49ee05ecc03a 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5214,11 +5214,8 @@ void btrfs_evict_inode(struct inode *inode)
trace_btrfs_inode_evict(inode);
- if (!root) {
- fsverity_cleanup_inode(inode);
- clear_inode(inode);
- return;
- }
+ if (!root)
+ goto cleanup;
evict_inode_truncate_pages(inode);
@@ -5318,6 +5315,9 @@ void btrfs_evict_inode(struct inode *inode)
* to retry these periodically in the future.
*/
btrfs_remove_delayed_node(BTRFS_I(inode));
+
+cleanup:
+ fscrypt_put_encryption_info(inode);
fsverity_cleanup_inode(inode);
clear_inode(inode);
}
@@ -6068,6 +6068,12 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
return ret;
}
+ ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt);
+ if (ret) {
+ fscrypt_free_filename(&args->fname);
+ return ret;
+ }
+
/* 1 to add inode item */
*trans_num_items = 1;
/* 1 to add compression property */
@@ -6544,9 +6550,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)
@@ -8493,6 +8503,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));
}
@@ -8563,8 +8574,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)
@@ -9155,6 +9165,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);
@@ -9374,15 +9389,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)
@@ -9391,8 +9413,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);
@@ -9419,10 +9441,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) {
@@ -9441,10 +9476,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);
@@ -9461,6 +9496,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,
@@ -10939,7 +10997,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,
@@ -10949,4 +11007,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 cffdd6f7f8e8..08b1e2ded5be 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"
@@ -1144,6 +1145,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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 05/17] btrfs: add inode encryption contexts
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (3 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-09 20:20 ` Josef Bacik
2023-08-08 17:12 ` [PATCH v3 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
` (12 subsequent siblings)
17 siblings, 1 reply; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 | 118 ++++++++++++++++++++++++++++++++
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, 155 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 3a53dc59c1e4..d09d42210f37 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -1,8 +1,126 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/iversion.h>
#include "ctree.h"
+#include "accessors.h"
+#include "btrfs_inode.h"
+#include "disk-io.h"
+#include "fs.h"
#include "fscrypt.h"
+#include "ioctl.h"
+#include "messages.h"
+#include "transaction.h"
+#include "xattr.h"
+
+static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+ struct btrfs_key key = {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
+ .offset = 0,
+ };
+ struct btrfs_path *path;
+ struct extent_buffer *leaf;
+ unsigned long ptr;
+ int ret;
+
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
+ if (ret) {
+ len = -ENOENT;
+ goto out;
+ }
+
+ leaf = path->nodes[0];
+ ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ /* fscrypt provides max context length, but it could be less */
+ len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
+ read_extent_buffer(leaf, ctx, ptr, len);
+
+out:
+ btrfs_free_path(path);
+ return len;
+}
+
+static 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_trans_handle *trans = fs_data;
+ struct btrfs_key key = {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
+ .offset = 0,
+ };
+ struct btrfs_path *path;
+ int ret;
+
+ 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_abort_transaction(trans, ret);
+ return ret;
+ }
+
+ if (ret > 0) {
+ btrfs_release_path(path);
+ ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+ }
+ }
+
+ btrfs_fscrypt_update_context(path, ctx, len);
+
+ if (fs_data)
+ return ret;
+
+ BTRFS_I(inode)->flags |= BTRFS_INODE_ENCRYPT;
+ btrfs_sync_inode_flags_to_i_flags(inode);
+ inode_inc_iversion(inode);
+ inode->i_ctime = current_time(inode);
+ ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode));
+ if (!ret) {
+ btrfs_end_transaction(trans);
+ return ret;
+ }
+
+ btrfs_abort_transaction(trans, ret);
+ btrfs_end_transaction(trans);
+ 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 49ee05ecc03a..0d0f6f7f0d27 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"
@@ -6055,6 +6056,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);
@@ -6090,6 +6094,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)++;
@@ -6268,6 +6275,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.
@@ -6342,6 +6354,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 a895d105464b..05984e5448b4 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..029af0aeb65d 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_CTX_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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (4 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
` (11 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 +++++
fs/btrfs/sysfs.c | 6 ++++++
include/uapi/linux/btrfs.h | 1 +
4 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index ef07c6c252d8..15d0dc425e32 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -223,9 +223,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 08b1e2ded5be..0cc9c2909f64 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2421,6 +2421,11 @@ static int __init btrfs_print_mod_info(void)
", fsverity=yes"
#else
", fsverity=no"
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+ ", fscrypt=yes"
+#else
+ ", fscrypt=no"
#endif
;
pr_info("Btrfs loaded%s\n", options);
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index b1d1ac25237b..ac955f920edf 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -301,6 +301,9 @@ BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2);
#ifdef CONFIG_FS_VERITY
BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY);
#endif
+#ifdef CONFIG_FS_ENCRYPTION
+BTRFS_FEAT_ATTR_INCOMPAT(encryption, ENCRYPT);
+#endif /* CONFIG_FS_ENCRYPTION */
/*
* Features which depend on feature bits and may differ between each fs.
@@ -331,6 +334,9 @@ static struct attribute *btrfs_supported_feature_attrs[] = {
#ifdef CONFIG_FS_VERITY
BTRFS_FEAT_ATTR_PTR(verity),
#endif
+#ifdef CONFIG_FS_ENCRYPTION
+ BTRFS_FEAT_ATTR_PTR(encryption),
+#endif /* CONFIG_FS_ENCRYPTION */
NULL
};
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 07/17] btrfs: adapt readdir for encrypted and nokey names
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (5 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 08/17] btrfs: handle " Sweet Tea Dorminy
` (10 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 | 29 ++++++-
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 | 34 +++++++++
fs/btrfs/fscrypt.h | 19 +++++
fs/btrfs/inode.c | 158 ++++++++++++++++++++++++++-------------
fs/btrfs/root-tree.c | 8 +-
fs/btrfs/root-tree.h | 2 +-
fs/btrfs/tree-log.c | 3 +-
13 files changed, 317 insertions(+), 73 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 7abf98d56181..9effefd4a18c 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..e3aeba44156b 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1724,7 +1724,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 +1736,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 +1764,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 177d65d51447..4de42b7ce796 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -4108,6 +4108,44 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb,
}
}
+/* Take a sha256 of a portion of an extent buffer. */
+void extent_buffer_sha256(const struct extent_buffer *eb,
+ unsigned long start,
+ unsigned long len, u8 *out)
+{
+ size_t cur;
+ size_t offset;
+ struct page *page;
+ char *kaddr;
+ unsigned long i = get_eb_page_index(start);
+ struct sha256_state sctx;
+
+ if (check_eb_range(eb, start, len))
+ return;
+
+ offset = get_eb_offset_in_page(eb, start);
+
+ /*
+ * TODO: This should maybe be using the crypto API, not the fallback,
+ * but fscrypt uses the fallback and this is only used in emulation of
+ * fscrypt's buffer sha256 method.
+ */
+ sha256_init(&sctx);
+ while (len > 0) {
+ page = eb->pages[i];
+ assert_eb_page_uptodate(eb, page);
+
+ cur = min(len, PAGE_SIZE - offset);
+ kaddr = page_address(page);
+ sha256_update(&sctx, (u8 *)(kaddr + offset), cur);
+
+ len -= cur;
+ offset = 0;
+ i++;
+ }
+ sha256_final(&sctx, out);
+}
+
static void __write_extent_buffer(const struct extent_buffer *eb,
const void *srcv, unsigned long start,
unsigned long len, bool use_memmove)
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index adda14c1b763..2646507a2509 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 d09d42210f37..fcd926907f82 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -5,13 +5,47 @@
#include "accessors.h"
#include "btrfs_inode.h"
#include "disk-io.h"
+#include "ioctl.h"
#include "fs.h"
#include "fscrypt.h"
#include "ioctl.h"
#include "messages.h"
+#include "root-tree.h"
#include "transaction.h"
#include "xattr.h"
+/*
+ * This function is extremely similar to fscrypt_match_name() but uses an
+ * extent_buffer.
+ */
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf, unsigned long de_name,
+ u32 de_name_len)
+{
+ const struct fscrypt_nokey_name *nokey_name =
+ (const struct fscrypt_nokey_name *)fname->crypto_buf.name;
+ u8 digest[SHA256_DIGEST_SIZE];
+
+ if (likely(fname->disk_name.name)) {
+ if (de_name_len != fname->disk_name.len)
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name,
+ de_name, de_name_len);
+ }
+
+ if (de_name_len <= sizeof(nokey_name->bytes))
+ return false;
+
+ if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name,
+ sizeof(nokey_name->bytes)))
+ return false;
+
+ extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes),
+ de_name_len - sizeof(nokey_name->bytes), digest);
+
+ return !memcmp(digest, nokey_name->sha256, sizeof(digest));
+}
+
static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
struct btrfs_key key = {
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 80adb7e56826..1647bbbcd609 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -4,9 +4,28 @@
#define BTRFS_FSCRYPT_H
#include <linux/fscrypt.h>
+#include "extent_io.h"
#include "fs.h"
+#ifdef CONFIG_FS_ENCRYPTION
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name, u32 de_name_len);
+
+#else
+static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name,
+ u32 de_name_len)
+{
+ if (de_name_len != fname_len(fname))
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
+ de_name_len);
+}
+#endif /* CONFIG_FS_ENCRYPTION */
+
extern const struct fscrypt_operations btrfs_fscrypt_ops;
#endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 0d0f6f7f0d27..053c7170ce6e 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4052,7 +4052,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;
@@ -4070,7 +4070,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;
@@ -4098,11 +4098,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;
}
@@ -4123,8 +4126,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);
}
/*
@@ -4142,7 +4147,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);
@@ -4155,7 +4160,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;
@@ -4206,7 +4211,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;
@@ -4243,8 +4248,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) {
@@ -4261,8 +4264,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;
@@ -4288,7 +4291,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;
@@ -4305,7 +4308,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;
@@ -4629,7 +4632,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);
/*
@@ -5330,32 +5333,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;
@@ -5367,13 +5361,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;
}
@@ -5645,20 +5639,27 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_root *sub_root = root;
struct btrfs_key location;
+ struct fscrypt_name fname;
u8 di_type = 0;
int ret = 0;
if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
- ret = btrfs_inode_by_name(BTRFS_I(dir), dentry, &location, &di_type);
- if (ret < 0)
+ ret = fscrypt_prepare_lookup(dir, dentry, &fname);
+ if (ret)
return ERR_PTR(ret);
+ ret = btrfs_inode_by_name(BTRFS_I(dir), &fname, &location, &di_type);
+ if (ret < 0) {
+ inode = ERR_PTR(ret);
+ goto cleanup;
+ }
+
if (location.type == BTRFS_INODE_ITEM_KEY) {
inode = btrfs_iget(dir->i_sb, location.objectid, root);
if (IS_ERR(inode))
- return inode;
+ goto cleanup;
/* Do extra check against inode mode with di_type */
if (btrfs_inode_type(inode) != di_type) {
@@ -5667,9 +5668,10 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
inode->i_mode, btrfs_inode_type(inode),
di_type);
iput(inode);
- return ERR_PTR(-EUCLEAN);
+ inode = ERR_PTR(-EUCLEAN);
+ goto cleanup;
}
- return inode;
+ goto cleanup;
}
ret = fixup_tree_root_location(fs_info, BTRFS_I(dir), dentry,
@@ -5684,7 +5686,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
btrfs_put_root(sub_root);
if (IS_ERR(inode))
- return inode;
+ goto cleanup;
down_read(&fs_info->cleanup_work_sem);
if (!sb_rdonly(inode->i_sb))
@@ -5696,6 +5698,8 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
}
}
+cleanup:
+ fscrypt_free_filename(&fname);
return inode;
}
@@ -5792,18 +5796,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;
@@ -5821,6 +5839,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;
@@ -5832,8 +5851,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)
@@ -5847,8 +5871,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);
@@ -5868,7 +5920,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;
@@ -5899,6 +5952,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;
}
@@ -6409,6 +6464,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));
@@ -6466,7 +6522,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) {
@@ -8839,7 +8895,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));
}
@@ -8854,7 +8910,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));
}
@@ -9103,7 +9159,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));
}
@@ -9122,7 +9178,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 8ad7e7e38d18..3bf746668e07 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -901,9 +901,10 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
const struct fscrypt_str *name)
{
+ struct fscrypt_name fname = { .disk_name = *name, };
int ret;
- ret = btrfs_unlink_inode(trans, dir, inode, name);
+ ret = btrfs_unlink_inode(trans, dir, inode, &fname);
if (ret)
return ret;
/*
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 08/17] btrfs: handle nokey names.
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (6 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-09 20:32 ` Josef Bacik
2023-08-08 17:12 ` [PATCH v3 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
` (9 subsequent siblings)
17 siblings, 1 reply; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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.
Additionally, for nokey names, if we find the nokey name on disk we can
update the fscrypt_name with the disk name, so add that to searching for
diritems.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/dir-item.c | 37 +++++++++++++++++++++++++++++++++++--
fs/btrfs/fscrypt.c | 27 +++++++++++++++++++++++++++
fs/btrfs/fscrypt.h | 11 +++++++++++
3 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index da95ae411d72..ee7dad888f53 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -231,6 +231,28 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
return di;
}
+/*
+ * If appropriate, populate the disk name for a fscrypt_name looked up without
+ * a key.
+ *
+ * @path: The path to the extent buffer in which the name was found.
+ * @di: The dir item corresponding.
+ * @fname: The fscrypt_name to perhaps populate.
+ *
+ * Returns: 0 if the name is already populated or the dir item doesn't exist
+ * or the name was successfully populated, else an error code.
+ */
+static int ensure_disk_name_from_dir_item(struct btrfs_path *path,
+ struct btrfs_dir_item *di,
+ struct fscrypt_name *name)
+{
+ if (name->disk_name.name || !di)
+ return 0;
+
+ return btrfs_fscrypt_get_disk_name(path->nodes[0], di,
+ &name->disk_name);
+}
+
/*
* Lookup for a directory item by fscrypt_name.
*
@@ -257,8 +279,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
key.objectid = dir;
key.type = BTRFS_DIR_ITEM_KEY;
- key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
- /* XXX get the right hash for no-key names */
+
+ if (!name->disk_name.name)
+ key.offset = name->hash | ((u64)name->minor_hash << 32);
+ else
+ key.offset = btrfs_name_hash(name->disk_name.name,
+ name->disk_name.len);
ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
if (ret == 0)
@@ -266,6 +292,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT))
return NULL;
+ if (ret == 0)
+ ret = ensure_disk_name_from_dir_item(path, di, name);
if (ret < 0)
di = ERR_PTR(ret);
@@ -382,7 +410,12 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
btrfs_for_each_slot(root, &key, &key, path, ret) {
if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY)
break;
+
di = btrfs_match_dir_item_fname(root->fs_info, path, name);
+ if (di)
+ ret = ensure_disk_name_from_dir_item(path, di, name);
+ if (ret)
+ break;
if (di)
return di;
}
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index fcd926907f82..bdd58415f93c 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -14,6 +14,33 @@
#include "transaction.h"
#include "xattr.h"
+/*
+ * From a given location in a leaf, read a name into a qstr (usually a
+ * fscrypt_name's disk_name), allocating the required buffer. Used for
+ * nokey names.
+ */
+int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+ struct btrfs_dir_item *dir_item,
+ struct fscrypt_str *name)
+{
+ unsigned long de_name_len = btrfs_dir_name_len(leaf, dir_item);
+ unsigned long de_name = (unsigned long)(dir_item + 1);
+ /*
+ * For no-key names, we use this opportunity to find the disk
+ * name, so future searches don't need to deal with nokey names
+ * and we know what the encrypted size is.
+ */
+ name->name = kmalloc(de_name_len, GFP_NOFS);
+
+ if (!name->name)
+ return -ENOMEM;
+
+ read_extent_buffer(leaf, name->name, de_name, de_name_len);
+
+ name->len = de_name_len;
+ return 0;
+}
+
/*
* This function is extremely similar to fscrypt_match_name() but uses an
* extent_buffer.
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 1647bbbcd609..c08fd52c99b4 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -9,11 +9,22 @@
#include "fs.h"
#ifdef CONFIG_FS_ENCRYPTION
+int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+ struct btrfs_dir_item *di,
+ struct fscrypt_str *qstr);
+
bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
struct extent_buffer *leaf,
unsigned long de_name, u32 de_name_len);
#else
+static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
+ struct btrfs_dir_item *di,
+ struct fscrypt_str *qstr)
+{
+ return 0;
+}
+
static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
struct extent_buffer *leaf,
unsigned long de_name,
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 09/17] btrfs: implement fscrypt ioctls
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (7 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 08/17] btrfs: handle " Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
` (8 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 05984e5448b4..03ea92218749 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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (8 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
` (7 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 3282adc84d52..b6fd4ed78905 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -82,7 +82,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 03ea92218749..91ad59519900 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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 11/17] btrfs: add get_devices hook for fscrypt
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (9 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
` (6 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/fscrypt.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index bdd58415f93c..499073376110 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"
/*
@@ -179,9 +181,44 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
}
+static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
+ unsigned int *num_devs)
+{
+ struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+ int nr_devices = fs_devices->open_devices;
+ struct block_device **devs;
+ struct btrfs_device *device;
+ int i = 0;
+
+ devs = kmalloc_array(nr_devices, sizeof(*devs), GFP_NOFS | GFP_NOWAIT);
+ if (!devs)
+ return ERR_PTR(-ENOMEM);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) {
+ if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA,
+ &device->dev_state) ||
+ !device->bdev ||
+ test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state))
+ continue;
+
+ devs[i++] = device->bdev;
+
+ if (i >= nr_devices)
+ break;
+
+ }
+ rcu_read_unlock();
+
+ *num_devs = i;
+ return devs;
+}
+
const struct fscrypt_operations btrfs_fscrypt_ops = {
.get_context = btrfs_fscrypt_get_context,
.set_context = btrfs_fscrypt_set_context,
.empty_dir = btrfs_fscrypt_empty_dir,
+ .get_devices = btrfs_fscrypt_get_devices,
.key_prefix = "btrfs:"
};
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 12/17] btrfs: turn on inlinecrypt mount option for encrypt
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (10 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
` (5 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ioctl.c | 3 +++
fs/btrfs/super.c | 10 ++++++++++
2 files changed, 13 insertions(+)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 91ad59519900..42525a8119ee 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4574,6 +4574,9 @@ long btrfs_ioctl(struct file *file, unsigned int
* state persists.
*/
btrfs_set_fs_incompat(fs_info, ENCRYPT);
+ if (!(inode->i_sb->s_flags & SB_INLINECRYPT)) {
+ inode->i_sb->s_flags |= SB_INLINECRYPT;
+ }
return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
}
case FS_IOC_GET_ENCRYPTION_POLICY:
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 0cc9c2909f64..1e9a93c6750a 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1165,6 +1165,16 @@ static int btrfs_fill_super(struct super_block *sb,
return err;
}
+ if (btrfs_fs_incompat(fs_info, ENCRYPT)) {
+ if (IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT)) {
+ sb->s_flags |= SB_INLINECRYPT;
+ } else {
+ btrfs_err(fs_info, "encryption not supported");
+ err = -EINVAL;
+ goto fail_close;
+ }
+ }
+
inode = btrfs_iget(sb, BTRFS_FIRST_FREE_OBJECTID, fs_info->fs_root);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 13/17] btrfs: turn on the encryption ioctls
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (11 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
` (4 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 4de42b7ce796..2e13bb615fa6 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -715,6 +715,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 = (page_offset(page) + 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
@@ -753,6 +761,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.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 14/17] btrfs: create and free extent fscrypt_infos
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (12 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
` (3 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 | 18 ++++++++++++++++++
fs/btrfs/extent_map.h | 1 +
fs/btrfs/fscrypt.c | 13 +++++++++++++
fs/btrfs/fscrypt.h | 4 ++++
fs/btrfs/inode.c | 6 ++++++
5 files changed, 42 insertions(+)
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 0cdb3e86f29b..cb4b7517b253 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -9,6 +9,7 @@
#include "extent_map.h"
#include "compression.h"
#include "btrfs_inode.h"
+#include "fscrypt.h"
static struct kmem_cache *extent_map_cache;
@@ -65,6 +66,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 +210,12 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
if (!list_empty(&prev->list) || !list_empty(&next->list))
return 0;
+ /*
+ * Don't merge adjacent encrypted maps.
+ */
+ if (prev->fscrypt_info || next->fscrypt_info)
+ return 0;
+
ASSERT(next->block_start != EXTENT_MAP_DELALLOC &&
prev->block_start != EXTENT_MAP_DELALLOC);
@@ -817,6 +826,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
split->generation = gen;
split->flags = flags;
split->compress_type = em->compress_type;
+ btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info,
+ &split->fscrypt_info);
replace_extent_mapping(em_tree, em, split, modified);
free_extent_map(split);
split = split2;
@@ -858,6 +869,8 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
split->orig_block_len = 0;
}
+ btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info,
+ &split->fscrypt_info);
if (extent_map_in_tree(em)) {
replace_extent_mapping(em_tree, em, split,
modified);
@@ -1019,6 +1032,8 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre,
split_pre->flags = flags;
split_pre->compress_type = em->compress_type;
split_pre->generation = em->generation;
+ btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info,
+ &split_pre->fscrypt_info);
replace_extent_mapping(em_tree, em, split_pre, 1);
@@ -1038,6 +1053,9 @@ int split_extent_map(struct btrfs_inode *inode, u64 start, u64 len, u64 pre,
split_mid->flags = flags;
split_mid->compress_type = em->compress_type;
split_mid->generation = em->generation;
+ btrfs_fscrypt_copy_fscrypt_info(inode, em->fscrypt_info,
+ &split_mid->fscrypt_info);
+
add_extent_mapping(em_tree, split_mid, 1);
/* Once for us */
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 35d27c756e08..3ffb74412d31 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -50,6 +50,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/fscrypt.c b/fs/btrfs/fscrypt.c
index 499073376110..5508cbc6bccb 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -215,6 +215,19 @@ static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
return devs;
}
+void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode,
+ struct fscrypt_info *from,
+ struct fscrypt_info **to_ptr)
+{
+ if (from == NULL) {
+ *to_ptr = NULL;
+ return;
+ }
+
+ fscrypt_get_extent_info_ref(from);
+ *to_ptr = from;
+}
+
const struct fscrypt_operations btrfs_fscrypt_ops = {
.get_context = btrfs_fscrypt_get_context,
.set_context = btrfs_fscrypt_set_context,
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index c08fd52c99b4..a489bb54b3b1 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -37,6 +37,10 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
}
#endif /* CONFIG_FS_ENCRYPTION */
+void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode,
+ struct fscrypt_info *from,
+ struct fscrypt_info **to_ptr);
+
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 053c7170ce6e..7726957284c1 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7319,6 +7319,12 @@ 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);
+ }
+
ret = btrfs_replace_extent_map_range(inode, em, true);
if (ret) {
free_extent_map(em);
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 15/17] btrfs: start tracking extent encryption context info
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (13 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
` (2 subsequent siblings)
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
Cc: Sweet Tea Dorminy
This puts the long-preserved 1-byte encryption field to work, storing
whether the extent is encrypted.
Since there is no way for a btrfs inode to be encrypted right now, we
set context length to be 0 in all locations.
This also adds the new encryption_context member to the end of the
file_extent item, which will begin with a length of the fscrypt context
following it; and an accessor for the context size. This allows checking
that the context size matches the item size.
It could be possible to pack the size and the encryption type into the
u8 encryption field, but this allows more flexibility in the future.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/accessors.h | 3 ++-
fs/btrfs/file-item.c | 5 +++++
fs/btrfs/fscrypt.h | 21 +++++++++++++++++++
fs/btrfs/inode.c | 25 ++++++++++++++++++----
fs/btrfs/tree-checker.c | 37 ++++++++++++++++++++++++++-------
fs/btrfs/tree-log.c | 2 ++
include/uapi/linux/btrfs_tree.h | 12 ++++++++++-
7 files changed, 91 insertions(+), 14 deletions(-)
diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index 8cfc8214109c..93b11d5e21a9 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -935,7 +935,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
struct btrfs_file_extent_item, disk_num_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
struct btrfs_file_extent_item, compression, 8);
-
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
+ struct btrfs_file_extent_item, encryption, 8);
BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
BTRFS_SETGET_FUNCS(file_extent_disk_bytenr, struct btrfs_file_extent_item,
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 696bf695d8eb..f83f7020ed89 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -18,6 +18,7 @@
#include "print-tree.h"
#include "compression.h"
#include "fs.h"
+#include "fscrypt.h"
#include "accessors.h"
#include "file-item.h"
#include "super.h"
@@ -1272,6 +1273,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 -
@@ -1294,6 +1296,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 a489bb54b3b1..26627df9d5f9 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -4,10 +4,31 @@
#define BTRFS_FSCRYPT_H
#include <linux/fscrypt.h>
+#include "accessors.h"
#include "extent_io.h"
#include "fs.h"
+static inline u32
+btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
+ struct btrfs_file_extent_item *e)
+{
+ if (!btrfs_file_extent_encryption(eb, e))
+ return 0;
+
+ return btrfs_get_32(eb, e, offsetof(struct btrfs_file_extent_item,
+ encryption_context));
+}
+
+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));
+}
+
+
#ifdef CONFIG_FS_ENCRYPTION
int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
struct btrfs_dir_item *di,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7726957284c1..ed0579577263 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3000,7 +3000,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
@@ -6929,8 +6929,23 @@ 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);
+ u32 ctxsize = btrfs_file_extent_encryption_ctxsize(leaf, item);
+
+
+ if (encryption == 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) {
/*
@@ -9623,7 +9638,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 038dfa8f1788..7f0fa170f3de 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -26,6 +26,7 @@
#include "volumes.h"
#include "misc.h"
#include "fs.h"
+#include "fscrypt.h"
#include "accessors.h"
#include "file-item.h"
#include "inode-item.h"
@@ -208,6 +209,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 +261,11 @@ static int check_extent_data_item(struct extent_buffer *leaf,
BTRFS_NR_COMPRESS_TYPES - 1);
return -EUCLEAN;
}
- if (unlikely(btrfs_file_extent_encryption(leaf, fi))) {
+ policy = btrfs_file_extent_encryption(leaf, fi);
+ if (unlikely(policy >= BTRFS_NR_ENCRYPTION_TYPES)) {
file_extent_err(leaf, slot,
- "invalid encryption for file extent, have %u expect 0",
- btrfs_file_extent_encryption(leaf, fi));
+ "invalid encryption for file extent, have %u expect range [0, %u]",
+ policy, BTRFS_NR_ENCRYPTION_TYPES - 1);
return -EUCLEAN;
}
if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
@@ -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 3bf746668e07..9c73ae09f0a1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4634,6 +4634,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
u64 extent_offset = em->start - em->orig_start;
u64 block_len;
int ret;
+ u8 encryption = 0;
btrfs_set_stack_file_extent_generation(&fi, trans->transid);
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4655,6 +4656,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
btrfs_set_stack_file_extent_num_bytes(&fi, em->len);
btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes);
btrfs_set_stack_file_extent_compression(&fi, em->compress_type);
+ btrfs_set_stack_file_extent_encryption(&fi, encryption);
ret = log_extent_csums(trans, inode, log, em, ctx);
if (ret)
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index 029af0aeb65d..62b95ceee735 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1048,8 +1048,14 @@ struct btrfs_file_extent_item {
* but not for stat.
*/
__u8 compression;
+
+ /*
+ * Type of encryption in use. Unencrypted value is 0.
+ */
__u8 encryption;
- __le16 other_encoding; /* spare for later use */
+
+ /* spare for later use */
+ __le16 other_encoding;
/* are we inline data or a real extent? */
__u8 type;
@@ -1075,6 +1081,10 @@ struct btrfs_file_extent_item {
* always reflects the size uncompressed and without encoding.
*/
__le64 num_bytes;
+ /*
+ * the encryption context, if any
+ */
+ __u8 encryption_context[0];
} __attribute__ ((__packed__));
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 16/17] btrfs: explicitly track file extent length and encryption
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (14 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
2023-08-09 20:39 ` [PATCH v3 00/17] btrfs: add encryption feature Josef Bacik
17 siblings, 0 replies; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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.
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 | 8 +++++---
5 files changed, 17 insertions(+), 7 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 73038908876a..4988c9317234 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2246,14 +2246,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 ed0579577263..de6778fe65c8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2885,6 +2885,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();
@@ -2904,7 +2907,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;
@@ -2915,7 +2918,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;
}
@@ -9629,6 +9632,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));
@@ -9661,6 +9665,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 9c73ae09f0a1..49001bf8a0b2 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4634,7 +4634,9 @@ 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 = 0;
+ size_t fscrypt_context_size = 0;
+ u8 encryption = IS_ENCRYPTED(&inode->vfs_inode) ?
+ BTRFS_ENCRYPTION_FSCRYPT : 0;
btrfs_set_stack_file_extent_generation(&fi, trans->transid);
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4676,7 +4678,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
drop_args.start = em->start;
drop_args.end = em->start + em->len;
drop_args.replace_extent = true;
- drop_args.extent_item_size = sizeof(fi);
+ drop_args.extent_item_size = sizeof(fi) + fscrypt_context_size;
ret = btrfs_drop_extents(trans, log, inode, &drop_args);
if (ret)
return ret;
@@ -4688,7 +4690,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
key.offset = em->start;
ret = btrfs_insert_empty_item(trans, log, path, &key,
- sizeof(fi));
+ sizeof(fi) + fscrypt_context_size);
if (ret)
return ret;
}
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (15 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
@ 2023-08-08 17:12 ` Sweet Tea Dorminy
2023-08-09 20:38 ` Josef Bacik
2023-08-09 20:39 ` [PATCH v3 00/17] btrfs: add encryption feature Josef Bacik
17 siblings, 1 reply; 22+ messages in thread
From: Sweet Tea Dorminy @ 2023-08-08 17:12 UTC (permalink / raw)
To: Chris Mason, Josef Bacik, David Sterba, Theodore Y . Ts'o,
Jaegeuk Kim, kernel-team, linux-btrfs, linux-fscrypt,
Eric Biggers
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 | 7 +++
fs/btrfs/fscrypt.c | 108 ++++++++++++++++++++++++++++++++++++++++++-
fs/btrfs/fscrypt.h | 35 ++++++++++++++
fs/btrfs/inode.c | 37 +++++++++++++--
fs/btrfs/tree-log.c | 14 +++++-
5 files changed, 194 insertions(+), 7 deletions(-)
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index f83f7020ed89..880fb7810152 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1299,6 +1299,13 @@ 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));
+
+ if (ctxsize) {
+ unsigned long ptr = (unsigned long)fi->encryption_context;
+ int res = btrfs_fscrypt_load_extent_info(inode, leaf, ptr,
+ ctxsize, &em->fscrypt_info);
+ ASSERT(res == 0);
+ }
} 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 5508cbc6bccb..7324375af0ac 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -75,6 +75,65 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
return !memcmp(digest, nokey_name->sha256, sizeof(digest));
}
+int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer, size_t *context_len)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ int ret;
+
+ if (!IS_ENCRYPTED(&inode->vfs_inode))
+ return 0;
+
+
+ ret = fscrypt_set_extent_context(info, context_buffer + sizeof(u32),
+ FSCRYPT_SET_CONTEXT_MAX_SIZE);
+ if (ret < 0) {
+ btrfs_err(fs_info, "fscrypt context could not be saved");
+ return ret;
+ }
+
+ /* the return value, if nonnegative, is the fscrypt context size */
+ ret += sizeof(u32);
+
+ put_unaligned_le32(ret, context_buffer);
+
+ *context_len = ret;
+ return 0;
+}
+
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr,
+ u8 ctxsize,
+ struct fscrypt_info **info_ptr)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ int res;
+ unsigned int nofs_flags;
+ u32 len;
+
+ read_extent_buffer(leaf, context, ptr, ctxsize);
+
+ nofs_flags = memalloc_nofs_save();
+ res = fscrypt_load_extent_info(&inode->vfs_inode,
+ context + sizeof(u32),
+ ctxsize - sizeof(u32), info_ptr);
+ memalloc_nofs_restore(nofs_flags);
+
+ if (res)
+ btrfs_err(fs_info, "Unable to load fscrypt info: %d", res);
+
+ len = get_unaligned_le32(context);
+ if (len != ctxsize) {
+ res = -EINVAL;
+ btrfs_err(fs_info, "fscrypt info size mismatches");
+ }
+
+ return res;
+}
+
static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
struct btrfs_key key = {
@@ -138,11 +197,14 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
if (!trans)
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
- if (IS_ERR(trans))
+ if (IS_ERR(trans)) {
+ btrfs_free_path(path);
return PTR_ERR(trans);
+ }
ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
if (ret < 0) {
+ btrfs_free_path(path);
btrfs_abort_transaction(trans, ret);
return ret;
}
@@ -151,12 +213,13 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
btrfs_release_path(path);
ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len);
if (ret) {
+ btrfs_free_path(path);
btrfs_abort_transaction(trans, ret);
return ret;
}
}
-
btrfs_fscrypt_update_context(path, ctx, len);
+ btrfs_free_path(path);
if (fs_data)
return ret;
@@ -166,6 +229,7 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
inode_inc_iversion(inode);
inode->i_ctime = current_time(inode);
ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode));
+
if (!ret) {
btrfs_end_transaction(trans);
return ret;
@@ -181,6 +245,45 @@ 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) {
+ btrfs_err(BTRFS_I(inode)->root->fs_info,
+ "extent context requested for block %llu of inode %lu without an extent",
+ lblk_num, inode->i_ino);
+ 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->orig_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)
{
@@ -232,6 +335,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 26627df9d5f9..46d003b0e4c3 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -9,6 +9,9 @@
#include "fs.h"
+#define BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE \
+ (sizeof(u32) + FSCRYPT_SET_CONTEXT_MAX_SIZE)
+
static inline u32
btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
struct btrfs_file_extent_item *e)
@@ -38,6 +41,15 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
struct extent_buffer *leaf,
unsigned long de_name, u32 de_name_len);
+int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer, size_t *context_len);
+
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr, u8 ctxsize,
+ struct fscrypt_info **info_ptr);
+
#else
static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
struct btrfs_dir_item *di,
@@ -56,8 +68,31 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
de_name_len);
}
+
+static inline int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer,
+ size_t *context_len)
+{
+ return -EINVAL;
+}
+
+static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr,
+ u8 ctxsize,
+ struct fscrypt_info **info_ptr)
+{
+ return -EINVAL;
+}
+
#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);
void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode,
struct fscrypt_info *from,
struct fscrypt_info **to_ptr);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index de6778fe65c8..de9e7073017f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2885,17 +2885,41 @@ 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;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ u8 encryption = 0;
+
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
+ if (IS_ENCRYPTED(&inode->vfs_inode)) {
+ 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;
+ }
+
+ ret = btrfs_fscrypt_fill_extent_context(inode, fscrypt_info,
+ context,
+ &fscrypt_context_size);
+ if (ret)
+ goto out;
+ encryption = BTRFS_ENCRYPTION_FSCRYPT;
+
+ }
+
+ btrfs_set_stack_file_extent_encryption(stack_fi, 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.
*
@@ -2928,6 +2952,11 @@ 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));
+ write_extent_buffer(leaf, context,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(struct btrfs_file_extent_item),
+ fscrypt_context_size);
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 49001bf8a0b2..28b86e338434 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -22,6 +22,7 @@
#include "zoned.h"
#include "inode-item.h"
#include "fs.h"
+#include "fscrypt.h"
#include "accessors.h"
#include "extent-tree.h"
#include "root-tree.h"
@@ -4637,6 +4638,14 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
size_t fscrypt_context_size = 0;
u8 encryption = IS_ENCRYPTED(&inode->vfs_inode) ?
BTRFS_ENCRYPTION_FSCRYPT : 0;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+
+ ret = btrfs_fscrypt_fill_extent_context(inode, em->fscrypt_info,
+ context, &fscrypt_context_size);
+ if (ret)
+ return ret;
+
+ btrfs_set_stack_file_extent_encryption(&fi, encryption);
btrfs_set_stack_file_extent_generation(&fi, trans->transid);
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4658,7 +4667,6 @@ 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)
@@ -4698,6 +4706,10 @@ 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));
+ write_extent_buffer(leaf, context,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(fi), fscrypt_context_size);
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
--
2.41.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v3 05/17] btrfs: add inode encryption contexts
2023-08-08 17:12 ` [PATCH v3 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
@ 2023-08-09 20:20 ` Josef Bacik
0 siblings, 0 replies; 22+ messages in thread
From: Josef Bacik @ 2023-08-09 20:20 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Chris Mason, David Sterba, Theodore Y . Ts'o, Jaegeuk Kim,
kernel-team, linux-btrfs, linux-fscrypt, Eric Biggers,
Omar Sandoval
On Tue, Aug 08, 2023 at 01:12:07PM -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 | 118 ++++++++++++++++++++++++++++++++
> 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, 155 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
> index 3a53dc59c1e4..d09d42210f37 100644
> --- a/fs/btrfs/fscrypt.c
> +++ b/fs/btrfs/fscrypt.c
> @@ -1,8 +1,126 @@
> // SPDX-License-Identifier: GPL-2.0
>
> +#include <linux/iversion.h>
> #include "ctree.h"
> +#include "accessors.h"
> +#include "btrfs_inode.h"
> +#include "disk-io.h"
> +#include "fs.h"
> #include "fscrypt.h"
> +#include "ioctl.h"
> +#include "messages.h"
> +#include "transaction.h"
> +#include "xattr.h"
> +
> +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
> +{
> + struct btrfs_key key = {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
> + .offset = 0,
> + };
> + struct btrfs_path *path;
> + struct extent_buffer *leaf;
> + unsigned long ptr;
> + int ret;
> +
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
> + if (ret) {
> + len = -ENOENT;
> + goto out;
> + }
> +
> + leaf = path->nodes[0];
> + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
> + /* fscrypt provides max context length, but it could be less */
> + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
> + read_extent_buffer(leaf, ctx, ptr, len);
> +
> +out:
> + btrfs_free_path(path);
> + return len;
> +}
> +
> +static 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_trans_handle *trans = fs_data;
> + struct btrfs_key key = {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTX_ITEM_KEY,
> + .offset = 0,
> + };
> + struct btrfs_path *path;
> + int ret;
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + if (!trans)
> + trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
This doesn't appear to be called without a trans, so this whole !trans case can
be eliminated.
Additionally it doesn't appear the inode context will exist ever, so you can
just unconditionally insert the empty item and update it. Thanks,
Josef
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 08/17] btrfs: handle nokey names.
2023-08-08 17:12 ` [PATCH v3 08/17] btrfs: handle " Sweet Tea Dorminy
@ 2023-08-09 20:32 ` Josef Bacik
0 siblings, 0 replies; 22+ messages in thread
From: Josef Bacik @ 2023-08-09 20:32 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Chris Mason, David Sterba, Theodore Y . Ts'o, Jaegeuk Kim,
kernel-team, linux-btrfs, linux-fscrypt, Eric Biggers
On Tue, Aug 08, 2023 at 01:12:10PM -0400, Sweet Tea Dorminy wrote:
> For encrypted or unencrypted names, we calculate the offset for the dir
> item by hashing the name for the dir item. However, this doesn't work
> for a long nokey name, where we do not have the complete ciphertext.
> Instead, fscrypt stores the filesystem-provided hash in the nokey name,
> and we can extract it from the fscrypt_name structure in such a case.
>
> Additionally, for nokey names, if we find the nokey name on disk we can
> update the fscrypt_name with the disk name, so add that to searching for
> diritems.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/dir-item.c | 37 +++++++++++++++++++++++++++++++++++--
> fs/btrfs/fscrypt.c | 27 +++++++++++++++++++++++++++
> fs/btrfs/fscrypt.h | 11 +++++++++++
> 3 files changed, 73 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
> index da95ae411d72..ee7dad888f53 100644
> --- a/fs/btrfs/dir-item.c
> +++ b/fs/btrfs/dir-item.c
> @@ -231,6 +231,28 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
> return di;
> }
>
> +/*
> + * If appropriate, populate the disk name for a fscrypt_name looked up without
> + * a key.
> + *
> + * @path: The path to the extent buffer in which the name was found.
> + * @di: The dir item corresponding.
> + * @fname: The fscrypt_name to perhaps populate.
> + *
> + * Returns: 0 if the name is already populated or the dir item doesn't exist
> + * or the name was successfully populated, else an error code.
> + */
> +static int ensure_disk_name_from_dir_item(struct btrfs_path *path,
> + struct btrfs_dir_item *di,
> + struct fscrypt_name *name)
> +{
> + if (name->disk_name.name || !di)
> + return 0;
> +
> + return btrfs_fscrypt_get_disk_name(path->nodes[0], di,
> + &name->disk_name);
> +}
> +
> /*
> * Lookup for a directory item by fscrypt_name.
> *
> @@ -257,8 +279,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
>
> key.objectid = dir;
> key.type = BTRFS_DIR_ITEM_KEY;
> - key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
> - /* XXX get the right hash for no-key names */
> +
> + if (!name->disk_name.name)
> + key.offset = name->hash | ((u64)name->minor_hash << 32);
> + else
> + key.offset = btrfs_name_hash(name->disk_name.name,
> + name->disk_name.len);
>
> ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
> if (ret == 0)
> @@ -266,6 +292,8 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
>
> if (ret == -ENOENT || (di && IS_ERR(di) && PTR_ERR(di) == -ENOENT))
> return NULL;
> + if (ret == 0)
> + ret = ensure_disk_name_from_dir_item(path, di, name);
> if (ret < 0)
> di = ERR_PTR(ret);
>
> @@ -382,7 +410,12 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
> btrfs_for_each_slot(root, &key, &key, path, ret) {
> if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY)
> break;
> +
> di = btrfs_match_dir_item_fname(root->fs_info, path, name);
> + if (di)
> + ret = ensure_disk_name_from_dir_item(path, di, name);
> + if (ret)
> + break;
This is a little wonky, I'd rather just
if (!di)
continue;
ret = ensure_disk_name_from_dir_item(path, di, name);
if (ret)
break
return di;
Thanks,
Josef
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts
2023-08-08 17:12 ` [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
@ 2023-08-09 20:38 ` Josef Bacik
0 siblings, 0 replies; 22+ messages in thread
From: Josef Bacik @ 2023-08-09 20:38 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Chris Mason, David Sterba, Theodore Y . Ts'o, Jaegeuk Kim,
kernel-team, linux-btrfs, linux-fscrypt, Eric Biggers
On Tue, Aug 08, 2023 at 01:12:19PM -0400, Sweet Tea Dorminy wrote:
> 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 | 7 +++
> fs/btrfs/fscrypt.c | 108 ++++++++++++++++++++++++++++++++++++++++++-
> fs/btrfs/fscrypt.h | 35 ++++++++++++++
> fs/btrfs/inode.c | 37 +++++++++++++--
> fs/btrfs/tree-log.c | 14 +++++-
> 5 files changed, 194 insertions(+), 7 deletions(-)
>
> diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
> index f83f7020ed89..880fb7810152 100644
> --- a/fs/btrfs/file-item.c
> +++ b/fs/btrfs/file-item.c
> @@ -1299,6 +1299,13 @@ 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));
> +
> + if (ctxsize) {
> + unsigned long ptr = (unsigned long)fi->encryption_context;
> + int res = btrfs_fscrypt_load_extent_info(inode, leaf, ptr,
> + ctxsize, &em->fscrypt_info);
> + ASSERT(res == 0);
> + }
> } 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 5508cbc6bccb..7324375af0ac 100644
> --- a/fs/btrfs/fscrypt.c
> +++ b/fs/btrfs/fscrypt.c
> @@ -75,6 +75,65 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
> return !memcmp(digest, nokey_name->sha256, sizeof(digest));
> }
>
> +int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
> + struct fscrypt_info *info,
> + u8 *context_buffer, size_t *context_len)
> +{
> + struct btrfs_fs_info *fs_info = inode->root->fs_info;
> + int ret;
> +
> + if (!IS_ENCRYPTED(&inode->vfs_inode))
> + return 0;
> +
> +
> + ret = fscrypt_set_extent_context(info, context_buffer + sizeof(u32),
> + FSCRYPT_SET_CONTEXT_MAX_SIZE);
> + if (ret < 0) {
> + btrfs_err(fs_info, "fscrypt context could not be saved");
> + return ret;
> + }
> +
> + /* the return value, if nonnegative, is the fscrypt context size */
> + ret += sizeof(u32);
> +
> + put_unaligned_le32(ret, context_buffer);
> +
> + *context_len = ret;
> + return 0;
> +}
> +
> +int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
> + struct extent_buffer *leaf,
> + unsigned long ptr,
> + u8 ctxsize,
> + struct fscrypt_info **info_ptr)
> +{
> + struct btrfs_fs_info *fs_info = inode->root->fs_info;
> + u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
> + int res;
> + unsigned int nofs_flags;
> + u32 len;
> +
> + read_extent_buffer(leaf, context, ptr, ctxsize);
> +
> + nofs_flags = memalloc_nofs_save();
> + res = fscrypt_load_extent_info(&inode->vfs_inode,
> + context + sizeof(u32),
> + ctxsize - sizeof(u32), info_ptr);
> + memalloc_nofs_restore(nofs_flags);
> +
> + if (res)
> + btrfs_err(fs_info, "Unable to load fscrypt info: %d", res);
We are we not returning an error here? Seems like we could end up with garbage
in context and then we'd just turn the error into -EINVAL below.
> +
> + len = get_unaligned_le32(context);
> + if (len != ctxsize) {
> + res = -EINVAL;
> + btrfs_err(fs_info, "fscrypt info size mismatches");
> + }
> +
> + return res;
> +}
> +
> static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
> {
> struct btrfs_key key = {
> @@ -138,11 +197,14 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
>
> if (!trans)
> trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
> - if (IS_ERR(trans))
> + if (IS_ERR(trans)) {
> + btrfs_free_path(path);
> return PTR_ERR(trans);
> + }
>
> ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
> if (ret < 0) {
> + btrfs_free_path(path);
> btrfs_abort_transaction(trans, ret);
> return ret;
> }
> @@ -151,12 +213,13 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
> btrfs_release_path(path);
> ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len);
> if (ret) {
> + btrfs_free_path(path);
> btrfs_abort_transaction(trans, ret);
> return ret;
> }
> }
> -
> btrfs_fscrypt_update_context(path, ctx, len);
> + btrfs_free_path(path);
>
These are unrelated changes, but my earlier comment was about re-working this
function, so I assume this will go away once you implement the changes I asked
for. Thanks,
Josef
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 00/17] btrfs: add encryption feature
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
` (16 preceding siblings ...)
2023-08-08 17:12 ` [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
@ 2023-08-09 20:39 ` Josef Bacik
17 siblings, 0 replies; 22+ messages in thread
From: Josef Bacik @ 2023-08-09 20:39 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Chris Mason, David Sterba, Theodore Y . Ts'o, Jaegeuk Kim,
kernel-team, linux-btrfs, linux-fscrypt, Eric Biggers
On Tue, Aug 08, 2023 at 01:12:02PM -0400, Sweet Tea Dorminy wrote:
> Encryption has been desired for btrfs for a long time, in order to
> provide some measure of security for data at rest. However, since btrfs
> supports snapshots and reflinks, fscrypt encryption has previously been
> incompatible since it relies on single inode ownership of data
> locations. A design for fscrypt to support btrfs's requirements, and for
> btrfs to use encryption, was constructed in October '21 [1] and refined
> in November '22 [2].
>
> This patch series builds on two fscrypt patch series adding extent-based
> encryption to fscrypt, which allows using fscrypt in btrfs. The fscrypt
> patchsets have no effect without a user, and this patchset makes btrfs
> use the new extent encryption abilities of fscrypt.
>
> These constitute the first of several steps laid out in the design
> document [2]: the second step will be adding authenticated encryption
> support to the block layer, fscrypt, and then btrfs. Other steps will
> potentially add the ability to change the key used by a directory
> (either for all data or just newly written data), allow use of inline
> extents and verity items in combination with encryption, and enable
> send/receive of encrypted volumes. This changeset is not suitable for
> usage due to the lack of authenticated encryption.
>
> In addition to the fscrypt patchsets, [3] [4], this changeset requires
> the latest version of the btrfs-progs changeset, which is currently at
> [5], and the latest version of the fstests changeset, [6]. It is based
> on kdave/misc-next as of approximately now.
>
> This changeset passes all encryption tests in fstests, and also survives
> fsperf runs with lockdep turned on, including the previously failing
> dbench test.
>
> This version changes the format of extent contexts on disk as per
> Josef's comment on v2: the encryption field in file extents now only
> stores the fact of encryption with fscrypt, and the context stored at
> the end of the file extent now stores the length of the fscrypt extent
> as well as the fscrypt extent itself.
>
> I remain really excited about Qu's work to make extent buffers
> potentially be either folios or vmalloc'd memory -- this would allow
> eliminating change 'fscrypt: expose fscrypt_nokey_name' and the code
> using it.
>
Generally speaking I'm pretty happy with the state of this patchset. Please do
the fixups I've asked for and refresh, but I think we're getting close to the
end here. Thanks,
Josef
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2023-08-09 20:40 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-08 17:12 [PATCH v3 00/17] btrfs: add encryption feature Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 01/17] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 02/17] btrfs: disable verity " Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 03/17] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 04/17] btrfs: start using fscrypt hooks Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 05/17] btrfs: add inode encryption contexts Sweet Tea Dorminy
2023-08-09 20:20 ` Josef Bacik
2023-08-08 17:12 ` [PATCH v3 06/17] btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 07/17] btrfs: adapt readdir for encrypted and nokey names Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 08/17] btrfs: handle " Sweet Tea Dorminy
2023-08-09 20:32 ` Josef Bacik
2023-08-08 17:12 ` [PATCH v3 09/17] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 10/17] btrfs: add encryption to CONFIG_BTRFS_DEBUG Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 11/17] btrfs: add get_devices hook for fscrypt Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 12/17] btrfs: turn on inlinecrypt mount option for encrypt Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 13/17] btrfs: turn on the encryption ioctls Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 14/17] btrfs: create and free extent fscrypt_infos Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 15/17] btrfs: start tracking extent encryption context info Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 16/17] btrfs: explicitly track file extent length and encryption Sweet Tea Dorminy
2023-08-08 17:12 ` [PATCH v3 17/17] btrfs: save and load fscrypt extent contexts Sweet Tea Dorminy
2023-08-09 20:38 ` Josef Bacik
2023-08-09 20:39 ` [PATCH v3 00/17] btrfs: add encryption feature Josef Bacik
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).