* [PATCH v2 00/20] btrfs: add fscrypt integration
@ 2022-09-06 0:35 Sweet Tea Dorminy
2022-09-06 0:35 ` [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
` (21 more replies)
0 siblings, 22 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
This is a changeset adding encryption to btrfs.
Last October, Omar Sandoval sent out a design document for having fscrypt
integration with btrfs [1]. In summary, it proposes btrfs storing its
own encryption IVs on a per-file-extent basis. fscrypt usually encrypts
files using an IV derived from per-inode information; this would prevent
snapshotting or reflinking or data relocation for btrfs. We have
refined this into a fscrypt extent context object, opaque to the
filesystem, which fscrypt uses to generate an IV associated with each
block in an extent. Thus, all the inodes sharing a particular
key and file extent may decrypt the extent.
This series implements this integration for the simple
case, non-compressed data extents. Followup changes will allow
encryption of compressed extents, inline extents, and verity items, and
will add tests around subvolume encryption. This series should provide
encryption for the simplest cases, but this series should not be used in
production, as there are likely bugs.
Preliminary btrfs-progs changes are available at [2]; fstests changes
are available at [3].
[1]
https://lore.kernel.org/linux-btrfs/YXGyq+buM79A1S0L@relinquished.localdomain/
Changelog:
v2:
- Amended the fscrypt side to generically add extent contexts,
hopefully as per Eric Biggers' past comments. IVs are now entirely
abstracted within an extent context, and there is no longer a new
encryption policy, as DIRECT_KEY sufficiently encapsulates the
needs of extent-based encryption. Documented its usage in btrfs
briefly in the documentation.
- Adjusted the btrfs side to deal in opaque extent contexts. Improved
optimization to skip storing inode contexts if they are the same as
the inode's root item's inode context.
- Combined 'add fscrypt operation table to superblock' into 'start
using fscrypt hooks'.
- https://lore.kernel.org/linux-btrfs/cover.1662420176.git.sweettea-kernel@dorminy.me
- progs: https://lore.kernel.org/linux-btrfs/cover.1662417859.git.sweettea-kernel@dorminy.me
- tests: https://lore.kernel.org/linux-btrfs/cover.1662417905.git.sweettea-kernel@dorminy.me
v1:
- Recombined the fscrypt changes back into this patchset.
- Fixed several races and incorrectly ordered operations.
- Improved IV retrieval to correctly distinguish between
filename/symlink encryption and encryption of block 0 of a file.
- https://lore.kernel.org/linux-btrfs/cover.1660744500.git.sweettea-kernel@dorminy.me
- progs: https://lore.kernel.org/linux-btrfs/cover.1660729916.git.sweettea-kernel@dorminy.me
- tests: https://lore.kernel.org/linux-btrfs/cover.1660729861.git.sweettea-kernel@dorminy.me
RFC v2:
- Fixed all warnings and known incorrectnesses.
- Split fscrypt changes into their own patchset:
https://lore.kernel.org/linux-fscrypt/cover.1658623235.git.sweettea-kernel@dorminy.me
- Combined and reordered changes so that enabling fscrypt is the last change.
- Removed unnecessary factoring.
- Split a cleanup change off.
- https://lore.kernel.org/linux-btrfs/cover.1658623319.git.sweettea-kernel@dorminy.me
RFC v1:
- https://lore.kernel.org/linux-btrfs/cover.1657707686.git.sweettea-kernel@dorminy.me
Omar Sandoval (14):
fscrypt: expose fscrypt_nokey_name
fscrypt: add flag allowing partially-encrypted directories
fscrypt: add fscrypt_have_same_policy() to check inode compatibility
btrfs: store directory's encryption state
btrfs: factor a fscrypt_name matching method
btrfs: disable various operations on encrypted inodes
btrfs: start using fscrypt hooks.
btrfs: add fscrypt_context items.
btrfs: translate btrfs encryption flags and encrypted inode flag.
btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
btrfs: reuse encrypted filename hash when possible.
btrfs: adapt directory read and lookup to potentially encrypted
filenames
btrfs: encrypt normal file extent data if appropriate
btrfs: implement fscrypt ioctls
Sweet Tea Dorminy (6):
fscrypt: allow fscrypt_generate_iv() to distinguish filenames
fscrypt: add extent-based encryption
fscrypt: document btrfs' fscrypt quirks.
btrfs: use fscrypt_names instead of name/len everywhere.
btrfs: setup fscrypt_names from dentrys using helper
btrfs: store a fscrypt extent context per normal file extent
Documentation/filesystems/fscrypt.rst | 62 ++-
fs/btrfs/Makefile | 1 +
fs/btrfs/btrfs_inode.h | 3 +
fs/btrfs/ctree.h | 119 ++++--
fs/btrfs/delayed-inode.c | 48 ++-
fs/btrfs/delayed-inode.h | 9 +-
fs/btrfs/dir-item.c | 119 +++---
fs/btrfs/extent_io.c | 93 ++++-
fs/btrfs/extent_io.h | 2 +
fs/btrfs/extent_map.h | 4 +
fs/btrfs/file-item.c | 22 +-
fs/btrfs/file.c | 11 +-
fs/btrfs/fscrypt.c | 244 +++++++++++
fs/btrfs/fscrypt.h | 49 +++
fs/btrfs/inode-item.c | 84 ++--
fs/btrfs/inode-item.h | 14 +-
fs/btrfs/inode.c | 581 +++++++++++++++++++-------
fs/btrfs/ioctl.c | 80 +++-
fs/btrfs/ordered-data.c | 9 +-
fs/btrfs/ordered-data.h | 4 +-
fs/btrfs/print-tree.c | 4 +-
fs/btrfs/props.c | 11 +-
fs/btrfs/reflink.c | 8 +
fs/btrfs/root-tree.c | 20 +-
fs/btrfs/send.c | 141 ++++---
fs/btrfs/super.c | 8 +-
fs/btrfs/transaction.c | 43 +-
fs/btrfs/tree-checker.c | 56 ++-
fs/btrfs/tree-log.c | 237 ++++++-----
fs/btrfs/tree-log.h | 4 +-
fs/btrfs/xattr.c | 21 +-
fs/crypto/crypto.c | 24 +-
fs/crypto/fname.c | 60 +--
fs/crypto/fscrypt_private.h | 26 +-
fs/crypto/inline_crypt.c | 29 +-
fs/crypto/policy.c | 103 +++++
include/linux/fscrypt.h | 81 ++++
include/uapi/linux/btrfs.h | 1 +
include/uapi/linux/btrfs_tree.h | 26 ++
39 files changed, 1881 insertions(+), 580 deletions(-)
create mode 100644 fs/btrfs/fscrypt.c
create mode 100644 fs/btrfs/fscrypt.h
--
2.35.1
^ permalink raw reply [flat|nested] 66+ messages in thread
* [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 13:41 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
` (20 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
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>
---
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 12bd61d20f69..6c092a1533f7 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 7d2f1e0f23b1..a236d8c6d0da 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.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
2022-09-06 0:35 ` [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 13:43 ` Josef Bacik
` (2 more replies)
2022-09-06 0:35 ` [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
` (19 subsequent siblings)
21 siblings, 3 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Creating several new subvolumes out of snapshots of another subvolume,
each for a different VM's storage, is a important usecase for btrfs. We
would like to give each VM a unique encryption key to use for new writes
to its subvolume, so that secure deletion of the VM's data is as simple
as securely deleting the key; to avoid needing multiple keys in each VM,
we envision the initial subvolume being unencrypted. However, this means
that the snapshot's directories would have a mix of encrypted and
unencrypted files. During lookup with a key, both unencrypted and
encrypted forms of the desired name must be queried.
To allow this, add another FS_CFLG to allow filesystems to opt into
partially encrypted directories.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/fname.c | 17 ++++++++++++++++-
include/linux/fscrypt.h | 2 ++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 6c092a1533f7..3bdece33e14d 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -414,6 +414,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
fname->usr_fname = iname;
if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) {
+unencrypted:
fname->disk_name.name = (unsigned char *)iname->name;
fname->disk_name.len = iname->len;
return 0;
@@ -448,8 +449,16 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
* user-supplied name
*/
- if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED)
+ if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) {
+ /*
+ * This isn't a valid nokey name, but it could be an unencrypted
+ * name if the filesystem allows partially encrypted
+ * directories.
+ */
+ if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL)
+ goto unencrypted;
return -ENOENT;
+ }
fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL);
if (fname->crypto_buf.name == NULL)
@@ -460,6 +469,12 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) ||
(ret > offsetof(struct fscrypt_nokey_name, sha256) &&
ret != FSCRYPT_NOKEY_NAME_MAX)) {
+ /* Again, this could be an unencrypted name. */
+ if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) {
+ kfree(fname->crypto_buf.name);
+ fname->crypto_buf.name = NULL;
+ goto unencrypted;
+ }
ret = -ENOENT;
goto errout;
}
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index a236d8c6d0da..a4e00314c91b 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -102,6 +102,8 @@ struct fscrypt_nokey_name {
* pages for writes and therefore won't need the fscrypt bounce page pool.
*/
#define FS_CFLG_OWN_PAGES (1U << 1)
+/* The filesystem allows partially encrypted directories/files. */
+#define FS_CFLG_ALLOW_PARTIAL (1U << 2)
/* Crypto operations for filesystems */
struct fscrypt_operations {
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
2022-09-06 0:35 ` [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 13:53 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
` (18 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Btrfs will need to check whether inode policies are identical for
various purposes: if two inodes want to share an extent, they must have
the same policy, including key identifier; symlinks must not span the
encrypted/unencrypted border; and certain encryption policies will allow
btrfs to store one fscrypt_context for multiple objects. Therefore, add
a function which allows checking the encryption policies of two inodes
to ensure they are identical.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/policy.c | 26 ++++++++++++++++++++++++++
include/linux/fscrypt.h | 1 +
2 files changed, 27 insertions(+)
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 80b8ca0f340b..ed8b7b6531e5 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -415,6 +415,32 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy)
return fscrypt_policy_from_context(policy, &ctx, ret);
}
+/**
+ * fscrypt_have_same_policy() - check whether two inodes have the same policy
+ * @inode1: the first inode
+ * @inode2: the second inode
+ *
+ * Return: %true if equal, else %false
+ */
+int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2)
+{
+ union fscrypt_policy policy1, policy2;
+ int err;
+
+ if (!IS_ENCRYPTED(inode1) && !IS_ENCRYPTED(inode2))
+ return true;
+ else if (!IS_ENCRYPTED(inode1) || !IS_ENCRYPTED(inode2))
+ return false;
+ err = fscrypt_get_policy(inode1, &policy1);
+ if (err)
+ return err;
+ err = fscrypt_get_policy(inode2, &policy2);
+ if (err)
+ return err;
+ return fscrypt_policies_equal(&policy1, &policy2);
+}
+EXPORT_SYMBOL(fscrypt_have_same_policy);
+
static int set_encryption_policy(struct inode *inode,
const union fscrypt_policy *policy)
{
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index a4e00314c91b..16d2252d5562 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -318,6 +318,7 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page)
void fscrypt_free_bounce_page(struct page *bounce_page);
/* policy.c */
+int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2);
int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg);
int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg);
int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg);
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (2 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 14:01 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
` (17 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
With the introduction of extent-based file content encryption, filenames
and file contents might no longer use the same IV generation scheme, and
so should not upass the same logical block number to
fscrypt_generate_iv(). In preparation, start passing U64_MAX as the
block number for filename IV generation, and make fscrypt_generate_iv()
translate this to 0 if extent-based encryption is not being used.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/crypto.c | 9 ++++++++-
fs/crypto/fname.c | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index e78be66bbf01..7fe5979fbea2 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -71,7 +71,7 @@ EXPORT_SYMBOL(fscrypt_free_bounce_page);
/*
* Generate the IV for the given logical block number within the given file.
- * For filenames encryption, lblk_num == 0.
+ * For filenames encryption, lblk_num == U64_MAX.
*
* Keep this in sync with fscrypt_limit_io_blocks(). fscrypt_limit_io_blocks()
* needs to know about any IV generation methods where the low bits of IV don't
@@ -84,6 +84,13 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
memset(iv, 0, ci->ci_mode->ivsize);
+ /*
+ * Filename encryption. For inode-based policies, filenames are
+ * encrypted as though they are lblk 0.
+ */
+ if (lblk_num == U64_MAX)
+ lblk_num = 0;
+
if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
WARN_ON_ONCE(lblk_num > U32_MAX);
WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX);
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 3bdece33e14d..16b64e0589e4 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -79,7 +79,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
memset(out + iname->len, 0, olen - iname->len);
/* Initialize the IV */
- fscrypt_generate_iv(&iv, 0, ci);
+ fscrypt_generate_iv(&iv, U64_MAX, ci);
/* Set up the encryption request */
req = skcipher_request_alloc(tfm, GFP_NOFS);
@@ -134,7 +134,7 @@ static int fname_decrypt(const struct inode *inode,
crypto_req_done, &wait);
/* Initialize IV */
- fscrypt_generate_iv(&iv, 0, ci);
+ fscrypt_generate_iv(&iv, U64_MAX, ci);
/* Create decryption request */
sg_init_one(&src_sg, iname->name, iname->len);
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 05/20] fscrypt: add extent-based encryption
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (3 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 19:59 ` Omar Sandoval
` (2 more replies)
2022-09-06 0:35 ` [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
` (16 subsequent siblings)
21 siblings, 3 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
Some filesystems need to encrypt data based on extents, rather than on
inodes, due to features incompatible with inode-based encryption. For
instance, btrfs can have multiple inodes referencing a single block of
data, and moves logical data blocks to different physical locations on
disk in the background; these two features mean inode or
physical-location-based policies will not work for btrfs.
This change introduces fscrypt_extent_context objects, in analogy to
existing context objects based on inodes. For a filesystem which uses
extents, a new hook provides a new fscrypt_extent_context. During file
content encryption/decryption, the existing fscrypt_context object
provides key information, while the new fscrypt_extent_context provides
IV information. For filename encryption, the existing IV generation
methods are still used, since filenames are not stored in extents.
As individually keyed inodes prevent sharing of extents, such policies
are forbidden for filesystems with extent-based encryption.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/crypto.c | 15 +++++++-
fs/crypto/fscrypt_private.h | 26 ++++++++++++-
fs/crypto/inline_crypt.c | 29 +++++++++++---
fs/crypto/policy.c | 77 +++++++++++++++++++++++++++++++++++++
include/linux/fscrypt.h | 41 ++++++++++++++++++++
5 files changed, 178 insertions(+), 10 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 7fe5979fbea2..77537736096b 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -81,8 +81,19 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
const struct fscrypt_info *ci)
{
u8 flags = fscrypt_policy_flags(&ci->ci_policy);
+ struct inode *inode = ci->ci_inode;
+ const struct fscrypt_operations *s_cop = inode->i_sb->s_cop;
memset(iv, 0, ci->ci_mode->ivsize);
+ if (s_cop->get_extent_context && lblk_num != U64_MAX) {
+ size_t extent_offset;
+ union fscrypt_extent_context ctx;
+ int ret = fscrypt_get_extent_context(inode, lblk_num, &ctx, &extent_offset, NULL);
+ WARN_ON_ONCE(ret != 0);
+ memcpy(iv->raw, ctx.v1.iv, ci->ci_mode->ivsize);
+ iv->lblk_num = iv->lblk_num + cpu_to_le64(extent_offset);
+ return;
+ }
/*
* Filename encryption. For inode-based policies, filenames are
@@ -93,8 +104,8 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
WARN_ON_ONCE(lblk_num > U32_MAX);
- WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX);
- lblk_num |= (u64)ci->ci_inode->i_ino << 32;
+ WARN_ON_ONCE(inode->i_ino > U32_MAX);
+ lblk_num |= (u64)inode->i_ino << 32;
} else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
WARN_ON_ONCE(lblk_num > U32_MAX);
lblk_num = (u32)(ci->ci_hashed_ino + lblk_num);
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 3afdaa084773..2092ef63c80a 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -165,6 +165,27 @@ fscrypt_policy_flags(const union fscrypt_policy *policy)
BUG();
}
+#define FSCRYPT_MAX_IV_SIZE 32
+
+/*
+ * fscrypt_extent_context - the encryption context for an extent
+ *
+ * For filesystems that support extent encryption, this context provides the
+ * necessary randomly-initialized IV in order to encrypt/decrypt the data
+ * stored in the extent. It is stored alongside each extent, and is
+ * insufficient to decrypt the extent: the extent's owning inode(s) provide the
+ * policy information (including key identifier) necessary to decrypt.
+ */
+struct fscrypt_extent_context_v1 {
+ u8 version;
+ u8 iv[FSCRYPT_MAX_IV_SIZE];
+};
+
+union fscrypt_extent_context {
+ u8 version;
+ struct fscrypt_extent_context_v1 v1;
+};
+
/*
* For encrypted symlinks, the ciphertext length is stored at the beginning
* of the string in little-endian format.
@@ -279,8 +300,6 @@ fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
#define fscrypt_err(inode, fmt, ...) \
fscrypt_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
-#define FSCRYPT_MAX_IV_SIZE 32
-
union fscrypt_iv {
struct {
/* logical block number within the file */
@@ -628,5 +647,8 @@ int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
const union fscrypt_context *ctx_u,
int ctx_size);
const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir);
+int fscrypt_get_extent_context(const struct inode *inode, u64 lblk_num,
+ union fscrypt_extent_context *ctx,
+ size_t *extent_offset, size_t *extent_length);
#endif /* _FSCRYPT_PRIVATE_H */
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 90f3e68f166e..0537f710047e 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -1,3 +1,4 @@
+
// SPDX-License-Identifier: GPL-2.0
/*
* Inline encryption support for fscrypt
@@ -466,6 +467,7 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
*/
u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
{
+ const struct fscrypt_operations *s_cop = inode->i_sb->s_cop;
const struct fscrypt_info *ci;
u32 dun;
@@ -476,14 +478,29 @@ u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
return nr_blocks;
ci = inode->i_crypt_info;
- if (!(fscrypt_policy_flags(&ci->ci_policy) &
- FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32))
- return nr_blocks;
- /* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */
+ if (s_cop->get_extent_context) {
+ size_t extent_offset, extent_length;
+ int ret = fscrypt_get_extent_context(inode, lblk, NULL,
+ &extent_offset,
+ &extent_length);
+ if (ret < 0) {
+ WARN_ON_ONCE(ret < 0);
+ return 1;
+ }
+ return extent_length - extent_offset;
+ }
- dun = ci->ci_hashed_ino + lblk;
+ if ((fscrypt_policy_flags(&ci->ci_policy) &
+ FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) {
+ /*
+ * With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to
+ * 0.
+ */
+ dun = ci->ci_hashed_ino + lblk;
+ return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun);
+ }
- return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun);
+ return nr_blocks;
}
EXPORT_SYMBOL_GPL(fscrypt_limit_io_blocks);
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index ed8b7b6531e5..2a92500e1e08 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -175,6 +175,13 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
return false;
}
+ if (inode->i_sb->s_cop->get_extent_context &&
+ !(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
+ fscrypt_warn(inode,
+ "v1 with direct key required for this filesystem");
+ return false;
+ }
+
if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
!supported_direct_key_modes(inode, policy->contents_encryption_mode,
policy->filenames_encryption_mode))
@@ -222,6 +229,13 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
return false;
}
+ if (inode->i_sb->s_cop->get_extent_context &&
+ !(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY)) {
+ fscrypt_warn(inode,
+ "Direct key policy required for this filesystem using extent-based encryption");
+ return false;
+ }
+
if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
!supported_direct_key_modes(inode, policy->contents_encryption_mode,
policy->filenames_encryption_mode))
@@ -781,6 +795,69 @@ int fscrypt_set_context(struct inode *inode, void *fs_data)
}
EXPORT_SYMBOL_GPL(fscrypt_set_context);
+/**
+ * fscrypt_get_extent_context() - Get the fscrypt extent context for a location
+ * @inode: an inode associated with the extent
+ * @lblk_num: a logical block number within the inode owned by the extent
+ * @ctx: a pointer to return the context found (may be NULL)
+ * @extent_offset: a pointer to return the offset of @lblk_num within the
+ * extent (may be NULL)
+ * @extent_length: a pointer to return the length of the extent found (may be
+ * NULL)
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int fscrypt_get_extent_context(const struct inode *inode, u64 lblk_num,
+ union fscrypt_extent_context *ctx,
+ size_t *extent_offset, size_t *extent_length)
+{
+ int ret;
+ int ctxsize = (ctx == NULL ? 0 : sizeof(*ctx));
+
+ if (!IS_ENCRYPTED(inode))
+ return -ENODATA;
+
+ ret = inode->i_sb->s_cop->get_extent_context(inode, lblk_num, ctx,
+ ctxsize, extent_offset,
+ extent_length);
+ if (ret == ctxsize && (!ctx || ctx->version == 1))
+ return 0;
+ if (ret > 0)
+ return -EINVAL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fscrypt_get_extent_context);
+
+/**
+ * fscrypt_set_extent_context() - Set an extent's fscrypt context
+ *
+ * @inode: an inode to which the extent belongs
+ * @extent: private data referring to the extent, given by the FS and passed
+ * to ->set_extent_context()
+ *
+ * This should be called after fscrypt_prepare_new_inode(), generally during a
+ * filesystem transaction. Everything here must be %GFP_NOFS-safe.
+ *
+ * Return: 0 on success, -errno on failure
+ */
+int fscrypt_set_extent_context(struct inode *inode, void *extent)
+{
+ struct fscrypt_info *ci = inode->i_crypt_info;
+ union fscrypt_extent_context ctx;
+ int ivsize;
+
+ if (!IS_ENCRYPTED(inode))
+ return -ENODATA;
+
+ ivsize = ci->ci_mode->ivsize;
+ ctx.v1.version = 1;
+ get_random_bytes(ctx.v1.iv, ivsize);
+
+ return inode->i_sb->s_cop->set_extent_context(extent,
+ &ctx, ivsize + 1);
+}
+EXPORT_SYMBOL_GPL(fscrypt_set_extent_context);
+
/**
* fscrypt_parse_test_dummy_encryption() - parse the test_dummy_encryption mount option
* @param: the mount option
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 16d2252d5562..136395f4d82d 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -94,6 +94,13 @@ struct fscrypt_nokey_name {
/* Maximum value for the third parameter of fscrypt_operations.set_context(). */
#define FSCRYPT_SET_CONTEXT_MAX_SIZE 40
+/*
+ * Maximum value for the third parameter of
+ * fscrypt_operations.set_extent_context(). Update if fscrypt_private.h:
+ * FSCRYPT_MAX_IVSIZE changes
+ */
+#define FSCRYPT_EXTENT_CONTEXT_MAX_SIZE 33
+
#ifdef CONFIG_FS_ENCRYPTION
/*
@@ -152,6 +159,39 @@ struct fscrypt_operations {
int (*set_context)(struct inode *inode, const void *ctx, size_t len,
void *fs_data);
+ /*
+ * Get the fscrypt extent context for a given inode and lblk number.
+ *
+ * @inode: the inode to which the extent belongs
+ * @lblk_num: the block number within the file whose extent is being
+ * queried
+ * @ctx: the buffer into which to get the context (may be NULL)
+ * @len: the length of the @ctx buffer in bytes
+ * @extent_offset: a pointer to return the offset of @lblk_num within
+ * the extent whose context is returned (may be NULL)
+ * @extent_length: a pointer to return the total length of the extent
+ * whose context was found (may be NULL)
+ *
+ * Return: On success, returns the length of the context in bytes,
+ * which may be less than @len. On failure, returns -ENODATA if the
+ * extent doesn't have a context, -ERANGE if the context is longer
+ * than @len, or another -errno code.
+ */
+ int (*get_extent_context)(const struct inode *inode, u64 lblk_num,
+ void *ctx, size_t len,
+ size_t *extent_offset, size_t *extent_length);
+
+ /*
+ * Set the fscrypt extent context for an extent.
+ *
+ * @extent: an opaque pointer to the filesystem's extent object
+ * @ctx: the buffer containing the extent context to set
+ * @len: the length of the @ctx buffer in bytes
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+ int (*set_extent_context)(void *extent, void *ctx, size_t len);
+
/*
* Get the dummy fscrypt policy in use on the filesystem (if any).
*
@@ -326,6 +366,7 @@ int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
int fscrypt_context_for_new_inode(void *ctx, struct inode *inode);
int fscrypt_set_context(struct inode *inode, void *fs_data);
+int fscrypt_set_extent_context(struct inode *inode, void *extent);
struct fscrypt_dummy_policy {
const union fscrypt_policy *policy;
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (4 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 15:34 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 07/20] btrfs: store directory's encryption state Sweet Tea Dorminy
` (15 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
As btrfs has a couple of quirks in its encryption compared to other filesystems, they
should be documented like ext4's quirks. Additionally, extent-based
contents encryption, being wholly new, deserves its own section to
compare against file-based contents encryption.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
Documentation/filesystems/fscrypt.rst | 62 +++++++++++++++++++--------
1 file changed, 45 insertions(+), 17 deletions(-)
diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 5ba5817c17c2..9dd7e8c0090a 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -31,7 +31,7 @@ However, except for filenames, fscrypt does not encrypt filesystem
metadata.
Unlike eCryptfs, which is a stacked filesystem, fscrypt is integrated
-directly into supported filesystems --- currently ext4, F2FS, and
+directly into supported filesystems --- currently btrfs, ext4, F2FS, and
UBIFS. This allows encrypted files to be read and written without
caching both the decrypted and encrypted pages in the pagecache,
thereby nearly halving the memory used and bringing it in line with
@@ -41,10 +41,10 @@ causing application compatibility issues; fscrypt allows the full 255
bytes (NAME_MAX). Finally, unlike eCryptfs, the fscrypt API can be
used by unprivileged users, with no need to mount anything.
-fscrypt does not support encrypting files in-place. Instead, it
-supports marking an empty directory as encrypted. Then, after
-userspace provides the key, all regular files, directories, and
-symbolic links created in that directory tree are transparently
+For most filesystems, fscrypt does not support encrypting files
+in-place. Instead, it supports marking an empty directory as encrypted.
+Then, after userspace provides the key, all regular files, directories,
+and symbolic links created in that directory tree are transparently
encrypted.
Threat model
@@ -280,6 +280,11 @@ included in the IV. Moreover:
key derived using the KDF. Users may use the same master key for
other v2 encryption policies.
+For filesystems with extent-based content encryption (e.g. btrfs),
+this is the only choice. Data shared among multiple inodes must share
+the exact same key, therefore necessitating inodes using the same key
+for contents encryption.
+
IV_INO_LBLK_64 policies
-----------------------
@@ -374,12 +379,12 @@ to individual filesystems. However, authenticated encryption (AE)
modes are not currently supported because of the difficulty of dealing
with ciphertext expansion.
-Contents encryption
--------------------
+File-based contents encryption
+------------------------------
-For file contents, each filesystem block is encrypted independently.
-Starting from Linux kernel 5.5, encryption of filesystems with block
-size less than system's page size is supported.
+For most filesystems, each filesystem block within each file is
+encrypted independently. Starting from Linux kernel 5.5, encryption of
+filesystems with block size less than system's page size is supported.
Each block's IV is set to the logical block number within the file as
a little endian number, except that:
@@ -403,6 +408,20 @@ Note that because file logical block numbers are included in the IVs,
filesystems must enforce that blocks are never shifted around within
encrypted files, e.g. via "collapse range" or "insert range".
+Extent-based contents encryption
+--------------------------------
+
+For certain filesystems (currently only btrfs), data is encrypted on a
+per-extent basis. Each filesystem block in a data extent is encrypted
+independently. Multiple files may refer to the extent, as long as they
+all share the same key. The filesystem may relocate the extent on disk,
+as long as the encrypted data within the extent retains its offset
+within the data extent.
+
+Each extent stores a random starting IV; each block's IV within this is
+set to the logical block number within the extent as a little endian
+number.
+
Filenames encryption
--------------------
@@ -525,13 +544,14 @@ This structure must be initialized as follows:
struct fscrypt_policy_v2.
If the file is not yet encrypted, then FS_IOC_SET_ENCRYPTION_POLICY
-verifies that the file is an empty directory. If so, the specified
-encryption policy is assigned to the directory, turning it into an
-encrypted directory. After that, and after providing the
-corresponding master key as described in `Adding keys`_, all regular
-files, directories (recursively), and symlinks created in the
-directory will be encrypted, inheriting the same encryption policy.
-The filenames in the directory's entries will be encrypted as well.
+verifies that the file is an empty directory, unless btrfs is being
+used. If so, the specified encryption policy is assigned to the
+directory, turning it into an encrypted directory. After that, and
+after providing the corresponding master key as described in `Adding
+keys`_, all regular files, directories (recursively), and symlinks
+created in the directory will be encrypted, inheriting the same
+encryption policy. The filenames in the directory's entries will be
+encrypted as well.
Alternatively, if the file is already encrypted, then
FS_IOC_SET_ENCRYPTION_POLICY validates that the specified encryption
@@ -552,6 +572,14 @@ Note that the ext4 filesystem does not allow the root directory to be
encrypted, even if it is empty. Users who want to encrypt an entire
filesystem with one key should consider using dm-crypt instead.
+Note that btrfs permits setting a currently unencrypted 'subvolume' to
+encrypted. This means all newly written data, and files, will be
+encrypted, but existing data and filenames will remain unencrypted. This
+is intended for use in containers: initially identical unencrypted
+snapshot volumes provide the base for multiple containers' filesystems,
+but after each encrypts their volume with a different key, any new
+sensitive data written by the container will be encrypted.
+
FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors:
- ``EACCES``: the file is not owned by the process's uid, nor does the
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 07/20] btrfs: store directory's encryption state
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (5 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 15:37 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper Sweet Tea Dorminy
` (14 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
For directories with encrypted files/filenames, we need to store a flag
indicating this fact. There's no room in other fields, so we'll need to
borrow a bit from dir_type. Since it's now a combination of type and
flags, we rename it to dir_flags to reflect its new usage.
The new flag, FT_FSCRYPT, indicates a (perhaps partially) encrypted
directory, which is orthogonal to file type; therefore, add the new
flag, and make conversion from directory type to file type strip the
flag.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 15 +++++++++++++--
fs/btrfs/delayed-inode.c | 6 +++---
fs/btrfs/delayed-inode.h | 2 +-
fs/btrfs/dir-item.c | 4 ++--
fs/btrfs/inode.c | 15 +++++++++------
fs/btrfs/print-tree.c | 4 ++--
fs/btrfs/send.c | 2 +-
fs/btrfs/tree-checker.c | 2 +-
fs/btrfs/tree-log.c | 20 ++++++++++----------
include/uapi/linux/btrfs_tree.h | 7 +++++++
10 files changed, 49 insertions(+), 28 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 7007c7974a2e..1793b0e16a14 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2184,10 +2184,10 @@ BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16);
/* struct btrfs_dir_item */
BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
-BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
+BTRFS_SETGET_FUNCS(dir_flags, struct btrfs_dir_item, type, 8);
BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16);
BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64);
-BTRFS_SETGET_STACK_FUNCS(stack_dir_type, struct btrfs_dir_item, type, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_dir_flags, struct btrfs_dir_item, type, 8);
BTRFS_SETGET_STACK_FUNCS(stack_dir_data_len, struct btrfs_dir_item,
data_len, 16);
BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item,
@@ -2195,6 +2195,17 @@ BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item,
BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item,
transid, 64);
+static inline u8 btrfs_dir_ftype(const struct extent_buffer *eb,
+ const struct btrfs_dir_item *item)
+{
+ return btrfs_dir_flags_to_ftype(btrfs_dir_flags(eb, item));
+}
+
+static inline u8 btrfs_stack_dir_ftype(const struct btrfs_dir_item *item)
+{
+ return btrfs_dir_flags_to_ftype(btrfs_stack_dir_flags(item));
+}
+
static inline void btrfs_dir_item_key(const struct extent_buffer *eb,
const struct btrfs_dir_item *item,
struct btrfs_disk_key *key)
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index cac5169eaf8d..7e405aafab86 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1412,7 +1412,7 @@ void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info)
int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
const char *name, int name_len,
struct btrfs_inode *dir,
- struct btrfs_disk_key *disk_key, u8 type,
+ struct btrfs_disk_key *disk_key, u8 flags,
u64 index)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
@@ -1443,7 +1443,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
btrfs_set_stack_dir_transid(dir_item, trans->transid);
btrfs_set_stack_dir_data_len(dir_item, 0);
btrfs_set_stack_dir_name_len(dir_item, name_len);
- btrfs_set_stack_dir_type(dir_item, type);
+ btrfs_set_stack_dir_flags(dir_item, flags);
memcpy((char *)(dir_item + 1), name, name_len);
data_len = delayed_item->data_len + sizeof(struct btrfs_item);
@@ -1753,7 +1753,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
name = (char *)(di + 1);
name_len = btrfs_stack_dir_name_len(di);
- d_type = fs_ftype_to_dtype(di->type);
+ 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,
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index 0163ca637a96..4f21daa3dbc7 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -113,7 +113,7 @@ static inline void btrfs_init_delayed_root(
int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
const char *name, int name_len,
struct btrfs_inode *dir,
- struct btrfs_disk_key *disk_key, u8 type,
+ struct btrfs_disk_key *disk_key, u8 flags,
u64 index);
int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 72fb2c518a2b..e37b075afa96 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -81,7 +81,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
leaf = path->nodes[0];
btrfs_cpu_key_to_disk(&disk_key, &location);
btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
- btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR);
+ btrfs_set_dir_flags(leaf, dir_item, BTRFS_FT_XATTR);
btrfs_set_dir_name_len(leaf, dir_item, name_len);
btrfs_set_dir_transid(leaf, dir_item, trans->transid);
btrfs_set_dir_data_len(leaf, dir_item, data_len);
@@ -140,7 +140,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
leaf = path->nodes[0];
btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
- btrfs_set_dir_type(leaf, dir_item, type);
+ btrfs_set_dir_flags(leaf, dir_item, type);
btrfs_set_dir_data_len(leaf, dir_item, 0);
btrfs_set_dir_name_len(leaf, dir_item, name_len);
btrfs_set_dir_transid(leaf, dir_item, trans->transid);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e5284f2686c8..97e17b9bd34f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5563,7 +5563,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
location->objectid, location->type, location->offset);
}
if (!ret)
- *type = btrfs_dir_type(path->nodes[0], di);
+ *type = btrfs_dir_ftype(path->nodes[0], di);
out:
btrfs_free_path(path);
return ret;
@@ -6001,6 +6001,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
btrfs_for_each_slot(root, &key, &found_key, path, ret) {
struct dir_entry *entry;
struct extent_buffer *leaf = path->nodes[0];
+ u8 di_flags;
if (found_key.objectid != key.objectid)
break;
@@ -6024,13 +6025,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
goto again;
}
+ di_flags = btrfs_dir_flags(leaf, di);
entry = addr;
- put_unaligned(name_len, &entry->name_len);
name_ptr = (char *)(entry + 1);
- read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1),
- name_len);
- put_unaligned(fs_ftype_to_dtype(btrfs_dir_type(leaf, di)),
- &entry->type);
+ 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(btrfs_dir_flags_to_ftype(di_flags)),
+ &entry->type);
btrfs_dir_item_key_to_cpu(leaf, di, &location);
put_unaligned(location.objectid, &entry->ino);
put_unaligned(found_key.offset, &entry->offset);
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index dd8777872143..6d9d99bf3536 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -240,9 +240,9 @@ void btrfs_print_leaf(struct extent_buffer *l)
case BTRFS_DIR_ITEM_KEY:
di = btrfs_item_ptr(l, i, struct btrfs_dir_item);
btrfs_dir_item_key_to_cpu(l, di, &found_key);
- pr_info("\t\tdir oid %llu type %u\n",
+ pr_info("\t\tdir oid %llu flags %u\n",
found_key.objectid,
- btrfs_dir_type(l, di));
+ btrfs_dir_flags(l, di));
break;
case BTRFS_ROOT_ITEM_KEY:
ri = btrfs_item_ptr(l, i, struct btrfs_root_item);
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 4ef4167072b8..127673c81a02 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1092,7 +1092,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
data_len = btrfs_dir_data_len(eb, di);
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
- if (btrfs_dir_type(eb, di) == BTRFS_FT_XATTR) {
+ if (btrfs_dir_ftype(eb, di) == BTRFS_FT_XATTR) {
if (name_len > XATTR_NAME_MAX) {
ret = -ENAMETOOLONG;
goto out;
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 43f905ab0a18..77c53a7491cf 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -528,7 +528,7 @@ static int check_dir_item(struct extent_buffer *leaf,
}
/* dir type check */
- dir_type = btrfs_dir_type(leaf, di);
+ dir_type = btrfs_dir_ftype(leaf, di);
if (unlikely(dir_type >= BTRFS_FT_MAX)) {
dir_item_err(leaf, slot,
"invalid dir item type, have %u expect [0, %u)",
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 020e01ea44bc..8467fadd6a23 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1809,7 +1809,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
struct btrfs_path *path,
struct btrfs_dir_item *dst_di,
const struct btrfs_key *log_key,
- u8 log_type,
+ u8 log_flags,
bool exists)
{
struct btrfs_key found_key;
@@ -1819,7 +1819,7 @@ static int delete_conflicting_dir_entry(struct btrfs_trans_handle *trans,
if (found_key.objectid == log_key->objectid &&
found_key.type == log_key->type &&
found_key.offset == log_key->offset &&
- btrfs_dir_type(path->nodes[0], dst_di) == log_type)
+ btrfs_dir_flags(path->nodes[0], dst_di) == log_flags)
return 1;
/*
@@ -1864,7 +1864,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
struct btrfs_key log_key;
struct btrfs_key search_key;
struct inode *dir;
- u8 log_type;
+ u8 log_flags;
bool exists;
int ret;
bool update_size = true;
@@ -1881,7 +1881,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
goto out;
}
- log_type = btrfs_dir_type(eb, di);
+ log_flags = btrfs_dir_flags(eb, di);
read_extent_buffer(eb, name, (unsigned long)(di + 1),
name_len);
@@ -1900,8 +1900,8 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
goto out;
} else if (dir_dst_di) {
ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path,
- dir_dst_di, &log_key, log_type,
- exists);
+ dir_dst_di, &log_key,
+ log_flags, exists);
if (ret < 0)
goto out;
dir_dst_matches = (ret == 1);
@@ -1918,7 +1918,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
} else if (index_dst_di) {
ret = delete_conflicting_dir_entry(trans, BTRFS_I(dir), path,
index_dst_di, &log_key,
- log_type, exists);
+ log_flags, exists);
if (ret < 0)
goto out;
index_dst_matches = (ret == 1);
@@ -2028,7 +2028,7 @@ static noinline int replay_one_dir_item(struct btrfs_trans_handle *trans,
* to ever delete the parent directory has it would result in stale
* dentries that can never be deleted.
*/
- if (ret == 1 && btrfs_dir_type(eb, di) != BTRFS_FT_DIR) {
+ if (ret == 1 && btrfs_dir_ftype(eb, di) != BTRFS_FT_DIR) {
struct btrfs_path *fixup_path;
struct btrfs_key di_key;
@@ -5422,7 +5422,7 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans,
}
di = btrfs_item_ptr(leaf, i, struct btrfs_dir_item);
- type = btrfs_dir_type(leaf, di);
+ type = btrfs_dir_ftype(leaf, di);
if (btrfs_dir_transid(leaf, di) < trans->transid)
continue;
btrfs_dir_item_key_to_cpu(leaf, di, &di_key);
@@ -6262,7 +6262,7 @@ static int log_new_delayed_dentries(struct btrfs_trans_handle *trans,
continue;
}
- if (btrfs_stack_dir_type(dir_item) == BTRFS_FT_DIR)
+ if (btrfs_stack_dir_ftype(dir_item) == BTRFS_FT_DIR)
log_mode = LOG_INODE_ALL;
ctx->log_new_dentries = false;
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index 5f32a2a495dc..e6031d18d521 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -359,6 +359,13 @@ enum btrfs_csum_type {
#define BTRFS_FT_SYMLINK 7
#define BTRFS_FT_XATTR 8
#define BTRFS_FT_MAX 9
+/* Name is encrypted. */
+#define BTRFS_FT_FSCRYPT_NAME 0x80
+
+static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags)
+{
+ return flags & ~BTRFS_FT_FSCRYPT_NAME;
+}
/*
* The key defines the order in the tree, and so it also defines (optimal)
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (6 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 07/20] btrfs: store directory's encryption state Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 19:11 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method Sweet Tea Dorminy
` (13 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
Most places that we create fscrypt_names, we are doing so from a dentry.
Fscrypt provides a helper for this common pattern:
fscrypt_setup_filename() initializes a filename to search for from a
dentry, performing encryption of the plaintext if it can and should be
done. This converts each setup of a fscrypt_name from a dentry to use
this helper; at present, since there are no encrypted directories,
nothing goes down the filename encryption paths.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/inode.c | 153 ++++++++++++++++++++++++++---------------
fs/btrfs/transaction.c | 26 +++++--
fs/btrfs/tree-log.c | 12 ++--
3 files changed, 123 insertions(+), 68 deletions(-)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 3e513ce5bb08..761293d763b6 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4404,14 +4404,17 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
struct btrfs_trans_handle *trans;
struct inode *inode = d_inode(dentry);
int ret;
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((unsigned char *)dentry->d_name.name, dentry->d_name.len)
- };
+ struct fscrypt_name fname;
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (ret)
+ return ret;
trans = __unlink_start_trans(dir);
- if (IS_ERR(trans))
- return PTR_ERR(trans);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
0);
@@ -4428,6 +4431,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
}
out:
+ fscrypt_free_filename(&fname);
btrfs_end_transaction(trans);
btrfs_btree_balance_dirty(BTRFS_I(dir)->root->fs_info);
return ret;
@@ -4446,10 +4450,11 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
int ret;
u64 objectid;
u64 dir_ino = btrfs_ino(BTRFS_I(dir));
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((char *) dentry->d_name.name,
- dentry->d_name.len)
- };
+ struct fscrypt_name fname;
+
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (ret)
+ return ret;
if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
objectid = inode->root->root_key.objectid;
@@ -4457,12 +4462,15 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
objectid = inode->location.objectid;
} else {
WARN_ON(1);
+ fscrypt_free_filename(&fname);
return -EINVAL;
}
path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
di = btrfs_lookup_dir_item(trans, root, path, dir_ino, &fname, -1);
if (IS_ERR_OR_NULL(di)) {
@@ -4529,6 +4537,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
btrfs_abort_transaction(trans, ret);
out:
btrfs_free_path(path);
+ fscrypt_free_filename(&fname);
return ret;
}
@@ -4795,9 +4804,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
int err = 0;
struct btrfs_trans_handle *trans;
u64 last_unlink_trans;
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((unsigned char *)dentry->d_name.name, dentry->d_name.len)
- };
+ struct fscrypt_name fname;
if (inode->i_size > BTRFS_EMPTY_DIR_SIZE)
return -ENOTEMPTY;
@@ -4810,9 +4817,15 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
return btrfs_delete_subvolume(dir, dentry);
}
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (err)
+ return err;
+
trans = __unlink_start_trans(dir);
- if (IS_ERR(trans))
- return PTR_ERR(trans);
+ if (IS_ERR(trans)) {
+ err = PTR_ERR(trans);
+ goto out_notrans;
+ }
if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
err = btrfs_unlink_subvol(trans, dir, dentry);
@@ -4846,7 +4859,9 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
}
out:
btrfs_end_transaction(trans);
+out_notrans:
btrfs_btree_balance_dirty(fs_info);
+ fscrypt_free_filename(&fname);
return err;
}
@@ -5540,7 +5555,7 @@ void btrfs_evict_inode(struct inode *inode)
/*
* Return the key found in the dir entry in the location pointer, fill @type
- * with BTRFS_FT_*, and return 0.
+ * with BTRFS_FT_*, and return 0. Used only for lookups, not removals.
*
* If no dir entries were found, returns -ENOENT.
* If found a corrupted location in dir entry, returns -EUCLEAN.
@@ -5552,15 +5567,16 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
struct btrfs_path *path;
struct btrfs_root *root = BTRFS_I(dir)->root;
int ret = 0;
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((char *) dentry->d_name.name,
- dentry->d_name.len)
- };
+ struct fscrypt_name fname;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 1, &fname);
+ if (ret)
+ goto out;
+
di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
&fname, 0);
if (IS_ERR_OR_NULL(di)) {
@@ -5580,6 +5596,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
if (!ret)
*type = btrfs_dir_ftype(path->nodes[0], di);
out:
+ fscrypt_free_filename(&fname);
btrfs_free_path(path);
return ret;
}
@@ -5600,9 +5617,15 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
struct btrfs_root_ref *ref;
struct extent_buffer *leaf;
struct btrfs_key key;
+ struct fscrypt_name fname;
int ret;
int err = 0;
+ ret = fscrypt_setup_filename(dir, &dentry->d_name, 0,
+ &fname);
+ if (ret)
+ return ret;
+
path = btrfs_alloc_path();
if (!path) {
err = -ENOMEM;
@@ -5648,6 +5671,7 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
err = 0;
out:
btrfs_free_path(path);
+ fscrypt_free_filename(&fname);
return err;
}
@@ -6254,16 +6278,17 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
struct inode *inode = args->inode;
int ret;
+ if (!args->orphan) {
+ ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0,
+ &args->fname);
+ if (ret)
+ return ret;
+ }
+
ret = posix_acl_create(dir, &inode->i_mode, &args->default_acl, &args->acl);
- if (ret)
+ if (ret) {
+ fscrypt_free_filename(&args->fname);
return ret;
-
- if (!args->orphan) {
- char *name = (char *) args->dentry->d_name.name;
- int name_len = args->dentry->d_name.len;
- args->fname = (struct fscrypt_name) {
- .disk_name = FSTR_INIT(name, name_len),
- };
}
/* 1 to add inode item */
@@ -6304,6 +6329,7 @@ void btrfs_new_inode_args_destroy(struct btrfs_new_inode_args *args)
{
posix_acl_release(args->acl);
posix_acl_release(args->default_acl);
+ fscrypt_free_filename(&args->fname);
}
/*
@@ -6731,10 +6757,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
struct btrfs_root *root = BTRFS_I(dir)->root;
struct inode *inode = d_inode(old_dentry);
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((char *) dentry->d_name.name,
- dentry->d_name.len)
- };
+ struct fscrypt_name fname;
u64 index;
int err;
int drop_inode = 0;
@@ -6746,6 +6769,10 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
if (inode->i_nlink >= BTRFS_LINK_MAX)
return -EMLINK;
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname);
+ if (err)
+ goto fail;
+
err = btrfs_set_inode_index(BTRFS_I(dir), &index);
if (err)
goto fail;
@@ -6796,6 +6823,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
}
fail:
+ fscrypt_free_filename(&fname);
if (trans)
btrfs_end_transaction(trans);
if (drop_inode) {
@@ -9181,14 +9209,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
int ret;
int ret2;
bool need_abort = false;
- struct fscrypt_name old_name = {
- .disk_name = FSTR_INIT((char *) old_dentry->d_name.name,
- old_dentry->d_name.len)
- };
- struct fscrypt_name new_name = {
- .disk_name = FSTR_INIT((char *) new_dentry->d_name.name,
- new_dentry->d_name.len)
- };
+ struct fscrypt_name old_fname, new_fname;
/*
* For non-subvolumes allow exchange only within one subvolume, in the
@@ -9200,6 +9221,16 @@ static int btrfs_rename_exchange(struct inode *old_dir,
new_ino != BTRFS_FIRST_FREE_OBJECTID))
return -EXDEV;
+ ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname);
+ if (ret)
+ return ret;
+
+ ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname);
+ if (ret) {
+ fscrypt_free_filename(&old_fname);
+ return ret;
+ }
+
/* close the race window with snapshot create/destroy ioctl */
if (old_ino == BTRFS_FIRST_FREE_OBJECTID ||
new_ino == BTRFS_FIRST_FREE_OBJECTID)
@@ -9267,7 +9298,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
/* force full log commit if subvolume involved. */
btrfs_set_log_full_commit(trans);
} else {
- ret = btrfs_insert_inode_ref(trans, dest, &new_name, old_ino,
+ ret = btrfs_insert_inode_ref(trans, dest, &new_fname, old_ino,
btrfs_ino(BTRFS_I(new_dir)),
old_idx);
if (ret)
@@ -9280,7 +9311,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
/* force full log commit if subvolume involved. */
btrfs_set_log_full_commit(trans);
} else {
- ret = btrfs_insert_inode_ref(trans, root, &old_name, new_ino,
+ ret = btrfs_insert_inode_ref(trans, root, &old_fname, new_ino,
btrfs_ino(BTRFS_I(old_dir)),
new_idx);
if (ret) {
@@ -9314,7 +9345,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
ret = btrfs_unlink_subvol(trans, old_dir, old_dentry);
} else { /* src is an inode */
ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
- BTRFS_I(old_dentry->d_inode), &old_name,
+ BTRFS_I(old_dentry->d_inode), &old_fname,
&old_rename_ctx);
if (!ret)
ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
@@ -9329,7 +9360,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
ret = btrfs_unlink_subvol(trans, new_dir, new_dentry);
} else { /* dest is an inode */
ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
- BTRFS_I(new_dentry->d_inode), &new_name,
+ BTRFS_I(new_dentry->d_inode), &new_fname,
&new_rename_ctx);
if (!ret)
ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode));
@@ -9340,14 +9371,14 @@ static int btrfs_rename_exchange(struct inode *old_dir,
}
ret = btrfs_add_link(trans, BTRFS_I(new_dir), BTRFS_I(old_inode),
- &new_name, 0, old_idx);
+ &new_fname, 0, old_idx);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out_fail;
}
ret = btrfs_add_link(trans, BTRFS_I(old_dir), BTRFS_I(new_inode),
- &old_name, 0, new_idx);
+ &old_fname, 0, new_idx);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out_fail;
@@ -9390,6 +9421,8 @@ static int btrfs_rename_exchange(struct inode *old_dir,
old_ino == BTRFS_FIRST_FREE_OBJECTID)
up_read(&fs_info->subvol_sem);
+ fscrypt_free_filename(&new_fname);
+ fscrypt_free_filename(&old_fname);
return ret;
}
@@ -9429,14 +9462,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
int ret;
int ret2;
u64 old_ino = btrfs_ino(BTRFS_I(old_inode));
- struct fscrypt_name old_fname = {
- .disk_name = FSTR_INIT((char *)old_dentry->d_name.name,
- old_dentry->d_name.len)
- };
- struct fscrypt_name new_fname = {
- .disk_name = FSTR_INIT((char *)new_dentry->d_name.name,
- new_dentry->d_name.len)
- };
+ struct fscrypt_name old_fname, new_fname;
if (btrfs_ino(BTRFS_I(new_dir)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
return -EPERM;
@@ -9453,6 +9479,16 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
new_inode->i_size > BTRFS_EMPTY_DIR_SIZE)
return -ENOTEMPTY;
+ ret = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_fname);
+ if (ret)
+ return ret;
+
+ ret = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_fname);
+ if (ret) {
+ fscrypt_free_filename(&old_fname);
+ return ret;
+ }
+
/* check for collisions, even if the name isn't there */
ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, &new_fname);
@@ -9461,11 +9497,11 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
/* we shouldn't get
* eexist without a new_inode */
if (WARN_ON(!new_inode)) {
- return ret;
+ goto out_fscrypt_names;
}
} else {
/* maybe -EOVERFLOW */
- return ret;
+ goto out_fscrypt_names;
}
}
ret = 0;
@@ -9639,6 +9675,9 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
out_whiteout_inode:
if (flags & RENAME_WHITEOUT)
iput(whiteout_args.inode);
+out_fscrypt_names:
+ fscrypt_free_filename(&old_fname);
+ fscrypt_free_filename(&new_fname);
return ret;
}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index b83e618a255d..873366d3c314 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -6,6 +6,7 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/sched/mm.h>
#include <linux/writeback.h>
#include <linux/pagemap.h>
#include <linux/blkdev.h>
@@ -1619,10 +1620,8 @@ create_pending_snapshot(struct btrfs_trans_handle *trans,
u64 index = 0;
u64 objectid;
u64 root_flags;
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((char *) pending->dentry->d_name.name,
- pending->dentry->d_name.len)
- };
+ unsigned int mem_flags;
+ struct fscrypt_name fname;
ASSERT(pending->path);
path = pending->path;
@@ -1630,9 +1629,22 @@ create_pending_snapshot(struct btrfs_trans_handle *trans,
ASSERT(pending->root_item);
new_root_item = pending->root_item;
+ /*
+ * Since this is during btrfs_commit_transaction() and more items
+ * joining the transaction at this point would be bad, use NOFS
+ * allocations so that no new writes are kicked off.
+ */
+ mem_flags = memalloc_nofs_save();
+ pending->error = fscrypt_setup_filename(parent_inode,
+ &pending->dentry->d_name, 0,
+ &fname);
+ memalloc_nofs_restore(mem_flags);
+ if (pending->error)
+ goto free_pending;
+
pending->error = btrfs_get_free_objectid(tree_root, &objectid);
if (pending->error)
- goto no_free_objectid;
+ goto free_fname;
/*
* Make qgroup to skip current new snapshot's qgroupid, as it is
@@ -1846,7 +1858,9 @@ create_pending_snapshot(struct btrfs_trans_handle *trans,
trans->bytes_reserved = 0;
clear_skip_qgroup:
btrfs_clear_skip_qgroup(trans);
-no_free_objectid:
+free_fname:
+ fscrypt_free_filename(&fname);
+free_pending:
kfree(new_root_item);
pending->root_item = NULL;
btrfs_free_path(path);
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 40cfaa9feff3..53195cfe6a3d 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -7443,13 +7443,13 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
if (old_dir && old_dir->logged_trans == trans->transid) {
struct btrfs_root *log = old_dir->root->log_root;
struct btrfs_path *path;
- struct fscrypt_name fname = {
- .disk_name = FSTR_INIT((char *) old_dentry->d_name.name,
- old_dentry->d_name.len)
- };
-
+ struct fscrypt_name fname;
ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX);
+ ret = fscrypt_setup_filename(&old_dir->vfs_inode,
+ &old_dentry->d_name, 0, &fname);
+ if (ret)
+ goto out;
/*
* We have two inodes to update in the log, the old directory and
* the inode that got renamed, so we must pin the log to prevent
@@ -7469,6 +7469,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
path = btrfs_alloc_path();
if (!path) {
ret = -ENOMEM;
+ fscrypt_free_filename(&fname);
goto out;
}
@@ -7498,6 +7499,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
mutex_unlock(&old_dir->log_mutex);
btrfs_free_path(path);
+ fscrypt_free_filename(&fname);
if (ret < 0)
goto out;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (7 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 19:27 ` Josef Bacik
2022-09-09 10:15 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
` (12 subsequent siblings)
21 siblings, 2 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Now that everything in btrfs is dealing in fscrypt_names, fscrypt has a
useful function, fscrypt_match_name(), to check whether a fscrypt_name
matches a provided buffer. However, btrfs buffers are struct
extent_buffer rather than a raw char array, so we need to implement our
own imitation of fscrypt_match_name() that deals in extent_buffers,
falling back to a simple memcpy if fscrypt isn't compiled. We
can then use this matching method in btrfs_match_dir_item_name() and
other locations.
This also provides a useful occasion to introduce the new fscrypt file
for btrfs, handling the fscrypt-specific functions needed.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/Makefile | 1 +
fs/btrfs/dir-item.c | 13 +++++++------
fs/btrfs/extent_io.c | 37 +++++++++++++++++++++++++++++++++++++
fs/btrfs/extent_io.h | 2 ++
fs/btrfs/fscrypt.c | 32 ++++++++++++++++++++++++++++++++
fs/btrfs/fscrypt.h | 25 +++++++++++++++++++++++++
fs/btrfs/inode-item.c | 8 ++++----
fs/btrfs/inode.c | 11 ++++-------
fs/btrfs/root-tree.c | 7 ++++---
9 files changed, 116 insertions(+), 20 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 99f9995670ea..b6444490cdbc 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -38,6 +38,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/dir-item.c b/fs/btrfs/dir-item.c
index b4c1e2a40401..8d7c3c32ed8e 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -5,6 +5,7 @@
#include "ctree.h"
#include "disk-io.h"
+#include "fscrypt.h"
#include "transaction.h"
/*
@@ -390,15 +391,15 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
total_len = btrfs_item_size(leaf, path->slots[0]);
while (cur < total_len) {
- unsigned long name_ptr = (unsigned long)(dir_item + 1);
- this_len = sizeof(*dir_item) +
- btrfs_dir_name_len(leaf, dir_item) +
+ int dir_name_len = btrfs_dir_name_len(leaf, dir_item);
+ this_len = sizeof(*dir_item) + dir_name_len +
btrfs_dir_data_len(leaf, dir_item);
- if (btrfs_dir_name_len(leaf, dir_item) == fname_len(fname) &&
- memcmp_extent_buffer(leaf, fname_name(fname), name_ptr,
- fname_len(fname)) == 0)
+ if (btrfs_fscrypt_match_name(fname, leaf,
+ (unsigned long)(dir_item + 1),
+ dir_name_len)) {
return dir_item;
+ }
cur += this_len;
dir_item = (struct btrfs_dir_item *)((char *)dir_item +
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 591c191a58bc..a467a7553bd9 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+#include <crypto/sha2.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/bio.h>
@@ -6892,6 +6893,42 @@ void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start,
}
}
+void extent_buffer_sha256(const struct extent_buffer *eb, unsigned long start,
+ unsigned long len, u8 *out)
+{
+ size_t cur;
+ size_t offset;
+ struct page *page;
+ char *kaddr;
+ unsigned long i = get_eb_page_index(start);
+ struct sha256_state sctx;
+
+ if (check_eb_range(eb, start, len))
+ return;
+
+ offset = get_eb_offset_in_page(eb, start);
+
+ /*
+ * TODO: This should maybe be using the crypto API, not the fallback,
+ * but fscrypt uses the fallback and this is only used in emulation of
+ * fscrypt's buffer sha256 method.
+ */
+ sha256_init(&sctx);
+ while (len > 0) {
+ page = eb->pages[i];
+ assert_eb_page_uptodate(eb, page);
+
+ cur = min(len, PAGE_SIZE - offset);
+ kaddr = page_address(page);
+ sha256_update(&sctx, (u8 *)(kaddr + offset), cur);
+
+ len -= cur;
+ offset = 0;
+ i++;
+ }
+ sha256_final(&sctx, out);
+}
+
void copy_extent_buffer_full(const struct extent_buffer *dst,
const struct extent_buffer *src)
{
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 69a86ae6fd50..d6c1b91cd690 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -222,6 +222,8 @@ void memmove_extent_buffer(const struct extent_buffer *dst,
unsigned long len);
void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start,
unsigned long len);
+void extent_buffer_sha256(const struct extent_buffer *eb, unsigned long start,
+ unsigned long len, u8 *out);
int extent_buffer_test_bit(const struct extent_buffer *eb, unsigned long start,
unsigned long pos);
void extent_buffer_bitmap_set(const struct extent_buffer *eb, unsigned long start,
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
new file mode 100644
index 000000000000..2ed844dd61d0
--- /dev/null
+++ b/fs/btrfs/fscrypt.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Facebook
+ */
+
+#include "ctree.h"
+#include "fscrypt.h"
+
+/* fscrypt_match_name() but for an extent_buffer. */
+bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
+ struct extent_buffer *leaf, unsigned long de_name,
+ u32 de_name_len)
+{
+ const struct fscrypt_nokey_name *nokey_name =
+ (const void *)fname->crypto_buf.name;
+ u8 digest[SHA256_DIGEST_SIZE];
+
+ if (likely(fname->disk_name.name)) {
+ if (de_name_len != fname->disk_name.len)
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name,
+ de_name, de_name_len);
+ }
+ if (de_name_len <= sizeof(nokey_name->bytes))
+ return false;
+ if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name,
+ sizeof(nokey_name->bytes)))
+ return false;
+ extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes),
+ de_name_len - sizeof(nokey_name->bytes), digest);
+ return !memcmp(digest, nokey_name->sha256, sizeof(digest));
+}
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
new file mode 100644
index 000000000000..7f24d12e6ee0
--- /dev/null
+++ b/fs/btrfs/fscrypt.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef BTRFS_FSCRYPT_H
+#define BTRFS_FSCRYPT_H
+
+#include <linux/fscrypt.h>
+
+#ifdef CONFIG_FS_ENCRYPTION
+bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name, u32 de_name_len);
+
+#else
+static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name, u32 de_name_len)
+{
+ if (de_name_len != fname->disk_name.len)
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name,
+ de_name, de_name_len);
+}
+#endif
+
+#endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c
index 78053eb9589c..4ad75f9573aa 100644
--- a/fs/btrfs/inode-item.c
+++ b/fs/btrfs/inode-item.c
@@ -7,6 +7,7 @@
#include "ctree.h"
#include "inode-item.h"
#include "disk-io.h"
+#include "fscrypt.h"
#include "transaction.h"
#include "print-tree.h"
@@ -62,10 +63,9 @@ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref(
name_ptr = (unsigned long)(&extref->name);
ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
- if (ref_name_len == fname_len(fname) &&
- btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
- (memcmp_extent_buffer(leaf, fname_name(fname), name_ptr,
- fname_len(fname)) == 0))
+ if (btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
+ btrfs_fscrypt_match_name(fname, leaf, name_ptr,
+ ref_name_len))
return extref;
cur_offset += ref_name_len + sizeof(*extref);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 761293d763b6..482c5b3d9e70 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -55,6 +55,7 @@
#include "zoned.h"
#include "subpage.h"
#include "inode-item.h"
+#include "fscrypt.h"
struct btrfs_iget_args {
u64 ino;
@@ -5646,14 +5647,10 @@ static int fixup_tree_root_location(struct btrfs_fs_info *fs_info,
leaf = path->nodes[0];
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
- if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir)) ||
- btrfs_root_ref_name_len(leaf, ref) != dentry->d_name.len)
+ if (btrfs_root_ref_dirid(leaf, ref) != btrfs_ino(BTRFS_I(dir)))
goto out;
-
- ret = memcmp_extent_buffer(leaf, dentry->d_name.name,
- (unsigned long)(ref + 1),
- dentry->d_name.len);
- if (ret)
+ if (!btrfs_fscrypt_match_name(&fname, leaf, (unsigned long)(ref + 1),
+ btrfs_root_ref_name_len(leaf, ref)))
goto out;
btrfs_release_path(path);
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index f44f02c22027..bbb215007896 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -8,6 +8,7 @@
#include "ctree.h"
#include "transaction.h"
#include "disk-io.h"
+#include "fscrypt.h"
#include "print-tree.h"
#include "qgroup.h"
#include "space-info.h"
@@ -351,14 +352,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) != fname_len(fname)) ||
- memcmp_extent_buffer(leaf, fname_name(fname), ptr,
- fname_len(fname))) {
+ !btrfs_fscrypt_match_name(fname, leaf, ptr, name_len)) {
ret = -ENOENT;
goto out;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (8 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 20:11 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 12/20] btrfs: start using fscrypt hooks Sweet Tea Dorminy
` (11 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Initially, only normal data extents, using the normal (non-direct) IO
path, will be encrypted. This change forbids various other bits:
- allows reflinking only if both inodes have the same encryption status
- disables compressing encrypted inodes
- disables direct IO on encrypted inodes
- disable inline data on encrypted inodes
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/btrfs_inode.h | 3 +++
fs/btrfs/file.c | 4 ++--
fs/btrfs/inode.c | 3 ++-
fs/btrfs/reflink.c | 7 +++++++
4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index b160b8e124e0..ff668686717b 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -400,6 +400,9 @@ static inline bool btrfs_inode_in_log(struct btrfs_inode *inode, u64 generation)
*/
static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode)
{
+ if (IS_ENCRYPTED(&inode->vfs_inode))
+ return false;
+
if (inode->flags & BTRFS_INODE_NODATACOW ||
inode->flags & BTRFS_INODE_NODATASUM)
return false;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 0a76ae8b8e96..7216ac1f860c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1895,7 +1895,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
goto relock;
}
- if (check_direct_IO(fs_info, from, pos)) {
+ if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) {
btrfs_inode_unlock(inode, ilock_flags);
goto buffered;
}
@@ -3729,7 +3729,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to)
ssize_t read = 0;
ssize_t ret;
- if (fsverity_active(inode))
+ if (IS_ENCRYPTED(inode) || fsverity_active(inode))
return 0;
if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos))
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 482c5b3d9e70..fea48c12a33a 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -409,7 +409,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 9acf47b11fe6..d22086e1cbc8 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -805,6 +805,13 @@ 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 (!fscrypt_have_same_policy(inode_in, 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.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 12/20] btrfs: start using fscrypt hooks.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (9 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 20:17 ` David Sterba
2022-09-08 19:42 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 13/20] btrfs: add fscrypt_context items Sweet Tea Dorminy
` (10 subsequent siblings)
21 siblings, 2 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
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.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 1 +
fs/btrfs/file.c | 3 ++
fs/btrfs/fscrypt.c | 3 ++
fs/btrfs/fscrypt.h | 1 +
fs/btrfs/inode.c | 91 ++++++++++++++++++++++++++++++++++++++++------
fs/btrfs/super.c | 3 ++
6 files changed, 90 insertions(+), 12 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 230537a007b6..2b9ba8d77861 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3416,6 +3416,7 @@ struct btrfs_new_inode_args {
*/
struct posix_acl *default_acl;
struct posix_acl *acl;
+ bool encrypt;
};
int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
unsigned int *trans_num_items);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 7216ac1f860c..929a0308676c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3695,6 +3695,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
int ret;
filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC;
+ 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
index 2ed844dd61d0..9829d280a6bc 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -30,3 +30,6 @@ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
de_name_len - sizeof(nokey_name->bytes), digest);
return !memcmp(digest, nokey_name->sha256, sizeof(digest));
}
+
+const struct fscrypt_operations btrfs_fscrypt_ops = {
+};
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 7f24d12e6ee0..07884206c8a1 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -22,4 +22,5 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
}
#endif
+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 fea48c12a33a..eb42e4bf55b9 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5450,6 +5450,7 @@ void btrfs_evict_inode(struct inode *inode)
trace_btrfs_inode_evict(inode);
if (!root) {
+ fscrypt_put_encryption_info(inode);
fsverity_cleanup_inode(inode);
clear_inode(inode);
return;
@@ -5551,6 +5552,7 @@ void btrfs_evict_inode(struct inode *inode)
* to retry these periodically in the future.
*/
btrfs_remove_delayed_node(BTRFS_I(inode));
+ fscrypt_put_encryption_info(inode);
fsverity_cleanup_inode(inode);
clear_inode(inode);
}
@@ -6289,6 +6291,10 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
return ret;
}
+ ret = fscrypt_prepare_new_inode(dir, inode, &args->encrypt);
+ if (ret)
+ return ret;
+
/* 1 to add inode item */
*trans_num_items = 1;
/* 1 to add compression property */
@@ -6767,9 +6773,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)
@@ -7050,6 +7060,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
unsigned long ptr;
char *map;
size_t size;
+ size_t inline_size;
size_t extent_offset;
size_t copy_size;
@@ -7057,9 +7068,13 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
goto out;
size = btrfs_file_extent_ram_bytes(leaf, item);
+ inline_size = btrfs_file_extent_inline_item_len(leaf, path->slots[0]);
+ ASSERT(btrfs_file_extent_encryption(leaf, item) ||
+ btrfs_file_extent_compression(leaf, item) ||
+ (size == inline_size));
extent_offset = page_offset(page) + pg_offset - extent_start;
copy_size = min_t(u64, PAGE_SIZE - pg_offset,
- size - extent_offset);
+ inline_size - extent_offset);
em->start = extent_start + extent_offset;
em->len = ALIGN(copy_size, fs_info->sectorsize);
em->orig_block_len = em->len;
@@ -9003,6 +9018,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));
}
@@ -9073,8 +9089,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)
@@ -9688,6 +9703,11 @@ static int btrfs_rename2(struct user_namespace *mnt_userns, struct inode *old_di
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);
@@ -9907,15 +9927,22 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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 NULL terminator, but we
+ * don't store that NULL.
+ */
+ 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)
@@ -9924,7 +9951,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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);
+ btrfs_i_size_write(BTRFS_I(inode), disk_link.len - 1);
inode_set_bytes(inode, name_len);
new_inode_args.inode = inode;
@@ -9952,10 +9979,23 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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) {
@@ -9974,10 +10014,11 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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);
+ /* ram size is the unencoded size */
btrfs_set_file_extent_ram_bytes(leaf, ei, name_len);
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);
@@ -9994,6 +10035,29 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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,
@@ -11576,7 +11640,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,
@@ -11586,4 +11650,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 b7118345c6f7..ba7008108bbb 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -47,6 +47,8 @@
#include "tests/btrfs-tests.h"
#include "block-group.h"
#include "discard.h"
+#include "fscrypt.h"
+
#include "qgroup.h"
#include "raid56.h"
#define CREATE_TRACE_POINTS
@@ -1448,6 +1450,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.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 13/20] btrfs: add fscrypt_context items.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (10 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 12/20] btrfs: start using fscrypt hooks Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 20:43 ` David Sterba
2022-09-08 20:06 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
` (9 subsequent siblings)
21 siblings, 2 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
In order to store per-inode information such as the inode nonce and the
key identifier, fscrypt stores a context item with each encrypted inode.
This can be implemented as a new item type, as fscrypt provides an
arbitrary blob for the filesystem to store.
This also provides a good place to implement full-subvolume encryption:
a subvolume flag permits setting one context for the whole subvolume.
However, since an unencrypted subvolume would be unable to read
encrypted data, encrypted subvolumes should only be snapshottable to
other encrypted subvolumes.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 3 +
fs/btrfs/fscrypt.c | 167 ++++++++++++++++++++++++++++++++
fs/btrfs/inode.c | 38 ++++++++
fs/btrfs/ioctl.c | 10 +-
fs/btrfs/tree-checker.c | 1 +
include/uapi/linux/btrfs_tree.h | 10 ++
6 files changed, 228 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 2b9ba8d77861..f0a16c32110d 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -33,6 +33,7 @@
#include "extent-io-tree.h"
#include "extent_io.h"
#include "extent_map.h"
+#include "fscrypt.h"
#include "async-thread.h"
#include "block-rsv.h"
#include "locking.h"
@@ -1667,6 +1668,7 @@ do { \
#define BTRFS_INODE_NOATIME (1U << 9)
#define BTRFS_INODE_DIRSYNC (1U << 10)
#define BTRFS_INODE_COMPRESS (1U << 11)
+#define BTRFS_INODE_FSCRYPT_CONTEXT (1U << 12)
#define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31)
@@ -1683,6 +1685,7 @@ do { \
BTRFS_INODE_NOATIME | \
BTRFS_INODE_DIRSYNC | \
BTRFS_INODE_COMPRESS | \
+ BTRFS_INODE_FSCRYPT_CONTEXT | \
BTRFS_INODE_ROOT_ITEM_INIT)
#define BTRFS_INODE_RO_VERITY (1U << 0)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 9829d280a6bc..b824bbd964bc 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -3,8 +3,13 @@
* Copyright (C) 2020 Facebook
*/
+#include <linux/iversion.h>
#include "ctree.h"
+#include "btrfs_inode.h"
+#include "disk-io.h"
#include "fscrypt.h"
+#include "transaction.h"
+#include "xattr.h"
/* fscrypt_match_name() but for an extent_buffer. */
bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
@@ -31,5 +36,167 @@ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
return !memcmp(digest, nokey_name->sha256, sizeof(digest));
}
+static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct inode *put_inode = NULL;
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ struct extent_buffer *leaf;
+ unsigned long ptr;
+ int ret;
+
+ if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
+ inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
+ root);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+ put_inode = inode;
+ }
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ key = (struct btrfs_key) {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
+ .offset = 0,
+ };
+
+ ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
+ if (ret) {
+ len = -EINVAL;
+ goto out;
+ }
+
+ leaf = path->nodes[0];
+ ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+ /* fscrypt provides max context length, but it could be less */
+ len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
+ read_extent_buffer(leaf, ctx, ptr, len);
+
+out:
+ btrfs_free_path(path);
+ iput(put_inode);
+ return len;
+}
+
+static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
+ size_t len, void *fs_data)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_trans_handle *trans;
+ int is_subvolume = inode->i_ino == BTRFS_FIRST_FREE_OBJECTID;
+ int ret;
+ struct btrfs_path *path;
+ struct btrfs_key key = {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
+ .offset = 0,
+ };
+
+ /*
+ * If the whole subvolume is encrypted, we expect that all children
+ * have the same policy.
+ */
+ if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
+ bool same_policy;
+ struct inode *root_inode = NULL;
+ root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
+ root);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+ same_policy = fscrypt_have_same_policy(inode, root_inode);
+ iput(root_inode);
+ if (same_policy)
+ return 0;
+ }
+
+ if (fs_data) {
+ /*
+ * We are setting the context as part of an existing
+ * transaction. This happens when we are inheriting the context
+ * for a new inode.
+ */
+ trans = fs_data;
+ } else {
+ /*
+ * 1 for the inode item
+ * 1 for the fscrypt item
+ * 1 for the root item if the inode is a subvolume
+ */
+ trans = btrfs_start_transaction(root, 2 + is_subvolume);
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
+ }
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
+ if (ret == 0) {
+ 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);
+ btrfs_free_path(path);
+ goto out;
+ } else if (ret < 0) {
+ goto out;
+ }
+ btrfs_free_path(path);
+
+ ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
+ if (ret)
+ goto out;
+
+ BTRFS_I(inode)->flags |= BTRFS_INODE_FSCRYPT_CONTEXT;
+ btrfs_sync_inode_flags_to_i_flags(inode);
+ inode_inc_iversion(inode);
+ inode->i_ctime = current_time(inode);
+ ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
+ if (ret)
+ goto out;
+
+ /*
+ * For new subvolumes, the root item is already initialized with
+ * the BTRFS_ROOT_SUBVOL_FSCRYPT flag.
+ */
+ if (!fs_data && is_subvolume) {
+ u64 root_flags = btrfs_root_flags(&root->root_item);
+
+ btrfs_set_root_flags(&root->root_item,
+ root_flags |
+ BTRFS_ROOT_SUBVOL_FSCRYPT);
+ ret = btrfs_update_root(trans, root->fs_info->tree_root,
+ &root->root_key,
+ &root->root_item);
+ }
+out:
+ if (fs_data)
+ return ret;
+
+ if (ret)
+ btrfs_abort_transaction(trans, ret);
+ else
+ btrfs_end_transaction(trans);
+ return ret;
+}
+
+static bool btrfs_fscrypt_empty_dir(struct inode *inode)
+{
+ /*
+ * We don't care about turning on encryption on a non-empty directory
+ * so we always return true.
+ */
+ return true;
+}
+
const struct fscrypt_operations btrfs_fscrypt_ops = {
+ .key_prefix = "btrfs:",
+ .get_context = btrfs_fscrypt_get_context,
+ .set_context = btrfs_fscrypt_set_context,
+ .empty_dir = btrfs_fscrypt_empty_dir,
};
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index eb42e4bf55b9..007abdf6de93 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6278,6 +6278,34 @@ 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 (IS_ENCRYPTED(dir) &&
+ !(BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT)) {
+ struct inode *root_inode;
+ bool encrypt;
+
+ root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
+ BTRFS_I(dir)->root);
+ if (IS_ERR(root_inode))
+ return PTR_ERR(root_inode);
+ /*
+ * TODO: perhaps instead of faking making a new dir to get a
+ * new context, it would be better to expose
+ * fscrypt_setup_encryption_info() for our use.
+ */
+ ret = fscrypt_prepare_new_inode(root_inode, dir, &encrypt);
+ if (!ret) {
+ ret = fscrypt_set_context(dir, NULL);
+ if (ret)
+ fscrypt_put_encryption_info(dir);
+ }
+ iput(root_inode);
+ if (ret)
+ return ret;
+ }
+
if (!args->orphan) {
ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0,
&args->fname);
@@ -6311,6 +6339,8 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
if (dir->i_security)
(*trans_num_items)++;
#endif
+ if (args->encrypt)
+ (*trans_num_items)++; /* 1 to add fscrypt item */
if (args->orphan) {
/* 1 to add orphan item */
(*trans_num_items)++;
@@ -6564,6 +6594,14 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
}
}
+ if (args->encrypt) {
+ ret = fscrypt_set_context(inode, trans);
+ if (ret) {
+ btrfs_abort_transaction(trans, ret);
+ goto discard;
+ }
+ }
+
inode_tree_add(inode);
trace_btrfs_inode_new(inode);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 10b8db56edda..8f5b65c43c8d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -658,7 +658,8 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
fs_info->nodesize);
btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
- btrfs_set_root_flags(root_item, 0);
+ btrfs_set_root_flags(root_item, new_inode_args.encrypt ?
+ BTRFS_ROOT_SUBVOL_FSCRYPT : 0);
btrfs_set_root_limit(root_item, 0);
btrfs_set_stack_inode_flags(inode_item, BTRFS_INODE_ROOT_ITEM_INIT);
@@ -787,6 +788,13 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
return -ETXTBSY;
}
+ if ((btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) &&
+ !IS_ENCRYPTED(dir)) {
+ btrfs_warn(fs_info,
+ "cannot snapshot encrypted volume to unencrypted destination");
+ return -EXDEV;
+ }
+
pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL);
if (!pending_snapshot)
return -ENOMEM;
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 2c6d48af1d2a..05f404c7498e 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1124,6 +1124,7 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key,
struct btrfs_fs_info *fs_info = leaf->fs_info;
struct btrfs_root_item ri = { 0 };
const u64 valid_root_flags = BTRFS_ROOT_SUBVOL_RDONLY |
+ BTRFS_ROOT_SUBVOL_FSCRYPT |
BTRFS_ROOT_SUBVOL_DEAD;
int ret;
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index e6031d18d521..061aabca56f3 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -144,6 +144,8 @@
#define BTRFS_VERITY_DESC_ITEM_KEY 36
#define BTRFS_VERITY_MERKLE_ITEM_KEY 37
+#define BTRFS_FSCRYPT_CTXT_ITEM_KEY 41
+
#define BTRFS_ORPHAN_ITEM_KEY 48
/* reserve 2-15 close to the inode for later flexibility */
@@ -633,6 +635,8 @@ struct btrfs_dir_item {
} __attribute__ ((__packed__));
#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+/* Top-level subvolume directory is encrypted with fscrypt. */
+#define BTRFS_ROOT_SUBVOL_FSCRYPT (1ULL << 1)
/*
* Internal in-memory flag that a subvolume has been marked for deletion but
@@ -788,6 +792,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.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (11 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 13/20] btrfs: add fscrypt_context items Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 20:07 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
` (8 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
In btrfs, a file can be encrypted either if its directory is encrypted
or its root subvolume is encrypted, so translate both to the standard
flags.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ioctl.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 8f5b65c43c8d..708e514aca25 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -7,6 +7,7 @@
#include <linux/bio.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/fscrypt.h>
#include <linux/fsnotify.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
@@ -147,6 +148,10 @@ static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode)
iflags |= FS_NOCOW_FL;
if (ro_flags & BTRFS_INODE_RO_VERITY)
iflags |= FS_VERITY_FL;
+ if ((binode->flags & BTRFS_INODE_FSCRYPT_CONTEXT) ||
+ (btrfs_root_flags(&binode->root->root_item) &
+ BTRFS_ROOT_SUBVOL_FSCRYPT))
+ iflags |= FS_ENCRYPT_FL;
if (flags & BTRFS_INODE_NOCOMPRESS)
iflags |= FS_NOCOMP_FL;
@@ -176,10 +181,14 @@ void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
new_fl |= S_DIRSYNC;
if (binode->ro_flags & BTRFS_INODE_RO_VERITY)
new_fl |= S_VERITY;
+ if ((binode->flags & BTRFS_INODE_FSCRYPT_CONTEXT) ||
+ (btrfs_root_flags(&binode->root->root_item) &
+ BTRFS_ROOT_SUBVOL_FSCRYPT))
+ new_fl |= S_ENCRYPTED;
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);
}
/*
@@ -192,7 +201,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 */
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (12 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 21:10 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag Sweet Tea Dorminy
` (7 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Sweet Tea Dorminy
In order to encrypt data, each file extent must have its own persistent
fscrypt_extent_context, which is then provided to fscrypt upon request.
This is only needed for encrypted extents and is of variable size on
disk, so file extents must additionally keep track of their actual
length.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 30 +++++++++++
fs/btrfs/extent_map.h | 4 ++
fs/btrfs/file-item.c | 13 +++++
fs/btrfs/file.c | 4 +-
fs/btrfs/fscrypt.c | 21 ++++++++
fs/btrfs/fscrypt.h | 23 +++++++++
fs/btrfs/inode.c | 89 +++++++++++++++++++++++++--------
fs/btrfs/ordered-data.c | 9 +++-
fs/btrfs/ordered-data.h | 4 +-
fs/btrfs/reflink.c | 1 +
fs/btrfs/tree-checker.c | 36 ++++++++++---
fs/btrfs/tree-log.c | 11 +++-
include/uapi/linux/btrfs_tree.h | 9 ++++
13 files changed, 220 insertions(+), 34 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index f0a16c32110d..38927a867028 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -37,6 +37,7 @@
#include "async-thread.h"
#include "block-rsv.h"
#include "locking.h"
+#include "fscrypt.h"
struct btrfs_trans_handle;
struct btrfs_transaction;
@@ -1437,6 +1438,7 @@ struct btrfs_replace_extent_info {
u64 file_offset;
/* Pointer to a file extent item of type regular or prealloc. */
char *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
@@ -2659,6 +2661,16 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
struct btrfs_file_extent_item, disk_num_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
struct btrfs_file_extent_item, compression, 8);
+BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
+ struct btrfs_file_extent_item, encryption, 8);
+
+static inline u8
+btrfs_stack_file_extent_encryption_ctxsize(struct btrfs_file_extent_item *e)
+{
+ u8 ctxsize;
+ btrfs_unpack_encryption(e->encryption, NULL, &ctxsize);
+ return ctxsize;
+}
static inline unsigned long
btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
@@ -2691,6 +2703,24 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);
+static inline u8
+btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
+ struct btrfs_file_extent_item *e)
+{
+ u8 ctxsize;
+ btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e),
+ NULL, &ctxsize);
+ return ctxsize;
+}
+
+static inline u8
+btrfs_file_extent_ctxsize_from_item(const struct extent_buffer *leaf,
+ const struct btrfs_path *path)
+{
+ return (btrfs_item_size(leaf, path->slots[0]) -
+ sizeof(struct btrfs_file_extent_item));
+}
+
/*
* this returns the number of bytes used by the item on disk, minus the
* size of any extent headers. If a file is compressed on disk, this is
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index d2fa32ffe304..21eacab2f325 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -5,6 +5,7 @@
#include <linux/rbtree.h>
#include <linux/refcount.h>
+#include "fscrypt.h"
#define EXTENT_MAP_LAST_BYTE ((u64)-4)
#define EXTENT_MAP_HOLE ((u64)-3)
@@ -27,6 +28,8 @@ enum {
EXTENT_FLAG_FS_MAPPING,
/* This em is merged from two or more physically adjacent ems */
EXTENT_FLAG_MERGED,
+ /* This em has a fscrypt extent context */
+ EXTENT_FLAG_ENCRYPTED,
};
struct extent_map {
@@ -50,6 +53,7 @@ struct extent_map {
*/
u64 generation;
unsigned long flags;
+ struct btrfs_fscrypt_extent_context fscrypt_context;
/* 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/file-item.c b/fs/btrfs/file-item.c
index 29999686d234..ec2950656ddc 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1216,6 +1216,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 -
@@ -1231,6 +1232,10 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
em->compress_type = compress_type;
em->block_start = bytenr;
em->block_len = em->orig_block_len;
+ } else if (btrfs_file_extent_encryption(leaf, fi)) {
+ set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+ em->block_start = bytenr;
+ em->block_len = em->orig_block_len;
} else {
bytenr += btrfs_file_extent_offset(leaf, fi);
em->block_start = bytenr;
@@ -1238,6 +1243,14 @@ 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));
+ em->fscrypt_context.len = ctxsize;
+
+ read_extent_buffer(leaf, em->fscrypt_context.buffer,
+ (unsigned long)fi->fscrypt_context,
+ ctxsize);
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
em->block_start = EXTENT_MAP_INLINE;
em->start = extent_start;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 929a0308676c..1b464aac9a41 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2644,14 +2644,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/fscrypt.c b/fs/btrfs/fscrypt.c
index b824bbd964bc..c52a5a8788dd 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -194,9 +194,30 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
return true;
}
+static int btrfs_fscrypt_get_extent_context(const struct inode *inode,
+ u64 lblk_num, void *ctx,
+ size_t len,
+ size_t *extent_offset,
+ size_t *extent_length)
+{
+ return len;
+}
+
+static int btrfs_fscrypt_set_extent_context(void *extent, void *ctx,
+ size_t len)
+{
+ struct btrfs_fscrypt_extent_context *extent_context = extent;
+ memcpy(extent_context->buffer, ctx, len);
+ extent_context->len = len;
+ return 0;
+}
+
const struct fscrypt_operations btrfs_fscrypt_ops = {
+ .flags = FS_CFLG_ALLOW_PARTIAL,
.key_prefix = "btrfs:",
.get_context = btrfs_fscrypt_get_context,
.set_context = btrfs_fscrypt_set_context,
.empty_dir = btrfs_fscrypt_empty_dir,
+ .get_extent_context = btrfs_fscrypt_get_extent_context,
+ .set_extent_context = btrfs_fscrypt_set_extent_context,
};
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 07884206c8a1..0b0be3b2d23e 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -5,6 +5,14 @@
#include <linux/fscrypt.h>
+#define BTRFS_ENCRYPTION_POLICY_MASK 0x03
+#define BTRFS_ENCRYPTION_CTXSIZE_MASK 0xfc
+
+struct btrfs_fscrypt_extent_context {
+ u8 buffer[FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ size_t len;
+};
+
#ifdef CONFIG_FS_ENCRYPTION
bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
struct extent_buffer *leaf,
@@ -22,5 +30,20 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
}
#endif
+static inline void btrfs_unpack_encryption(u8 encryption,
+ u8 *policy,
+ u8 *ctxsize)
+{
+ if (policy)
+ *policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK;
+ if (ctxsize)
+ *ctxsize = (encryption & BTRFS_ENCRYPTION_CTXSIZE_MASK) >> 2;
+}
+
+static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize)
+{
+ return policy | (ctxsize << 2);
+}
+
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 007abdf6de93..4c134a6486b3 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1015,7 +1015,6 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
ret = PTR_ERR(em);
goto out_free_reserve;
}
- free_extent_map(em);
ret = btrfs_add_ordered_extent(inode, start, /* file_offset */
async_extent->ram_size, /* num_bytes */
@@ -1024,7 +1023,9 @@ static int submit_one_async_extent(struct btrfs_inode *inode,
ins.offset, /* disk_num_bytes */
0, /* offset */
1 << BTRFS_ORDERED_COMPRESSED,
- async_extent->compress_type);
+ async_extent->compress_type,
+ &em->fscrypt_context);
+ free_extent_map(em);
if (ret) {
btrfs_drop_extent_cache(inode, start, end, 0);
goto out_free_reserve;
@@ -1297,12 +1298,13 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
ret = PTR_ERR(em);
goto out_reserve;
}
- free_extent_map(em);
ret = btrfs_add_ordered_extent(inode, start, ram_size, ram_size,
ins.objectid, cur_alloc_size, 0,
1 << BTRFS_ORDERED_REGULAR,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE,
+ &em->fscrypt_context);
+ free_extent_map(em);
if (ret)
goto out_drop_extent_cache;
@@ -2092,14 +2094,15 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
ret = PTR_ERR(em);
goto error;
}
- free_extent_map(em);
ret = btrfs_add_ordered_extent(inode,
cur_offset, nocow_args.num_bytes,
nocow_args.num_bytes,
nocow_args.disk_bytenr,
nocow_args.num_bytes, 0,
1 << BTRFS_ORDERED_PREALLOC,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE,
+ &em->fscrypt_context);
+ free_extent_map(em);
if (ret) {
btrfs_drop_extent_cache(inode, cur_offset,
nocow_end, 0);
@@ -2113,7 +2116,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
nocow_args.num_bytes,
0,
1 << BTRFS_ORDERED_NOCOW,
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE,
+ NULL);
if (ret)
goto error;
}
@@ -3046,6 +3050,7 @@ int btrfs_writepage_cow_fixup(struct page *page)
static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode, u64 file_pos,
struct btrfs_file_extent_item *stack_fi,
+ struct btrfs_fscrypt_extent_context *fscrypt_context,
const bool update_inode_bytes,
u64 qgroup_reserved)
{
@@ -3060,6 +3065,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi);
u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi);
struct btrfs_drop_extents_args drop_args = { 0 };
+ size_t context_len = fscrypt_context ? fscrypt_context->len : 0;
int ret;
path = btrfs_alloc_path();
@@ -3079,7 +3085,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) + context_len;
ret = btrfs_drop_extents(trans, root, inode, &drop_args);
if (ret)
goto out;
@@ -3090,7 +3096,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) + context_len);
if (ret)
goto out;
}
@@ -3099,6 +3105,11 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
write_extent_buffer(leaf, stack_fi,
btrfs_item_ptr_offset(leaf, path->slots[0]),
sizeof(struct btrfs_file_extent_item));
+ if (context_len)
+ write_extent_buffer(leaf, fscrypt_context->buffer,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(struct btrfs_file_extent_item),
+ context_len);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
@@ -3175,7 +3186,12 @@ 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 */
+ if (IS_ENCRYPTED(oe->inode)) {
+ u8 encryption = btrfs_pack_encryption(BTRFS_ENCRYPTION_FSCRYPT,
+ oe->fscrypt_context.len);
+ btrfs_set_stack_file_extent_encryption(&stack_fi, encryption);
+ }
+ /* Other encoding is reserved and always 0 */
/*
* For delalloc, when completing an ordered extent we update the inode's
@@ -3189,6 +3205,7 @@ static int insert_ordered_extent_file_extent(struct btrfs_trans_handle *trans,
return insert_reserved_file_extent(trans, BTRFS_I(oe->inode),
oe->file_offset, &stack_fi,
+ &oe->fscrypt_context,
update_inode_bytes, oe->qgroup_rsv);
}
@@ -7091,8 +7108,23 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
btrfs_extent_item_to_extent_map(inode, path, item, !page, em);
- if (extent_type == BTRFS_FILE_EXTENT_REG ||
- extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
+ if (extent_type == BTRFS_FILE_EXTENT_REG) {
+ u8 item_ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
+ u8 encryption = btrfs_file_extent_encryption(leaf, item);
+ u8 policy, ctxsize;
+ btrfs_unpack_encryption(encryption, &policy, &ctxsize);
+
+ if (policy == BTRFS_ENCRYPTION_FSCRYPT) {
+ if (ctxsize != item_ctxsize) {
+ btrfs_crit(fs_info,
+ "invalid encryption context size for inode %llu: itemsize %d item %d",
+ btrfs_ino(inode), ctxsize, item_ctxsize);
+ ret = -EUCLEAN;
+ goto out;
+ }
+ }
+ goto insert;
+ } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
goto insert;
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
unsigned long ptr;
@@ -7324,7 +7356,8 @@ static struct extent_map *btrfs_create_dio_extent(struct btrfs_inode *inode,
block_len, 0,
(1 << type) |
(1 << BTRFS_ORDERED_DIRECT),
- BTRFS_COMPRESS_NONE);
+ BTRFS_COMPRESS_NONE,
+ em ? &em->fscrypt_context : NULL);
if (ret) {
if (em) {
free_extent_map(em);
@@ -7625,6 +7658,15 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
em->compress_type = compress_type;
}
+ if (IS_ENCRYPTED(&inode->vfs_inode)) {
+ ret = fscrypt_set_extent_context(&inode->vfs_inode,
+ &em->fscrypt_context);
+ if (ret < 0) {
+ free_extent_map(em);
+ return ERR_PTR(ret);
+ }
+ }
+
do {
btrfs_drop_extent_cache(inode, em->start,
em->start + em->len - 1, 0);
@@ -9971,6 +10013,7 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
struct extent_buffer *leaf;
struct fscrypt_str disk_link;
u32 name_len = strlen(symname);
+ u8 encryption;
/*
* fscrypt sets disk_link.len to be len + 1, including a NULL terminator, but we
@@ -10049,7 +10092,9 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
btrfs_set_file_extent_generation(leaf, ei, trans->transid);
btrfs_set_file_extent_type(leaf, ei,
BTRFS_FILE_EXTENT_INLINE);
- btrfs_set_file_extent_encryption(leaf, ei, 0);
+ encryption = btrfs_pack_encryption(IS_ENCRYPTED(inode) ?
+ BTRFS_ENCRYPTION_FSCRYPT : 0, 0);
+ btrfs_set_file_extent_encryption(leaf, ei, encryption);
btrfs_set_file_extent_compression(leaf, ei, 0);
btrfs_set_file_extent_other_encoding(leaf, ei, 0);
/* ram size is the unencoded size */
@@ -10119,16 +10164,18 @@ 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)
return ERR_PTR(qgroup_released);
if (trans) {
- ret = insert_reserved_file_extent(trans, inode,
- file_offset, &stack_fi,
- true, qgroup_released);
+ ret = insert_reserved_file_extent(trans, inode, file_offset,
+ &stack_fi, NULL, true,
+ qgroup_released);
if (ret)
goto free_qgroup;
return trans;
@@ -10140,6 +10187,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);
extent_info.is_new_extent = true;
extent_info.update_times = true;
extent_info.qgroup_reserved = qgroup_released;
@@ -11081,14 +11129,15 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
ret = PTR_ERR(em);
goto out_free_reserved;
}
- free_extent_map(em);
ret = btrfs_add_ordered_extent(inode, start, num_bytes, ram_bytes,
ins.objectid, ins.offset,
encoded->unencoded_offset,
(1 << BTRFS_ORDERED_ENCODED) |
(1 << BTRFS_ORDERED_COMPRESSED),
- compression);
+ compression,
+ &em->fscrypt_context);
+ free_extent_map(em);
if (ret) {
btrfs_drop_extent_cache(inode, start, end, 0);
goto out_free_reserved;
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index eb24a6d20ff8..ba06eba5ff27 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -164,7 +164,8 @@ static inline struct rb_node *tree_search(struct btrfs_ordered_inode_tree *tree,
int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
u64 num_bytes, u64 ram_bytes, u64 disk_bytenr,
u64 disk_num_bytes, u64 offset, unsigned flags,
- int compress_type)
+ int compress_type,
+ struct btrfs_fscrypt_extent_context *fscrypt_context)
{
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
@@ -199,6 +200,9 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
entry->disk_bytenr = disk_bytenr;
entry->disk_num_bytes = disk_num_bytes;
entry->offset = offset;
+ if (fscrypt_context && fscrypt_context->len)
+ memcpy(&entry->fscrypt_context, fscrypt_context,
+ sizeof(*fscrypt_context));
entry->bytes_left = num_bytes;
entry->inode = igrab(&inode->vfs_inode);
entry->compress_type = compress_type;
@@ -1080,7 +1084,8 @@ static int clone_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pos,
WARN_ON_ONCE(flags & (1 << BTRFS_ORDERED_COMPRESSED));
return btrfs_add_ordered_extent(BTRFS_I(inode), file_offset, len, len,
disk_bytenr, len, 0, flags,
- ordered->compress_type);
+ ordered->compress_type,
+ &ordered->fscrypt_context);
}
int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pre,
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 87792f85e2c4..686c4a79bbe9 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -99,6 +99,7 @@ struct btrfs_ordered_extent {
u64 disk_bytenr;
u64 disk_num_bytes;
u64 offset;
+ struct btrfs_fscrypt_extent_context fscrypt_context;
/* number of bytes that still need writing */
u64 bytes_left;
@@ -194,7 +195,8 @@ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
u64 num_bytes, u64 ram_bytes, u64 disk_bytenr,
u64 disk_num_bytes, u64 offset, unsigned flags,
- int compress_type);
+ int compress_type,
+ struct btrfs_fscrypt_extent_context *fscrypt_context);
void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry,
struct btrfs_ordered_sum *sum);
struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *inode,
diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index d22086e1cbc8..466dfccba094 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -495,6 +495,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-checker.c b/fs/btrfs/tree-checker.c
index 05f404c7498e..a78b2882df1b 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -204,6 +204,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,
@@ -255,10 +256,12 @@ static int check_extent_data_item(struct extent_buffer *leaf,
BTRFS_NR_COMPRESS_TYPES - 1);
return -EUCLEAN;
}
- if (unlikely(btrfs_file_extent_encryption(leaf, fi))) {
+ btrfs_unpack_encryption(btrfs_file_extent_encryption(leaf, fi),
+ &policy, NULL);
+ if (unlikely(policy >= BTRFS_NR_ENCRYPTION_TYPES)) {
file_extent_err(leaf, slot,
- "invalid encryption for file extent, have %u expect 0",
- btrfs_file_extent_encryption(leaf, fi));
+ "invalid encryption for file extent, have %u expect range [0, %u]",
+ policy, BTRFS_NR_ENCRYPTION_TYPES - 1);
return -EUCLEAN;
}
if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
@@ -287,12 +290,29 @@ 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 53195cfe6a3d..11b51b591a75 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4651,6 +4651,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 = btrfs_pack_encryption(IS_ENCRYPTED(&inode->vfs_inode) ?
+ BTRFS_ENCRYPTION_FSCRYPT : 0,
+ em->fscrypt_context.len);
btrfs_set_stack_file_extent_generation(&fi, trans->transid);
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4672,6 +4675,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)
@@ -4703,7 +4707,8 @@ 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) +
+ em->fscrypt_context.len);
if (ret)
return ret;
}
@@ -4711,6 +4716,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, &em->fscrypt_context.buffer,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(fi), em->fscrypt_context.len);
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index 061aabca56f3..78e831fdd7c6 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -820,6 +820,10 @@ struct btrfs_file_extent_item {
* but not for stat.
*/
__u8 compression;
+ /*
+ * This field contains 4 bits of encryption type in the lower bits,
+ * 4 bits of ivsize in the upper bits. The unencrypted value is 0.
+ */
__u8 encryption;
__le16 other_encoding; /* spare for later use */
@@ -848,6 +852,11 @@ struct btrfs_file_extent_item {
*/
__le64 num_bytes;
+ /*
+ * Fscrypt extent encryption context. Only present if extent is
+ * encrypted (stored in the encryption field).
+ */
+ __u8 fscrypt_context[0];
} __attribute__ ((__packed__));
struct btrfs_csum_item {
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (13 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-09 11:35 ` David Sterba
2022-09-12 1:36 ` Eric Biggers
2022-09-06 0:35 ` [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible Sweet Tea Dorminy
` (6 subsequent siblings)
21 siblings, 2 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
As fscrypt files will be incompatible with older filesystem versions,
new filesystems should be created with an incompat flag for fscrypt.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 6 ++++--
include/uapi/linux/btrfs.h | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 38927a867028..e8d000fcc85d 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -330,7 +330,8 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \
BTRFS_FEATURE_INCOMPAT_RAID1C34 | \
BTRFS_FEATURE_INCOMPAT_ZONED | \
- BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2)
+ BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \
+ BTRFS_FEATURE_INCOMPAT_FSCRYPT)
#else
#define BTRFS_FEATURE_INCOMPAT_SUPP \
(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
@@ -345,7 +346,8 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
BTRFS_FEATURE_INCOMPAT_NO_HOLES | \
BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \
BTRFS_FEATURE_INCOMPAT_RAID1C34 | \
- BTRFS_FEATURE_INCOMPAT_ZONED)
+ BTRFS_FEATURE_INCOMPAT_ZONED | \
+ BTRFS_FEATURE_INCOMPAT_FSCRYPT)
#endif
#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index c413fca6f581..d85587538928 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -316,6 +316,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_FSCRYPT (1ULL << 14)
struct btrfs_ioctl_feature_flags {
__u64 compat_flags;
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible.
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (14 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 21:24 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames Sweet Tea Dorminy
` (5 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
For encrypted fscrypt_names, we can reuse fscrypt's precomputed hash of
the encrypted name to generate our own hash, instead of rehashing the
unencrypted name (which may not be possible if it's a nokey name).
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ctree.h | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index e8d000fcc85d..aa599518c057 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2842,7 +2842,10 @@ static inline void btrfs_crc32c_final(u32 crc, u8 *result)
static inline u64 btrfs_name_hash(const struct fscrypt_name *name)
{
- return crc32c((u32)~1, fname_name(name), fname_len(name));
+ if (fname_name(name))
+ return crc32c((u32)~1, fname_name(name), fname_len(name));
+ else
+ return name->hash | ((u64)name->minor_hash << 32);
}
/*
@@ -2851,8 +2854,20 @@ static inline u64 btrfs_name_hash(const struct fscrypt_name *name)
static inline u64 btrfs_extref_hash(u64 parent_objectid,
const struct fscrypt_name *name)
{
- return (u64) crc32c(parent_objectid, fname_name(name),
- fname_len(name));
+ /*
+ * If the name is encrypted and we don't have the key, we can use the
+ * fscrypt-provided hash instead of the normal name, and do the steps
+ * of crc32c() manually. Else, just hash the name, parent objectid,
+ * and name length.
+ */
+ if (fname_name(name))
+ return (u64) crc32c(parent_objectid, fname_name(name),
+ fname_len(name));
+ else
+ return (__crc32c_le_combine(parent_objectid,
+ name->hash,
+ fname_len(name)) ^
+ __crc32c_le_shift(~1, fname_len(name)));
}
static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (15 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 20:15 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
` (4 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
When filenames can be encrypted, if we don't initially match a desired
filename, we have to try decrypting it with the directory key to see if
it matches in plaintext. Similarly, for readdir, we need to read
encrypted directory items as well as unencrypted.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/delayed-inode.c | 32 +++++++++++---
fs/btrfs/delayed-inode.h | 4 +-
fs/btrfs/dir-item.c | 23 ++++++++++
fs/btrfs/inode.c | 90 ++++++++++++++++++++++++++++++++++++----
4 files changed, 134 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 069326654074..5eef6f96c6b6 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1493,9 +1493,9 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
ret = __btrfs_add_delayed_item(delayed_node, delayed_item);
if (unlikely(ret)) {
+ // TODO: It would be nice to print the base64encoded name here maybe?
btrfs_err(trans->fs_info,
- "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)",
- fname_len(fname), fname_name(fname),
+ "err add delayed dir index item into the insertion tree of the delayed node (root id: %llu, inode id: %llu, errno: %d)",
delayed_node->root->root_key.objectid,
delayed_node->inode_id, ret);
BUG();
@@ -1721,7 +1721,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;
@@ -1731,6 +1733,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;
@@ -1758,8 +1761,27 @@ 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_FSCRYPT_NAME) {
+ 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 8abeb78af14e..9491bf0b7576 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -156,7 +156,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 8d7c3c32ed8e..6b1ea32419fb 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -120,6 +120,9 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_disk_key disk_key;
u32 data_size;
+ if (fname->crypto_buf.name)
+ type |= BTRFS_FT_FSCRYPT_NAME;
+
key.objectid = btrfs_ino(dir);
key.type = BTRFS_DIR_ITEM_KEY;
key.offset = btrfs_name_hash(fname);
@@ -385,6 +388,18 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
u32 cur = 0;
u32 this_len;
struct extent_buffer *leaf;
+ bool encrypted = (fname->crypto_buf.name != NULL);
+ struct fscrypt_name unencrypted_fname;
+
+ if (encrypted) {
+ unencrypted_fname = (struct fscrypt_name){
+ .usr_fname = fname->usr_fname,
+ .disk_name = {
+ .name = (unsigned char *)fname->usr_fname->name,
+ .len = fname->usr_fname->len,
+ },
+ };
+ }
leaf = path->nodes[0];
dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
@@ -401,6 +416,14 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
return dir_item;
}
+ if (encrypted &&
+ btrfs_dir_name_len(leaf, dir_item) == fname_len(&unencrypted_fname) &&
+ btrfs_fscrypt_match_name(&unencrypted_fname, leaf,
+ (unsigned long)(dir_item + 1),
+ dir_name_len)) {
+ return dir_item;
+ }
+
cur += this_len;
dir_item = (struct btrfs_dir_item *)((char *)dir_item +
this_len);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 4c134a6486b3..1c7681d7770c 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4292,6 +4292,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
u64 index;
u64 ino = btrfs_ino(inode);
u64 dir_ino = btrfs_ino(dir);
+ u64 di_name_len;
path = btrfs_alloc_path();
if (!path) {
@@ -4304,6 +4305,13 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
ret = di ? PTR_ERR(di) : -ENOENT;
goto err;
}
+
+ /*
+ * We have to read the actual name length off disk -- the fname
+ * provided may have been a nokey_name with uncertain length.
+ */
+ di_name_len = btrfs_dir_name_len(path->nodes[0], di);
+
ret = btrfs_delete_one_dir_name(trans, root, path, di);
if (ret)
goto err;
@@ -4371,7 +4379,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 - fname_len(fname) * 2);
+ btrfs_i_size_write(dir, dir->vfs_inode.i_size - di_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);
@@ -5882,12 +5890,25 @@ 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);
+ if (BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT) {
+ ret = fscrypt_prepare_lookup(dir, dentry, &fname);
+ if (ret)
+ return ERR_PTR(ret);
+ } else {
+ fname = (struct fscrypt_name) {
+ .usr_fname = &dentry->d_name,
+ .disk_name = FSTR_INIT((char *) dentry->d_name.name,
+ dentry->d_name.len),
+ };
+ }
+
ret = btrfs_inode_by_name(dir, dentry, &location, &di_type);
if (ret < 0)
return ERR_PTR(ret);
@@ -6029,18 +6050,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_FSCRYPT_CONTEXT) {
+ 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;
@@ -6058,6 +6093,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 di_flags;
+ u32 nokey_len;
if (found_key.objectid != key.objectid)
break;
@@ -6069,8 +6105,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)
@@ -6084,8 +6125,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
di_flags = 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 (di_flags & BTRFS_FT_FSCRYPT_NAME) {
+ 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.
+ */
+ if (!fscrypt_has_encryption_key(inode)) {
+ struct fscrypt_name fname = {
+ .disk_name = fstr,
+ };
+ u64 name_hash = btrfs_name_hash(&fname);
+ 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(btrfs_dir_flags_to_ftype(di_flags)),
@@ -6107,7 +6176,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;
@@ -6138,6 +6208,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;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (16 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-08 20:19 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 20/20] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
` (3 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
Add in the necessary calls to encrypt and decrypt data to achieve
encryption of normal data.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/extent_io.c | 56 ++++++++++++++++++++++++++++++++++++-----
fs/btrfs/file-item.c | 9 +++++--
fs/btrfs/fscrypt.c | 23 ++++++++++++++++-
fs/btrfs/tree-checker.c | 11 +++++---
4 files changed, 87 insertions(+), 12 deletions(-)
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index a467a7553bd9..8adcee599844 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -183,6 +183,7 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
{
struct bio *bio;
struct bio_vec *bv;
+ struct page *first_page;
struct inode *inode;
int mirror_num;
@@ -191,13 +192,17 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
bio = bio_ctrl->bio;
bv = bio_first_bvec_all(bio);
- inode = bv->bv_page->mapping->host;
+ first_page = bio_first_page_all(bio);
+ if (fscrypt_is_bounce_page(first_page))
+ inode = fscrypt_pagecache_page(first_page)->mapping->host;
+ else
+ inode = first_page->mapping->host;
mirror_num = bio_ctrl->mirror_num;
/* Caller should ensure the bio has at least some range added */
ASSERT(bio->bi_iter.bi_size);
- btrfs_bio(bio)->file_offset = page_offset(bv->bv_page) + bv->bv_offset;
+ btrfs_bio(bio)->file_offset = page_offset(first_page) + bv->bv_offset;
if (!is_data_inode(inode))
btrfs_submit_metadata_bio(inode, bio, mirror_num);
@@ -2810,9 +2815,19 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio)
ASSERT(!bio_flagged(bio, BIO_CLONED));
bio_for_each_segment_all(bvec, bio, iter_all) {
struct page *page = bvec->bv_page;
- struct inode *inode = page->mapping->host;
- struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
- const u32 sectorsize = fs_info->sectorsize;
+ struct inode *inode;
+ struct btrfs_fs_info *fs_info;
+ u32 sectorsize;
+ struct page *bounce_page = NULL;
+
+ if (fscrypt_is_bounce_page(page)) {
+ bounce_page = page;
+ page = fscrypt_pagecache_page(bounce_page);
+ }
+
+ inode = page->mapping->host;
+ fs_info = btrfs_sb(inode->i_sb);
+ sectorsize = fs_info->sectorsize;
/* Our read/write should always be sector aligned. */
if (!IS_ALIGNED(bvec->bv_offset, sectorsize))
@@ -2833,7 +2848,7 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio)
}
end_extent_writepage(page, error, start, end);
-
+ fscrypt_free_bounce_page(bounce_page);
btrfs_page_clear_writeback(fs_info, page, start, bvec->bv_len);
}
@@ -3029,6 +3044,17 @@ static void end_bio_extent_readpage(struct btrfs_bio *bbio)
}
}
+ if (likely(uptodate)) {
+ if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
+ int ret = fscrypt_decrypt_pagecache_blocks(page,
+ bvec->bv_len,
+ bvec->bv_offset);
+ if (ret) {
+ error_bitmap = (unsigned int) -1;
+ uptodate = false;
+ }
+ }
+ }
if (likely(uptodate)) {
loff_t i_size = i_size_read(inode);
pgoff_t end_index = i_size >> PAGE_SHIFT;
@@ -3364,11 +3390,29 @@ static int submit_extent_page(blk_opf_t opf,
bool force_bio_submit)
{
int ret = 0;
+ struct page *bounce_page = NULL;
struct btrfs_inode *inode = BTRFS_I(page->mapping->host);
unsigned int cur = pg_offset;
ASSERT(bio_ctrl);
+ if ((opf & REQ_OP_MASK) == REQ_OP_WRITE &&
+ fscrypt_inode_uses_fs_layer_crypto(&inode->vfs_inode)) {
+ gfp_t gfp_flags = GFP_NOFS;
+
+ if (bio_ctrl->bio)
+ gfp_flags = GFP_NOWAIT | __GFP_NOWARN;
+ else
+ gfp_flags = GFP_NOFS;
+ bounce_page = fscrypt_encrypt_pagecache_blocks(page, size,
+ pg_offset,
+ gfp_flags);
+ if (IS_ERR(bounce_page))
+ return PTR_ERR(bounce_page);
+ page = bounce_page;
+ pg_offset = 0;
+ }
+
ASSERT(pg_offset < PAGE_SIZE && size <= PAGE_SIZE &&
pg_offset + size <= PAGE_SIZE);
if (force_bio_submit)
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ec2950656ddc..4a2a5cf73e9b 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -663,8 +663,13 @@ blk_status_t btrfs_csum_one_bio(struct btrfs_inode *inode, struct bio *bio,
shash->tfm = fs_info->csum_shash;
bio_for_each_segment(bvec, bio, iter) {
- if (use_page_offsets)
- offset = page_offset(bvec.bv_page) + bvec.bv_offset;
+ if (use_page_offsets) {
+ struct page *page = bvec.bv_page;
+
+ if (fscrypt_is_bounce_page(page))
+ page = fscrypt_pagecache_page(page);
+ offset = page_offset(page) + bvec.bv_offset;
+ }
if (!ordered) {
ordered = btrfs_lookup_ordered_extent(inode, offset);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index c52a5a8788dd..edde09d22b7a 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -200,7 +200,28 @@ static int btrfs_fscrypt_get_extent_context(const struct inode *inode,
size_t *extent_offset,
size_t *extent_length)
{
- return len;
+ 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) {
+ int ret = em->fscrypt_context.len;
+ if (ctx)
+ memcpy(ctx, em->fscrypt_context.buffer,
+ em->fscrypt_context.len);
+
+ if (extent_offset)
+ *extent_offset
+ = (offset - em->start) >> inode->i_blkbits;
+
+ if (extent_length)
+ *extent_length = em->len >> inode->i_blkbits;
+
+ free_extent_map(em);
+ return ret;
+ }
+ return -EINVAL;
}
static int btrfs_fscrypt_set_extent_context(void *extent, void *ctx,
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index a78b2882df1b..3115acdc360f 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -273,9 +273,14 @@ static int check_extent_data_item(struct extent_buffer *leaf,
return -EUCLEAN;
}
- /* Compressed inline extent has no on-disk size, skip it */
- if (btrfs_file_extent_compression(leaf, fi) !=
- BTRFS_COMPRESS_NONE)
+ /*
+ * Compressed inline extent has no on-disk size; encrypted has
+ * variable size; skip them
+ */
+ if ((btrfs_file_extent_compression(leaf, fi) !=
+ BTRFS_COMPRESS_NONE) ||
+ (btrfs_file_extent_encryption(leaf, fi) !=
+ BTRFS_ENCRYPTION_NONE))
return 0;
/* Uncompressed inline extent size must match item size */
--
2.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* [PATCH v2 20/20] btrfs: implement fscrypt ioctls
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (17 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
@ 2022-09-06 0:35 ` Sweet Tea Dorminy
2022-09-07 21:33 ` David Sterba
2022-09-06 22:35 ` [PATCH v2 00/20] btrfs: add fscrypt integration Eric Biggers
` (2 subsequent siblings)
21 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 0:35 UTC (permalink / raw)
To: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
Cc: Omar Sandoval, Sweet Tea Dorminy
From: Omar Sandoval <osandov@osandov.com>
These ioctls allow encryption to be set up.
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 708e514aca25..ea1c14b26206 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -5457,6 +5457,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, FSCRYPT);
+ 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.35.1
^ permalink raw reply related [flat|nested] 66+ messages in thread
* Re: [PATCH v2 00/20] btrfs: add fscrypt integration
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (18 preceding siblings ...)
2022-09-06 0:35 ` [PATCH v2 20/20] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
@ 2022-09-06 22:35 ` Eric Biggers
2022-09-06 23:01 ` Sweet Tea Dorminy
2022-09-07 19:38 ` David Sterba
[not found] ` <2b32b14368c67eb8591ccc4b0cf9d19358dfae23.1662420176.git.sweettea-kernel@dorminy.me>
21 siblings, 1 reply; 66+ messages in thread
From: Eric Biggers @ 2022-09-06 22:35 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:15PM -0400, Sweet Tea Dorminy wrote:
> This is a changeset adding encryption to btrfs.
What git tree and commit does this apply to?
- Eric
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 00/20] btrfs: add fscrypt integration
2022-09-06 22:35 ` [PATCH v2 00/20] btrfs: add fscrypt integration Eric Biggers
@ 2022-09-06 23:01 ` Sweet Tea Dorminy
2022-09-06 23:10 ` Eric Biggers
0 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-06 23:01 UTC (permalink / raw)
To: Eric Biggers
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On 9/6/22 18:35, Eric Biggers wrote:
> On Mon, Sep 05, 2022 at 08:35:15PM -0400, Sweet Tea Dorminy wrote:
>> This is a changeset adding encryption to btrfs.
>
> What git tree and commit does this apply to?
https://github.com/kdave/btrfs-devel/; branch misc-next. Should apply
cleanly to its current tip, 76ccdc004e12312ea056811d530043ff11d050c6 .
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 00/20] btrfs: add fscrypt integration
2022-09-06 23:01 ` Sweet Tea Dorminy
@ 2022-09-06 23:10 ` Eric Biggers
2022-09-07 0:01 ` Sweet Tea Dorminy
0 siblings, 1 reply; 66+ messages in thread
From: Eric Biggers @ 2022-09-06 23:10 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Tue, Sep 06, 2022 at 07:01:15PM -0400, Sweet Tea Dorminy wrote:
>
>
> On 9/6/22 18:35, Eric Biggers wrote:
> > On Mon, Sep 05, 2022 at 08:35:15PM -0400, Sweet Tea Dorminy wrote:
> > > This is a changeset adding encryption to btrfs.
> >
> > What git tree and commit does this apply to?
>
> https://github.com/kdave/btrfs-devel/; branch misc-next. Should apply
> cleanly to its current tip, 76ccdc004e12312ea056811d530043ff11d050c6 .
Patch 8 wasn't received by linux-fscrypt for some reason, any idea why?
$ b4 am cover.1662420176.git.sweettea-kernel@dorminy.me
Looking up https://lore.kernel.org/linux-fscrypt/cover.1662420176.git.sweettea-kernel%40dorminy.me
Grabbing thread from lore.kernel.org/linux-fscrypt/cover.1662420176.git.sweettea-kernel%40dorminy.me/t.mbox.gz
Analyzing 22 messages in the thread
Checking attestation on all messages, may take a moment...
---
[PATCH v2 1/20] fscrypt: expose fscrypt_nokey_name
[PATCH v2 2/20] fscrypt: add flag allowing partially-encrypted directories
[PATCH v2 3/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility
[PATCH v2 4/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames
[PATCH v2 5/20] fscrypt: add extent-based encryption
[PATCH v2 6/20] fscrypt: document btrfs' fscrypt quirks.
[PATCH v2 7/20] btrfs: store directory's encryption state
ERROR: missing [8/20]!
[PATCH v2 9/20] btrfs: setup fscrypt_names from dentrys using helper
[PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
[PATCH v2 11/20] btrfs: disable various operations on encrypted inodes
[PATCH v2 12/20] btrfs: start using fscrypt hooks.
[PATCH v2 13/20] btrfs: add fscrypt_context items.
[PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag.
[PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent
[PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
[PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible.
[PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames
[PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate
[PATCH v2 20/20] btrfs: implement fscrypt ioctls
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 00/20] btrfs: add fscrypt integration
2022-09-06 23:10 ` Eric Biggers
@ 2022-09-07 0:01 ` Sweet Tea Dorminy
0 siblings, 0 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-07 0:01 UTC (permalink / raw)
To: Eric Biggers
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On 2022-09-06 19:10, Eric Biggers wrote:
> On Tue, Sep 06, 2022 at 07:01:15PM -0400, Sweet Tea Dorminy wrote:
>>
>>
>> On 9/6/22 18:35, Eric Biggers wrote:
>> > On Mon, Sep 05, 2022 at 08:35:15PM -0400, Sweet Tea Dorminy wrote:
>> > > This is a changeset adding encryption to btrfs.
>> >
>> > What git tree and commit does this apply to?
>>
>> https://github.com/kdave/btrfs-devel/; branch misc-next. Should apply
>> cleanly to its current tip, 76ccdc004e12312ea056811d530043ff11d050c6 .
>
> Patch 8 wasn't received by linux-fscrypt for some reason, any idea why?
>
> $ b4 am cover.1662420176.git.sweettea-kernel@dorminy.me
> Looking up
> https://lore.kernel.org/linux-fscrypt/cover.1662420176.git.sweettea-kernel%40dorminy.me
> Grabbing thread from
> lore.kernel.org/linux-fscrypt/cover.1662420176.git.sweettea-kernel%40dorminy.me/t.mbox.gz
> Analyzing 22 messages in the thread
> Checking attestation on all messages, may take a moment...
> ---
> [PATCH v2 1/20] fscrypt: expose fscrypt_nokey_name
> [PATCH v2 2/20] fscrypt: add flag allowing partially-encrypted
> directories
> [PATCH v2 3/20] fscrypt: add fscrypt_have_same_policy() to check
> inode compatibility
> [PATCH v2 4/20] fscrypt: allow fscrypt_generate_iv() to distinguish
> filenames
> [PATCH v2 5/20] fscrypt: add extent-based encryption
> [PATCH v2 6/20] fscrypt: document btrfs' fscrypt quirks.
> [PATCH v2 7/20] btrfs: store directory's encryption state
> ERROR: missing [8/20]!
> [PATCH v2 9/20] btrfs: setup fscrypt_names from dentrys using helper
> [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
> [PATCH v2 11/20] btrfs: disable various operations on encrypted
> inodes
> [PATCH v2 12/20] btrfs: start using fscrypt hooks.
> [PATCH v2 13/20] btrfs: add fscrypt_context items.
> [PATCH v2 14/20] btrfs: translate btrfs encryption flags and
> encrypted inode flag.
> [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal
> file extent
> [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature
> flag.
> [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible.
> [PATCH v2 18/20] btrfs: adapt directory read and lookup to
> potentially encrypted filenames
> [PATCH v2 19/20] btrfs: encrypt normal file extent data if
> appropriate
> [PATCH v2 20/20] btrfs: implement fscrypt ioctls
Patch 8 is large and dull, and bounced from linux-fscrypt@ for size, but
b4 gets it for me. b4 -p linux-fscrypt recreates the missing [8/20]; is
it possible that option is set somewhere in your configuration?
$ b4 am -o- cover.1662420176.git.sweettea-kernel@dorminy.me | git am
Looking up
https://lore.kernel.org/r/cover.1662420176.git.sweettea-kernel%40dorminy.me
Grabbing thread from
lore.kernel.org/all/cover.1662420176.git.sweettea-kernel%40dorminy.me/t.mbox.gz
Analyzing 23 messages in the thread
Checking attestation on all messages, may take a moment...
---
✓ [PATCH v2 1/20] fscrypt: expose fscrypt_nokey_name
✓ [PATCH v2 2/20] fscrypt: add flag allowing partially-encrypted
directories
✓ [PATCH v2 3/20] fscrypt: add fscrypt_have_same_policy() to check
inode compatibility
✓ [PATCH v2 4/20] fscrypt: allow fscrypt_generate_iv() to distinguish
filenames
✓ [PATCH v2 5/20] fscrypt: add extent-based encryption
✓ [PATCH v2 6/20] fscrypt: document btrfs' fscrypt quirks.
✓ [PATCH v2 7/20] btrfs: store directory's encryption state
✓ [PATCH v2 8/20] btrfs: use fscrypt_names instead of name/len
everywhere.
✓ [PATCH v2 9/20] btrfs: setup fscrypt_names from dentrys using helper
✓ [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
✓ [PATCH v2 11/20] btrfs: disable various operations on encrypted
inodes
+ Reported-by: kernel test robot <lkp@intel.com> (✓ DKIM/intel.com)
✓ [PATCH v2 12/20] btrfs: start using fscrypt hooks.
✓ [PATCH v2 13/20] btrfs: add fscrypt_context items.
✓ [PATCH v2 14/20] btrfs: translate btrfs encryption flags and
encrypted inode flag.
✓ [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal
file extent
✓ [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature
flag.
✓ [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible.
✓ [PATCH v2 18/20] btrfs: adapt directory read and lookup to
potentially encrypted filenames
✓ [PATCH v2 19/20] btrfs: encrypt normal file extent data if
appropriate
✓ [PATCH v2 20/20] btrfs: implement fscrypt ioctls
---
✓ Signed: DKIM/dorminy.me
---
Total patches: 20
---
Link:
https://lore.kernel.org/r/cover.1662420176.git.sweettea-kernel@dorminy.me
...
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 00/20] btrfs: add fscrypt integration
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
` (19 preceding siblings ...)
2022-09-06 22:35 ` [PATCH v2 00/20] btrfs: add fscrypt integration Eric Biggers
@ 2022-09-07 19:38 ` David Sterba
[not found] ` <2b32b14368c67eb8591ccc4b0cf9d19358dfae23.1662420176.git.sweettea-kernel@dorminy.me>
21 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 19:38 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On Mon, Sep 05, 2022 at 08:35:15PM -0400, Sweet Tea Dorminy wrote:
> This is a changeset adding encryption to btrfs.
>
> Last October, Omar Sandoval sent out a design document for having fscrypt
> integration with btrfs [1]. In summary, it proposes btrfs storing its
> own encryption IVs on a per-file-extent basis. fscrypt usually encrypts
> files using an IV derived from per-inode information; this would prevent
> snapshotting or reflinking or data relocation for btrfs. We have
> refined this into a fscrypt extent context object, opaque to the
> filesystem, which fscrypt uses to generate an IV associated with each
> block in an extent. Thus, all the inodes sharing a particular
> key and file extent may decrypt the extent.
>
> This series implements this integration for the simple
> case, non-compressed data extents. Followup changes will allow
> encryption of compressed extents, inline extents, and verity items, and
> will add tests around subvolume encryption. This series should provide
> encryption for the simplest cases, but this series should not be used in
> production, as there are likely bugs.
>
> Preliminary btrfs-progs changes are available at [2]; fstests changes
> are available at [3].
I did a quick pass to check if there's anything that could be merged to
6.1 as preparatory, the fname is a candidate but I've also seen random
coding style issues that I'd like to get fixed first.
One thing I've noticed is that the incompat bit is only defined but not
used anywhere. Any functional change should be guarded behind it, and
once the implementation is complete the enabling part is in a separate
patch.
Regarding the build config options, I assume that the fscrypt support is
optional, so it should build with and without the option. I'm not sure
I've see enough ifdefs for that. For such features there should be a
line in btrfs_print_mod_info, like we have eg. for verity.
I'll post other comments under the patches.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 05/20] fscrypt: add extent-based encryption
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
@ 2022-09-07 19:59 ` Omar Sandoval
2022-09-08 15:33 ` Josef Bacik
2022-09-12 1:34 ` Eric Biggers
2 siblings, 0 replies; 66+ messages in thread
From: Omar Sandoval @ 2022-09-07 19:59 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On Mon, Sep 05, 2022 at 08:35:20PM -0400, Sweet Tea Dorminy wrote:
> Some filesystems need to encrypt data based on extents, rather than on
> inodes, due to features incompatible with inode-based encryption. For
> instance, btrfs can have multiple inodes referencing a single block of
> data, and moves logical data blocks to different physical locations on
> disk in the background; these two features mean inode or
> physical-location-based policies will not work for btrfs.
I really like how this abstracts away the encryption details from the
filesystem.
> This change introduces fscrypt_extent_context objects, in analogy to
> existing context objects based on inodes. For a filesystem which uses
> extents,
This makes it sounds like all filesystems that store allocations as
extents should define these, but ext4 (for example) uses extents but is
fine with inode-based encryption policies. Perhaps this can say
something like "A filesytem can opt into the extent-based encryption
policy by defining new hooks that manage a new fscrypt_extent_context."
> a new hook provides a new fscrypt_extent_context. During file
> content encryption/decryption, the existing fscrypt_context object
> provides key information, while the new fscrypt_extent_context provides
> IV information. For filename encryption, the existing IV generation
> methods are still used, since filenames are not stored in extents.
>
> As individually keyed inodes prevent sharing of extents, such policies
> are forbidden for filesystems with extent-based encryption.
This ends up forcing Btrfs to use Adiantum. However, I imagine that most
users would prefer to use AES if their CPU has AES instructions. From
what I understand, it should still be possible to use the AES encryption
modes with extent contexts, correct? We just need to decide how to make
that work with the encryption policy flags. I see a couple of options:
1. We add a specific FSCRYPT_POLICY_FLAG_EXTENT_BASED or something like
that which the user must specify for filesystems requiring
extent-based encryption.
2. The "default" mode (i.e., none of DIRECT_KEY, IV_INO_LBLK_64, nor
IV_INO_LBLK_32 are specified) automatically opts into extent-based
encryption for filesystems requiring it.
Either way, we should probably still disallow IV_INO_LBLK_64 and
IV_INO_LBLK_32 since neither of those make sense with per-extent IVs.
I'd love to hear what Eric would prefer here.
Thanks,
Omar
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 08/20] btrfs: use fscrypt_names instead of name/len everywhere.
[not found] ` <2b32b14368c67eb8591ccc4b0cf9d19358dfae23.1662420176.git.sweettea-kernel@dorminy.me>
@ 2022-09-07 20:04 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 20:04 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On Mon, Sep 05, 2022 at 08:35:23PM -0400, Sweet Tea Dorminy wrote:
> For encryption, the plaintext filenames provided by the VFS will need to
> be translated to ciphertext filenames on disk. Fscrypt provides a struct
> to encapsulate a potentially encrypted filename, struct fscrypt_name.
> This change converts every (name, len) pair to be a struct fscrypt_name,
> statically initialized, for ease of review and uniformity.
Is there some clear boundary where the name needs to be encoded or
decoded? I don't think we should use fscrypt_name in so many functions,
namely internal helpers that really only care about the plain name +
length. Such widespread use fscrypt structure would make it hard to
synchronize with the user space sources.
What we could do to ease the integragtion with the fscrypt name is to
use the qstr structure, ie. something that's easily convertible to the
fscrypt_name::usr_fname.
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -21,11 +21,13 @@
> #include <linux/pagemap.h>
> #include <linux/btrfs.h>
> #include <linux/btrfs_tree.h>
> +#include <linux/fscrypt.h>
> #include <linux/workqueue.h>
> #include <linux/security.h>
> #include <linux/sizes.h>
> #include <linux/dynamic_debug.h>
> #include <linux/refcount.h>
> +#include <linux/crc32.h>
> #include <linux/crc32c.h>
> #include <linux/iomap.h>
> #include "extent-io-tree.h"
> @@ -2803,18 +2805,19 @@ static inline void btrfs_crc32c_final(u32 crc, u8 *result)
> put_unaligned_le32(~crc, result);
> }
>
> -static inline u64 btrfs_name_hash(const char *name, int len)
> +static inline u64 btrfs_name_hash(const struct fscrypt_name *name)
> {
> - return crc32c((u32)~1, name, len);
> + return crc32c((u32)~1, fname_name(name), fname_len(name));
This for example is a primitive helper that just hashes the correct
bytes and does not need to know anything about whether it's encrypted or
not. That should be set up higher in the call chain.
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -3863,11 +3863,19 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
> static u64 xattr_default = 0;
> int scanned = 0;
>
> + struct fscrypt_name name_access = {
> + .disk_name = FSTR_INIT(XATTR_NAME_POSIX_ACL_ACCESS,
> + strlen(XATTR_NAME_POSIX_ACL_ACCESS))
> + };
> +
> + struct fscrypt_name name_default = {
> + .disk_name = FSTR_INIT(XATTR_NAME_POSIX_ACL_DEFAULT,
> + strlen(XATTR_NAME_POSIX_ACL_DEFAULT))
> + };
> +
> if (!xattr_access) {
> - xattr_access = btrfs_name_hash(XATTR_NAME_POSIX_ACL_ACCESS,
> - strlen(XATTR_NAME_POSIX_ACL_ACCESS));
> - xattr_default = btrfs_name_hash(XATTR_NAME_POSIX_ACL_DEFAULT,
> - strlen(XATTR_NAME_POSIX_ACL_DEFAULT));
> + xattr_access = btrfs_name_hash(&name_access);
> + xattr_default = btrfs_name_hash(&name_default);
And here it needs extra structure just to pass plain strings.
> + __func__, fname_name(&fname), btrfs_ino(BTRFS_I(dir)),
> location->objectid, location->type, location->offset);
> }
> if (!ret)
> @@ -6243,6 +6258,14 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
> if (ret)
> return ret;
>
> + if (!args->orphan) {
> + char *name = (char *) args->dentry->d_name.name;
> + int name_len = args->dentry->d_name.len;
Please put a newline between declaration and statement block.
> + args->fname = (struct fscrypt_name) {
> + .disk_name = FSTR_INIT(name, name_len),
> + };
Please don't use this construct to intialize compounds, we don't use it
anywhere. There are more examples for other structures too.
> + }
> +
> /* 1 to add inode item */
> *trans_num_items = 1;
> /* 1 to add compression property */
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -1596,8 +1596,9 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans,
> * happens, we should return the error number. If the error which just affect
> * the creation of the pending snapshots, just return 0.
> */
> -static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
> - struct btrfs_pending_snapshot *pending)
> +static noinline int
> +create_pending_snapshot(struct btrfs_trans_handle *trans,
> + struct btrfs_pending_snapshot *pending)
Please keep the specifiers and type on the same line as the function
name, the parameters can slightly overfrlow the 80 char limit if it
avoids a line break, otherwise the patameters go on the next line.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes
2022-09-06 0:35 ` [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2022-09-07 20:11 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 20:11 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:26PM -0400, Sweet Tea Dorminy wrote:
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -1895,7 +1895,7 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from)
> goto relock;
> }
>
> - if (check_direct_IO(fs_info, from, pos)) {
> + if (IS_ENCRYPTED(inode) || check_direct_IO(fs_info, from, pos)) {
I'm not sure if we want the IS_ENCRYPTED as an explicit check or put it
to check_direct_IO, but probably to make it obvious that direct io does
not work with it a separate check is ok.
> btrfs_inode_unlock(inode, ilock_flags);
> goto buffered;
> }
> @@ -3729,7 +3729,7 @@ static ssize_t btrfs_direct_read(struct kiocb *iocb, struct iov_iter *to)
> ssize_t read = 0;
> ssize_t ret;
>
> - if (fsverity_active(inode))
> + if (IS_ENCRYPTED(inode) || fsverity_active(inode))
Yeah as we have something similar for verity.
> return 0;
>
> if (check_direct_read(btrfs_sb(inode->i_sb), to, iocb->ki_pos))
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 482c5b3d9e70..fea48c12a33a 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -409,7 +409,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 9acf47b11fe6..d22086e1cbc8 100644
> --- a/fs/btrfs/reflink.c
> +++ b/fs/btrfs/reflink.c
> @@ -805,6 +805,13 @@ 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.
> + */
The comment fits on one line (and slight 80 chars overflow is ok).
> + if (!fscrypt_have_same_policy(inode_in, inode_out)) {
> + return -EINVAL;
Single statements don't need the { }
> + }
> +
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 12/20] btrfs: start using fscrypt hooks.
2022-09-06 0:35 ` [PATCH v2 12/20] btrfs: start using fscrypt hooks Sweet Tea Dorminy
@ 2022-09-07 20:17 ` David Sterba
2022-09-07 20:42 ` Sweet Tea Dorminy
2022-09-08 19:42 ` Josef Bacik
1 sibling, 1 reply; 66+ messages in thread
From: David Sterba @ 2022-09-07 20:17 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:27PM -0400, Sweet Tea Dorminy wrote:
> 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.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 1 +
> fs/btrfs/file.c | 3 ++
> fs/btrfs/fscrypt.c | 3 ++
> fs/btrfs/fscrypt.h | 1 +
> fs/btrfs/inode.c | 91 ++++++++++++++++++++++++++++++++++++++++------
> fs/btrfs/super.c | 3 ++
> 6 files changed, 90 insertions(+), 12 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 230537a007b6..2b9ba8d77861 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -3416,6 +3416,7 @@ struct btrfs_new_inode_args {
> */
> struct posix_acl *default_acl;
> struct posix_acl *acl;
> + bool encrypt;
> };
> int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
> unsigned int *trans_num_items);
> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> index 7216ac1f860c..929a0308676c 100644
> --- a/fs/btrfs/file.c
> +++ b/fs/btrfs/file.c
> @@ -3695,6 +3695,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
> int ret;
>
> filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC;
> + ret = fscrypt_file_open(inode, filp);
> + if (ret)
> + return ret;
>
> ret = fsverity_file_open(inode, filp);
Can fsverity and fscrypt can be used at the same time?
> if (ret)
> --- a/fs/btrfs/fscrypt.h
> +++ b/fs/btrfs/fscrypt.h
> @@ -22,4 +22,5 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
> }
> #endif
>
> +extern const struct fscrypt_operations btrfs_fscrypt_ops;
Please keep a blank line before the last #endif
> #endif /* BTRFS_FSCRYPT_H */
> @@ -9907,15 +9927,22 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, 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 NULL terminator, but we
> + * don't store that NULL.
I think it should be referred to as NUL character, or as '\0'.
> + */
> + 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)
> @@ -9994,6 +10035,29 @@ static int btrfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -47,6 +47,8 @@
> #include "tests/btrfs-tests.h"
> #include "block-group.h"
> #include "discard.h"
> +#include "fscrypt.h"
No newline please
> +
> #include "qgroup.h"
> #include "raid56.h"
> #define CREATE_TRACE_POINTS
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 12/20] btrfs: start using fscrypt hooks.
2022-09-07 20:17 ` David Sterba
@ 2022-09-07 20:42 ` Sweet Tea Dorminy
2022-09-12 1:50 ` Eric Biggers
0 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-07 20:42 UTC (permalink / raw)
To: dsterba
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
>> diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
>> index 7216ac1f860c..929a0308676c 100644
>> --- a/fs/btrfs/file.c
>> +++ b/fs/btrfs/file.c
>> @@ -3695,6 +3695,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
>> int ret;
>>
>> filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC;
>> + ret = fscrypt_file_open(inode, filp);
>> + if (ret)
>> + return ret;
>>
>> ret = fsverity_file_open(inode, filp);
>
> Can fsverity and fscrypt can be used at the same time?
Yes, and there's a fstest, generic/576, checking the hooks are in the
right order.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 13/20] btrfs: add fscrypt_context items.
2022-09-06 0:35 ` [PATCH v2 13/20] btrfs: add fscrypt_context items Sweet Tea Dorminy
@ 2022-09-07 20:43 ` David Sterba
2022-09-08 20:06 ` Josef Bacik
1 sibling, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 20:43 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:28PM -0400, Sweet Tea Dorminy wrote:
> +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
> +{
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct inode *put_inode = NULL;
> + struct btrfs_key key;
> + struct btrfs_path *path;
> + struct extent_buffer *leaf;
> + unsigned long ptr;
> + int ret;
> +
> + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
> + inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
> + root);
> + if (IS_ERR(inode))
> + return PTR_ERR(inode);
> + put_inode = inode;
> + }
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + key = (struct btrfs_key) {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
> + .offset = 0,
> + };
Please use the following for key initialization.
key.objectid = ...;
key.type = ...;
key.offset = ...;
> +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
> + size_t len, void *fs_data)
> +{
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_trans_handle *trans;
> + int is_subvolume = inode->i_ino == BTRFS_FIRST_FREE_OBJECTID;
> + int ret;
> + struct btrfs_path *path;
> + struct btrfs_key key = {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
> + .offset = 0,
> + };
This kind of initialization in the declaration block is ok, possibly
with only the simple initializers like btrfs_ino or normal constants.
> + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
> + bool same_policy;
> + struct inode *root_inode = NULL;
Newlines between declrataions and statements
> + root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
> + root);
> + if (IS_ERR(inode))
> + return PTR_ERR(inode);
> + same_policy = fscrypt_have_same_policy(inode, root_inode);
> + iput(root_inode);
> + if (same_policy)
> + return 0;
> + }
> +
> + if (fs_data) {
> + /*
> + * We are setting the context as part of an existing
> + * transaction. This happens when we are inheriting the context
> + * for a new inode.
> + */
> + trans = fs_data;
> + } else {
> + /*
> + * 1 for the inode item
> + * 1 for the fscrypt item
> + * 1 for the root item if the inode is a subvolume
> + */
> + trans = btrfs_start_transaction(root, 2 + is_subvolume);
> + if (IS_ERR(trans))
> + return PTR_ERR(trans);
> + }
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
> + if (ret == 0) {
> + struct extent_buffer *leaf = path->nodes[0];
> + unsigned long ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
Newlines between declrataions and statements, you'll find the rest
> + 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);
> + btrfs_free_path(path);
> + goto out;
> + } else if (ret < 0) {
> + goto out;
> + }
> + btrfs_free_path(path);
> +
> + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
> + if (ret)
> + goto out;
> +
> + BTRFS_I(inode)->flags |= BTRFS_INODE_FSCRYPT_CONTEXT;
> + btrfs_sync_inode_flags_to_i_flags(inode);
> + inode_inc_iversion(inode);
> + inode->i_ctime = current_time(inode);
> + ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
> + if (ret)
> + goto out;
> +
> + /*
> + * For new subvolumes, the root item is already initialized with
> + * the BTRFS_ROOT_SUBVOL_FSCRYPT flag.
> + */
> + if (!fs_data && is_subvolume) {
> + u64 root_flags = btrfs_root_flags(&root->root_item);
> +
> + btrfs_set_root_flags(&root->root_item,
> + root_flags |
> + BTRFS_ROOT_SUBVOL_FSCRYPT);
> + ret = btrfs_update_root(trans, root->fs_info->tree_root,
> + &root->root_key,
> + &root->root_item);
> + }
> +out:
> + if (fs_data)
> + return ret;
> +
> + if (ret)
> + btrfs_abort_transaction(trans, ret);
> + else
> + btrfs_end_transaction(trans);
> + return ret;
> +}
> +
> + if (args->encrypt)
> + (*trans_num_items)++; /* 1 to add fscrypt item */
Please put the comment on a separate line, like it's on the lines below.
> if (args->orphan) {
> /* 1 to add orphan item */
> (*trans_num_items)++;
> @@ -787,6 +788,13 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
> return -ETXTBSY;
> }
>
> + if ((btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) &&
> + !IS_ENCRYPTED(dir)) {
> + btrfs_warn(fs_info,
> + "cannot snapshot encrypted volume to unencrypted destination");
Do we want to print that to the log? There are several EXDEV causes,
only another one prints a message and I think it should not.
> + return -EXDEV;
> + }
> +
> pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_KERNEL);
> if (!pending_snapshot)
> return -ENOMEM;
> --- a/include/uapi/linux/btrfs_tree.h
> +++ b/include/uapi/linux/btrfs_tree.h
> @@ -144,6 +144,8 @@
> #define BTRFS_VERITY_DESC_ITEM_KEY 36
> #define BTRFS_VERITY_MERKLE_ITEM_KEY 37
>
> +#define BTRFS_FSCRYPT_CTXT_ITEM_KEY 41
The context is per inode, so OK the key is needed and the number is
leaving enough space in case we'd need to add more.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent
2022-09-06 0:35 ` [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
@ 2022-09-07 21:10 ` David Sterba
2022-09-07 21:39 ` Sweet Tea Dorminy
0 siblings, 1 reply; 66+ messages in thread
From: David Sterba @ 2022-09-07 21:10 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On Mon, Sep 05, 2022 at 08:35:30PM -0400, Sweet Tea Dorminy wrote:
> In order to encrypt data, each file extent must have its own persistent
> fscrypt_extent_context, which is then provided to fscrypt upon request.
> This is only needed for encrypted extents and is of variable size on
> disk, so file extents must additionally keep track of their actual
> length.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 30 +++++++++++
> fs/btrfs/extent_map.h | 4 ++
> fs/btrfs/file-item.c | 13 +++++
> fs/btrfs/file.c | 4 +-
> fs/btrfs/fscrypt.c | 21 ++++++++
> fs/btrfs/fscrypt.h | 23 +++++++++
> fs/btrfs/inode.c | 89 +++++++++++++++++++++++++--------
> fs/btrfs/ordered-data.c | 9 +++-
> fs/btrfs/ordered-data.h | 4 +-
> fs/btrfs/reflink.c | 1 +
> fs/btrfs/tree-checker.c | 36 ++++++++++---
> fs/btrfs/tree-log.c | 11 +++-
> include/uapi/linux/btrfs_tree.h | 9 ++++
> 13 files changed, 220 insertions(+), 34 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index f0a16c32110d..38927a867028 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -37,6 +37,7 @@
> #include "async-thread.h"
> #include "block-rsv.h"
> #include "locking.h"
> +#include "fscrypt.h"
>
> struct btrfs_trans_handle;
> struct btrfs_transaction;
> @@ -1437,6 +1438,7 @@ struct btrfs_replace_extent_info {
> u64 file_offset;
> /* Pointer to a file extent item of type regular or prealloc. */
> char *extent_buf;
> + u32 extent_buf_size;
Please add a comment
> /*
> * 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
> @@ -2659,6 +2661,16 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
> struct btrfs_file_extent_item, disk_num_bytes, 64);
> BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
> struct btrfs_file_extent_item, compression, 8);
> +BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
> + struct btrfs_file_extent_item, encryption, 8);
> +
> +static inline u8
> +btrfs_stack_file_extent_encryption_ctxsize(struct btrfs_file_extent_item *e)
type name(...) on the same line
> +{
> + u8 ctxsize;
> + btrfs_unpack_encryption(e->encryption, NULL, &ctxsize);
> + return ctxsize;
> +}
>
> static inline unsigned long
> btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
> --- a/fs/btrfs/fscrypt.h
> +++ b/fs/btrfs/fscrypt.h
> @@ -5,6 +5,14 @@
>
> #include <linux/fscrypt.h>
>
> +#define BTRFS_ENCRYPTION_POLICY_MASK 0x03
> +#define BTRFS_ENCRYPTION_CTXSIZE_MASK 0xfc
Where do the numbers come from?
> +
> +struct btrfs_fscrypt_extent_context {
> + u8 buffer[FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
FSCRYPT_EXTENT_CONTEXT_MAX_SIZE is 33 and btrfs_fscrypt_extent_context
is part of extent_map, that's quite common object. Random sample from my
desktop right now is around 800k objects so this is noticeable. Needs a
second look.
> + size_t len;
> +};
> +
> #ifdef CONFIG_FS_ENCRYPTION
> bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
> struct extent_buffer *leaf,
> @@ -22,5 +30,20 @@ static bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
> }
> #endif
>
> +static inline void btrfs_unpack_encryption(u8 encryption,
> + u8 *policy,
> + u8 *ctxsize)
> +{
> + if (policy)
> + *policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK;
> + if (ctxsize)
> + *ctxsize = (encryption & BTRFS_ENCRYPTION_CTXSIZE_MASK) >> 2;
> +}
> +
> +static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize)
> +{
> + return policy | (ctxsize << 2);
What does 2 mean? It should be some symbolic define with explanation as
it's defining on-disk format.
> +}
> +
> extern const struct fscrypt_operations btrfs_fscrypt_ops;
> #endif /* BTRFS_FSCRYPT_H */
> --- a/fs/btrfs/ordered-data.h
> +++ b/fs/btrfs/ordered-data.h
> @@ -99,6 +99,7 @@ struct btrfs_ordered_extent {
> u64 disk_bytenr;
> u64 disk_num_bytes;
> u64 offset;
> + struct btrfs_fscrypt_extent_context fscrypt_context;
And another embedded btrfs_fscrypt_extent_context, that can also get a
lot of slab objects.
> /* number of bytes that still need writing */
> u64 bytes_left;
> --- a/include/uapi/linux/btrfs_tree.h
> +++ b/include/uapi/linux/btrfs_tree.h
> @@ -820,6 +820,10 @@ struct btrfs_file_extent_item {
> * but not for stat.
> */
> __u8 compression;
> + /*
> + * This field contains 4 bits of encryption type in the lower bits,
> + * 4 bits of ivsize in the upper bits. The unencrypted value is 0.
Is there some rationale for this format? Can the encryption bytes be
used only for the type and the other_encoding for the IV?
> + */
> __u8 encryption;
> __le16 other_encoding; /* spare for later use */
>
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible.
2022-09-06 0:35 ` [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible Sweet Tea Dorminy
@ 2022-09-07 21:24 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 21:24 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:32PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> For encrypted fscrypt_names, we can reuse fscrypt's precomputed hash of
> the encrypted name to generate our own hash, instead of rehashing the
> unencrypted name (which may not be possible if it's a nokey name).
Can you please describe how this works when the key is not available?
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 21 ++++++++++++++++++---
> 1 file changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index e8d000fcc85d..aa599518c057 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -2842,7 +2842,10 @@ static inline void btrfs_crc32c_final(u32 crc, u8 *result)
>
> static inline u64 btrfs_name_hash(const struct fscrypt_name *name)
> {
> - return crc32c((u32)~1, fname_name(name), fname_len(name));
> + if (fname_name(name))
> + return crc32c((u32)~1, fname_name(name), fname_len(name));
> + else
> + return name->hash | ((u64)name->minor_hash << 32);
So this is another on-disk format change.
> }
>
> /*
> @@ -2851,8 +2854,20 @@ static inline u64 btrfs_name_hash(const struct fscrypt_name *name)
> static inline u64 btrfs_extref_hash(u64 parent_objectid,
> const struct fscrypt_name *name)
> {
> - return (u64) crc32c(parent_objectid, fname_name(name),
> - fname_len(name));
> + /*
> + * If the name is encrypted and we don't have the key, we can use the
> + * fscrypt-provided hash instead of the normal name, and do the steps
> + * of crc32c() manually. Else, just hash the name, parent objectid,
> + * and name length.
> + */
> + if (fname_name(name))
> + return (u64) crc32c(parent_objectid, fname_name(name),
> + fname_len(name));
> + else
> + return (__crc32c_le_combine(parent_objectid,
> + name->hash,
> + fname_len(name)) ^
> + __crc32c_le_shift(~1, fname_len(name)));
How stable is the __crc32c_le_* API?
> }
>
> static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
> --
> 2.35.1
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 20/20] btrfs: implement fscrypt ioctls
2022-09-06 0:35 ` [PATCH v2 20/20] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
@ 2022-09-07 21:33 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-07 21:33 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:35PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> These ioctls allow encryption to be set up.
>
> 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 708e514aca25..ea1c14b26206 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -5457,6 +5457,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, FSCRYPT);
> + 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);
I've looked what ext4 does for the ioctls and there's a check before
each case if the feature is supported, do we need something like that as
well?
> case FITRIM:
> return btrfs_ioctl_fitrim(fs_info, argp);
> case BTRFS_IOC_SNAP_CREATE:
> --
> 2.35.1
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent
2022-09-07 21:10 ` David Sterba
@ 2022-09-07 21:39 ` Sweet Tea Dorminy
2022-09-09 10:04 ` David Sterba
0 siblings, 1 reply; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-07 21:39 UTC (permalink / raw)
To: dsterba
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
> FSCRYPT_EXTENT_CONTEXT_MAX_SIZE is 33 and btrfs_fscrypt_extent_context
> is part of extent_map, that's quite common object. Random sample from
> my
> desktop right now is around 800k objects so this is noticeable. Needs a
> second look.
>
>> +
>> extern const struct fscrypt_operations btrfs_fscrypt_ops;
>> #endif /* BTRFS_FSCRYPT_H */
>> --- a/fs/btrfs/ordered-data.h
>> +++ b/fs/btrfs/ordered-data.h
>> @@ -99,6 +99,7 @@ struct btrfs_ordered_extent {
>> u64 disk_bytenr;
>> u64 disk_num_bytes;
>> u64 offset;
>> + struct btrfs_fscrypt_extent_context fscrypt_context;
>
> And another embedded btrfs_fscrypt_extent_context, that can also get a
> lot of slab objects.
I could certainly define fscrypt_extent_context's as a separate btree
object type, and/or have them be separately allocated and just have a
pointer in the various structures to keep track of them. I didn't have a
separate object for them since its only a 17 or 33 byte object (at
present) on a per-btrfs_file_extent basis, but maybe that would be
better?
I could also #ifdef CONFIG_FS_ENCRYPTION the member in each structure,
if that would help over and beyond either of the previous things.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name
2022-09-06 0:35 ` [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
@ 2022-09-08 13:41 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 13:41 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:16PM -0400, Sweet Tea Dorminy wrote:
> 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>
Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
@ 2022-09-08 13:43 ` Josef Bacik
2022-09-12 1:42 ` Eric Biggers
2022-09-13 10:07 ` Anand Jain
2 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 13:43 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:17PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Creating several new subvolumes out of snapshots of another subvolume,
> each for a different VM's storage, is a important usecase for btrfs. We
> would like to give each VM a unique encryption key to use for new writes
> to its subvolume, so that secure deletion of the VM's data is as simple
> as securely deleting the key; to avoid needing multiple keys in each VM,
> we envision the initial subvolume being unencrypted. However, this means
> that the snapshot's directories would have a mix of encrypted and
> unencrypted files. During lookup with a key, both unencrypted and
> encrypted forms of the desired name must be queried.
>
> To allow this, add another FS_CFLG to allow filesystems to opt into
> partially encrypted directories.
>
> 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>
Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility
2022-09-06 0:35 ` [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
@ 2022-09-08 13:53 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 13:53 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:18PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Btrfs will need to check whether inode policies are identical for
> various purposes: if two inodes want to share an extent, they must have
> the same policy, including key identifier; symlinks must not span the
> encrypted/unencrypted border; and certain encryption policies will allow
> btrfs to store one fscrypt_context for multiple objects. Therefore, add
> a function which allows checking the encryption policies of two inodes
> to ensure they are identical.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/crypto/policy.c | 26 ++++++++++++++++++++++++++
> include/linux/fscrypt.h | 1 +
> 2 files changed, 27 insertions(+)
>
> diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
> index 80b8ca0f340b..ed8b7b6531e5 100644
> --- a/fs/crypto/policy.c
> +++ b/fs/crypto/policy.c
> @@ -415,6 +415,32 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy)
> return fscrypt_policy_from_context(policy, &ctx, ret);
> }
>
> +/**
> + * fscrypt_have_same_policy() - check whether two inodes have the same policy
> + * @inode1: the first inode
> + * @inode2: the second inode
> + *
> + * Return: %true if equal, else %false
> + */
> +int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2)
> +{
> + union fscrypt_policy policy1, policy2;
> + int err;
> +
> + if (!IS_ENCRYPTED(inode1) && !IS_ENCRYPTED(inode2))
> + return true;
> + else if (!IS_ENCRYPTED(inode1) || !IS_ENCRYPTED(inode2))
> + return false;
> + err = fscrypt_get_policy(inode1, &policy1);
> + if (err)
> + return err;
> + err = fscrypt_get_policy(inode2, &policy2);
> + if (err)
> + return err;
These things can return random errors, so you're mixing bool with errnos, and
then you're using this function as if it only returns bools. I'm not sure what
the best thing to do is here for consistency, maybe return 0 for no match, 1 for
match, and then err and handle it that way. At the very least the callers need
to be updated to handle the errors. But I really don't like the mixing of bool
returns with ERRNO returns. An alternative would be to have a helper do the
getting of the policies which will give you the errno's appropriately, and then
do the call to fscrypt_policies_equal with the policies you grab. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames
2022-09-06 0:35 ` [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
@ 2022-09-08 14:01 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 14:01 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:19PM -0400, Sweet Tea Dorminy wrote:
> With the introduction of extent-based file content encryption, filenames
> and file contents might no longer use the same IV generation scheme, and
> so should not upass the same logical block number to
> fscrypt_generate_iv(). In preparation, start passing U64_MAX as the
> block number for filename IV generation, and make fscrypt_generate_iv()
> translate this to 0 if extent-based encryption is not being used.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
I had to go look at how you used this, because it seemed superflous to me, but
it's because later you put the IV generation stuff above this particular bit of
code. You say that we set it to 0 if extent-based encryption is not being used,
but looking at this in vimdiff I don't know where that's going to be. So
perhaps something like
I will be adding code to generate IV's for extent-based encryption before
falling through to the other policy types, and I will check for U64_MAX to skip
the extent-based generation. At this point we'll want to switch back to 0 for
filenames.
Or some other such description. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 05/20] fscrypt: add extent-based encryption
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
2022-09-07 19:59 ` Omar Sandoval
@ 2022-09-08 15:33 ` Josef Bacik
2022-09-12 1:34 ` Eric Biggers
2 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 15:33 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:20PM -0400, Sweet Tea Dorminy wrote:
> Some filesystems need to encrypt data based on extents, rather than on
> inodes, due to features incompatible with inode-based encryption. For
> instance, btrfs can have multiple inodes referencing a single block of
> data, and moves logical data blocks to different physical locations on
> disk in the background; these two features mean inode or
> physical-location-based policies will not work for btrfs.
>
> This change introduces fscrypt_extent_context objects, in analogy to
> existing context objects based on inodes. For a filesystem which uses
> extents, a new hook provides a new fscrypt_extent_context. During file
> content encryption/decryption, the existing fscrypt_context object
> provides key information, while the new fscrypt_extent_context provides
> IV information. For filename encryption, the existing IV generation
> methods are still used, since filenames are not stored in extents.
>
> As individually keyed inodes prevent sharing of extents, such policies
> are forbidden for filesystems with extent-based encryption.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/crypto/crypto.c | 15 +++++++-
> fs/crypto/fscrypt_private.h | 26 ++++++++++++-
> fs/crypto/inline_crypt.c | 29 +++++++++++---
> fs/crypto/policy.c | 77 +++++++++++++++++++++++++++++++++++++
> include/linux/fscrypt.h | 41 ++++++++++++++++++++
> 5 files changed, 178 insertions(+), 10 deletions(-)
>
> diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
> index 7fe5979fbea2..77537736096b 100644
> --- a/fs/crypto/crypto.c
> +++ b/fs/crypto/crypto.c
> @@ -81,8 +81,19 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
> const struct fscrypt_info *ci)
> {
> u8 flags = fscrypt_policy_flags(&ci->ci_policy);
> + struct inode *inode = ci->ci_inode;
> + const struct fscrypt_operations *s_cop = inode->i_sb->s_cop;
>
> memset(iv, 0, ci->ci_mode->ivsize);
> + if (s_cop->get_extent_context && lblk_num != U64_MAX) {
> + size_t extent_offset;
> + union fscrypt_extent_context ctx;
> + int ret = fscrypt_get_extent_context(inode, lblk_num, &ctx, &extent_offset, NULL);
Newline between declarations and code, and since you use the warnon i'd do
int ret;
ret = fscrypt_get_extent_context();
WARN_ON_ONCE(ret);
> + WARN_ON_ONCE(ret != 0);
> + memcpy(iv->raw, ctx.v1.iv, ci->ci_mode->ivsize);
> + iv->lblk_num = iv->lblk_num + cpu_to_le64(extent_offset);
> + return;
> + }
>
> /*
> * Filename encryption. For inode-based policies, filenames are
> @@ -93,8 +104,8 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
>
> if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
> WARN_ON_ONCE(lblk_num > U32_MAX);
> - WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX);
> - lblk_num |= (u64)ci->ci_inode->i_ino << 32;
> + WARN_ON_ONCE(inode->i_ino > U32_MAX);
> + lblk_num |= (u64)inode->i_ino << 32;
> } else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
> WARN_ON_ONCE(lblk_num > U32_MAX);
> lblk_num = (u32)(ci->ci_hashed_ino + lblk_num);
> diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
> index 3afdaa084773..2092ef63c80a 100644
> --- a/fs/crypto/fscrypt_private.h
> +++ b/fs/crypto/fscrypt_private.h
> @@ -165,6 +165,27 @@ fscrypt_policy_flags(const union fscrypt_policy *policy)
> BUG();
> }
>
> +#define FSCRYPT_MAX_IV_SIZE 32
> +
> +/*
> + * fscrypt_extent_context - the encryption context for an extent
> + *
Whitespace.
> + * For filesystems that support extent encryption, this context provides the
> + * necessary randomly-initialized IV in order to encrypt/decrypt the data
> + * stored in the extent. It is stored alongside each extent, and is
> + * insufficient to decrypt the extent: the extent's owning inode(s) provide the
> + * policy information (including key identifier) necessary to decrypt.
> + */
> +struct fscrypt_extent_context_v1 {
> + u8 version;
> + u8 iv[FSCRYPT_MAX_IV_SIZE];
> +};
> +
> +union fscrypt_extent_context {
> + u8 version;
> + struct fscrypt_extent_context_v1 v1;
> +};
> +
> /*
> * For encrypted symlinks, the ciphertext length is stored at the beginning
> * of the string in little-endian format.
> @@ -279,8 +300,6 @@ fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
> #define fscrypt_err(inode, fmt, ...) \
> fscrypt_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
>
> -#define FSCRYPT_MAX_IV_SIZE 32
> -
> union fscrypt_iv {
> struct {
> /* logical block number within the file */
> @@ -628,5 +647,8 @@ int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
> const union fscrypt_context *ctx_u,
> int ctx_size);
> const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir);
> +int fscrypt_get_extent_context(const struct inode *inode, u64 lblk_num,
> + union fscrypt_extent_context *ctx,
> + size_t *extent_offset, size_t *extent_length);
>
> #endif /* _FSCRYPT_PRIVATE_H */
> diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
> index 90f3e68f166e..0537f710047e 100644
> --- a/fs/crypto/inline_crypt.c
> +++ b/fs/crypto/inline_crypt.c
> @@ -1,3 +1,4 @@
> +
Whitespace. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks.
2022-09-06 0:35 ` [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
@ 2022-09-08 15:34 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 15:34 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:21PM -0400, Sweet Tea Dorminy wrote:
> As btrfs has a couple of quirks in its encryption compared to other filesystems, they
> should be documented like ext4's quirks. Additionally, extent-based
> contents encryption, being wholly new, deserves its own section to
> compare against file-based contents encryption.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Idk, I did :set spell
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 07/20] btrfs: store directory's encryption state
2022-09-06 0:35 ` [PATCH v2 07/20] btrfs: store directory's encryption state Sweet Tea Dorminy
@ 2022-09-08 15:37 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 15:37 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:22PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> For directories with encrypted files/filenames, we need to store a flag
> indicating this fact. There's no room in other fields, so we'll need to
> borrow a bit from dir_type. Since it's now a combination of type and
> flags, we rename it to dir_flags to reflect its new usage.
>
> The new flag, FT_FSCRYPT, indicates a (perhaps partially) encrypted
> directory, which is orthogonal to file type; therefore, add the new
> flag, and make conversion from directory type to file type strip the
> flag.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 15 +++++++++++++--
> fs/btrfs/delayed-inode.c | 6 +++---
> fs/btrfs/delayed-inode.h | 2 +-
> fs/btrfs/dir-item.c | 4 ++--
> fs/btrfs/inode.c | 15 +++++++++------
> fs/btrfs/print-tree.c | 4 ++--
> fs/btrfs/send.c | 2 +-
> fs/btrfs/tree-checker.c | 2 +-
> fs/btrfs/tree-log.c | 20 ++++++++++----------
> include/uapi/linux/btrfs_tree.h | 7 +++++++
> 10 files changed, 49 insertions(+), 28 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 7007c7974a2e..1793b0e16a14 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -2184,10 +2184,10 @@ BTRFS_SETGET_FUNCS(root_ref_name_len, struct btrfs_root_ref, name_len, 16);
>
> /* struct btrfs_dir_item */
> BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
> -BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
> +BTRFS_SETGET_FUNCS(dir_flags, struct btrfs_dir_item, type, 8);
> BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16);
> BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64);
> -BTRFS_SETGET_STACK_FUNCS(stack_dir_type, struct btrfs_dir_item, type, 8);
> +BTRFS_SETGET_STACK_FUNCS(stack_dir_flags, struct btrfs_dir_item, type, 8);
> BTRFS_SETGET_STACK_FUNCS(stack_dir_data_len, struct btrfs_dir_item,
> data_len, 16);
> BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item,
> @@ -2195,6 +2195,17 @@ BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item,
> BTRFS_SETGET_STACK_FUNCS(stack_dir_transid, struct btrfs_dir_item,
> transid, 64);
>
> +static inline u8 btrfs_dir_ftype(const struct extent_buffer *eb,
> + const struct btrfs_dir_item *item)
> +{
> + return btrfs_dir_flags_to_ftype(btrfs_dir_flags(eb, item));
> +}
> +
> +static inline u8 btrfs_stack_dir_ftype(const struct btrfs_dir_item *item)
> +{
> + return btrfs_dir_flags_to_ftype(btrfs_stack_dir_flags(item));
> +}
> +
> static inline void btrfs_dir_item_key(const struct extent_buffer *eb,
> const struct btrfs_dir_item *item,
> struct btrfs_disk_key *key)
> diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
> index cac5169eaf8d..7e405aafab86 100644
> --- a/fs/btrfs/delayed-inode.c
> +++ b/fs/btrfs/delayed-inode.c
> @@ -1412,7 +1412,7 @@ void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info)
> int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
> const char *name, int name_len,
> struct btrfs_inode *dir,
> - struct btrfs_disk_key *disk_key, u8 type,
> + struct btrfs_disk_key *disk_key, u8 flags,
> u64 index)
> {
> struct btrfs_fs_info *fs_info = trans->fs_info;
> @@ -1443,7 +1443,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
> btrfs_set_stack_dir_transid(dir_item, trans->transid);
> btrfs_set_stack_dir_data_len(dir_item, 0);
> btrfs_set_stack_dir_name_len(dir_item, name_len);
> - btrfs_set_stack_dir_type(dir_item, type);
> + btrfs_set_stack_dir_flags(dir_item, flags);
> memcpy((char *)(dir_item + 1), name, name_len);
>
> data_len = delayed_item->data_len + sizeof(struct btrfs_item);
> @@ -1753,7 +1753,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
> name = (char *)(di + 1);
> name_len = btrfs_stack_dir_name_len(di);
>
> - d_type = fs_ftype_to_dtype(di->type);
> + 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,
> diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
> index 0163ca637a96..4f21daa3dbc7 100644
> --- a/fs/btrfs/delayed-inode.h
> +++ b/fs/btrfs/delayed-inode.h
> @@ -113,7 +113,7 @@ static inline void btrfs_init_delayed_root(
> int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
> const char *name, int name_len,
> struct btrfs_inode *dir,
> - struct btrfs_disk_key *disk_key, u8 type,
> + struct btrfs_disk_key *disk_key, u8 flags,
> u64 index);
>
> int btrfs_delete_delayed_dir_index(struct btrfs_trans_handle *trans,
> diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
> index 72fb2c518a2b..e37b075afa96 100644
> --- a/fs/btrfs/dir-item.c
> +++ b/fs/btrfs/dir-item.c
> @@ -81,7 +81,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
> leaf = path->nodes[0];
> btrfs_cpu_key_to_disk(&disk_key, &location);
> btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
> - btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR);
> + btrfs_set_dir_flags(leaf, dir_item, BTRFS_FT_XATTR);
> btrfs_set_dir_name_len(leaf, dir_item, name_len);
> btrfs_set_dir_transid(leaf, dir_item, trans->transid);
> btrfs_set_dir_data_len(leaf, dir_item, data_len);
> @@ -140,7 +140,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, const char *name,
>
> leaf = path->nodes[0];
> btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
> - btrfs_set_dir_type(leaf, dir_item, type);
> + btrfs_set_dir_flags(leaf, dir_item, type);
> btrfs_set_dir_data_len(leaf, dir_item, 0);
> btrfs_set_dir_name_len(leaf, dir_item, name_len);
> btrfs_set_dir_transid(leaf, dir_item, trans->transid);
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index e5284f2686c8..97e17b9bd34f 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -5563,7 +5563,7 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
> location->objectid, location->type, location->offset);
> }
> if (!ret)
> - *type = btrfs_dir_type(path->nodes[0], di);
> + *type = btrfs_dir_ftype(path->nodes[0], di);
> out:
> btrfs_free_path(path);
> return ret;
> @@ -6001,6 +6001,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
> btrfs_for_each_slot(root, &key, &found_key, path, ret) {
> struct dir_entry *entry;
> struct extent_buffer *leaf = path->nodes[0];
> + u8 di_flags;
>
> if (found_key.objectid != key.objectid)
> break;
> @@ -6024,13 +6025,15 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
> goto again;
> }
>
> + di_flags = btrfs_dir_flags(leaf, di);
You're already doing this just for the thing below, why not just do
u8 ftype;
ftype = btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di);
> entry = addr;
> - put_unaligned(name_len, &entry->name_len);
> name_ptr = (char *)(entry + 1);
> - read_extent_buffer(leaf, name_ptr, (unsigned long)(di + 1),
> - name_len);
> - put_unaligned(fs_ftype_to_dtype(btrfs_dir_type(leaf, di)),
> - &entry->type);
> + 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(btrfs_dir_flags_to_ftype(di_flags)),
> + &entry->type);
then here do
put unaligned(fs_ftyp_to_dtype(ftype), &entry->type);
to make it a little cleaner. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper
2022-09-06 0:35 ` [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper Sweet Tea Dorminy
@ 2022-09-08 19:11 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 19:11 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:24PM -0400, Sweet Tea Dorminy wrote:
> Most places that we create fscrypt_names, we are doing so from a dentry.
> Fscrypt provides a helper for this common pattern:
> fscrypt_setup_filename() initializes a filename to search for from a
> dentry, performing encryption of the plaintext if it can and should be
> done. This converts each setup of a fscrypt_name from a dentry to use
> this helper; at present, since there are no encrypted directories,
> nothing goes down the filename encryption paths.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
<snip>
> diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
> index 40cfaa9feff3..53195cfe6a3d 100644
> --- a/fs/btrfs/tree-log.c
> +++ b/fs/btrfs/tree-log.c
> @@ -7443,13 +7443,13 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
> if (old_dir && old_dir->logged_trans == trans->transid) {
> struct btrfs_root *log = old_dir->root->log_root;
> struct btrfs_path *path;
> - struct fscrypt_name fname = {
> - .disk_name = FSTR_INIT((char *) old_dentry->d_name.name,
> - old_dentry->d_name.len)
> - };
> -
> + struct fscrypt_name fname;
> ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX);
>
> + ret = fscrypt_setup_filename(&old_dir->vfs_inode,
> + &old_dentry->d_name, 0, &fname);
> + if (ret)
> + goto out;
> /*
> * We have two inodes to update in the log, the old directory and
> * the inode that got renamed, so we must pin the log to prevent
> @@ -7469,6 +7469,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
There's another error case just above this that you missed. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-06 0:35 ` [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method Sweet Tea Dorminy
@ 2022-09-08 19:27 ` Josef Bacik
2022-09-09 10:15 ` David Sterba
1 sibling, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 19:27 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:25PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Now that everything in btrfs is dealing in fscrypt_names, fscrypt has a
> useful function, fscrypt_match_name(), to check whether a fscrypt_name
> matches a provided buffer. However, btrfs buffers are struct
> extent_buffer rather than a raw char array, so we need to implement our
> own imitation of fscrypt_match_name() that deals in extent_buffers,
> falling back to a simple memcpy if fscrypt isn't compiled. We
> can then use this matching method in btrfs_match_dir_item_name() and
> other locations.
>
> This also provides a useful occasion to introduce the new fscrypt file
> for btrfs, handling the fscrypt-specific functions needed.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
The code is fine, but I was very confused about why we do this sha256 thing.
Perhaps point at the code for fscrypt_nokey_name and indicate that it exists to
be interoperable with no-key actions on the file system. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 12/20] btrfs: start using fscrypt hooks.
2022-09-06 0:35 ` [PATCH v2 12/20] btrfs: start using fscrypt hooks Sweet Tea Dorminy
2022-09-07 20:17 ` David Sterba
@ 2022-09-08 19:42 ` Josef Bacik
1 sibling, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 19:42 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:27PM -0400, Sweet Tea Dorminy wrote:
> 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.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 1 +
> fs/btrfs/file.c | 3 ++
> fs/btrfs/fscrypt.c | 3 ++
> fs/btrfs/fscrypt.h | 1 +
> fs/btrfs/inode.c | 91 ++++++++++++++++++++++++++++++++++++++++------
> fs/btrfs/super.c | 3 ++
> 6 files changed, 90 insertions(+), 12 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 230537a007b6..2b9ba8d77861 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -3416,6 +3416,7 @@ struct btrfs_new_inode_args {
> */
> struct posix_acl *default_acl;
> struct posix_acl *acl;
> + bool encrypt;
This doesn't appear to be used in this patch. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 13/20] btrfs: add fscrypt_context items.
2022-09-06 0:35 ` [PATCH v2 13/20] btrfs: add fscrypt_context items Sweet Tea Dorminy
2022-09-07 20:43 ` David Sterba
@ 2022-09-08 20:06 ` Josef Bacik
1 sibling, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 20:06 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:28PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> In order to store per-inode information such as the inode nonce and the
> key identifier, fscrypt stores a context item with each encrypted inode.
> This can be implemented as a new item type, as fscrypt provides an
> arbitrary blob for the filesystem to store.
>
> This also provides a good place to implement full-subvolume encryption:
> a subvolume flag permits setting one context for the whole subvolume.
> However, since an unencrypted subvolume would be unable to read
> encrypted data, encrypted subvolumes should only be snapshottable to
> other encrypted subvolumes.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 3 +
> fs/btrfs/fscrypt.c | 167 ++++++++++++++++++++++++++++++++
> fs/btrfs/inode.c | 38 ++++++++
> fs/btrfs/ioctl.c | 10 +-
> fs/btrfs/tree-checker.c | 1 +
> include/uapi/linux/btrfs_tree.h | 10 ++
> 6 files changed, 228 insertions(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 2b9ba8d77861..f0a16c32110d 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -33,6 +33,7 @@
> #include "extent-io-tree.h"
> #include "extent_io.h"
> #include "extent_map.h"
> +#include "fscrypt.h"
> #include "async-thread.h"
> #include "block-rsv.h"
> #include "locking.h"
> @@ -1667,6 +1668,7 @@ do { \
> #define BTRFS_INODE_NOATIME (1U << 9)
> #define BTRFS_INODE_DIRSYNC (1U << 10)
> #define BTRFS_INODE_COMPRESS (1U << 11)
> +#define BTRFS_INODE_FSCRYPT_CONTEXT (1U << 12)
>
> #define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31)
>
> @@ -1683,6 +1685,7 @@ do { \
> BTRFS_INODE_NOATIME | \
> BTRFS_INODE_DIRSYNC | \
> BTRFS_INODE_COMPRESS | \
> + BTRFS_INODE_FSCRYPT_CONTEXT | \
> BTRFS_INODE_ROOT_ITEM_INIT)
>
> #define BTRFS_INODE_RO_VERITY (1U << 0)
> diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
> index 9829d280a6bc..b824bbd964bc 100644
> --- a/fs/btrfs/fscrypt.c
> +++ b/fs/btrfs/fscrypt.c
> @@ -3,8 +3,13 @@
> * Copyright (C) 2020 Facebook
> */
>
> +#include <linux/iversion.h>
> #include "ctree.h"
> +#include "btrfs_inode.h"
> +#include "disk-io.h"
> #include "fscrypt.h"
> +#include "transaction.h"
> +#include "xattr.h"
>
> /* fscrypt_match_name() but for an extent_buffer. */
> bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
> @@ -31,5 +36,167 @@ bool btrfs_fscrypt_match_name(const struct fscrypt_name *fname,
> return !memcmp(digest, nokey_name->sha256, sizeof(digest));
> }
>
> +static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
> +{
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct inode *put_inode = NULL;
> + struct btrfs_key key;
> + struct btrfs_path *path;
> + struct extent_buffer *leaf;
> + unsigned long ptr;
> + int ret;
> +
> + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
> + inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
> + root);
> + if (IS_ERR(inode))
> + return PTR_ERR(inode);
> + put_inode = inode;
> + }
> +
> + path = btrfs_alloc_path();
> + if (!path)
> + return -ENOMEM;
> +
> + key = (struct btrfs_key) {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
> + .offset = 0,
> + };
> +
> + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0);
> + if (ret) {
> + len = -EINVAL;
> + goto out;
> + }
> +
> + leaf = path->nodes[0];
> + ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
> + /* fscrypt provides max context length, but it could be less */
> + len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
> + read_extent_buffer(leaf, ctx, ptr, len);
> +
> +out:
> + btrfs_free_path(path);
> + iput(put_inode);
> + return len;
> +}
> +
> +static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
> + size_t len, void *fs_data)
> +{
> + struct btrfs_root *root = BTRFS_I(inode)->root;
> + struct btrfs_trans_handle *trans;
> + int is_subvolume = inode->i_ino == BTRFS_FIRST_FREE_OBJECTID;
> + int ret;
> + struct btrfs_path *path;
> + struct btrfs_key key = {
> + .objectid = btrfs_ino(BTRFS_I(inode)),
> + .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
> + .offset = 0,
> + };
> +
> + /*
> + * If the whole subvolume is encrypted, we expect that all children
> + * have the same policy.
> + */
> + if (btrfs_root_flags(&root->root_item) & BTRFS_ROOT_SUBVOL_FSCRYPT) {
> + bool same_policy;
> + struct inode *root_inode = NULL;
Newline.
> + root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
> + root);
> + if (IS_ERR(inode))
> + return PTR_ERR(inode);
> + same_policy = fscrypt_have_same_policy(inode, root_inode);
> + iput(root_inode);
> + if (same_policy)
> + return 0;
> + }
> +
> + if (fs_data) {
> + /*
> + * We are setting the context as part of an existing
> + * transaction. This happens when we are inheriting the context
> + * for a new inode.
> + */
> + trans = fs_data;
> + } else {
> + /*
> + * 1 for the inode item
> + * 1 for the fscrypt item
> + * 1 for the root item if the inode is a subvolume
> + */
> + trans = btrfs_start_transaction(root, 2 + is_subvolume);
> + if (IS_ERR(trans))
> + return PTR_ERR(trans);
> + }
> +
> + path = btrfs_alloc_path();
> + if (!path)
Gotta end the transaction here.
> + return -ENOMEM;
Newline.
> + ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
> + if (ret == 0) {
> + 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);
> + btrfs_free_path(path);
> + goto out;
> + } else if (ret < 0) {
> + goto out;
> + }
I prefer the
> + btrfs_free_path(path);
> +
> + ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
> + if (ret)
> + goto out;
Use btrfs_release_path, and then use btrfs_insert_empty_item() so you don't have
to re-allocate another path. In fact this whole thing can be done like this
ret = search;
if (ret < 0) {
goto out;
} else if (ret == 1) {
btrfs_release_path(path);
ret = btrfs_insert_empty_item();
if (ret < 0)
goto out;
}
leaf = path->nodes[0];
ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
write_extent_buffer(leaf, ctx, ptr, len);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
because the bit below still needs to happen, the iversion needs to change and
such even if we already have the context because we changed the context of the
inode.
> +
> + BTRFS_I(inode)->flags |= BTRFS_INODE_FSCRYPT_CONTEXT;
> + btrfs_sync_inode_flags_to_i_flags(inode);
> + inode_inc_iversion(inode);
> + inode->i_ctime = current_time(inode);
> + ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
> + if (ret)
> + goto out;
> +
> + /*
> + * For new subvolumes, the root item is already initialized with
> + * the BTRFS_ROOT_SUBVOL_FSCRYPT flag.
> + */
> + if (!fs_data && is_subvolume) {
> + u64 root_flags = btrfs_root_flags(&root->root_item);
> +
> + btrfs_set_root_flags(&root->root_item,
> + root_flags |
> + BTRFS_ROOT_SUBVOL_FSCRYPT);
> + ret = btrfs_update_root(trans, root->fs_info->tree_root,
> + &root->root_key,
> + &root->root_item);
> + }
> +out:
Put
if (ret)
btrfs_abort_transaction(trans, ret);
here because we'll need to abort the transaction if there was an error at this
point. The error handling needs to be completely reworked, because we really
only need to abort if we have an error after we've inserted the context, any of
the errors before we do that are recoverable so we don't need to abort for that.
> + if (fs_data)
> + return ret;
> +
> + if (ret)
> + btrfs_abort_transaction(trans, ret);
> + else
> + btrfs_end_transaction(trans);
Additionally we always end the transaction. The abort just aborts, it doesn't
end the transaction.
> + return ret;
> +}
> +
> +static bool btrfs_fscrypt_empty_dir(struct inode *inode)
> +{
> + /*
> + * We don't care about turning on encryption on a non-empty directory
> + * so we always return true.
> + */
> + return true;
> +}
> +
> const struct fscrypt_operations btrfs_fscrypt_ops = {
> + .key_prefix = "btrfs:",
> + .get_context = btrfs_fscrypt_get_context,
> + .set_context = btrfs_fscrypt_set_context,
> + .empty_dir = btrfs_fscrypt_empty_dir,
> };
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index eb42e4bf55b9..007abdf6de93 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -6278,6 +6278,34 @@ 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 (IS_ENCRYPTED(dir) &&
> + !(BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT)) {
> + struct inode *root_inode;
> + bool encrypt;
> +
> + root_inode = btrfs_iget(inode->i_sb, BTRFS_FIRST_FREE_OBJECTID,
> + BTRFS_I(dir)->root);
> + if (IS_ERR(root_inode))
> + return PTR_ERR(root_inode);
> + /*
> + * TODO: perhaps instead of faking making a new dir to get a
> + * new context, it would be better to expose
> + * fscrypt_setup_encryption_info() for our use.
> + */
> + ret = fscrypt_prepare_new_inode(root_inode, dir, &encrypt);
> + if (!ret) {
> + ret = fscrypt_set_context(dir, NULL);
> + if (ret)
> + fscrypt_put_encryption_info(dir);
> + }
> + iput(root_inode);
> + if (ret)
> + return ret;
> + }
> +
> if (!args->orphan) {
> ret = fscrypt_setup_filename(dir, &args->dentry->d_name, 0,
> &args->fname);
> @@ -6311,6 +6339,8 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
> if (dir->i_security)
> (*trans_num_items)++;
> #endif
> + if (args->encrypt)
> + (*trans_num_items)++; /* 1 to add fscrypt item */
> if (args->orphan) {
> /* 1 to add orphan item */
> (*trans_num_items)++;
> @@ -6564,6 +6594,14 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
> }
> }
>
> + if (args->encrypt) {
> + ret = fscrypt_set_context(inode, trans);
> + if (ret) {
> + btrfs_abort_transaction(trans, ret);
> + goto discard;
> + }
> + }
> +
Ok never mind the bit above about aborting the transaction if (fs_data) since
your'e doing it here. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag.
2022-09-06 0:35 ` [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
@ 2022-09-08 20:07 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 20:07 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:29PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> In btrfs, a file can be encrypted either if its directory is encrypted
> or its root subvolume is encrypted, so translate both to the standard
> flags.
>
> 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>
Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames
2022-09-06 0:35 ` [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames Sweet Tea Dorminy
@ 2022-09-08 20:15 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 20:15 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:33PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> When filenames can be encrypted, if we don't initially match a desired
> filename, we have to try decrypting it with the directory key to see if
> it matches in plaintext. Similarly, for readdir, we need to read
> encrypted directory items as well as unencrypted.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/delayed-inode.c | 32 +++++++++++---
> fs/btrfs/delayed-inode.h | 4 +-
> fs/btrfs/dir-item.c | 23 ++++++++++
> fs/btrfs/inode.c | 90 ++++++++++++++++++++++++++++++++++++----
> 4 files changed, 134 insertions(+), 15 deletions(-)
>
> diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
> index 069326654074..5eef6f96c6b6 100644
> --- a/fs/btrfs/delayed-inode.c
> +++ b/fs/btrfs/delayed-inode.c
> @@ -1493,9 +1493,9 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
>
> ret = __btrfs_add_delayed_item(delayed_node, delayed_item);
> if (unlikely(ret)) {
> + // TODO: It would be nice to print the base64encoded name here maybe?
> btrfs_err(trans->fs_info,
> - "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)",
> - fname_len(fname), fname_name(fname),
> + "err add delayed dir index item into the insertion tree of the delayed node (root id: %llu, inode id: %llu, errno: %d)",
> delayed_node->root->root_key.objectid,
> delayed_node->inode_id, ret);
> BUG();
> @@ -1721,7 +1721,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;
> @@ -1731,6 +1733,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;
> @@ -1758,8 +1761,27 @@ 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_FSCRYPT_NAME) {
> + 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 8abeb78af14e..9491bf0b7576 100644
> --- a/fs/btrfs/delayed-inode.h
> +++ b/fs/btrfs/delayed-inode.h
> @@ -156,7 +156,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 8d7c3c32ed8e..6b1ea32419fb 100644
> --- a/fs/btrfs/dir-item.c
> +++ b/fs/btrfs/dir-item.c
> @@ -120,6 +120,9 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
> struct btrfs_disk_key disk_key;
> u32 data_size;
>
> + if (fname->crypto_buf.name)
> + type |= BTRFS_FT_FSCRYPT_NAME;
> +
> key.objectid = btrfs_ino(dir);
> key.type = BTRFS_DIR_ITEM_KEY;
> key.offset = btrfs_name_hash(fname);
> @@ -385,6 +388,18 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
> u32 cur = 0;
> u32 this_len;
> struct extent_buffer *leaf;
> + bool encrypted = (fname->crypto_buf.name != NULL);
> + struct fscrypt_name unencrypted_fname;
> +
> + if (encrypted) {
> + unencrypted_fname = (struct fscrypt_name){
> + .usr_fname = fname->usr_fname,
> + .disk_name = {
> + .name = (unsigned char *)fname->usr_fname->name,
> + .len = fname->usr_fname->len,
> + },
> + };
> + }
>
> leaf = path->nodes[0];
> dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
> @@ -401,6 +416,14 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
> return dir_item;
> }
>
> + if (encrypted &&
Whitespace.
> + btrfs_dir_name_len(leaf, dir_item) == fname_len(&unencrypted_fname) &&
> + btrfs_fscrypt_match_name(&unencrypted_fname, leaf,
> + (unsigned long)(dir_item + 1),
> + dir_name_len)) {
> + return dir_item;
> + }
> +
> cur += this_len;
> dir_item = (struct btrfs_dir_item *)((char *)dir_item +
> this_len);
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 4c134a6486b3..1c7681d7770c 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -4292,6 +4292,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
> u64 index;
> u64 ino = btrfs_ino(inode);
> u64 dir_ino = btrfs_ino(dir);
> + u64 di_name_len;
>
> path = btrfs_alloc_path();
> if (!path) {
> @@ -4304,6 +4305,13 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
> ret = di ? PTR_ERR(di) : -ENOENT;
> goto err;
> }
> +
> + /*
> + * We have to read the actual name length off disk -- the fname
> + * provided may have been a nokey_name with uncertain length.
> + */
> + di_name_len = btrfs_dir_name_len(path->nodes[0], di);
> +
> ret = btrfs_delete_one_dir_name(trans, root, path, di);
> if (ret)
> goto err;
> @@ -4371,7 +4379,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 - fname_len(fname) * 2);
> + btrfs_i_size_write(dir, dir->vfs_inode.i_size - di_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);
> @@ -5882,12 +5890,25 @@ 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);
>
> + if (BTRFS_I(dir)->flags & BTRFS_INODE_FSCRYPT_CONTEXT) {
> + ret = fscrypt_prepare_lookup(dir, dentry, &fname);
> + if (ret)
> + return ERR_PTR(ret);
> + } else {
> + fname = (struct fscrypt_name) {
> + .usr_fname = &dentry->d_name,
> + .disk_name = FSTR_INIT((char *) dentry->d_name.name,
> + dentry->d_name.len),
> + };
> + }
> +
> ret = btrfs_inode_by_name(dir, dentry, &location, &di_type);
> if (ret < 0)
> return ERR_PTR(ret);
> @@ -6029,18 +6050,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_FSCRYPT_CONTEXT) {
> + 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;
> @@ -6058,6 +6093,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 di_flags;
> + u32 nokey_len;
>
> if (found_key.objectid != key.objectid)
> break;
> @@ -6069,8 +6105,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)
> @@ -6084,8 +6125,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
> di_flags = 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 (di_flags & BTRFS_FT_FSCRYPT_NAME) {
> + 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.
> + */
> + if (!fscrypt_has_encryption_key(inode)) {
> + struct fscrypt_name fname = {
> + .disk_name = fstr,
> + };
> + u64 name_hash = btrfs_name_hash(&fname);
> + 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;
You have this same work in the delayed inode stuff, the only exception is that
you don't do the hash thing. I assume with the delayed inode that
fscrypt_has_encryption_key() would be true, so you could stuff all this in a
helper and use the helper from both places? Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate
2022-09-06 0:35 ` [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
@ 2022-09-08 20:19 ` Josef Bacik
0 siblings, 0 replies; 66+ messages in thread
From: Josef Bacik @ 2022-09-08 20:19 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:34PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Add in the necessary calls to encrypt and decrypt data to achieve
> encryption of normal data.
>
A little more description of what these bounce pages are and what we're doing
with them.
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/extent_io.c | 56 ++++++++++++++++++++++++++++++++++++-----
> fs/btrfs/file-item.c | 9 +++++--
> fs/btrfs/fscrypt.c | 23 ++++++++++++++++-
> fs/btrfs/tree-checker.c | 11 +++++---
> 4 files changed, 87 insertions(+), 12 deletions(-)
>
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index a467a7553bd9..8adcee599844 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -183,6 +183,7 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
> {
> struct bio *bio;
> struct bio_vec *bv;
> + struct page *first_page;
> struct inode *inode;
> int mirror_num;
>
> @@ -191,13 +192,17 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
>
> bio = bio_ctrl->bio;
> bv = bio_first_bvec_all(bio);
> - inode = bv->bv_page->mapping->host;
> + first_page = bio_first_page_all(bio);
> + if (fscrypt_is_bounce_page(first_page))
> + inode = fscrypt_pagecache_page(first_page)->mapping->host;
> + else
> + inode = first_page->mapping->host;
> mirror_num = bio_ctrl->mirror_num;
>
> /* Caller should ensure the bio has at least some range added */
> ASSERT(bio->bi_iter.bi_size);
>
> - btrfs_bio(bio)->file_offset = page_offset(bv->bv_page) + bv->bv_offset;
> + btrfs_bio(bio)->file_offset = page_offset(first_page) + bv->bv_offset;
>
> if (!is_data_inode(inode))
> btrfs_submit_metadata_bio(inode, bio, mirror_num);
> @@ -2810,9 +2815,19 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio)
> ASSERT(!bio_flagged(bio, BIO_CLONED));
> bio_for_each_segment_all(bvec, bio, iter_all) {
> struct page *page = bvec->bv_page;
> - struct inode *inode = page->mapping->host;
> - struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
> - const u32 sectorsize = fs_info->sectorsize;
> + struct inode *inode;
> + struct btrfs_fs_info *fs_info;
> + u32 sectorsize;
> + struct page *bounce_page = NULL;
> +
> + if (fscrypt_is_bounce_page(page)) {
> + bounce_page = page;
> + page = fscrypt_pagecache_page(bounce_page);
> + }
> +
> + inode = page->mapping->host;
> + fs_info = btrfs_sb(inode->i_sb);
> + sectorsize = fs_info->sectorsize;
>
> /* Our read/write should always be sector aligned. */
> if (!IS_ALIGNED(bvec->bv_offset, sectorsize))
> @@ -2833,7 +2848,7 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio)
> }
>
> end_extent_writepage(page, error, start, end);
> -
> + fscrypt_free_bounce_page(bounce_page);
> btrfs_page_clear_writeback(fs_info, page, start, bvec->bv_len);
> }
>
> @@ -3029,6 +3044,17 @@ static void end_bio_extent_readpage(struct btrfs_bio *bbio)
> }
> }
>
> + if (likely(uptodate)) {
> + if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
> + int ret = fscrypt_decrypt_pagecache_blocks(page,
> + bvec->bv_len,
> + bvec->bv_offset);
> + if (ret) {
> + error_bitmap = (unsigned int) -1;
> + uptodate = false;
> + }
This thing. Thanks,
Josef
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent
2022-09-07 21:39 ` Sweet Tea Dorminy
@ 2022-09-09 10:04 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-09 10:04 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On Wed, Sep 07, 2022 at 05:39:04PM -0400, Sweet Tea Dorminy wrote:
>
> > FSCRYPT_EXTENT_CONTEXT_MAX_SIZE is 33 and btrfs_fscrypt_extent_context
> > is part of extent_map, that's quite common object. Random sample from
> > my
> > desktop right now is around 800k objects so this is noticeable. Needs a
> > second look.
> >
> >> +
> >> extern const struct fscrypt_operations btrfs_fscrypt_ops;
> >> #endif /* BTRFS_FSCRYPT_H */
> >> --- a/fs/btrfs/ordered-data.h
> >> +++ b/fs/btrfs/ordered-data.h
> >> @@ -99,6 +99,7 @@ struct btrfs_ordered_extent {
> >> u64 disk_bytenr;
> >> u64 disk_num_bytes;
> >> u64 offset;
> >> + struct btrfs_fscrypt_extent_context fscrypt_context;
> >
> > And another embedded btrfs_fscrypt_extent_context, that can also get a
> > lot of slab objects.
>
> I could certainly define fscrypt_extent_context's as a separate btree
> object type, and/or have them be separately allocated and just have a
> pointer in the various structures to keep track of them. I didn't have a
> separate object for them since its only a 17 or 33 byte object (at
> present) on a per-btrfs_file_extent basis, but maybe that would be
> better?
btrfs_ordered_extent is in memory so I don't know what you mean by a
btree object here, that's used for on-disk structures. All the new
fscrypt code adds a lot of stack space and increases data structures but
that's something that needs to be effectively, so if the pointers can be
shared then please find a way. I still don't have the whole picture so
can't say what would be best, there are still other things to address so
once that's done we can focus on the memory consumption. You can keep it
as is for now but keep notes that it needs another look later.
> I could also #ifdef CONFIG_FS_ENCRYPTION the member in each structure,
> if that would help over and beyond either of the previous things.
I think all the fscrypt related code should be behind ifdef
CONFIG_FS_ENCRYPTION, so the struct members too.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-06 0:35 ` [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method Sweet Tea Dorminy
2022-09-08 19:27 ` Josef Bacik
@ 2022-09-09 10:15 ` David Sterba
2022-09-09 13:00 ` Christoph Hellwig
1 sibling, 1 reply; 66+ messages in thread
From: David Sterba @ 2022-09-09 10:15 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:25PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
> @@ -0,0 +1,32 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 Facebook
> + */
Please use only SPDX in new files
https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
2022-09-06 0:35 ` [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag Sweet Tea Dorminy
@ 2022-09-09 11:35 ` David Sterba
2022-09-12 1:36 ` Eric Biggers
1 sibling, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-09 11:35 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:31PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
Subject: Re: [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
And for subjects please use lower case after "btrfs: " and don't put "."
at the end of the line.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-09 10:15 ` David Sterba
@ 2022-09-09 13:00 ` Christoph Hellwig
2022-09-09 13:34 ` David Sterba
2022-09-09 13:41 ` Chris Mason
0 siblings, 2 replies; 66+ messages in thread
From: Christoph Hellwig @ 2022-09-09 13:00 UTC (permalink / raw)
To: David Sterba
Cc: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
linux-spdx
On Fri, Sep 09, 2022 at 12:15:21PM +0200, David Sterba wrote:
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2020 Facebook
> > + */
>
> Please use only SPDX in new files
>
> https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
The wiki is incorrect. The SPDX tag deals with the licensing tags
only. It is not a replacement for the copyright notice in any way, and
having been involved with Copyright enforcement I can tell you that
at least in some jurisdictions Copytight notices absolutely do matter.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-09 13:00 ` Christoph Hellwig
@ 2022-09-09 13:34 ` David Sterba
2022-09-16 22:18 ` J Lovejoy
2022-09-09 13:41 ` Chris Mason
1 sibling, 1 reply; 66+ messages in thread
From: David Sterba @ 2022-09-09 13:34 UTC (permalink / raw)
To: Christoph Hellwig
Cc: David Sterba, Sweet Tea Dorminy, Theodore Y. Ts'o,
Jaegeuk Kim, Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
linux-spdx
On Fri, Sep 09, 2022 at 06:00:13AM -0700, Christoph Hellwig wrote:
> On Fri, Sep 09, 2022 at 12:15:21PM +0200, David Sterba wrote:
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (C) 2020 Facebook
> > > + */
> >
> > Please use only SPDX in new files
> >
> > https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
>
> The wiki is incorrect. The SPDX tag deals with the licensing tags
> only. It is not a replacement for the copyright notice in any way, and
> having been involved with Copyright enforcement I can tell you that
> at least in some jurisdictions Copytight notices absolutely do matter.
I believe you and can update the wiki text so it's more explicit about
the license an copyright.
Otherwise I don't see much point in the copyright notices unless they're
complete list of every person and company that touched that file. With
that we haven't been adding that to new files for some time and I want
to be consistent with that. In each case the patch author was asked to
resubmit without the notice. It worked so far.
If not, either the patch will be respectfully rejected or somebody
else creates the file first and then the patch applied.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-09 13:00 ` Christoph Hellwig
2022-09-09 13:34 ` David Sterba
@ 2022-09-09 13:41 ` Chris Mason
1 sibling, 0 replies; 66+ messages in thread
From: Chris Mason @ 2022-09-09 13:41 UTC (permalink / raw)
To: Christoph Hellwig, David Sterba
Cc: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Josef Bacik, David Sterba, linux-fscrypt,
linux-btrfs, kernel-team, Omar Sandoval, linux-spdx
On 9/9/22 9:00 AM, Christoph Hellwig wrote:
> On Fri, Sep 09, 2022 at 12:15:21PM +0200, David Sterba wrote:
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (C) 2020 Facebook
>>> + */
>>
>> Please use only SPDX in new files
>>
>> https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
>
> The wiki is incorrect. The SPDX tag deals with the licensing tags
> only. It is not a replacement for the copyright notice in any way, and
> having been involved with Copyright enforcement I can tell you that
> at least in some jurisdictions Copytight notices absolutely do matter.
I agree, SPDX != copyright, and they should probably be in different
paragraphs in the wiki because it's misleading as it stands.
Signed-off-by is targeted at declaring that you have permission to put
this code into this project, which is somewhat different from copyright.
For Meta code, I'm happy to use the git history method. But, I'm not
completely comfortable with enforcing that on other companies or
individuals unless it's common practice elsewhere in the kernel.
-chris
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 05/20] fscrypt: add extent-based encryption
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
2022-09-07 19:59 ` Omar Sandoval
2022-09-08 15:33 ` Josef Bacik
@ 2022-09-12 1:34 ` Eric Biggers
2 siblings, 0 replies; 66+ messages in thread
From: Eric Biggers @ 2022-09-12 1:34 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team
On Mon, Sep 05, 2022 at 08:35:20PM -0400, Sweet Tea Dorminy wrote:
> Some filesystems need to encrypt data based on extents, rather than on
> inodes, due to features incompatible with inode-based encryption. For
> instance, btrfs can have multiple inodes referencing a single block of
> data, and moves logical data blocks to different physical locations on
> disk in the background; these two features mean inode or
> physical-location-based policies will not work for btrfs.
>
> This change introduces fscrypt_extent_context objects, in analogy to
> existing context objects based on inodes. For a filesystem which uses
> extents, a new hook provides a new fscrypt_extent_context. During file
> content encryption/decryption, the existing fscrypt_context object
> provides key information, while the new fscrypt_extent_context provides
> IV information. For filename encryption, the existing IV generation
> methods are still used, since filenames are not stored in extents.
>
> As individually keyed inodes prevent sharing of extents, such policies
> are forbidden for filesystems with extent-based encryption.
>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/crypto/crypto.c | 15 +++++++-
> fs/crypto/fscrypt_private.h | 26 ++++++++++++-
> fs/crypto/inline_crypt.c | 29 +++++++++++---
> fs/crypto/policy.c | 77 +++++++++++++++++++++++++++++++++++++
> include/linux/fscrypt.h | 41 ++++++++++++++++++++
> 5 files changed, 178 insertions(+), 10 deletions(-)
>
I'm on vacation at the moment, but I've been thinking about this patchset and
I'll leave some quick high-level feedback.
I think that you've sort of ended up with something reasonable in this patch,
though maybe not for exactly the reasons you thought, and it still needs to be
tweaked a bit.
Thinking abstractly, an encryption policy indicates that a set of files is
encrypted with a particular key. That set of files contains a set of objects
containing "file contents", each containing a sequence of file contents blocks.
File contents encryption is the encryption that is applied to these objects.
With other filesystems, the "file contents objects" are inodes. With btrfs, the
"file contents objects" are extents.
I think it's fair to say that this is just a difference in the filesystems, and
it doesn't need to be explicitly indicated in the encryption policy.
What *is* super important, though, is to keep the cryptography consistent.
Consider the existing default setting, which derives a per-inode key from the
master key and a per-inode nonce, and sets the IV to the offset into the inode.
There is a natural mapping of that to extent-based encryption: derive a
per-extent key from the master key and a per-extent nonce, and set the IV to the
offset into the extent.
But you haven't actually implemented that. I assume that you've discarded
per-extent keys as infeasible?
If that's the case, then the alternative is to do file contents encryption with
a per-mode key, using an IV generation method that makes the IVs identify both
the file contents object *and* the offset into it, rather than just the latter.
The existing methods for that are DIRECT_KEY, IV_INO_LBLK_32, and
IV_INO_LBLK_64. DIRECT_KEY uses a 16-byte nonce to identify the file contents
object. IV_INO_LBLK_32 and IV_INO_LBLK_64 use a filesystem-assigned ID;
currently this ID is inode number, but if they were to be applied to
extent-based encryption, it would be an "extent number" instead.
So if you do want to implement the DIRECT_KEY method, the natural thing to do
would be to store a 16-byte nonce along with each extent, and use the DIRECT_KEY
IV generation method as-is. It seems that you've done it a bit differently; you
store a 32-byte nonce and generate the IV as 'nonce + lblk_num', instead of
'nonce || lblk_num'. I think that's a mistake -- it should be exactly the same.
If the issue is that the 'nonce || lblk_num' method doesn't allow for AES-XTS
support, we could extend DIRECT_KEY to do 'nonce + lblk_num' *if* the algorithm
has a 16-byte IV size and thus has to tolerate some chance of IV reuse. Note
that this change would be unrelated to extent-based encryption, and could be
applied regardless of it.
Side note: please don't use the phrase "file-based encryption" to distinguish
from "extent-based encryption", as "file-based encryption" is already in use to
distinguish from block-device based encryption. (See e.g. all the Android
documentation that refers to file-based encryption.) Maybe use "inode-based
encryption", or "inode-based file contents encryption" to be extra clear.
- Eric
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag.
2022-09-06 0:35 ` [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag Sweet Tea Dorminy
2022-09-09 11:35 ` David Sterba
@ 2022-09-12 1:36 ` Eric Biggers
1 sibling, 0 replies; 66+ messages in thread
From: Eric Biggers @ 2022-09-12 1:36 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:31PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> As fscrypt files will be incompatible with older filesystem versions,
> new filesystems should be created with an incompat flag for fscrypt.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/btrfs/ctree.h | 6 ++++--
> include/uapi/linux/btrfs.h | 1 +
> 2 files changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 38927a867028..e8d000fcc85d 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -330,7 +330,8 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
> BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \
> BTRFS_FEATURE_INCOMPAT_RAID1C34 | \
> BTRFS_FEATURE_INCOMPAT_ZONED | \
> - BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2)
> + BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 | \
> + BTRFS_FEATURE_INCOMPAT_FSCRYPT)
Shouldn't this flag be named "encrypt", to be consistent with the other
filesystems?
- Eric
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
2022-09-08 13:43 ` Josef Bacik
@ 2022-09-12 1:42 ` Eric Biggers
2022-09-15 18:58 ` Sweet Tea Dorminy
2022-09-13 10:07 ` Anand Jain
2 siblings, 1 reply; 66+ messages in thread
From: Eric Biggers @ 2022-09-12 1:42 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
On Mon, Sep 05, 2022 at 08:35:17PM -0400, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Creating several new subvolumes out of snapshots of another subvolume,
> each for a different VM's storage, is a important usecase for btrfs. We
> would like to give each VM a unique encryption key to use for new writes
> to its subvolume, so that secure deletion of the VM's data is as simple
> as securely deleting the key; to avoid needing multiple keys in each VM,
> we envision the initial subvolume being unencrypted. However, this means
> that the snapshot's directories would have a mix of encrypted and
> unencrypted files. During lookup with a key, both unencrypted and
> encrypted forms of the desired name must be queried.
>
> To allow this, add another FS_CFLG to allow filesystems to opt into
> partially encrypted directories.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/crypto/fname.c | 17 ++++++++++++++++-
> include/linux/fscrypt.h | 2 ++
> 2 files changed, 18 insertions(+), 1 deletion(-)
I'm still trying to wrap my head around what this part involves exactly. This
is a pretty big change in semantics.
Could this be moved to the end of the patchset, or is this a fundamental part of
the btrfs fscrypt support that the rest of your patchset depends on? I'd think
it would be a lot easier to review if this change was an add-on at the end.
One thing to keep in mind is that FS_IOC_SET_ENCRYPTION_POLICY failing on
nonempty directories can actually be very useful, since it makes it possible to
detect bugs where people create files in encrypted directories (expecting that
they are encrypted) before an encryption policy actually gets assigned. Since
FS_IOC_SET_ENCRYPTION_POLICY fails in that case, such bugs can be detected and
fixed.
If it succeeds, then everything would just silently work and some files wouldn't
be encrypted as intended. That's kind of scary.
It might be warranted to use an encryption policy flag to explicitly indicate
that mixing encrypted and unencrypted files is being allowed.
- Eric
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 12/20] btrfs: start using fscrypt hooks.
2022-09-07 20:42 ` Sweet Tea Dorminy
@ 2022-09-12 1:50 ` Eric Biggers
0 siblings, 0 replies; 66+ messages in thread
From: Eric Biggers @ 2022-09-12 1:50 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: dsterba, Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval
On Wed, Sep 07, 2022 at 04:42:40PM -0400, Sweet Tea Dorminy wrote:
>
> > > diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
> > > index 7216ac1f860c..929a0308676c 100644
> > > --- a/fs/btrfs/file.c
> > > +++ b/fs/btrfs/file.c
> > > @@ -3695,6 +3695,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
> > > int ret;
> > > filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC;
> > > + ret = fscrypt_file_open(inode, filp);
> > > + if (ret)
> > > + return ret;
> > > ret = fsverity_file_open(inode, filp);
> >
> > Can fsverity and fscrypt can be used at the same time?
>
> Yes, and there's a fstest, generic/576, checking the hooks are in the right
> order.
In order to use them at the same time, you'd need to implement encryption of the
verity metadata, which it appears you haven't done yet. So for now it can't be
allowed by btrfs. ext4 and f2fs get the verity metadata encryption "for free"
because they store it appended to the file contents. That's why ext4 and f2fs
allow fscrypt+fsverity together. However, btrfs stores the verity metadata
separately, so it would need to handle encrypting it explicitly.
- Eric
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
2022-09-08 13:43 ` Josef Bacik
2022-09-12 1:42 ` Eric Biggers
@ 2022-09-13 10:07 ` Anand Jain
2022-09-13 11:02 ` Neal Gompa
2 siblings, 1 reply; 66+ messages in thread
From: Anand Jain @ 2022-09-13 10:07 UTC (permalink / raw)
To: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team
Cc: Omar Sandoval
On 06/09/2022 08:35, Sweet Tea Dorminy wrote:
> From: Omar Sandoval <osandov@osandov.com>
>
> Creating several new subvolumes out of snapshots of another subvolume,
> each for a different VM's storage, is a important usecase for btrfs.
> We
> would like to give each VM a unique encryption key to use for new writes
> to its subvolume, so that secure deletion of the VM's data is as simple
> as securely deleting the key; to avoid needing multiple keys in each VM,
> we envision the initial subvolume being unencrypted.
In this usecase, you didn't mention if the original subvolume is
encrypted, assuming it is not, so this usecase is the same as mentioned
in the design document. Now, in this usecase, what happens if the
original subvolume is encrypted? Can we still avoid the multiple keys?
How is that going to work? Or we don't yet support subvolumes out of
snapshots of another encrypted subvolume?
Thanks,
-Anand
> However, this means
> that the snapshot's directories would have a mix of encrypted and
> unencrypted files. During lookup with a key, both unencrypted and
> encrypted forms of the desired name must be queried.
>
> To allow this, add another FS_CFLG to allow filesystems to opt into
> partially encrypted directories.
>
> Signed-off-by: Omar Sandoval <osandov@osandov.com>
> Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
> ---
> fs/crypto/fname.c | 17 ++++++++++++++++-
> include/linux/fscrypt.h | 2 ++
> 2 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
> index 6c092a1533f7..3bdece33e14d 100644
> --- a/fs/crypto/fname.c
> +++ b/fs/crypto/fname.c
> @@ -414,6 +414,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
> fname->usr_fname = iname;
>
> if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) {
> +unencrypted:
> fname->disk_name.name = (unsigned char *)iname->name;
> fname->disk_name.len = iname->len;
> return 0;
> @@ -448,8 +449,16 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
> * user-supplied name
> */
>
> - if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED)
> + if (iname->len > FSCRYPT_NOKEY_NAME_MAX_ENCODED) {
> + /*
> + * This isn't a valid nokey name, but it could be an unencrypted
> + * name if the filesystem allows partially encrypted
> + * directories.
> + */
> + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL)
> + goto unencrypted;
> return -ENOENT;
> + }
>
> fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL);
> if (fname->crypto_buf.name == NULL)
> @@ -460,6 +469,12 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
> if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) ||
> (ret > offsetof(struct fscrypt_nokey_name, sha256) &&
> ret != FSCRYPT_NOKEY_NAME_MAX)) {
> + /* Again, this could be an unencrypted name. */
> + if (dir->i_sb->s_cop->flags & FS_CFLG_ALLOW_PARTIAL) {
> + kfree(fname->crypto_buf.name);
> + fname->crypto_buf.name = NULL;
> + goto unencrypted;
> + }
> ret = -ENOENT;
> goto errout;
> }
> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
> index a236d8c6d0da..a4e00314c91b 100644
> --- a/include/linux/fscrypt.h
> +++ b/include/linux/fscrypt.h
> @@ -102,6 +102,8 @@ struct fscrypt_nokey_name {
> * pages for writes and therefore won't need the fscrypt bounce page pool.
> */
> #define FS_CFLG_OWN_PAGES (1U << 1)
> +/* The filesystem allows partially encrypted directories/files. */
> +#define FS_CFLG_ALLOW_PARTIAL (1U << 2)
>
> /* Crypto operations for filesystems */
> struct fscrypt_operations {
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-13 10:07 ` Anand Jain
@ 2022-09-13 11:02 ` Neal Gompa
0 siblings, 0 replies; 66+ messages in thread
From: Neal Gompa @ 2022-09-13 11:02 UTC (permalink / raw)
To: Anand Jain
Cc: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
Chris Murphy
On Tue, Sep 13, 2022 at 6:21 AM Anand Jain <anand.jain@oracle.com> wrote:
>
> On 06/09/2022 08:35, Sweet Tea Dorminy wrote:
> > From: Omar Sandoval <osandov@osandov.com>
> >
> > Creating several new subvolumes out of snapshots of another subvolume,
> > each for a different VM's storage, is a important usecase for btrfs.
>
> > We
> > would like to give each VM a unique encryption key to use for new writes
> > to its subvolume, so that secure deletion of the VM's data is as simple
> > as securely deleting the key; to avoid needing multiple keys in each VM,
> > we envision the initial subvolume being unencrypted.
>
> In this usecase, you didn't mention if the original subvolume is
> encrypted, assuming it is not, so this usecase is the same as mentioned
> in the design document. Now, in this usecase, what happens if the
> original subvolume is encrypted? Can we still avoid the multiple keys?
> How is that going to work? Or we don't yet support subvolumes out of
> snapshots of another encrypted subvolume?
>
Count this as the first official "email triggered by Plumbers" I
suppose, but I've been looking forward to Btrfs encryption for Fedora
for a variety of use-cases:
* Converting a disk from unencrypted to encrypted without reinstalling[1]
* Encrypting system and user data with different credentials[2][3]
* Mass managed systems with dual-credential encryption (one rooted in
org, other by user)[1]
The way I envision Fedora's usage of this feature going is as follows:
The system is shipped (OEM install; e.g. Lenovo, Slimbook, etc.) or
installed without encryption (from Anaconda). The firstboot process
helps the user get setup, and asks if they want disk encryption
(provided there is no policy forcing it on). If the answer is yes,
then the root subvolume and the home subvolume are configured to be
encrypted using the on-board secure enclave (TPM or whatever). When
the user creation step occurs, if disk encryption was set on earlier,
then the user subvolume inside the home subvolume gets configured to
be encrypted using the login credentials. If it wasn't set on earlier,
we could also still offer to encrypt the user data here. If this is a
mass-managed system, an additional decryption key would be available
for the system manager to use in the event of credential loss, damaged
TPM, etc.
For the cloudy scenario, we'd probably provide tools to orchestrate
this from cloud-init for confidential computing stuff too.
For all this to work, I expect the following:
* Encryption to support *somehow* any number of decryption keys/methods
* Nesting of subvolumes with differing encryption methods
* The ability to blindly backup and restore subvolumes without needing
to decrypt
On a personal system, I expect at most two credentials in use: one
keyed to the system and one keyed to the user. For a corporate/mass
managed system, I expect a third one: one keyed to the management
platform. This is necessary because in a lot of jurisdictions, you
don't necessarily own the data on your corporate laptop, and the owner
has the right to be able to access that data. But a more prosaic or
banal reason is that people forget their credentials and being able to
reset them without losing all their data is usually a good thing. :)
Hopefully I've transcribed what I said properly into this email. :)
[1]: https://pagure.io/fedora-btrfs/project/issue/10
[2]: https://pagure.io/fedora-workstation/issue/82
[3]: https://pagure.io/fedora-workstation/issue/136
--
Neal Gompa (FAS: ngompa)
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories
2022-09-12 1:42 ` Eric Biggers
@ 2022-09-15 18:58 ` Sweet Tea Dorminy
0 siblings, 0 replies; 66+ messages in thread
From: Sweet Tea Dorminy @ 2022-09-15 18:58 UTC (permalink / raw)
To: Eric Biggers
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Chris Mason, Josef Bacik,
David Sterba, linux-fscrypt, linux-btrfs, kernel-team,
Omar Sandoval
> I'm still trying to wrap my head around what this part involves
> exactly. This
> is a pretty big change in semantics.
>
> Could this be moved to the end of the patchset, or is this a
> fundamental part of
> the btrfs fscrypt support that the rest of your patchset depends on?
> I'd think
> it would be a lot easier to review if this change was an add-on at the
> end.
Definitely.
>
> One thing to keep in mind is that FS_IOC_SET_ENCRYPTION_POLICY failing
> on
> nonempty directories can actually be very useful, since it makes it
> possible to
> detect bugs where people create files in encrypted directories
> (expecting that
> they are encrypted) before an encryption policy actually gets assigned.
> Since
> FS_IOC_SET_ENCRYPTION_POLICY fails in that case, such bugs can be
> detected and
> fixed.
I agree that this has risks of inadvertent misuse in that fashion.
The usecase I'm oriented towards is: someone builds an unencrypted
subvolume with a container base filesystem, takes several snapshots of
the subvolume, starts a container on each subvolume, and has each
container encrypt its designated subvolume going forward with a
different key. This usecase needs some way to mark a subvolume/directory
already containing files as encrypted going forward; I've had a hard
time coming up with a way to both protect users against such accidental
misuse, but also allow this container usecase.
>
> It might be warranted to use an encryption policy flag to explicitly
> indicate
> that mixing encrypted and unencrypted files is being allowed.
Could it be sufficient to check either empty or read-only, something
like (is_empty_dir(inode) || (FS_CFLG_PARTIAL && !inode_permission(...,
inode, MAY_WRITE)))? Then the user is unable to accidentally write
unencrypted data, since they've taken an action to make the directory
read-only, until they've set a policy and key and turned the directory
read-write again.
Thanks!
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-09 13:34 ` David Sterba
@ 2022-09-16 22:18 ` J Lovejoy
2022-09-19 2:00 ` Bradley M. Kuhn
2022-09-19 16:52 ` David Sterba
0 siblings, 2 replies; 66+ messages in thread
From: J Lovejoy @ 2022-09-16 22:18 UTC (permalink / raw)
To: dsterba, Christoph Hellwig
Cc: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
linux-spdx
On 9/9/22 7:34 AM, David Sterba wrote:
> On Fri, Sep 09, 2022 at 06:00:13AM -0700, Christoph Hellwig wrote:
>> On Fri, Sep 09, 2022 at 12:15:21PM +0200, David Sterba wrote:
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) 2020 Facebook
>>>> + */
>>> Please use only SPDX in new files
>>>
>>> https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
>> The wiki is incorrect. The SPDX tag deals with the licensing tags
>> only. It is not a replacement for the copyright notice in any way, and
>> having been involved with Copyright enforcement I can tell you that
>> at least in some jurisdictions Copytight notices absolutely do matter.
> I believe you and can update the wiki text so it's more explicit about
> the license an copyright.
Can you update the wiki text to remove "SPDX" from the heading and
remove the sentence stating, "An initiative started in 2017 [1] aims to
unify licensing information in all files using SPDX tags, this is driven
by the Linux Foundation."
Thanks,
Jilayne
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-16 22:18 ` J Lovejoy
@ 2022-09-19 2:00 ` Bradley M. Kuhn
2022-09-19 17:20 ` David Sterba
2022-09-19 16:52 ` David Sterba
1 sibling, 1 reply; 66+ messages in thread
From: Bradley M. Kuhn @ 2022-09-19 2:00 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-btrfs, linux-spdx, linux-fscrypt, kernel-team
Regarding
https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
On Fri, Sep 09, 2022 at 06:00:13AM -0700, Christoph Hellwig wrote:
> > > The wiki is incorrect. The SPDX tag deals with the licensing tags
> > > only. It is not a replacement for the copyright notice in any way, and
> > > having been involved with Copyright enforcement I can tell you that at
> > > least in some jurisdictions Copytight notices absolutely do matter.
This is a very good point.
The current Wiki page for btrfs (linked above) says:
> There's no need to put the copyright notices in individual files that are
> new, renamed or split.
…
> Note that removing the copyright from existing files is not trivial and
> would require asking the original authors or current copyright holders. The
> status will be inconsistent but at least new contributions won't continue
> adding new ones. The current licensing practices are believed to be
> sufficient.
This is admittedly a very tough problem to solve. Nevertheless, the concern
that I have with that recommendation above is that it gives copyright holders
whose notices are grandfathered an additional notice preservation that new
copyright holders don't have equal access to. It's particular problematic
because new contributors are unable to have contributions included unless
they remove copyright notices.
Again, I realize the trade-offs are really tough here; removing existing
copyright notices without explicit permission is a *serious* problem (both a
GPL violation and a statutory violation of copyright generally in many
jurisdictions). OTOH, a list of every last copyright holder is painfully
unwieldy — even if you combine it into a single location.
Most importantly, I want to point out the bigger, implicit trade-off here
that some may not realize. If you relying on Git history to have copyright
notice information, it does make the entire Git repository a required part of
the complete, corresponding source under GPLv2. This will become even more
certain when contributors are being told that they may *not* include a
copyright notice and that their copyright information will appear in metadata
instead. They can reasonably interpret the “appropriately publish on each
copy an appropriate copyright notice” in GPLv2§1 to mean the copyright
notices in the Git metadata.
J Lovejoy wrote:
> Can you update the wiki text to remove "SPDX" from the heading and remove
> the sentence stating, "An initiative started in 2017 [1] aims to unify
> licensing information in all files using SPDX tags, this is driven by the
> Linux Foundation."
All of that seems accurate to me. What part is not accurate?
Splitting the information to talk about copyright and license separately
seems a good idea, but removing accurate explanations doesn't seem like a
good idea to me …
-- bkuhn
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-16 22:18 ` J Lovejoy
2022-09-19 2:00 ` Bradley M. Kuhn
@ 2022-09-19 16:52 ` David Sterba
1 sibling, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-19 16:52 UTC (permalink / raw)
To: J Lovejoy
Cc: dsterba, Christoph Hellwig, Sweet Tea Dorminy,
Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team, Omar Sandoval, linux-spdx
On Fri, Sep 16, 2022 at 04:18:47PM -0600, J Lovejoy wrote:
>
>
> On 9/9/22 7:34 AM, David Sterba wrote:
> > On Fri, Sep 09, 2022 at 06:00:13AM -0700, Christoph Hellwig wrote:
> >> On Fri, Sep 09, 2022 at 12:15:21PM +0200, David Sterba wrote:
> >>>> +// SPDX-License-Identifier: GPL-2.0
> >>>> +/*
> >>>> + * Copyright (C) 2020 Facebook
> >>>> + */
> >>> Please use only SPDX in new files
> >>>
> >>> https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
> >> The wiki is incorrect. The SPDX tag deals with the licensing tags
> >> only. It is not a replacement for the copyright notice in any way, and
> >> having been involved with Copyright enforcement I can tell you that
> >> at least in some jurisdictions Copytight notices absolutely do matter.
> > I believe you and can update the wiki text so it's more explicit about
> > the license an copyright.
>
> Can you update the wiki text to remove "SPDX" from the heading and
> remove the sentence stating, "An initiative started in 2017 [1] aims to
> unify licensing information in all files using SPDX tags, this is driven
> by the Linux Foundation."
I can consider that if you tell me why I should do that, but I don't
find anything wrong with the sentence (and I wrote it originally). It's
merely stating that something happened and points to a well known linux
kernel related web site with more details about it.
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method
2022-09-19 2:00 ` Bradley M. Kuhn
@ 2022-09-19 17:20 ` David Sterba
0 siblings, 0 replies; 66+ messages in thread
From: David Sterba @ 2022-09-19 17:20 UTC (permalink / raw)
To: Bradley M. Kuhn
Cc: Christoph Hellwig, linux-btrfs, linux-spdx, linux-fscrypt,
kernel-team
On Sun, Sep 18, 2022 at 07:00:17PM -0700, Bradley M. Kuhn wrote:
> Regarding
> https://btrfs.wiki.kernel.org/index.php/Developer%27s_FAQ#Copyright_notices_in_files.2C_SPDX
>
> On Fri, Sep 09, 2022 at 06:00:13AM -0700, Christoph Hellwig wrote:
>
> > > > The wiki is incorrect. The SPDX tag deals with the licensing tags
> > > > only. It is not a replacement for the copyright notice in any way, and
> > > > having been involved with Copyright enforcement I can tell you that at
> > > > least in some jurisdictions Copytight notices absolutely do matter.
>
> This is a very good point.
I've expanded the page hopefully correcting the confusion. It has 3
sections, about spdx, about copyright and the community perspective.
> The current Wiki page for btrfs (linked above) says:
> > There's no need to put the copyright notices in individual files that are
> > new, renamed or split.
> …
> > Note that removing the copyright from existing files is not trivial and
> > would require asking the original authors or current copyright holders. The
> > status will be inconsistent but at least new contributions won't continue
> > adding new ones. The current licensing practices are believed to be
> > sufficient.
>
> This is admittedly a very tough problem to solve. Nevertheless, the concern
> that I have with that recommendation above is that it gives copyright holders
> whose notices are grandfathered an additional notice preservation that new
> copyright holders don't have equal access to. It's particular problematic
> because new contributors are unable to have contributions included unless
> they remove copyright notices.
>
> Again, I realize the trade-offs are really tough here; removing existing
> copyright notices without explicit permission is a *serious* problem (both a
> GPL violation and a statutory violation of copyright generally in many
> jurisdictions). OTOH, a list of every last copyright holder is painfully
> unwieldy — even if you combine it into a single location.
>
> Most importantly, I want to point out the bigger, implicit trade-off here
> that some may not realize. If you relying on Git history to have copyright
> notice information, it does make the entire Git repository a required part of
> the complete, corresponding source under GPLv2. This will become even more
> certain when contributors are being told that they may *not* include a
> copyright notice and that their copyright information will appear in metadata
> instead. They can reasonably interpret the “appropriately publish on each
> copy an appropriate copyright notice” in GPLv2§1 to mean the copyright
> notices in the Git metadata.
Thanks for the reply. Oh well, so we basically don't have good options.
^ permalink raw reply [flat|nested] 66+ messages in thread
end of thread, other threads:[~2022-09-19 17:27 UTC | newest]
Thread overview: 66+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-09-06 0:35 [PATCH v2 00/20] btrfs: add fscrypt integration Sweet Tea Dorminy
2022-09-06 0:35 ` [PATCH v2 01/20] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
2022-09-08 13:41 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 02/20] fscrypt: add flag allowing partially-encrypted directories Sweet Tea Dorminy
2022-09-08 13:43 ` Josef Bacik
2022-09-12 1:42 ` Eric Biggers
2022-09-15 18:58 ` Sweet Tea Dorminy
2022-09-13 10:07 ` Anand Jain
2022-09-13 11:02 ` Neal Gompa
2022-09-06 0:35 ` [PATCH v2 03/20] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
2022-09-08 13:53 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 04/20] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
2022-09-08 14:01 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 05/20] fscrypt: add extent-based encryption Sweet Tea Dorminy
2022-09-07 19:59 ` Omar Sandoval
2022-09-08 15:33 ` Josef Bacik
2022-09-12 1:34 ` Eric Biggers
2022-09-06 0:35 ` [PATCH v2 06/20] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
2022-09-08 15:34 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 07/20] btrfs: store directory's encryption state Sweet Tea Dorminy
2022-09-08 15:37 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 09/20] btrfs: setup fscrypt_names from dentrys using helper Sweet Tea Dorminy
2022-09-08 19:11 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 10/20] btrfs: factor a fscrypt_name matching method Sweet Tea Dorminy
2022-09-08 19:27 ` Josef Bacik
2022-09-09 10:15 ` David Sterba
2022-09-09 13:00 ` Christoph Hellwig
2022-09-09 13:34 ` David Sterba
2022-09-16 22:18 ` J Lovejoy
2022-09-19 2:00 ` Bradley M. Kuhn
2022-09-19 17:20 ` David Sterba
2022-09-19 16:52 ` David Sterba
2022-09-09 13:41 ` Chris Mason
2022-09-06 0:35 ` [PATCH v2 11/20] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
2022-09-07 20:11 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 12/20] btrfs: start using fscrypt hooks Sweet Tea Dorminy
2022-09-07 20:17 ` David Sterba
2022-09-07 20:42 ` Sweet Tea Dorminy
2022-09-12 1:50 ` Eric Biggers
2022-09-08 19:42 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 13/20] btrfs: add fscrypt_context items Sweet Tea Dorminy
2022-09-07 20:43 ` David Sterba
2022-09-08 20:06 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 14/20] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
2022-09-08 20:07 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 15/20] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
2022-09-07 21:10 ` David Sterba
2022-09-07 21:39 ` Sweet Tea Dorminy
2022-09-09 10:04 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 16/20] btrfs: Add new FEATURE_INCOMPAT_FSCRYPT feature flag Sweet Tea Dorminy
2022-09-09 11:35 ` David Sterba
2022-09-12 1:36 ` Eric Biggers
2022-09-06 0:35 ` [PATCH v2 17/20] btrfs: reuse encrypted filename hash when possible Sweet Tea Dorminy
2022-09-07 21:24 ` David Sterba
2022-09-06 0:35 ` [PATCH v2 18/20] btrfs: adapt directory read and lookup to potentially encrypted filenames Sweet Tea Dorminy
2022-09-08 20:15 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 19/20] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
2022-09-08 20:19 ` Josef Bacik
2022-09-06 0:35 ` [PATCH v2 20/20] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
2022-09-07 21:33 ` David Sterba
2022-09-06 22:35 ` [PATCH v2 00/20] btrfs: add fscrypt integration Eric Biggers
2022-09-06 23:01 ` Sweet Tea Dorminy
2022-09-06 23:10 ` Eric Biggers
2022-09-07 0:01 ` Sweet Tea Dorminy
2022-09-07 19:38 ` David Sterba
[not found] ` <2b32b14368c67eb8591ccc4b0cf9d19358dfae23.1662420176.git.sweettea-kernel@dorminy.me>
2022-09-07 20:04 ` [PATCH v2 08/20] btrfs: use fscrypt_names instead of name/len everywhere David Sterba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox