* [PATCH v5 01/18] fscrypt: expose fscrypt_nokey_name
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 02/18] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
` (17 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
---
fs/crypto/fname.c | 39 +--------------------------------------
include/linux/fscrypt.h | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 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 4f5f8a651213..7661db0b5bec 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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 02/18] fscrypt: add fscrypt_have_same_policy() to check inode compatibility
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 01/18] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 03/18] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
` (16 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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 | 35 +++++++++++++++++++++++++++++++++++
include/linux/fscrypt.h | 8 ++++++++
2 files changed, 43 insertions(+)
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 46757c3052ef..715870d4e530 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -415,6 +415,41 @@ 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
+ * @same_ptr: a pointer to return whether they are the same
+ *
+ * Return: 0 or an error code.
+ */
+int fscrypt_have_same_policy(struct inode *inode1, struct inode *inode2,
+ bool *same_ptr)
+{
+ union fscrypt_policy policy1, policy2;
+ int err;
+
+ if (!IS_ENCRYPTED(inode1) && !IS_ENCRYPTED(inode2)) {
+ *same_ptr = true;
+ return 0;
+ }
+ if (!IS_ENCRYPTED(inode1) || !IS_ENCRYPTED(inode2)) {
+ *same_ptr = false;
+ return 0;
+ }
+
+ err = fscrypt_get_policy(inode1, &policy1);
+ if (err)
+ return err;
+ err = fscrypt_get_policy(inode2, &policy2);
+ if (err)
+ return err;
+
+ *same_ptr = fscrypt_policies_equal(&policy1, &policy2);
+ return 0;
+}
+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 7661db0b5bec..0069f92ee3da 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -313,6 +313,8 @@ 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,
+ bool *same_ptr);
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);
@@ -490,6 +492,12 @@ static inline void fscrypt_free_bounce_page(struct page *bounce_page)
}
/* policy.c */
+static inline int fscrypt_have_same_policy(struct inode *inode1,
+ struct inode *inode2)
+{
+ return 1;
+}
+
static inline int fscrypt_ioctl_set_policy(struct file *filp,
const void __user *arg)
{
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 03/18] fscrypt: allow fscrypt_generate_iv() to distinguish filenames
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 01/18] fscrypt: expose fscrypt_nokey_name Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 02/18] fscrypt: add fscrypt_have_same_policy() to check inode compatibility Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 04/18] fscrypt: add extent-based encryption Sweet Tea Dorminy
` (15 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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
For extent-based file contents encryption, filenames will need to
generate an IV based on the inode context, while file contents will need
to generate an IV based on the extent context. Currently filenames and
the first block of file contents both pass fscrypt_generate_iv() a block
number of 0, making it hard to distinguish the two cases.
To enable distinguishing these two cases for extent-based encryption,
this change adjusts all callers to pass U64_MAX when requesting an IV
for filename encryption, and then changes fscrypt_generate_iv() to
convert U64_MAX to 0 for traditional inode-context encryption. For
extent-based encryption, any block number other than U64_MAX will get an
IV from the extent context, while U64_MAX will indicate falling back to
inode contexts.
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 6c092a1533f7..b3e7e3a66312 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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 04/18] fscrypt: add extent-based encryption
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (2 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 03/18] fscrypt: allow fscrypt_generate_iv() to distinguish filenames Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 05/18] fscrypt: extent direct key policies for " Sweet Tea Dorminy
` (14 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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 traditional inode-based
file contents encryption 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 opts to
use extent-based encryption, 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 nonce information. For filename encryption,
the existing IV generation methods are still used, since filenames are
not stored in extents.
Only direct key policies are allowed, but any encryption mode is allowed
within that constraint.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/crypto.c | 20 +++++++++--
fs/crypto/fscrypt_private.h | 25 +++++++++++++-
fs/crypto/inline_crypt.c | 28 ++++++++++++----
fs/crypto/policy.c | 66 +++++++++++++++++++++++++++++++++++++
include/linux/fscrypt.h | 47 ++++++++++++++++++++++++++
5 files changed, 176 insertions(+), 10 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 7fe5979fbea2..08b495dc5c0c 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -81,8 +81,22 @@ 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);
+ memset(iv, 0, sizeof(*iv));
+ if (s_cop->get_extent_context && lblk_num != U64_MAX) {
+ size_t extent_offset;
+ union fscrypt_extent_context ctx;
+ int ret;
+
+ ret = fscrypt_get_extent_context(inode, lblk_num, &ctx,
+ &extent_offset, NULL);
+ WARN_ON_ONCE(ret);
+ memcpy(iv->raw, ctx.v1.iv.raw, sizeof(*iv));
+ iv->lblk_num += cpu_to_le64(extent_offset);
+ return;
+ }
/*
* Filename encryption. For inode-based policies, filenames are
@@ -93,8 +107,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 d5f68a0c5d15..9c4cae2580de 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -280,7 +280,6 @@ fscrypt_msg(const struct inode *inode, const char *level, const char *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 */
@@ -293,6 +292,27 @@ union fscrypt_iv {
__le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)];
};
+
+/*
+ * 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;
+ union fscrypt_iv iv;
+} __packed;
+
+union fscrypt_extent_context {
+ u8 version;
+ struct fscrypt_extent_context_v1 v1;
+};
+
+
void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
const struct fscrypt_info *ci);
@@ -662,5 +682,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 cea8b14007e6..6adb72c52ce2 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -460,6 +460,7 @@ EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
*/
u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks)
{
+ const struct fscrypt_operations *s_cop = inode->i_sb->s_cop;
const struct fscrypt_info *ci;
u32 dun;
@@ -470,14 +471,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 715870d4e530..a874afcf9652 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -222,6 +222,10 @@ 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))
+ return false;
+
if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
!supported_direct_key_modes(inode, policy->contents_encryption_mode,
policy->filenames_encryption_mode))
@@ -786,6 +790,68 @@ 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
+ * @lblk_num: the offset into the inode at which the extent starts
+ * @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, u64 lblk_num, void *extent)
+{
+ union fscrypt_extent_context ctx;
+
+ if (!IS_ENCRYPTED(inode))
+ return -ENODATA;
+
+ ctx.v1.version = 1;
+ get_random_bytes(ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE);
+
+ return inode->i_sb->s_cop->set_extent_context(extent,
+ &ctx, sizeof(ctx));
+}
+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 0069f92ee3da..402fc0ca7abf 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
/*
@@ -150,6 +157,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).
*
@@ -322,6 +362,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, u64 offset, void *extent);
struct fscrypt_dummy_policy {
const union fscrypt_policy *policy;
@@ -531,6 +572,12 @@ static inline int fscrypt_set_context(struct inode *inode, void *fs_data)
return -EOPNOTSUPP;
}
+static inline int fscrypt_set_extent_context(struct inode *inode, u64 offset,
+ void *extent)
+{
+ return -EOPNOTSUPP;
+}
+
struct fscrypt_dummy_policy {
};
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 05/18] fscrypt: extent direct key policies for extent-based encryption
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (3 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 04/18] fscrypt: add extent-based encryption Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 06/18] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
` (13 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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
For inode-based direct key encryption policies, the inode provides a
nonce, and the encryption IV is generated by concatenating the nonce and
the offset into the inode. For extent-based direct key policies,
however, we would like to use 16-byte nonces in combination with various
AES modes with 16-byte IVs. Additionally, since contents and filenames
are encrypted with different context items in this case, we don't need
to require the encryption modes match in the two cases.
This change allows extent-based encryption to use 16-byte IVs with
direct key policies, and allows a mismatch of modes (under the usual
compatible modes constraints).
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/crypto/crypto.c | 15 +++++++++++++--
fs/crypto/fscrypt_private.h | 4 +---
fs/crypto/policy.c | 4 ++++
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 08b495dc5c0c..144a3a59ce51 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -93,8 +93,19 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
ret = fscrypt_get_extent_context(inode, lblk_num, &ctx,
&extent_offset, NULL);
WARN_ON_ONCE(ret);
- memcpy(iv->raw, ctx.v1.iv.raw, sizeof(*iv));
- iv->lblk_num += cpu_to_le64(extent_offset);
+ if (ci->ci_mode->ivsize < offsetofend(union fscrypt_iv, nonce)) {
+ /*
+ * We need a 16 byte IV, but our nonce is 16 bytes.
+ * Copy to the start of the buffer and add the extent
+ * offset manually.
+ */
+ memcpy(iv->raw, ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE);
+ iv->lblk_num = cpu_to_le64(extent_offset +
+ le64_to_cpu(iv->lblk_num));
+ return;
+ }
+ memcpy(iv->nonce, ctx.v1.nonce, FSCRYPT_FILE_NONCE_SIZE);
+ iv->lblk_num = cpu_to_le64(extent_offset);
return;
}
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 9c4cae2580de..bb2a18c83e56 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -292,7 +292,6 @@ union fscrypt_iv {
__le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)];
};
-
/*
* fscrypt_extent_context - the encryption context for an extent
*
@@ -304,7 +303,7 @@ union fscrypt_iv {
*/
struct fscrypt_extent_context_v1 {
u8 version;
- union fscrypt_iv iv;
+ u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
} __packed;
union fscrypt_extent_context {
@@ -312,7 +311,6 @@ union fscrypt_extent_context {
struct fscrypt_extent_context_v1 v1;
};
-
void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
const struct fscrypt_info *ci);
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index a874afcf9652..39ef447ebaec 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -91,6 +91,10 @@ static bool supported_direct_key_modes(const struct inode *inode,
{
const struct fscrypt_mode *mode;
+ /* Extent-based encryption allows any mixed mode and IV size */
+ if (inode->i_sb->s_cop->get_extent_context)
+ return true;
+
if (contents_mode != filenames_mode) {
fscrypt_warn(inode,
"Direct key flag not allowed with different contents and filenames modes");
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 06/18] fscrypt: document btrfs' fscrypt quirks.
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (4 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 05/18] fscrypt: extent direct key policies for " Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 07/18] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
` (12 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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 inode-based contents encryption.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
---
Documentation/filesystems/fscrypt.rst | 31 +++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 5ba5817c17c2..d551635865c3 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
@@ -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
--------------------
+Inode-based contents encryption
+-------------------------------
-For file contents, each filesystem block is encrypted independently.
-Starting from Linux kernel 5.5, encryption of filesystems with block
-size less than system's page size is supported.
+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 nonce; each block within the extent has an IV
+based on this nonce and the logical block number within the extent as a
+little endian number.
+
Filenames encryption
--------------------
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 07/18] btrfs: disable various operations on encrypted inodes
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (5 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 06/18] fscrypt: document btrfs' fscrypt quirks Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 08/18] btrfs: start using fscrypt hooks Sweet Tea Dorminy
` (11 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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 | 10 ++++++++++
4 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index d21c30bf7053..5b94609f138d 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -385,6 +385,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 b7855f794ba6..1724ec898f40 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1491,7 +1491,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;
}
@@ -3737,7 +3737,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 255959574724..972a49796bb9 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -445,7 +445,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 9d728107536e..579dbf057ffa 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -801,6 +801,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
u64 bs = BTRFS_I(inode_out)->root->fs_info->sb->s_blocksize;
u64 wb_len;
int ret;
+ bool same;
if (!(remap_flags & REMAP_FILE_DEDUP)) {
struct btrfs_root *root_out = BTRFS_I(inode_out)->root;
@@ -811,6 +812,15 @@ 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.
+ */
+ ret = fscrypt_have_same_policy(inode_in, inode_out, &same);
+ if (ret)
+ return ret;
+ if (!same)
+ 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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 08/18] btrfs: start using fscrypt hooks
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (6 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 07/18] btrfs: disable various operations on encrypted inodes Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 09/18] btrfs: add fscrypt_context items Sweet Tea Dorminy
` (10 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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, and introduce the new fscrypt.[ch] files to hold the
fscrypt-specific functionality.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/Makefile | 1 +
fs/btrfs/btrfs_inode.h | 1 +
fs/btrfs/file.c | 3 ++
fs/btrfs/fscrypt.c | 7 ++++
fs/btrfs/fscrypt.h | 10 +++++
fs/btrfs/inode.c | 85 ++++++++++++++++++++++++++++++++++++------
fs/btrfs/super.c | 2 +
7 files changed, 97 insertions(+), 12 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 84fb3b4c35b0..dcdfc35ece76 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/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 5b94609f138d..32fa68946f07 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -462,6 +462,7 @@ struct btrfs_new_inode_args {
struct posix_acl *default_acl;
struct posix_acl *acl;
struct fscrypt_name fname;
+ bool encrypt;
};
int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 1724ec898f40..2a9808f0c012 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3703,6 +3703,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
int ret;
filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC | FMODE_BUF_WASYNC;
+ ret = fscrypt_file_open(inode, filp);
+ if (ret)
+ return ret;
ret = fsverity_file_open(inode, filp);
if (ret)
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
new file mode 100644
index 000000000000..48ab99dfe48d
--- /dev/null
+++ b/fs/btrfs/fscrypt.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "ctree.h"
+#include "fscrypt.h"
+
+const struct fscrypt_operations btrfs_fscrypt_ops = {
+};
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
new file mode 100644
index 000000000000..7f4e6888bd43
--- /dev/null
+++ b/fs/btrfs/fscrypt.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef BTRFS_FSCRYPT_H
+#define BTRFS_FSCRYPT_H
+
+#include <linux/fscrypt.h>
+
+extern const struct fscrypt_operations btrfs_fscrypt_ops;
+
+#endif /* BTRFS_FSCRYPT_H */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 972a49796bb9..6904b2228112 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5462,6 +5462,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;
@@ -5563,6 +5564,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);
}
@@ -6308,6 +6310,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 */
@@ -6784,9 +6790,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)
@@ -8908,6 +8918,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));
}
@@ -8978,8 +8989,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)
@@ -9568,6 +9578,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);
@@ -9787,15 +9802,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 NUL terminator, but we
+ * don't store that '\0' character.
+ */
+ err = fscrypt_prepare_symlink(dir, symname, name_len,
+ BTRFS_MAX_INLINE_DATA_SIZE(fs_info) + 1,
+ &disk_link);
+ if (err)
+ return err;
inode = new_inode(dir->i_sb);
if (!inode)
@@ -9804,7 +9826,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;
@@ -9832,10 +9854,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) {
@@ -9854,10 +9889,10 @@ 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);
- btrfs_set_file_extent_ram_bytes(leaf, ei, name_len);
+ btrfs_set_file_extent_ram_bytes(leaf, ei, disk_link.len - 1);
ptr = btrfs_file_extent_inline_start(ei);
- write_extent_buffer(leaf, symname, ptr, name_len);
+ write_extent_buffer(leaf, disk_link.name, ptr, disk_link.len - 1);
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
@@ -9874,6 +9909,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,
@@ -11445,7 +11503,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,
@@ -11455,4 +11513,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 d54bfec8e506..1b32103b14d5 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -48,6 +48,7 @@
#include "tests/btrfs-tests.h"
#include "block-group.h"
#include "discard.h"
+#include "fscrypt.h"
#include "qgroup.h"
#include "raid56.h"
#include "fs.h"
@@ -1135,6 +1136,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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 09/18] btrfs: add fscrypt_context items
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (7 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 08/18] btrfs: start using fscrypt hooks Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:52 ` [PATCH v5 10/18] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
` (9 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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 | 1 +
fs/btrfs/fscrypt.c | 184 ++++++++++++++++++++++++++++++++
fs/btrfs/inode.c | 39 +++++++
fs/btrfs/ioctl.c | 7 +-
fs/btrfs/tree-checker.c | 1 +
include/uapi/linux/btrfs_tree.h | 12 +++
6 files changed, 243 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 5649f8907984..be628dd3ecd4 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -32,6 +32,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"
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 48ab99dfe48d..babaa0fffed4 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -1,7 +1,191 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/iversion.h>
#include "ctree.h"
+#include "accessors.h"
+#include "btrfs_inode.h"
+#include "disk-io.h"
+#include "fs.h"
#include "fscrypt.h"
+#include "messages.h"
+#include "transaction.h"
+#include "xattr.h"
+
+static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_key key = {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
+ .offset = 0,
+ };
+ struct inode *put_inode = NULL;
+ 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;
+
+ 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 void btrfs_fscrypt_update_context(struct btrfs_path *path,
+ const void *ctx, size_t len)
+{
+ struct extent_buffer *leaf = path->nodes[0];
+ unsigned long ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
+
+ len = min_t(size_t, len, btrfs_item_size(leaf, path->slots[0]));
+ write_extent_buffer(leaf, ctx, ptr, len);
+ btrfs_mark_buffer_dirty(leaf);
+}
+
+static int __btrfs_fscrypt_set_context(struct inode *inode,
+ struct btrfs_trans_handle *trans,
+ const void *ctx, size_t len)
+{
+ struct btrfs_path *path;
+ int ret;
+ struct btrfs_key key = {
+ .objectid = btrfs_ino(BTRFS_I(inode)),
+ .type = BTRFS_FSCRYPT_CTXT_ITEM_KEY,
+ .offset = 0,
+ };
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
+ if (ret == 0) {
+ btrfs_fscrypt_update_context(path, ctx, len);
+ btrfs_free_path(path);
+ return ret;
+ }
+
+ btrfs_free_path(path);
+ if (ret < 0)
+ return ret;
+
+ ret = btrfs_insert_item(trans, BTRFS_I(inode)->root, &key, (void *) ctx, len);
+ if (ret)
+ return ret;
+
+ 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, BTRFS_I(inode)->root, BTRFS_I(inode));
+ if (!ret)
+ return ret;
+
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+}
+
+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;
+
+ /*
+ * 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);
+ ret = fscrypt_have_same_policy(inode, root_inode, &same_policy);
+ iput(root_inode);
+
+ if (ret)
+ return ret;
+ 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.
+ */
+ return __btrfs_fscrypt_set_context(inode, fs_data, ctx, len);
+ }
+
+ /*
+ * 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);
+
+ ret = __btrfs_fscrypt_set_context(inode, trans, ctx, len);
+
+ /*
+ * For new subvolumes, the root item is already initialized with
+ * the BTRFS_ROOT_SUBVOL_FSCRYPT flag.
+ */
+ if (!ret && 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);
+ }
+
+ btrfs_end_transaction(trans);
+ return ret;
+}
+
+static bool btrfs_fscrypt_empty_dir(struct inode *inode)
+{
+ return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
+}
const struct fscrypt_operations btrfs_fscrypt_ops = {
+ .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 6904b2228112..1be0b53abc83 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6297,6 +6297,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);
@@ -6330,6 +6358,9 @@ int btrfs_new_inode_prepare(struct btrfs_new_inode_args *args,
if (dir->i_security)
(*trans_num_items)++;
#endif
+ /* 1 to add fscrypt item */
+ if (args->encrypt)
+ (*trans_num_items)++;
if (args->orphan) {
/* 1 to add orphan item */
(*trans_num_items)++;
@@ -6583,6 +6614,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 9c1cb5113178..5bb34da6ee25 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -669,7 +669,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);
@@ -798,6 +799,10 @@ 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))
+ 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 1c2d418dda6a..77e678efef65 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1123,6 +1123,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 29895ffa470d..52641fdc65bc 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -161,6 +161,8 @@
#define BTRFS_VERITY_DESC_ITEM_KEY 36
#define BTRFS_VERITY_MERKLE_ITEM_KEY 37
+#define BTRFS_FSCRYPT_CTXT_ITEM_KEY 41
+
#define BTRFS_ORPHAN_ITEM_KEY 48
/* reserve 2-15 close to the inode for later flexibility */
@@ -399,6 +401,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags)
#define BTRFS_INODE_NOATIME (1U << 9)
#define BTRFS_INODE_DIRSYNC (1U << 10)
#define BTRFS_INODE_COMPRESS (1U << 11)
+#define BTRFS_INODE_FSCRYPT_CONTEXT (1U << 12)
#define BTRFS_INODE_ROOT_ITEM_INIT (1U << 31)
@@ -415,6 +418,7 @@ static inline __u8 btrfs_dir_flags_to_ftype(__u8 flags)
BTRFS_INODE_NOATIME | \
BTRFS_INODE_DIRSYNC | \
BTRFS_INODE_COMPRESS | \
+ BTRFS_INODE_FSCRYPT_CONTEXT | \
BTRFS_INODE_ROOT_ITEM_INIT)
#define BTRFS_INODE_RO_VERITY (1U << 0)
@@ -860,6 +864,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
@@ -1015,6 +1021,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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 10/18] btrfs: translate btrfs encryption flags and encrypted inode flag
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (8 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 09/18] btrfs: add fscrypt_context items Sweet Tea Dorminy
@ 2022-11-02 11:52 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 11/18] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
` (8 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:52 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>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
---
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 5bb34da6ee25..a40025e18216 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>
@@ -158,6 +159,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;
@@ -187,10 +192,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);
}
/*
@@ -203,7 +212,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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 11/18] btrfs: store a fscrypt extent context per normal file extent
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (9 preceding siblings ...)
2022-11-02 11:52 ` [PATCH v5 10/18] btrfs: translate btrfs encryption flags and encrypted inode flag Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 12/18] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
` (7 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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.
This puts the long-preserved 1-byte encryption field to work. Right now we
don't anticipate very many encryption policies, so 2 bits should be
ample; similarly right now we can't imagine a extent context larger than
fscrypt's current inode contexts, which are 40 bytes, so 6 bits is ample
to store the extent context's size; and therefore we can pack these
together into the one-byte encryption field without touching other space
reserved for future use.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/accessors.h | 29 +++++++++++
fs/btrfs/ctree.h | 3 ++
fs/btrfs/extent_map.c | 7 +++
fs/btrfs/extent_map.h | 4 ++
fs/btrfs/file-item.c | 16 ++++++
fs/btrfs/file.c | 4 +-
fs/btrfs/fscrypt.c | 22 ++++++++
fs/btrfs/fscrypt.h | 35 +++++++++++++
fs/btrfs/inode.c | 90 ++++++++++++++++++++++++++-------
fs/btrfs/ordered-data.c | 11 +++-
fs/btrfs/ordered-data.h | 4 +-
fs/btrfs/reflink.c | 1 +
fs/btrfs/tree-checker.c | 37 +++++++++++---
fs/btrfs/tree-log.c | 13 ++++-
include/uapi/linux/btrfs_tree.h | 9 ++++
15 files changed, 252 insertions(+), 33 deletions(-)
diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index cb59b69d2af1..8cc06c2abb6a 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -944,6 +944,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)
@@ -976,6 +986,25 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);
+static inline u8
+btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
+ struct btrfs_file_extent_item *e)
+{
+ u8 ctxsize;
+
+ btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e),
+ NULL, &ctxsize);
+ return ctxsize;
+}
+
+static inline u8
+btrfs_file_extent_ctxsize_from_item(const struct extent_buffer *leaf,
+ const struct btrfs_path *path)
+{
+ return (btrfs_item_size(leaf, path->slots[0]) -
+ sizeof(struct btrfs_file_extent_item));
+}
+
/*
* 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 the compressed
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index be628dd3ecd4..79d1e5d41209 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -38,6 +38,7 @@
#include "locking.h"
#include "misc.h"
#include "fs.h"
+#include "fscrypt.h"
struct btrfs_trans_handle;
struct btrfs_transaction;
@@ -348,6 +349,8 @@ struct btrfs_replace_extent_info {
u64 file_offset;
/* Pointer to a file extent item of type regular or prealloc. */
char *extent_buf;
+ /* The length of @extent_buf */
+ u32 extent_buf_size;
/*
* Set to true when attempting to replace a file range with a new extent
* described by this structure, set to false when attempting to clone an
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 4a4362f5cc52..849c253650cb 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -207,6 +207,13 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
if (!list_empty(&prev->list) || !list_empty(&next->list))
return 0;
+ /*
+ * Don't merge adjacent maps with different fscrypt_contexts.
+ */
+ if (!memcmp(&prev->fscrypt_context, &next->fscrypt_context,
+ sizeof(next->fscrypt_context)))
+ return 0;
+
ASSERT(next->block_start != EXTENT_MAP_DELALLOC &&
prev->block_start != EXTENT_MAP_DELALLOC);
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 68d3f2c9ea1d..3fa70d6b4750 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 456f71b42a9c..36b7e2a8d698 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -1233,6 +1233,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 -
@@ -1248,6 +1249,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;
@@ -1255,6 +1260,17 @@ 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));
+
+#ifdef CONFIG_FS_ENCRYPTION
+ em->fscrypt_context.len = ctxsize;
+
+ read_extent_buffer(leaf, em->fscrypt_context.buffer,
+ (unsigned long)fi->fscrypt_context,
+ ctxsize);
+#endif /* CONFIG_FS_ENCRYPTION */
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
em->block_start = EXTENT_MAP_INLINE;
em->start = extent_start;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 2a9808f0c012..2d9c001c63aa 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2246,14 +2246,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = extent_info->file_offset;
ret = btrfs_insert_empty_item(trans, root, path, &key,
- sizeof(struct btrfs_file_extent_item));
+ extent_info->extent_buf_size);
if (ret)
return ret;
leaf = path->nodes[0];
slot = path->slots[0];
write_extent_buffer(leaf, extent_info->extent_buf,
btrfs_item_ptr_offset(leaf, slot),
- sizeof(struct btrfs_file_extent_item));
+ extent_info->extent_buf_size);
extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
ASSERT(btrfs_file_extent_type(leaf, extent) != BTRFS_FILE_EXTENT_INLINE);
btrfs_set_file_extent_offset(leaf, extent, extent_info->data_offset);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index babaa0fffed4..2c9f2d84b539 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -10,6 +10,7 @@
#include "messages.h"
#include "transaction.h"
#include "xattr.h"
+#include "fscrypt.h"
static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
@@ -183,9 +184,30 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
}
+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 = {
.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 7f4e6888bd43..86dc0e0b91b9 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -5,6 +5,41 @@
#include <linux/fscrypt.h>
+#define BTRFS_ENCRYPTION_POLICY_BITS 2
+#define BTRFS_ENCRYPTION_CTXSIZE_BITS 6
+
+#define BTRFS_ENCRYPTION_POLICY_MASK ((1 << BTRFS_ENCRYPTION_POLICY_BITS) - 1)
+#define BTRFS_ENCRYPTION_CTXSIZE_MASK \
+ (((1 << BTRFS_ENCRYPTION_CTXSIZE_BITS) - 1) << \
+ BTRFS_ENCRYPTION_POLICY_BITS)
+
+#ifdef CONFIG_FS_ENCRYPTION
+struct btrfs_fscrypt_extent_context {
+ u8 buffer[FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ u8 len;
+};
+#else
+struct btrfs_fscrypt_extent_context {
+ u8 len;
+};
+#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) >>
+ BTRFS_ENCRYPTION_POLICY_BITS);
+}
+
+static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize)
+{
+ return policy | (ctxsize << BTRFS_ENCRYPTION_POLICY_BITS);
+}
+
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 1be0b53abc83..332900b41488 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1051,7 +1051,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 */
@@ -1060,7 +1059,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_map_range(inode, start, end, false);
goto out_free_reserve;
@@ -1332,12 +1333,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;
@@ -2131,14 +2133,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_map_range(inode, cur_offset,
nocow_end, false);
@@ -2152,7 +2155,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;
}
@@ -3084,6 +3088,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)
{
@@ -3098,6 +3103,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();
@@ -3117,7 +3123,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;
@@ -3128,7 +3134,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;
}
@@ -3137,6 +3143,13 @@ 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));
+#ifdef CONFIG_FS_ENCRYPTION
+ 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);
+#endif /* CONFIG_FS_ENCRYPTION */
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
@@ -3213,7 +3226,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
@@ -3227,6 +3245,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);
}
@@ -7133,8 +7152,24 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
btrfs_extent_item_to_extent_map(inode, path, item, em);
- if (extent_type == BTRFS_FILE_EXTENT_REG ||
- extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
+ if (extent_type == BTRFS_FILE_EXTENT_REG) {
+ u8 item_ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
+ u8 encryption = btrfs_file_extent_encryption(leaf, item);
+ u8 policy, ctxsize;
+
+ btrfs_unpack_encryption(encryption, &policy, &ctxsize);
+
+ if (policy == BTRFS_ENCRYPTION_FSCRYPT) {
+ if (ctxsize != item_ctxsize) {
+ btrfs_crit(fs_info,
+ "invalid encryption context size for inode %llu: itemsize %d item %d",
+ btrfs_ino(inode), ctxsize, item_ctxsize);
+ ret = -EUCLEAN;
+ goto out;
+ }
+ }
+ goto insert;
+ } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
goto insert;
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
/*
@@ -7216,7 +7251,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);
@@ -7491,6 +7527,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
int type)
{
struct extent_map *em;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
int ret;
ASSERT(type == BTRFS_ORDERED_PREALLOC ||
@@ -7518,6 +7555,16 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
em->compress_type = compress_type;
}
+ if (IS_ENCRYPTED(&inode->vfs_inode)) {
+ u64 lblk = em->start >> fs_info->sectorsize_bits;
+ ret = fscrypt_set_extent_context(&inode->vfs_inode, lblk,
+ &em->fscrypt_context);
+ if (ret < 0) {
+ free_extent_map(em);
+ return ERR_PTR(ret);
+ }
+ }
+
ret = btrfs_replace_extent_map_range(inode, em, true);
if (ret) {
free_extent_map(em);
@@ -9847,6 +9894,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 NUL terminator, but we
@@ -9994,16 +10042,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;
@@ -10015,6 +10065,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;
@@ -10945,14 +10996,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_map_range(inode, start, end, false);
goto out_free_reserved;
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 8fda1949b71b..c5df41843cce 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -167,7 +167,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;
@@ -202,6 +203,11 @@ 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;
+#ifdef CONFIG_FS_ENCRYPTION
+ if (fscrypt_context && fscrypt_context->len)
+ memcpy(&entry->fscrypt_context, fscrypt_context,
+ sizeof(*fscrypt_context));
+#endif /* CONFIG_FS_ENCRYPTION */
entry->bytes_left = num_bytes;
entry->inode = igrab(&inode->vfs_inode);
entry->compress_type = compress_type;
@@ -1110,7 +1116,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 89f82b78f590..a25c63dea8e0 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;
@@ -182,7 +183,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 579dbf057ffa..4b09d559cc8f 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -501,6 +501,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 77e678efef65..e72c8176f7bc 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -207,6 +207,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,
@@ -258,10 +259,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) {
@@ -290,12 +293,30 @@ static int check_extent_data_item(struct extent_buffer *leaf,
return 0;
}
- /* Regular or preallocated extent has fixed item size */
- if (unlikely(item_size != sizeof(*fi))) {
- file_extent_err(leaf, slot,
+ if (policy == BTRFS_ENCRYPTION_FSCRYPT) {
+ u8 ctxsize = btrfs_file_extent_encryption_ctxsize(leaf, fi);
+
+ if (unlikely(item_size != sizeof(*fi) + ctxsize)) {
+ file_extent_err(leaf, slot,
+ "invalid item size for encrypted file extent, have %u expect = %zu + context of size %u",
+ item_size, sizeof(*fi), ctxsize);
+ return -EUCLEAN;
+ }
+ /* Only regular extents should be encrypted. */
+ if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) {
+ file_extent_err(leaf, slot,
+ "invalid type for encrypted file extent, have %u expect %u",
+ btrfs_file_extent_type(leaf, fi),
+ BTRFS_FILE_EXTENT_REG);
+ return -EUCLEAN;
+ }
+ } else {
+ if (unlikely(item_size != sizeof(*fi))) {
+ file_extent_err(leaf, slot,
"invalid item size for reg/prealloc file extent, have %u expect %zu",
- item_size, sizeof(*fi));
- return -EUCLEAN;
+ item_size, sizeof(*fi));
+ return -EUCLEAN;
+ }
}
if (unlikely(CHECK_FE_ALIGNED(leaf, slot, fi, ram_bytes, sectorsize) ||
CHECK_FE_ALIGNED(leaf, slot, fi, disk_bytenr, sectorsize) ||
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 8d559b076405..9c211802efb1 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -4610,6 +4610,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))
@@ -4631,6 +4634,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)
@@ -4662,7 +4666,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;
}
@@ -4670,6 +4675,12 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
write_extent_buffer(leaf, &fi,
btrfs_item_ptr_offset(leaf, path->slots[0]),
sizeof(fi));
+#ifdef CONFIG_FS_ENCRYPTION
+ write_extent_buffer(leaf, &em->fscrypt_context.buffer,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(fi), em->fscrypt_context.len);
+#endif /* CONFIG_FS_ENCRYPTION */
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index 52641fdc65bc..4c0f77f3cfa9 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -1049,6 +1049,10 @@ struct btrfs_file_extent_item {
* but not for stat.
*/
__u8 compression;
+ /*
+ * This field contains 2 bits of encryption type in the lower bits,
+ * 6 bits of context size in the upper bits. The unencrypted value is 0.
+ */
__u8 encryption;
__le16 other_encoding; /* spare for later use */
@@ -1077,6 +1081,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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 12/18] btrfs: encrypt normal file extent data if appropriate
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (10 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 11/18] btrfs: store a fscrypt extent context per normal file extent Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 13/18] btrfs: Add new FEATURE_INCOMPAT_ENCRYPT feature flag Sweet Tea Dorminy
` (6 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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.
Since these are all page cache pages being encrypted, we can't encrypt
them in place and must encrypt/decrypt into a new page. fscrypt provides a pool
of pages for this purpose, which it calls bounce pages. For IO of
encrypted data, we use a bounce page for the actual IO, and
encrypt/decrypt from/to the actual page cache page on either side of the
IO.
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 | 33 +++++++++++++++++++++++-
fs/btrfs/tree-checker.c | 11 +++++---
4 files changed, 97 insertions(+), 12 deletions(-)
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 2ec989b83f54..97aaa74c4822 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -117,6 +117,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;
@@ -125,13 +126,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);
@@ -1018,9 +1023,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))
@@ -1041,7 +1056,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);
}
@@ -1233,6 +1248,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;
@@ -1567,11 +1593,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);
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 36b7e2a8d698..2faac05ce137 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -681,8 +681,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 2c9f2d84b539..661fe7cc119c 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -190,7 +190,38 @@ 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;
+ int ret;
+
+ /* Since IO must be in progress on this extent, this must succeed */
+ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, PAGE_SIZE);
+ if (!em)
+ return -EINVAL;
+
+ if (em->block_start == EXTENT_MAP_HOLE) {
+ btrfs_info(BTRFS_I(inode)->root->fs_info,
+ "extent context requested for block %llu of inode %lu without an extent",
+ lblk_num, inode->i_ino);
+ free_extent_map(em);
+ return -ENOENT;
+ }
+
+ ret = ctx ? em->fscrypt_context.len : 0;
+
+ 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;
}
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 e72c8176f7bc..84bdd6ca3d2b 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -276,9 +276,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.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 13/18] btrfs: Add new FEATURE_INCOMPAT_ENCRYPT feature flag.
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (11 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 12/18] btrfs: encrypt normal file extent data if appropriate Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 14/18] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
` (5 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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 encrypted files will be incompatible with older filesystem versions,
new filesystems should be created with an incompat flag for fscrypt,
which will gate access to the encryption ioctls.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/fs.h | 5 +++--
fs/btrfs/super.c | 5 +++++
include/uapi/linux/btrfs.h | 1 +
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index c7f2a512fba2..b12146d34dc5 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -185,7 +185,7 @@ enum {
#ifdef CONFIG_BTRFS_DEBUG
/*
- * Extent tree v2 supported only with CONFIG_BTRFS_DEBUG
+ * Extent tree v2 and encryption supported only with CONFIG_BTRFS_DEBUG
*/
#define BTRFS_FEATURE_INCOMPAT_SUPP \
(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
@@ -201,7 +201,8 @@ enum {
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_ENCRYPT)
#else
#define BTRFS_FEATURE_INCOMPAT_SUPP \
(BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 1b32103b14d5..a1e6b2446749 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2411,6 +2411,11 @@ static int __init btrfs_print_mod_info(void)
", fsverity=yes"
#else
", fsverity=no"
+#endif
+#ifdef CONFIG_FS_ENCRYPTION
+ ", fscrypt=yes"
+#else
+ ", fscrypt=no"
#endif
;
pr_info("Btrfs loaded, crc32c=%s%s\n", crc32c_impl(), options);
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 5655e89b962b..1d29f0df995b 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_ENCRYPT (1ULL << 14)
struct btrfs_ioctl_feature_flags {
__u64 compat_flags;
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 14/18] btrfs: implement fscrypt ioctls
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (12 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 13/18] btrfs: Add new FEATURE_INCOMPAT_ENCRYPT feature flag Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 15/18] btrfs: permit searching for nokey names for removal Sweet Tea Dorminy
` (4 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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 actually be used.
The set_encryption_policy ioctl is the thing which actually turns on
encryption, and therefore sets the ENCRYPT flag in the superblock. This
prevents the filesystem from being loaded on older kernels.
fscrypt provides CONFIG_FS_ENCRYPTION-disabled versions of all these
functions which just return -EOPNOTSUPP, so the ioctls don't need to be
compiled out if CONFIG_FS_ENCRYPTION isn't enabled.
We could instead gate this ioctl on the superblock having the flag set,
if we wanted to require mkfs with the encrypt flag in order to have a
filesystem with any encryption.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/ioctl.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index a40025e18216..9869a26e36ad 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4557,6 +4557,34 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_get_fslabel(fs_info, argp);
case FS_IOC_SETFSLABEL:
return btrfs_ioctl_set_fslabel(file, argp);
+ case FS_IOC_SET_ENCRYPTION_POLICY: {
+ if (!IS_ENABLED(CONFIG_FS_ENCRYPTION))
+ return -EOPNOTSUPP;
+ if (sb_rdonly(fs_info->sb))
+ return -EROFS;
+ /*
+ * If we crash before we commit, nothing encrypted could have
+ * been written so it doesn't matter whether the encrypted
+ * state persists.
+ */
+ btrfs_set_fs_incompat(fs_info, ENCRYPT);
+ return fscrypt_ioctl_set_policy(file, (const void __user *)arg);
+ }
+ case FS_IOC_GET_ENCRYPTION_POLICY:
+ return fscrypt_ioctl_get_policy(file, (void __user *)arg);
+ case FS_IOC_GET_ENCRYPTION_POLICY_EX:
+ return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
+ case FS_IOC_ADD_ENCRYPTION_KEY:
+ return fscrypt_ioctl_add_key(file, (void __user *)arg);
+ case FS_IOC_REMOVE_ENCRYPTION_KEY:
+ return fscrypt_ioctl_remove_key(file, (void __user *)arg);
+ case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
+ return fscrypt_ioctl_remove_key_all_users(file,
+ (void __user *)arg);
+ case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
+ return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
+ case FS_IOC_GET_ENCRYPTION_NONCE:
+ return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
case FITRIM:
return btrfs_ioctl_fitrim(fs_info, argp);
case BTRFS_IOC_SNAP_CREATE:
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 15/18] btrfs: permit searching for nokey names for removal
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (13 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 14/18] btrfs: implement fscrypt ioctls Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 16/18] btrfs: use correct name hash for nokey names Sweet Tea Dorminy
` (3 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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>
Deleting an encrypted file must always be permitted, even if the user
does not have the appropriate key. Therefore, for listing an encrypted
directory, so-called 'nokey' names are provided, and these nokey names
must be sufficient to look up and delete the appropriate encrypted
files. See 'struct fscrypt_nokey_name' for more information on the
format of these names.
The first part of supporting nokey names is allowing lookups by nokey
name. Only a few entry points need to support these: deleting a
directory, file, or subvolume -- each of these call
fscrypt_setup_filename() with a '1' argument, indicating that the key is
not required and therefore a nokey name may be provided. If a nokey name
is provided, the fscrypt_name returned by fscrypt_setup_filename() will
not have its disk_name field populated, but will have various other
fields set.
This change alters the relevant codepaths to pass a complete
fscrypt_name anywhere that it might contain a nokey name. When it does
contain a nokey name, the first time the name is successfully matched to
a stored name populates the disk name field of the fscrypt_name,
allowing the caller to use the normal disk name codepaths afterward.
Otherwise, the matching functionality is in close analogue to the
function fscrypt_match_name().
Functions where most callers are providing a fscrypt_str are duplicated
and adapted for a fscrypt_name, and functions where most callers are
providing a fscrypt_name are changed to so require at all callsites.
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/btrfs_inode.h | 2 +-
fs/btrfs/delayed-inode.c | 30 ++++++++-
fs/btrfs/delayed-inode.h | 4 +-
fs/btrfs/dir-item.c | 77 ++++++++++++++++++---
fs/btrfs/dir-item.h | 13 +++-
fs/btrfs/extent_io.c | 38 +++++++++++
fs/btrfs/extent_io.h | 3 +
fs/btrfs/fscrypt.c | 47 ++++++++++++-
fs/btrfs/fscrypt.h | 19 ++++++
fs/btrfs/inode.c | 141 ++++++++++++++++++++++++++-------------
fs/btrfs/root-tree.c | 8 ++-
fs/btrfs/root-tree.h | 2 +-
fs/btrfs/tree-log.c | 3 +-
13 files changed, 319 insertions(+), 68 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 32fa68946f07..f0935a95ec70 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -435,7 +435,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index);
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *dir, struct btrfs_inode *inode,
- const struct fscrypt_str *name);
+ struct fscrypt_name *name);
int btrfs_add_link(struct btrfs_trans_handle *trans,
struct btrfs_inode *parent_inode, struct btrfs_inode *inode,
const struct fscrypt_str *name, int add_backref, u64 index);
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index c024f97de9e0..f775e1111d3e 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1497,6 +1497,7 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans,
ret = __btrfs_add_delayed_item(delayed_node, delayed_item);
if (unlikely(ret)) {
+ // TODO: It would be nice to print the base64encoded name here maybe?
btrfs_err(trans->fs_info,
"err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)",
name_len, name, delayed_node->root->root_key.objectid,
@@ -1724,7 +1725,9 @@ int btrfs_should_delete_dir_index(struct list_head *del_list,
* btrfs_readdir_delayed_dir_index - read dir info stored in the delayed tree
*
*/
-int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
+int btrfs_readdir_delayed_dir_index(struct inode *inode,
+ struct fscrypt_str *fstr,
+ struct dir_context *ctx,
struct list_head *ins_list)
{
struct btrfs_dir_item *di;
@@ -1734,6 +1737,7 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
int name_len;
int over = 0;
unsigned char d_type;
+ size_t fstr_len = fstr->len;
if (list_empty(ins_list))
return 0;
@@ -1761,8 +1765,28 @@ int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
d_type = fs_ftype_to_dtype(btrfs_dir_flags_to_ftype(di->type));
btrfs_disk_key_to_cpu(&location, &di->location);
- over = !dir_emit(ctx, name, name_len,
- location.objectid, d_type);
+ if (di->type & BTRFS_FT_ENCRYPTED) {
+ int ret;
+ struct fscrypt_str iname = FSTR_INIT(name, name_len);
+
+ fstr->len = fstr_len;
+ /*
+ * The hash is only used when the encryption key is not
+ * available. But if we have delayed insertions, then we
+ * must have the encryption key available or we wouldn't
+ * have been able to create entries in the directory.
+ * So, we don't calculate the hash.
+ */
+ ret = fscrypt_fname_disk_to_usr(inode, 0, 0, &iname,
+ fstr);
+ if (ret)
+ return ret;
+ over = !dir_emit(ctx, fstr->name, fstr->len,
+ location.objectid, d_type);
+ } else {
+ over = !dir_emit(ctx, name, name_len, location.objectid,
+ d_type);
+ }
if (refcount_dec_and_test(&curr->refs))
kfree(curr);
diff --git a/fs/btrfs/delayed-inode.h b/fs/btrfs/delayed-inode.h
index 4f21daa3dbc7..a4f9fa27b126 100644
--- a/fs/btrfs/delayed-inode.h
+++ b/fs/btrfs/delayed-inode.h
@@ -155,7 +155,9 @@ void btrfs_readdir_put_delayed_items(struct inode *inode,
struct list_head *del_list);
int btrfs_should_delete_dir_index(struct list_head *del_list,
u64 index);
-int btrfs_readdir_delayed_dir_index(struct dir_context *ctx,
+int btrfs_readdir_delayed_dir_index(struct inode *inode,
+ struct fscrypt_str *fstr,
+ struct dir_context *ctx,
struct list_head *ins_list);
/* Used during directory logging. */
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 082eb0e19598..ce046cdabbe9 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -6,6 +6,7 @@
#include "messages.h"
#include "ctree.h"
#include "disk-io.h"
+#include "fscrypt.h"
#include "transaction.h"
#include "accessors.h"
#include "dir-item.h"
@@ -230,6 +231,47 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
return di;
}
+/*
+ * Lookup for a directory item by fscrypt_name.
+ *
+ * @trans: The transaction handle to use.
+ * @root: The root of the target tree.
+ * @path: Path to use for the search.
+ * @dir: The inode number (objectid) of the directory.
+ * @name: The fscrypt_name associated to the directory entry
+ * @mod: Used to indicate if the tree search is meant for a read only
+ * lookup or for a deletion lookup, so its value should be 0 or
+ * -1, respectively.
+ *
+ * Returns: NULL if the dir item does not exists, an error pointer if an error
+ * happened, or a pointer to a dir item if a dir item exists for the given name.
+ */
+struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+ struct fscrypt_name *name, int mod)
+{
+ struct btrfs_key key;
+ struct btrfs_dir_item *di;
+ int ret = 0;
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
+ /* XXX get the right hash for no-key names */
+
+ ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
+ if (ret == 0)
+ di = btrfs_match_dir_item_fname(root->fs_info, path, name);
+
+ if (ret == -ENOENT || (IS_ERR(di) && PTR_ERR(di) == -ENOENT))
+ return NULL;
+ if (ret < 0)
+ di = ERR_PTR(ret);
+
+ return di;
+}
+
int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
const struct fscrypt_str *name)
{
@@ -287,9 +329,9 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
}
/*
- * Lookup for a directory index item by name and index number.
+ * Lookup for a directory index item by fscrypt_name and index number.
*
- * @trans: The transaction handle to use. Can be NULL if @mod is 0.
+ * @trans: The transaction handle to use.
* @root: The root of the target tree.
* @path: Path to use for the search.
* @dir: The inode number (objectid) of the directory.
@@ -327,7 +369,7 @@ btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
struct btrfs_dir_item *
btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
- u64 dirid, const struct fscrypt_str *name)
+ u64 dirid, struct fscrypt_name *name)
{
struct btrfs_dir_item *di;
struct btrfs_key key;
@@ -340,9 +382,7 @@ btrfs_search_dir_index_item(struct btrfs_root *root, struct btrfs_path *path,
btrfs_for_each_slot(root, &key, &key, path, ret) {
if (key.objectid != dirid || key.type != BTRFS_DIR_INDEX_KEY)
break;
-
- di = btrfs_match_dir_item_name(root->fs_info, path,
- name->name, name->len);
+ di = btrfs_match_dir_item_fname(root->fs_info, path, name);
if (di)
return di;
}
@@ -378,9 +418,9 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
* this walks through all the entries in a dir item and finds one
* for a specific name.
*/
-struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
- struct btrfs_path *path,
- const char *name, int name_len)
+struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path,
+ struct fscrypt_name *name)
{
struct btrfs_dir_item *dir_item;
unsigned long name_ptr;
@@ -399,8 +439,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
btrfs_dir_data_len(leaf, dir_item);
name_ptr = (unsigned long)(dir_item + 1);
- if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
- memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
+ if (btrfs_fscrypt_match_name(name, leaf, name_ptr,
+ btrfs_dir_name_len(leaf, dir_item)))
return dir_item;
cur += this_len;
@@ -410,6 +450,21 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
return NULL;
}
+/*
+ * helper function to look at the directory item pointed to by 'path'
+ * this walks through all the entries in a dir item and finds one
+ * for a specific name.
+ */
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path,
+ const char *name, int name_len)
+{
+ struct fscrypt_name fname = {
+ .disk_name = FSTR_INIT((char *) name, name_len)
+ };
+ return btrfs_match_dir_item_fname(fs_info, path, &fname);
+}
+
/*
* given a pointer into a directory item, delete it. This
* handles items that have more than one entry in them.
diff --git a/fs/btrfs/dir-item.h b/fs/btrfs/dir-item.h
index aab4b7cc7fa0..4f42a4f6a4ec 100644
--- a/fs/btrfs/dir-item.h
+++ b/fs/btrfs/dir-item.h
@@ -3,6 +3,8 @@
#ifndef BTRFS_DIR_ITEM_H
#define BTRFS_DIR_ITEM_H
+#include <linux/fscrypt.h>
+
int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
const struct fscrypt_str *name);
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
@@ -12,6 +14,11 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 dir,
const struct fscrypt_str *name, int mod);
+struct btrfs_dir_item *btrfs_lookup_dir_item_fname(
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+ struct fscrypt_name *name, int mod);
struct btrfs_dir_item *btrfs_lookup_dir_index_item(
struct btrfs_trans_handle *trans,
struct btrfs_root *root,
@@ -19,7 +26,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index_item(
u64 index, const struct fscrypt_str *name, int mod);
struct btrfs_dir_item *btrfs_search_dir_index_item(struct btrfs_root *root,
struct btrfs_path *path, u64 dirid,
- const struct fscrypt_str *name);
+ struct fscrypt_name *name);
int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
@@ -39,4 +46,8 @@ struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_fs_info *fs_info,
const char *name,
int name_len);
+struct btrfs_dir_item *btrfs_match_dir_item_fname(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path,
+ struct fscrypt_name *name);
+
#endif
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 97aaa74c4822..a0450e4401e9 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -5329,6 +5329,44 @@ static void assert_eb_page_uptodate(const struct extent_buffer *eb,
}
}
+/* Take a sha256 of a portion of an extent buffer. */
+void extent_buffer_sha256(const struct extent_buffer *eb,
+ unsigned long start,
+ unsigned long len, u8 *out)
+{
+ size_t cur;
+ size_t offset;
+ struct page *page;
+ char *kaddr;
+ unsigned long i = get_eb_page_index(start);
+ struct sha256_state sctx;
+
+ if (check_eb_range(eb, start, len))
+ return;
+
+ offset = get_eb_offset_in_page(eb, start);
+
+ /*
+ * TODO: This should maybe be using the crypto API, not the fallback,
+ * but fscrypt uses the fallback and this is only used in emulation of
+ * fscrypt's buffer sha256 method.
+ */
+ sha256_init(&sctx);
+ while (len > 0) {
+ page = eb->pages[i];
+ assert_eb_page_uptodate(eb, page);
+
+ cur = min(len, PAGE_SIZE - offset);
+ kaddr = page_address(page);
+ sha256_update(&sctx, (u8 *)(kaddr + offset), cur);
+
+ len -= cur;
+ offset = 0;
+ i++;
+ }
+ sha256_final(&sctx, out);
+}
+
void write_extent_buffer_chunk_tree_uuid(const struct extent_buffer *eb,
const void *srcv)
{
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index a5ec1475988f..0a82604b0feb 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -203,6 +203,9 @@ static inline int extent_buffer_uptodate(const struct extent_buffer *eb)
int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv,
unsigned long start, unsigned long len);
+void extent_buffer_sha256(const struct extent_buffer *eb,
+ unsigned long start,
+ unsigned long len, u8 *out);
void read_extent_buffer(const struct extent_buffer *eb, void *dst,
unsigned long start,
unsigned long len);
diff --git a/fs/btrfs/fscrypt.c b/fs/btrfs/fscrypt.c
index 661fe7cc119c..6f4c843720c1 100644
--- a/fs/btrfs/fscrypt.c
+++ b/fs/btrfs/fscrypt.c
@@ -5,12 +5,57 @@
#include "accessors.h"
#include "btrfs_inode.h"
#include "disk-io.h"
+#include "ioctl.h"
#include "fs.h"
#include "fscrypt.h"
#include "messages.h"
#include "transaction.h"
#include "xattr.h"
-#include "fscrypt.h"
+#include "root-tree.h"
+
+/*
+ * This function is extremely similar to fscrypt_match_name() but uses an
+ * extent_buffer. Also, it edits the provided argument to populate the disk_name
+ * if we successfully match and previously were using a nokey name.
+ */
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf, unsigned long de_name,
+ u32 de_name_len)
+{
+ const struct fscrypt_nokey_name *nokey_name =
+ (const void *)fname->crypto_buf.name;
+ u8 digest[SHA256_DIGEST_SIZE];
+
+ if (likely(fname->disk_name.name)) {
+ if (de_name_len != fname->disk_name.len)
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name,
+ de_name, de_name_len);
+ }
+ if (de_name_len <= sizeof(nokey_name->bytes))
+ return false;
+ if (memcmp_extent_buffer(leaf, nokey_name->bytes, de_name,
+ sizeof(nokey_name->bytes)))
+ return false;
+ extent_buffer_sha256(leaf, de_name + sizeof(nokey_name->bytes),
+ de_name_len - sizeof(nokey_name->bytes), digest);
+ if (!memcmp(digest, nokey_name->sha256, sizeof(digest))) {
+ /*
+ * For no-key names, we use this opportunity to find the disk
+ * name, so future searches don't need to deal with nokey names
+ * and we know what the encrypted size is.
+ */
+ fname->disk_name.name = kmalloc(de_name_len, GFP_KERNEL | GFP_NOFS);
+ if (!fname->disk_name.name)
+ fname->disk_name.name = ERR_PTR(-ENOMEM);
+ else
+ read_extent_buffer(leaf, fname->disk_name.name,
+ de_name, de_name_len);
+ fname->disk_name.len = de_name_len;
+ return true;
+ }
+ return false;
+}
static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
diff --git a/fs/btrfs/fscrypt.h b/fs/btrfs/fscrypt.h
index 86dc0e0b91b9..65c082e91f65 100644
--- a/fs/btrfs/fscrypt.h
+++ b/fs/btrfs/fscrypt.h
@@ -4,6 +4,7 @@
#define BTRFS_FSCRYPT_H
#include <linux/fscrypt.h>
+#include "extent_io.h"
#define BTRFS_ENCRYPTION_POLICY_BITS 2
#define BTRFS_ENCRYPTION_CTXSIZE_BITS 6
@@ -24,6 +25,24 @@ struct btrfs_fscrypt_extent_context {
};
#endif
+#ifdef CONFIG_FS_ENCRYPTION
+bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name, u32 de_name_len);
+
+#else
+static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
+ struct extent_buffer *leaf,
+ unsigned long de_name,
+ u32 de_name_len)
+{
+ if (de_name_len != fname_len(fname))
+ return false;
+ return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
+ de_name_len);
+}
+#endif /* CONFIG_FS_ENCRYPTION */
+
static inline void btrfs_unpack_encryption(u8 encryption,
u8 *policy,
u8 *ctxsize)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 332900b41488..236f6e91b440 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4319,7 +4319,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans,
static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *dir,
struct btrfs_inode *inode,
- const struct fscrypt_str *name,
+ struct fscrypt_name *name,
struct btrfs_rename_ctx *rename_ctx)
{
struct btrfs_root *root = dir->root;
@@ -4337,7 +4337,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
goto out;
}
- di = btrfs_lookup_dir_item(trans, root, path, dir_ino, name, -1);
+ di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino, name, -1);
if (IS_ERR_OR_NULL(di)) {
ret = di ? PTR_ERR(di) : -ENOENT;
goto err;
@@ -4365,11 +4365,14 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
}
}
- ret = btrfs_del_inode_ref(trans, root, name, ino, dir_ino, &index);
+ ret = btrfs_del_inode_ref(trans, root, &name->disk_name, ino, dir_ino,
+ &index);
if (ret) {
+ /* This should print a base-64 encoded name if relevant? */
btrfs_info(fs_info,
"failed to delete reference to %.*s, inode %llu parent %llu",
- name->len, name->name, ino, dir_ino);
+ name->disk_name.len, name->disk_name.name, ino,
+ dir_ino);
btrfs_abort_transaction(trans, ret);
goto err;
}
@@ -4390,8 +4393,10 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
* operations on the log tree, increasing latency for applications.
*/
if (!rename_ctx) {
- btrfs_del_inode_ref_in_log(trans, root, name, inode, dir_ino);
- btrfs_del_dir_entries_in_log(trans, root, name, dir, index);
+ btrfs_del_inode_ref_in_log(trans, root, &name->disk_name,
+ inode, dir_ino);
+ btrfs_del_dir_entries_in_log(trans, root, &name->disk_name,
+ dir, index);
}
/*
@@ -4409,7 +4414,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
if (ret)
goto out;
- btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->len * 2);
+ btrfs_i_size_write(dir, dir->vfs_inode.i_size - name->disk_name.len * 2);
inode_inc_iversion(&inode->vfs_inode);
inode_inc_iversion(&dir->vfs_inode);
inode->vfs_inode.i_ctime = current_time(&inode->vfs_inode);
@@ -4422,7 +4427,7 @@ static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
struct btrfs_inode *dir, struct btrfs_inode *inode,
- const struct fscrypt_str *name)
+ struct fscrypt_name *name)
{
int ret;
@@ -4480,7 +4485,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
0);
ret = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
- &fname.disk_name);
+ &fname);
if (ret)
goto out;
@@ -4516,8 +4521,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
if (ret)
return ret;
- /* This needs to handle no-key deletions later on */
-
if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) {
objectid = inode->root->root_key.objectid;
} else if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
@@ -4534,8 +4537,8 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
goto out;
}
- di = btrfs_lookup_dir_item(trans, root, path, dir_ino,
- &fname.disk_name, -1);
+ di = btrfs_lookup_dir_item_fname(trans, root, path, dir_ino,
+ &fname, -1);
if (IS_ERR_OR_NULL(di)) {
ret = di ? PTR_ERR(di) : -ENOENT;
goto out;
@@ -4561,7 +4564,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
* call btrfs_del_root_ref, and it _shouldn't_ fail.
*/
if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) {
- di = btrfs_search_dir_index_item(root, path, dir_ino, &fname.disk_name);
+ di = btrfs_search_dir_index_item(root, path, dir_ino, &fname);
if (IS_ERR_OR_NULL(di)) {
if (!di)
ret = -ENOENT;
@@ -4578,7 +4581,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
} else {
ret = btrfs_del_root_ref(trans, objectid,
root->root_key.objectid, dir_ino,
- &index, &fname.disk_name);
+ &index, &fname);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out;
@@ -4881,8 +4884,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
if (err)
return err;
- /* This needs to handle no-key deletions later on */
-
trans = __unlink_start_trans(dir);
if (IS_ERR(trans)) {
err = PTR_ERR(trans);
@@ -4902,7 +4903,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
/* now the directory is empty */
err = btrfs_unlink_inode(trans, BTRFS_I(dir), BTRFS_I(d_inode(dentry)),
- &fname.disk_name);
+ &fname);
if (!err) {
btrfs_i_size_write(BTRFS_I(inode), 0);
/*
@@ -5595,27 +5596,20 @@ void btrfs_evict_inode(struct inode *inode)
* If no dir entries were found, returns -ENOENT.
* If found a corrupted location in dir entry, returns -EUCLEAN.
*/
-static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+static int btrfs_inode_by_name(struct inode *dir, struct fscrypt_name *fname,
struct btrfs_key *location, u8 *type)
{
struct btrfs_dir_item *di;
struct btrfs_path *path;
struct btrfs_root *root = BTRFS_I(dir)->root;
int ret = 0;
- 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;
-
- /* This needs to handle no-key deletions later on */
-
- di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(BTRFS_I(dir)),
- &fname.disk_name, 0);
+ di = btrfs_lookup_dir_item_fname(NULL, root, path,
+ btrfs_ino(BTRFS_I(dir)), fname, 0);
if (IS_ERR_OR_NULL(di)) {
ret = di ? PTR_ERR(di) : -ENOENT;
goto out;
@@ -5627,13 +5621,13 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
ret = -EUCLEAN;
btrfs_warn(root->fs_info,
"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
- __func__, fname.disk_name.name, btrfs_ino(BTRFS_I(dir)),
- location->objectid, location->type, location->offset);
+ __func__, fname->usr_fname->name,
+ btrfs_ino(BTRFS_I(dir)), location->objectid,
+ location->type, location->offset);
}
if (!ret)
*type = btrfs_dir_ftype(path->nodes[0], di);
out:
- fscrypt_free_filename(&fname);
btrfs_free_path(path);
return ret;
}
@@ -5905,13 +5899,18 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_root *sub_root = root;
struct btrfs_key location;
+ struct fscrypt_name fname;
u8 di_type = 0;
int ret = 0;
if (dentry->d_name.len > BTRFS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
- ret = btrfs_inode_by_name(dir, dentry, &location, &di_type);
+ ret = fscrypt_prepare_lookup(dir, dentry, &fname);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = btrfs_inode_by_name(dir, &fname, &location, &di_type);
if (ret < 0)
return ERR_PTR(ret);
@@ -6052,18 +6051,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;
@@ -6081,6 +6094,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
struct dir_entry *entry;
struct extent_buffer *leaf = path->nodes[0];
u8 ftype;
+ u32 nokey_len;
if (found_key.objectid != key.objectid)
break;
@@ -6092,8 +6106,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)
@@ -6107,8 +6126,36 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
ftype = btrfs_dir_flags_to_ftype(btrfs_dir_flags(leaf, di));
entry = addr;
name_ptr = (char *)(entry + 1);
- read_extent_buffer(leaf, name_ptr,
- (unsigned long)(di + 1), name_len);
+ if (btrfs_dir_flags(leaf, di) & BTRFS_FT_ENCRYPTED) {
+ struct fscrypt_str oname = FSTR_INIT(name_ptr,
+ nokey_len);
+ u32 hash = 0, minor_hash = 0;
+
+ read_extent_buffer(leaf, fstr.name,
+ (unsigned long)(di + 1), name_len);
+ fstr.len = name_len;
+ /*
+ * We're iterating through DIR_INDEX items, so we don't
+ * have the DIR_ITEM hash handy. Only compute it if
+ * we'll need it -- the nokey name stores it, so that
+ * we can look up the appropriate item by nokey name
+ * later on.
+ */
+ if (!fscrypt_has_encryption_key(inode)) {
+ u64 name_hash = btrfs_name_hash(fstr.name,
+ fstr.len);
+ hash = name_hash;
+ minor_hash = name_hash >> 32;
+ }
+ ret = fscrypt_fname_disk_to_usr(inode, hash, minor_hash,
+ &fstr, &oname);
+ if (ret)
+ goto err;
+ name_len = oname.len;
+ } else {
+ read_extent_buffer(leaf, name_ptr,
+ (unsigned long)(di + 1), name_len);
+ }
put_unaligned(name_len, &entry->name_len);
put_unaligned(fs_ftype_to_dtype(ftype), &entry->type);
btrfs_dir_item_key_to_cpu(leaf, di, &location);
@@ -6128,7 +6175,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;
@@ -6159,6 +6207,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;
}
@@ -6688,6 +6738,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
struct btrfs_root *root = parent_inode->root;
u64 ino = btrfs_ino(inode);
u64 parent_ino = btrfs_ino(parent_inode);
+ struct fscrypt_name fname = { .disk_name = *name };
if (unlikely(ino == BTRFS_FIRST_FREE_OBJECTID)) {
memcpy(&key, &inode->root->root_key, sizeof(key));
@@ -6745,7 +6796,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
int err;
err = btrfs_del_root_ref(trans, key.objectid,
root->root_key.objectid, parent_ino,
- &local_index, name);
+ &local_index, &fname);
if (err)
btrfs_abort_transaction(trans, err);
} else if (add_backref) {
@@ -9321,7 +9372,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
} else { /* src is an inode */
ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
BTRFS_I(old_dentry->d_inode),
- old_name, &old_rename_ctx);
+ &old_fname, &old_rename_ctx);
if (!ret)
ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
}
@@ -9336,7 +9387,7 @@ static int btrfs_rename_exchange(struct inode *old_dir,
} else { /* dest is an inode */
ret = __btrfs_unlink_inode(trans, BTRFS_I(new_dir),
BTRFS_I(new_dentry->d_inode),
- new_name, &new_rename_ctx);
+ &new_fname, &new_rename_ctx);
if (!ret)
ret = btrfs_update_inode(trans, dest, BTRFS_I(new_inode));
}
@@ -9583,7 +9634,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
} else {
ret = __btrfs_unlink_inode(trans, BTRFS_I(old_dir),
BTRFS_I(d_inode(old_dentry)),
- &old_fname.disk_name, &rename_ctx);
+ &old_fname, &rename_ctx);
if (!ret)
ret = btrfs_update_inode(trans, root, BTRFS_I(old_inode));
}
@@ -9602,7 +9653,7 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
} else {
ret = btrfs_unlink_inode(trans, BTRFS_I(new_dir),
BTRFS_I(d_inode(new_dentry)),
- &new_fname.disk_name);
+ &new_fname);
}
if (!ret && new_inode->i_nlink == 0)
ret = btrfs_orphan_add(trans,
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index 859874579456..5fa416ef54ad 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -10,6 +10,7 @@
#include "messages.h"
#include "transaction.h"
#include "disk-io.h"
+#include "fscrypt.h"
#include "print-tree.h"
#include "qgroup.h"
#include "space-info.h"
@@ -333,7 +334,7 @@ int btrfs_del_root(struct btrfs_trans_handle *trans,
int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
u64 ref_id, u64 dirid, u64 *sequence,
- const struct fscrypt_str *name)
+ struct fscrypt_name *name)
{
struct btrfs_root *tree_root = trans->fs_info->tree_root;
struct btrfs_path *path;
@@ -355,13 +356,14 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
if (ret < 0) {
goto out;
} else if (ret == 0) {
+ u32 name_len;
leaf = path->nodes[0];
ref = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_root_ref);
ptr = (unsigned long)(ref + 1);
+ name_len = btrfs_root_ref_name_len(leaf, ref);
if ((btrfs_root_ref_dirid(leaf, ref) != dirid) ||
- (btrfs_root_ref_name_len(leaf, ref) != name->len) ||
- memcmp_extent_buffer(leaf, name->name, ptr, name->len)) {
+ !btrfs_fscrypt_match_name(name, leaf, ptr, name_len)) {
ret = -ENOENT;
goto out;
}
diff --git a/fs/btrfs/root-tree.h b/fs/btrfs/root-tree.h
index cbbaca32126e..a57bbf7b0180 100644
--- a/fs/btrfs/root-tree.h
+++ b/fs/btrfs/root-tree.h
@@ -13,7 +13,7 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
const struct fscrypt_str *name);
int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id,
u64 ref_id, u64 dirid, u64 *sequence,
- const struct fscrypt_str *name);
+ struct fscrypt_name *name);
int btrfs_del_root(struct btrfs_trans_handle *trans, const struct btrfs_key *key);
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
const struct btrfs_key *key,
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 9c211802efb1..331f35972d77 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -925,9 +925,10 @@ static int unlink_inode_for_log_replay(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode,
const struct fscrypt_str *name)
{
+ struct fscrypt_name fname = { .disk_name = *name, };
int ret;
- ret = btrfs_unlink_inode(trans, dir, inode, name);
+ ret = btrfs_unlink_inode(trans, dir, inode, &fname);
if (ret)
return ret;
/*
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 16/18] btrfs: use correct name hash for nokey names
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (14 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 15/18] btrfs: permit searching for nokey names for removal Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 17/18] btrfs: encrypt verity items Sweet Tea Dorminy
` (2 subsequent siblings)
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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
For encrypted or unencrypted names, we calculate the offset for the dir
item by hashing the name for the dir item. However, this doesn't work
for a long nokey name, where we do not have the complete ciphertext.
Instead, fscrypt stores the filesystem-provided hash in the nokey name,
and we can extract it from the fscrypt_name structure in such a case.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/dir-item.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index ce046cdabbe9..70cba4003efa 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -257,8 +257,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item_fname(struct btrfs_trans_handle *tr
key.objectid = dir;
key.type = BTRFS_DIR_ITEM_KEY;
- key.offset = btrfs_name_hash(name->disk_name.name, name->disk_name.len);
- /* XXX get the right hash for no-key names */
+
+ if (!name->disk_name.name)
+ key.offset = name->hash | ((u64)name->minor_hash << 32);
+ else
+ key.offset = btrfs_name_hash(name->disk_name.name,
+ name->disk_name.len);
ret = btrfs_search_slot(trans, root, &key, path, mod, -mod);
if (ret == 0)
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 17/18] btrfs: encrypt verity items
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (15 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 16/18] btrfs: use correct name hash for nokey names Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-02 11:53 ` [PATCH v5 18/18] btrfs: allow encrypting compressed extents Sweet Tea Dorminy
2022-11-03 19:22 ` [PATCH v5 00/18] btrfs: add fscrypt integration Paul Crowley
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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
Verity items are deemed to have sensitive information about the file
contents, so verity items for a encrypted file should be encrypted. In
order to make sure there are extent contexts with which to encrypt,
encrypted verity items wait for the file data to be written before
writing the verity items; it may be better to store a new fscrypt extent
context with verity items.
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/verity.c | 124 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 109 insertions(+), 15 deletions(-)
diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index bf9eb693a6a7..c5784994c03e 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -8,6 +8,7 @@
#include <linux/security.h>
#include <linux/posix_acl_xattr.h>
#include <linux/iversion.h>
+#include <linux/fscrypt.h>
#include <linux/fsverity.h>
#include <linux/sched/mm.h>
#include "messages.h"
@@ -224,14 +225,52 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
struct btrfs_key key;
unsigned long copy_bytes;
unsigned long src_offset = 0;
- void *data;
+ void *data_pos;
int ret = 0;
+#ifdef CONFIG_FS_ENCRYPTION
+ struct page *ciphertext_page = NULL;
+ char *ciphertext_buf;
+
+ if (IS_ENCRYPTED(&inode->vfs_inode)) {
+ ciphertext_page = alloc_page(GFP_NOFS);
+ if (!ciphertext_page)
+ return -ENOMEM;
+ ciphertext_buf = kmap_local_page(ciphertext_page);
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
while (len > 0) {
+ const char *data = src + src_offset;
+ /*
+ * Insert 2K at a time mostly to be friendly for smaller leaf
+ * size filesystems
+ */
+ copy_bytes = min_t(u64, len, 2048);
+
+#ifdef CONFIG_FS_ENCRYPTION
+ if (ciphertext_page) {
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ u64 lblk_num = offset >> fs_info->sectorsize_bits;
+
+ memset(ciphertext_buf, 0, PAGE_SIZE);
+ memcpy(ciphertext_buf, data, copy_bytes);
+ copy_bytes = ALIGN(copy_bytes,
+ FSCRYPT_CONTENTS_ALIGNMENT);
+ ret = fscrypt_encrypt_block_inplace(&inode->vfs_inode,
+ ciphertext_page,
+ copy_bytes, 0,
+ lblk_num,
+ GFP_NOFS);
+ if (ret)
+ break;
+ data = ciphertext_buf;
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
+
/* 1 for the new item being inserted */
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
@@ -243,12 +282,6 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
key.type = key_type;
key.offset = offset;
- /*
- * Insert 2K at a time mostly to be friendly for smaller leaf
- * size filesystems
- */
- copy_bytes = min_t(u64, len, 2048);
-
ret = btrfs_insert_empty_item(trans, root, path, &key, copy_bytes);
if (ret) {
btrfs_end_transaction(trans);
@@ -257,18 +290,25 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
leaf = path->nodes[0];
- data = btrfs_item_ptr(leaf, path->slots[0], void);
- write_extent_buffer(leaf, src + src_offset,
- (unsigned long)data, copy_bytes);
+ data_pos = btrfs_item_ptr(leaf, path->slots[0], void);
+ write_extent_buffer(leaf, data,
+ (unsigned long)data_pos, copy_bytes);
offset += copy_bytes;
src_offset += copy_bytes;
- len -= copy_bytes;
+ len -= min_t(u64, copy_bytes, len);
btrfs_release_path(path);
btrfs_end_transaction(trans);
}
btrfs_free_path(path);
+#ifdef CONFIG_FS_ENCRYPTION
+ if (ciphertext_page) {
+ kunmap_local(ciphertext_buf);
+ __free_page(ciphertext_page);
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
+
return ret;
}
@@ -310,6 +350,17 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
void *data;
char *kaddr = dest;
int ret;
+#ifdef CONFIG_FS_ENCRYPTION
+ char *ciphertext_buf;
+ struct page *ciphertext_page = NULL;
+
+ if (dest && IS_ENCRYPTED(&inode->vfs_inode)) {
+ ciphertext_page = alloc_page(GFP_NOFS);
+ if (!ciphertext_page)
+ return -ENOMEM;
+ ciphertext_buf = kmap_local_page(ciphertext_page);
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
path = btrfs_alloc_path();
if (!path)
@@ -371,14 +422,41 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
/* Offset from the start of item for copying */
copy_offset = offset - key.offset;
+ data = btrfs_item_ptr(leaf, path->slots[0], void);
if (dest) {
+#ifdef CONFIG_FS_ENCRYPTION
+ if (ciphertext_page) {
+ struct btrfs_fs_info *fs_info =
+ inode->root->fs_info;
+ u64 lblk_num = offset >> fs_info->sectorsize_bits;
+
+ read_extent_buffer(leaf, ciphertext_buf,
+ (unsigned long)data + copy_offset,
+ item_end - offset);
+ ret = fscrypt_decrypt_block_inplace(&inode->vfs_inode,
+ ciphertext_page,
+ item_end - offset, 0,
+ lblk_num);
+ if (ret)
+ break;
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
+
if (dest_page)
kaddr = kmap_local_page(dest_page);
- data = btrfs_item_ptr(leaf, path->slots[0], void);
- read_extent_buffer(leaf, kaddr + dest_offset,
- (unsigned long)data + copy_offset,
- copy_bytes);
+ if (IS_ENABLED(CONFIG_FS_ENCRYPTION) &&
+ IS_ENCRYPTED(&inode->vfs_inode)) {
+#ifdef CONFIG_FS_ENCRYPTION
+ memcpy(kaddr + dest_offset,
+ ciphertext_buf + copy_offset,
+ copy_bytes);
+#endif /* CONFIG_FS_ENCRYPTION */
+ } else {
+ read_extent_buffer(leaf, kaddr + dest_offset,
+ (unsigned long)data + copy_offset,
+ copy_bytes);
+ }
if (dest_page)
kunmap_local(kaddr);
@@ -405,6 +483,12 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
}
}
out:
+#ifdef CONFIG_FS_ENCRYPTION
+ if (ciphertext_page) {
+ kunmap_local(ciphertext_buf);
+ __free_page(ciphertext_page);
+ }
+#endif /* CONFIG_FS_ENCRYPTION */
btrfs_free_path(path);
if (!ret)
ret = copied;
@@ -601,6 +685,16 @@ static int btrfs_begin_enable_verity(struct file *filp)
if (ret)
return ret;
+ if (IS_ENCRYPTED(file_inode(filp))) {
+ /*
+ * Make sure the data has been written (so that we can reuse
+ * its encryption info to encrypt verity items).
+ */
+ ret = btrfs_wait_ordered_range(file_inode(filp), 0, (u64)-1);
+ if (ret)
+ return ret;
+ }
+
/* 1 for the orphan item */
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans))
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH v5 18/18] btrfs: allow encrypting compressed extents
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (16 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 17/18] btrfs: encrypt verity items Sweet Tea Dorminy
@ 2022-11-02 11:53 ` Sweet Tea Dorminy
2022-11-03 19:22 ` [PATCH v5 00/18] btrfs: add fscrypt integration Paul Crowley
18 siblings, 0 replies; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-02 11:53 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
Conveniently, compressed extents are already padded to sector size, so
they can be encrypted in-place (which requires 16-byte alignment).
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
---
fs/btrfs/btrfs_inode.h | 3 ---
fs/btrfs/compression.c | 23 +++++++++++++++++++++++
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index f0935a95ec70..d7f2b9a3d42b 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -385,9 +385,6 @@ 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/compression.c b/fs/btrfs/compression.c
index 52df6c06cc91..038721a66414 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -202,6 +202,16 @@ static void end_compressed_bio_read(struct btrfs_bio *bbio)
status = errno_to_blk_status(ret);
}
}
+ if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
+ int err;
+ u64 lblk_num = start >> fs_info->sectorsize_bits;
+ err = fscrypt_decrypt_block_inplace(inode, bv.bv_page,
+ fs_info->sectorsize,
+ bv.bv_offset,
+ lblk_num);
+ if (err)
+ status = errno_to_blk_status(err);
+ }
}
if (status)
@@ -451,6 +461,19 @@ blk_status_t btrfs_submit_compressed_write(struct btrfs_inode *inode, u64 start,
real_size = min_t(u64, real_size, compressed_len - offset);
ASSERT(IS_ALIGNED(real_size, fs_info->sectorsize));
+ if (fscrypt_inode_uses_fs_layer_crypto(&inode->vfs_inode)) {
+ int err;
+ u64 lblk_num = start >> fs_info->sectorsize_bits;
+
+ err = fscrypt_encrypt_block_inplace(&inode->vfs_inode,
+ page, real_size, 0,
+ lblk_num, GFP_NOFS);
+ if (err) {
+ ret = errno_to_blk_status(err);
+ break;
+ }
+ }
+
if (use_append)
added = bio_add_zone_append_page(bio, page, real_size,
offset_in_page(offset));
--
2.37.3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-02 11:52 [PATCH v5 00/18] btrfs: add fscrypt integration Sweet Tea Dorminy
` (17 preceding siblings ...)
2022-11-02 11:53 ` [PATCH v5 18/18] btrfs: allow encrypting compressed extents Sweet Tea Dorminy
@ 2022-11-03 19:22 ` Paul Crowley
2022-11-16 20:08 ` Neal Gompa
2022-11-16 20:19 ` Sweet Tea Dorminy
18 siblings, 2 replies; 28+ messages in thread
From: Paul Crowley @ 2022-11-03 19:22 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
Thank you for creating this! I'm told the design document [1] no
longer reflects the current proposal in these patches. If that's so I
think it's worth bringing the design document up to date so we can
review the cryptography. Thanks!
[1] https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-03 19:22 ` [PATCH v5 00/18] btrfs: add fscrypt integration Paul Crowley
@ 2022-11-16 20:08 ` Neal Gompa
2022-11-16 20:35 ` Eric Biggers
2022-11-16 20:19 ` Sweet Tea Dorminy
1 sibling, 1 reply; 28+ messages in thread
From: Neal Gompa @ 2022-11-16 20:08 UTC (permalink / raw)
To: Paul Crowley
Cc: Sweet Tea Dorminy, Theodore Y. Ts'o, Jaegeuk Kim,
Eric Biggers, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team
On Thu, Nov 3, 2022 at 3:54 PM Paul Crowley <paulcrowley@google.com> wrote:
>
> Thank you for creating this! I'm told the design document [1] no
> longer reflects the current proposal in these patches. If that's so I
> think it's worth bringing the design document up to date so we can
> review the cryptography. Thanks!
>
> [1] https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
So this might be my ignorance here, but when I look at the patch set,
I don't really see any significant mathematics or cryptographic work
going on here. This seems to be primarily just interacting with the
fscrypt stuff that exists in the kernel already.
Could you please clarify what you mean here?
--
真実はいつも一つ!/ Always, there's only one truth!
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-16 20:08 ` Neal Gompa
@ 2022-11-16 20:35 ` Eric Biggers
0 siblings, 0 replies; 28+ messages in thread
From: Eric Biggers @ 2022-11-16 20:35 UTC (permalink / raw)
To: Neal Gompa
Cc: Paul Crowley, Sweet Tea Dorminy, Theodore Y. Ts'o,
Jaegeuk Kim, Chris Mason, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team
On Wed, Nov 16, 2022 at 03:08:26PM -0500, Neal Gompa wrote:
> On Thu, Nov 3, 2022 at 3:54 PM Paul Crowley <paulcrowley@google.com> wrote:
> >
> > Thank you for creating this! I'm told the design document [1] no
> > longer reflects the current proposal in these patches. If that's so I
> > think it's worth bringing the design document up to date so we can
> > review the cryptography. Thanks!
> >
> > [1] https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
>
> So this might be my ignorance here, but when I look at the patch set,
> I don't really see any significant mathematics or cryptographic work
> going on here. This seems to be primarily just interacting with the
> fscrypt stuff that exists in the kernel already.
>
> Could you please clarify what you mean here?
There absolutely is significant cryptographic work going on here. There needs
to be a new way to choose keys and IVs for file contents blocks, as the existing
ways are not appropriate for btrfs. That is the main difficulty we are having.
One idea is the one which this patchset is intended to implement. Other ideas
that have been brought up involve deriving per-extent keys, using per-block IVs,
or using authenticated encryption (or some combination of these). These ideas
would be better cryptographically then the one that this patchset actually
implements, so it needs to be properly documented why they've been ruled out.
(Or maybe they haven't really been ruled out -- I'm not sure they have.)
And as I've mentioned, if we do go with the current proposal, which results in
some IV reuse, it needs to be decided whether we should try to ameliorate that
by hashing part of the IV with a secret key, like IV_INO_LBLK_32 does.
Another area where new cryptographic design is needed is the encryption of the
fsverity metadata. ext4 and f2fs get encryption of the fsverity metadata "for
free" since they store it past EOF, but btrfs doesn't.
Anyway, I tried to get Paul's feedback on this patchset, but he
(understandingly) didn't want to dig through random mailing list discussions,
which don't really have all the information anyway.
I think updating the design document to fully reflect the current proposal and
the detailed reasoning behind it would be super helpful to get everyone on the
right page and to make sure the right design is being chosen.
- Eric
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-03 19:22 ` [PATCH v5 00/18] btrfs: add fscrypt integration Paul Crowley
2022-11-16 20:08 ` Neal Gompa
@ 2022-11-16 20:19 ` Sweet Tea Dorminy
2022-11-21 17:26 ` Sweet Tea Dorminy
1 sibling, 1 reply; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-16 20:19 UTC (permalink / raw)
To: Paul Crowley
Cc: Theodore Y. Ts'o, Jaegeuk Kim, Eric Biggers, Chris Mason,
Josef Bacik, David Sterba, linux-fscrypt, linux-btrfs,
kernel-team
On 11/3/22 15:22, Paul Crowley wrote:
> Thank you for creating this! I'm told the design document [1] no
> longer reflects the current proposal in these patches. If that's so I
> think it's worth bringing the design document up to date so we can
> review the cryptography. Thanks!
>
> [1] https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
I apologize for the delay; I realized when this thread was bumped just
now that my attempt to share the updated doc didn't seem to make it to
the mailing list.
https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
is an update of the design document that hopefully is what you're
requesting.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-16 20:19 ` Sweet Tea Dorminy
@ 2022-11-21 17:26 ` Sweet Tea Dorminy
2022-11-24 1:22 ` Sweet Tea Dorminy
0 siblings, 1 reply; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-21 17:26 UTC (permalink / raw)
To: Paul Crowley
Cc: Eric Biggers, Josef Bacik, David Sterba, linux-fscrypt,
linux-btrfs, kernel-team, Omar Sandoval, Chris Mason
I appreciate the conversation happening on the doc; thank ya'll for
reading and commenting.
Would it be worth having a meeting on Zoom or the like this week to
discuss the way forward for getting encryption for btrfs, be that one of
the extent-based encryption variations or AEAD?
Thanks!
Sweet Tea
On 11/16/22 15:19, Sweet Tea Dorminy wrote:
>
>
> On 11/3/22 15:22, Paul Crowley wrote:
>> Thank you for creating this! I'm told the design document [1] no
>> longer reflects the current proposal in these patches. If that's so I
>> think it's worth bringing the design document up to date so we can
>> review the cryptography. Thanks!
>>
>> [1]
>> https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
>
> I apologize for the delay; I realized when this thread was bumped just
> now that my attempt to share the updated doc didn't seem to make it to
> the mailing list.
>
> https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing is an update of the design document that hopefully is what you're requesting.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-21 17:26 ` Sweet Tea Dorminy
@ 2022-11-24 1:22 ` Sweet Tea Dorminy
2022-11-28 7:59 ` Christoph Hellwig
0 siblings, 1 reply; 28+ messages in thread
From: Sweet Tea Dorminy @ 2022-11-24 1:22 UTC (permalink / raw)
To: Paul Crowley
Cc: Eric Biggers, Josef Bacik, David Sterba, linux-fscrypt,
linux-btrfs, kernel-team, Omar Sandoval, Chris Mason
We had a very productive meeting and have greatly changed the design: we
now plan to implement authenticated encryption, and have per-extent keys
instead of per-extent nonces, which will greatly improve the
cryptographic characteristics over this version. Many thanks for the
discussion, both in the meeting and on the document.
The document has been updated to hopefully reflect the discussion we
had; further comments are always appreciated.
https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
I look forward to working on implementing the new design; thanks to all!
Sweet Tea
On 11/21/22 12:26, Sweet Tea Dorminy wrote:
> I appreciate the conversation happening on the doc; thank ya'll for
> reading and commenting.
>
> Would it be worth having a meeting on Zoom or the like this week to
> discuss the way forward for getting encryption for btrfs, be that one of
> the extent-based encryption variations or AEAD?
>
> Thanks!
>
> Sweet Tea
>
> On 11/16/22 15:19, Sweet Tea Dorminy wrote:
>>
>>
>> On 11/3/22 15:22, Paul Crowley wrote:
>>> Thank you for creating this! I'm told the design document [1] no
>>> longer reflects the current proposal in these patches. If that's so I
>>> think it's worth bringing the design document up to date so we can
>>> review the cryptography. Thanks!
>>>
>>> [1]
>>> https://docs.google.com/document/d/1iNnrqyZqJ2I5nfWKt7cd1T9xwU0iHhjhk9ALQW3XuII/edit
>>
>> I apologize for the delay; I realized when this thread was bumped just
>> now that my attempt to share the updated doc didn't seem to make it to
>> the mailing list.
>>
>> https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing is an update of the design document that hopefully is what you're requesting.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-24 1:22 ` Sweet Tea Dorminy
@ 2022-11-28 7:59 ` Christoph Hellwig
2022-11-28 18:44 ` Eric Biggers
2022-11-28 20:34 ` Paul Crowley
0 siblings, 2 replies; 28+ messages in thread
From: Christoph Hellwig @ 2022-11-28 7:59 UTC (permalink / raw)
To: Sweet Tea Dorminy
Cc: Paul Crowley, Eric Biggers, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
Chris Mason
On Wed, Nov 23, 2022 at 08:22:30PM -0500, Sweet Tea Dorminy wrote:
> The document has been updated to hopefully reflect the discussion we had;
> further comments are always appreciated. https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
How is this going to work with hardware encryption offload? I think
the number of keys for UFS and eMMC inline encryption, but Eric may
correct me.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-28 7:59 ` Christoph Hellwig
@ 2022-11-28 18:44 ` Eric Biggers
2022-11-28 20:34 ` Paul Crowley
1 sibling, 0 replies; 28+ messages in thread
From: Eric Biggers @ 2022-11-28 18:44 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Sweet Tea Dorminy, Paul Crowley, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
Chris Mason
On Sun, Nov 27, 2022 at 11:59:40PM -0800, Christoph Hellwig wrote:
> On Wed, Nov 23, 2022 at 08:22:30PM -0500, Sweet Tea Dorminy wrote:
> > The document has been updated to hopefully reflect the discussion we had;
> > further comments are always appreciated. https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
>
> How is this going to work with hardware encryption offload? I think
> the number of keys for UFS and eMMC inline encryption, but Eric may
> correct me.
First, traditional crypto accelerators via the crypto API will work in any case.
I think your question is specifically about inline encryption
(https://www.kernel.org/doc/html/latest/block/inline-encryption.html).
To use inline encryption hardware, consecutive blocks must use consecutive IVs,
and the nonzero part of the IVs needs to fit within the hardware's DUN size.
That's 64 bits for the UFS standard, and 32 bits for the eMMC standard.
fscrypt's "default" setting of per-file keys satisfies both of those
requirements. That means the current proposal for btrfs does too, since it's
the same as that "default" setting -- just with extents instead of files.
(For eMMC, extents would have to be limited to 2^32 blocks.)
The other consideration, which seems to be what you're asking about, is a
performance one: how well this performs on hardware where switching keys is very
expensive. The answer is not very well. Of course, that's the answer for
per-file keys too. Note that this is an issue for some inline encryption
hardware (e.g. Qualcomm ICE), but not others (e.g. Exynos FMP, Mediatek UFS).
The way this problem is "solved" in ext4 and f2fs is by also providing the (less
than cryptographically ideal) settings IV_INO_LBLK_64 and IV_INO_LBLK_32. Those
squeeze the inode number *and* file offset into a 64-bit or 32-bit IV, so that
per-file keys aren't needed.
There's a natural mapping of the IV_INO_LBLK_* settings onto extent-based
encryption. A 32-bit extent number would just be used instead of an inode
number. Or, if a 32-bit extent number is infeasible, an extent nonce of any
length hashed with a secret key could be used instead.
So yes, it would be possible to provide settings that optimize for hardware like
Qualcomm ICE, as ext4 and f2fs do with IV_INO_LBLK_*. However, it makes sense
to leave that for later until if/when someone actually has a use case for it.
- Eric
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v5 00/18] btrfs: add fscrypt integration
2022-11-28 7:59 ` Christoph Hellwig
2022-11-28 18:44 ` Eric Biggers
@ 2022-11-28 20:34 ` Paul Crowley
1 sibling, 0 replies; 28+ messages in thread
From: Paul Crowley @ 2022-11-28 20:34 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Sweet Tea Dorminy, Eric Biggers, Josef Bacik, David Sterba,
linux-fscrypt, linux-btrfs, kernel-team, Omar Sandoval,
Chris Mason
The kind of inline encryption hardware we see on Android devices tends
to have these limitations:
- as you indicate, loading keys can incur latency, so if many keys are
in use at once it can slow things down
- it's limited to using AES-XTS
- on UFS devices, the IV (transmitted in the DUN) must be zero in the
64 high bits
- consecutive blocks in the same operation use consecutive IVs
- there's no easy way to gather a checksum or MAC over the on-disk
ciphertext short of re-reading after writing
Android works around this with IV_INO_LBLK_64 policies, but these only
work well on the relatively small storage devices we use on Android.
In particular the IV limitation is very serious:
- inode numbers must be four bytes
- they must never change (so ext4 filesystem resizing is disabled)
- files cannot be more than 2^32 blocks
Things are worse on eMMC devices.
Even without this IV limitation, the security proofs for most AES
modes of operation start to look shaky as you approach the "birthday
bound" of encrypting 2^68 bytes with the same key. If your attack
model always assumes a point-in-time attack then you only have to
worry if you use a single key to encrypt a multi-exabyte storage
device; btrfs is designed to scale to such devices and more. If your
attack model includes an attacker who repeatedly gets access to the
storage device across time, then writing multiple exabytes with the
same key can be a problem even if some of those are overwritten. This
leads us to prefer per-extent AES keys (derived from a root key) if
possible. It's a shame AES doesn't have a 256-bit blocksize.
The way btrfs works also gives us some opportunities to do things a
little better. In general disk encryption has to make sacrifices to
deal with the limitation that IVs must be reused and there's no room
for a MAC. But because btrfs writes in whole extents, with fresh
metadata and checksum on each write, it becomes possible to use a
fresh IV and MAC for every new write. This opens up the possibility of
using an AEAD mode like AES-GCM. This combination gives us the
strongest proofs of security even against very generous attack models.
Our recommendation: btrfs should first build the ideal thing first
since it will have reasonable performance for most users, then later
design alternatives that make a few compromises for performance where
there is demand.
On Sun, 27 Nov 2022 at 23:59, Christoph Hellwig <hch@infradead.org> wrote:
>
> On Wed, Nov 23, 2022 at 08:22:30PM -0500, Sweet Tea Dorminy wrote:
> > The document has been updated to hopefully reflect the discussion we had;
> > further comments are always appreciated. https://docs.google.com/document/d/1janjxewlewtVPqctkWOjSa7OhCgB8Gdx7iDaCDQQNZA/edit?usp=sharing
>
> How is this going to work with hardware encryption offload? I think
> the number of keys for UFS and eMMC inline encryption, but Eric may
> correct me.
^ permalink raw reply [flat|nested] 28+ messages in thread