Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0ccdfe8
fscrypt: add per-extent encryption support
josefbacik May 13, 2026
062bd8b
fscrypt: allow inline encryption for extent based encryption
josefbacik May 13, 2026
cef707c
fscrypt: add a __fscrypt_file_open helper
josefbacik May 13, 2026
81d0c0d
fscrypt: conditionally don't wipe mk secret until the last active use…
josefbacik May 13, 2026
7ccfc2e
blk-crypto: add a process bio callback
josefbacik May 13, 2026
4d5e4c5
fscrypt: add a process_bio hook to fscrypt_operations
josefbacik May 13, 2026
659dbf6
fscrypt: expose fscrypt_nokey_name
osandov May 13, 2026
8aadae4
fscrypt: add documentation about extent encryption
josefbacik May 13, 2026
73ada51
btrfs: add infrastructure for safe em freeing
josefbacik May 13, 2026
b18f1ec
btrfs: start using fscrypt hooks
osandov May 13, 2026
b1de9e7
btrfs: add inode encryption contexts
osandov May 13, 2026
127d7e1
btrfs: add new FEATURE_INCOMPAT_ENCRYPT flag
osandov May 13, 2026
cdc299d
btrfs: adapt readdir for encrypted and nokey names
osandov May 13, 2026
e8f4b30
btrfs: handle nokey names
sweettea May 13, 2026
5e329aa
btrfs: implement fscrypt ioctls
osandov May 13, 2026
e249ec9
btrfs: select encryption dependencies if FS_ENCRYPTION
josefbacik May 13, 2026
8babb32
btrfs: add get_devices hook for fscrypt
sweettea May 13, 2026
cf5563c
btrfs: set file extent encryption excplicitly
sweettea May 13, 2026
45b0cbc
btrfs: add fscrypt_info and encryption_type to extent_map
sweettea May 13, 2026
f4b6c5f
btrfs: add fscrypt_info and encryption_type to ordered_extent
josefbacik May 13, 2026
f089949
btrfs: plumb through setting the fscrypt_info for ordered extents
josefbacik May 13, 2026
d030e31
btrfs: populate the ordered_extent with the fscrypt context
josefbacik May 13, 2026
148e6be
btrfs: keep track of fscrypt info and orig_start for dio reads
josefbacik May 13, 2026
fc79ba3
btrfs: add extent encryption context tree item type
josefbacik May 13, 2026
46b3554
btrfs: pass through fscrypt_extent_info to the file extent helpers
josefbacik May 13, 2026
fa7ef0a
btrfs: implement the fscrypt extent encryption hooks
josefbacik May 13, 2026
a69ffbf
btrfs: setup fscrypt_extent_info for new extents
josefbacik May 13, 2026
ba20e05
btrfs: populate ordered_extent with the orig offset
josefbacik May 13, 2026
700d528
btrfs: set the bio fscrypt context when applicable
josefbacik May 13, 2026
05a07cc
btrfs: add a bio argument to btrfs_csum_one_bio
josefbacik May 13, 2026
ed1e976
btrfs: limit encrypted writes to 256 segments
josefbacik May 13, 2026
4b43121
btrfs: implement process_bio cb for fscrypt
josefbacik May 13, 2026
574e11c
btrfs: implement read repair for encryption
josefbacik May 13, 2026
45506d5
btrfs: add test_dummy_encryption support
josefbacik May 13, 2026
5eea4fe
btrfs: make btrfs_ref_to_path handle encrypted filenames
josefbacik May 13, 2026
d4b6868
btrfs: deal with encrypted symlinks in send
josefbacik May 13, 2026
8cc8355
btrfs: decrypt file names for send
josefbacik May 13, 2026
f0d221e
btrfs: load the inode context before sending writes
josefbacik May 13, 2026
58b9f72
btrfs: set the appropriate free space settings in reconfigure
josefbacik May 13, 2026
f103ff2
btrfs: support encryption with log replay
josefbacik May 13, 2026
abae258
btrfs: disable auto defrag on encrypted files
josefbacik May 13, 2026
77cd2ca
btrfs: disable encryption on RAID5/6
josefbacik May 13, 2026
a57c000
btrfs: disable send if we have encryption enabled
josefbacik May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Documentation/filesystems/fscrypt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,21 @@ alternative master keys or to support rotating master keys. Instead,
the master keys may be wrapped in userspace, e.g. as is done by the
`fscrypt <https://github.com/google/fscrypt>`_ tool.

Per-extent encryption keys
--------------------------

For certain file systems, such as btrfs, it's desired to derive a
per-extent encryption key. This is to enable features such as snapshots
and reflink, where you could have different inodes pointing at the same
extent. When a new extent is created fscrypt randomly generates a
16-byte nonce and the file system stores it alongside the extent.
Then, it uses a KDF (as described in `Key derivation function`_) to
derive the extent's key from the master key and nonce.

Currently the inode's master key and encryption policy must match the
extent, so you cannot share extents between inodes that were encrypted
differently.

DIRECT_KEY policies
-------------------

Expand Down Expand Up @@ -1483,6 +1498,27 @@ by the kernel and is used as KDF input or as a tweak to cause
different files to be encrypted differently; see `Per-file encryption
keys`_ and `DIRECT_KEY policies`_.

Extent encryption context
-------------------------

The extent encryption context mirrors the important parts of the above
`Encryption context`_, with a few omissions. The struct is defined as
follows::

struct fscrypt_extent_context {
u8 version;
u8 encryption_mode;
u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
};

Currently all fields much match the containing inode's encryption
context, with the exception of the nonce.

Additionally extent encryption is only supported with
FSCRYPT_EXTENT_CONTEXT_V2 using the standard policy; all other policies
are disallowed.

Data path changes
-----------------

Expand All @@ -1506,6 +1542,11 @@ buffer. Some filesystems, such as UBIFS, already use temporary
buffers regardless of encryption. Other filesystems, such as ext4 and
F2FS, have to allocate bounce pages specially for encryption.

Inline encryption is not optional for extent encryption based file
systems; the amount of objects required to be kept around is too much.
Inline encryption handles the object lifetime details which results in a
cleaner implementation.

Filename hashing and encoding
-----------------------------

Expand Down
41 changes: 41 additions & 0 deletions block/blk-crypto-fallback.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,17 @@ static void __blk_crypto_fallback_encrypt_bio(struct bio *src_bio,
}
}

/* Process the encrypted bio before we submit it. */
if (bc->bc_key->crypto_cfg.process_bio) {
blk_status_t status;

status = bc->bc_key->crypto_cfg.process_bio(src_bio, enc_bio);
if (status != BLK_STS_OK) {
enc_bio->bi_status = status;
goto out_free_enc_bio;
}
}

submit_bio(enc_bio);
return;

Expand Down Expand Up @@ -358,6 +369,16 @@ static void blk_crypto_fallback_encrypt_bio(struct bio *src_bio)
struct blk_crypto_keyslot *slot;
blk_status_t status;

/*
* We cannot split bio's that have process_bio, as they require the original bio.
* The upper layer must make sure to limit the submitted bio's appropriately.
*/
if (bio_segments(src_bio) > BIO_MAX_VECS && bc->bc_key->crypto_cfg.process_bio) {
src_bio->bi_status = BLK_STS_RESOURCE;
bio_endio(src_bio);
return;
}

status = blk_crypto_get_keyslot(blk_crypto_fallback_profile,
bc->bc_key, &slot);
if (status != BLK_STS_OK) {
Expand Down Expand Up @@ -427,6 +448,13 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
struct blk_crypto_keyslot *slot;
blk_status_t status;

/* Process the bio first before trying to decrypt. */
if (bc->bc_key->crypto_cfg.process_bio) {
status = bc->bc_key->crypto_cfg.process_bio(bio, bio);
if (status != BLK_STS_OK)
goto out;
}

status = blk_crypto_get_keyslot(blk_crypto_fallback_profile,
bc->bc_key, &slot);
if (status == BLK_STS_OK) {
Expand All @@ -435,12 +463,25 @@ static void blk_crypto_fallback_decrypt_bio(struct work_struct *work)
blk_crypto_fallback_tfm(slot));
blk_crypto_put_keyslot(slot);
}
out:
mempool_free(f_ctx, bio_fallback_crypt_ctx_pool);

bio->bi_status = status;
bio_endio(bio);
}

/**
* blk_crypto_profile_is_fallback - check if this profile is the fallback
* profile
* @profile: the profile we're checking
*
* This is just a quick check to make sure @profile is the fallback profile.
*/
bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile)
{
return profile == blk_crypto_fallback_profile;
}

/**
* blk_crypto_fallback_decrypt_endio - queue bio for fallback decryption
*
Expand Down
8 changes: 8 additions & 0 deletions block/blk-crypto-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ int blk_crypto_fallback_start_using_mode(enum blk_crypto_mode_num mode_num);

int blk_crypto_fallback_evict_key(const struct blk_crypto_key *key);

bool blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile);

#else /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */

static inline int
Expand All @@ -241,6 +243,12 @@ blk_crypto_fallback_evict_key(const struct blk_crypto_key *key)
return 0;
}

static inline bool
blk_crypto_profile_is_fallback(struct blk_crypto_profile *profile)
{
return false;
}

#endif /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */

#endif /* __LINUX_BLK_CRYPTO_INTERNAL_H */
2 changes: 2 additions & 0 deletions block/blk-crypto-profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile,
return false;
if (!(profile->key_types_supported & cfg->key_type))
return false;
if (cfg->process_bio && !blk_crypto_profile_is_fallback(profile))
return false;
return true;
}

Expand Down
6 changes: 5 additions & 1 deletion block/blk-crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ int __blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio,
* @dun_bytes: number of bytes that will be used to specify the DUN when this
* key is used
* @data_unit_size: the data unit size to use for en/decryption
* @process_bio: the call back if the upper layer needs to process the encrypted
* bio
*
* Return: 0 on success, -errno on failure. The caller is responsible for
* zeroizing both blk_key and key_bytes when done with them.
Expand All @@ -309,7 +311,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key,
enum blk_crypto_key_type key_type,
enum blk_crypto_mode_num crypto_mode,
unsigned int dun_bytes,
unsigned int data_unit_size)
unsigned int data_unit_size,
blk_crypto_process_bio_t process_bio)
{
const struct blk_crypto_mode *mode;

Expand Down Expand Up @@ -343,6 +346,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key,
blk_key->crypto_cfg.dun_bytes = dun_bytes;
blk_key->crypto_cfg.data_unit_size = data_unit_size;
blk_key->crypto_cfg.key_type = key_type;
blk_key->crypto_cfg.process_bio = process_bio;
blk_key->data_unit_size_bits = ilog2(data_unit_size);
blk_key->size = key_size;
memcpy(blk_key->bytes, key_bytes, key_size);
Expand Down
4 changes: 4 additions & 0 deletions fs/btrfs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ config BTRFS_FS
select RAID6_PQ
select XOR_BLOCKS
select XXHASH
select FS_ENCRYPTION_ALGS if FS_ENCRYPTION
select FS_ENCRYPTION_INLINE_CRYPT if FS_ENCRYPTION
select BLK_INLINE_ENCRYPTION if FS_ENCRYPTION
select BLK_INLINE_ENCRYPTION_FALLBACK if FS_ENCRYPTION
depends on PAGE_SIZE_LESS_THAN_256KB

help
Expand Down
1 change: 1 addition & 0 deletions fs/btrfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_DEBUG) += 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 \
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/accessors.h
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,8 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
struct btrfs_file_extent_item, disk_num_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
struct btrfs_file_extent_item, compression, 8);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
struct btrfs_file_extent_item, encryption, 8);


BTRFS_SETGET_FUNCS(file_extent_type, struct btrfs_file_extent_item, type, 8);
Expand Down
43 changes: 39 additions & 4 deletions fs/btrfs/backref.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "extent-tree.h"
#include "relocation.h"
#include "tree-checker.h"
#include "fscrypt.h"

/* Just arbitrary numbers so we can be sure one of these happened. */
#define BACKREF_FOUND_SHARED 6
Expand Down Expand Up @@ -2104,6 +2105,39 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
return ret;
}

static int copy_resolved_iref_to_buf(struct btrfs_root *fs_root,
struct extent_buffer *eb, char *dest,
u64 parent, unsigned long name_off,
u32 name_len, s64 *bytes_left)
{
struct btrfs_fs_info *fs_info = fs_root->fs_info;
struct fscrypt_str fname = FSTR_INIT(NULL, 0);
int ret;

/* No encryption, just copy the name in. */
if (!btrfs_fs_incompat(fs_info, ENCRYPT)) {
*bytes_left -= name_len;
if (*bytes_left >= 0)
read_extent_buffer(eb, dest + *bytes_left, name_off, name_len);
return 0;
}

ret = fscrypt_fname_alloc_buffer(BTRFS_NAME_LEN, &fname);
if (ret)
return ret;

ret = btrfs_decrypt_name(fs_root, eb, name_off, name_len, parent, &fname);
if (ret)
goto out;

*bytes_left -= fname.len;
if (*bytes_left >= 0)
memcpy(dest + *bytes_left, fname.name, fname.len);
out:
fscrypt_fname_free_buffer(&fname);
return ret;
}

/*
* this iterates to turn a name (from iref/extref) into a full filesystem path.
* Elements of the path are separated by '/' and the path is guaranteed to be
Expand Down Expand Up @@ -2135,15 +2169,16 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
dest[bytes_left] = '\0';

while (1) {
bytes_left -= name_len;
if (bytes_left >= 0)
read_extent_buffer(eb, dest + bytes_left,
name_off, name_len);
ret = copy_resolved_iref_to_buf(fs_root, eb, dest, parent,
name_off, name_len, &bytes_left);
if (eb != eb_in) {
if (!path->skip_locking)
btrfs_tree_read_unlock(eb);
free_extent_buffer(eb);
}
if (ret)
break;

ret = btrfs_find_item(fs_root, path, parent, 0,
BTRFS_INODE_REF_KEY, &found_key);
if (ret > 0)
Expand Down
Loading