| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Tue, 21 Jan 2020 09:27:47 -0800 |
| Subject: ANDROID: dm: add dm-default-key target for metadata encryption |
| |
| Add a device-mapper target "dm-default-key" which assigns an encryption |
| key to bios that aren't for the contents of an encrypted file. |
| |
| This ensures that all blocks on-disk will be encrypted with some key, |
| without the performance hit of file contents being encrypted twice when |
| fscrypt (File-Based Encryption) is used. |
| |
| It is only appropriate to use dm-default-key when key configuration is |
| tightly controlled, like it is in Android, such that all fscrypt keys |
| are at least as hard to compromise as the default key. |
| |
| Compared to the original version of dm-default-key, this has been |
| modified to use the new vendor-independent inline encryption framework |
| (which works even when no inline encryption hardware is present), the |
| table syntax has been changed to match dm-crypt, and support for |
| specifying Adiantum encryption has been added. These changes also mean |
| that dm-default-key now always explicitly specifies the DUN (the IV). |
| |
| Also, to handle f2fs moving blocks of encrypted files around without the |
| key, and to handle ext4 and f2fs filesystems mounted without |
| '-o inlinecrypt', the mapping logic is no longer "set a key on the bio |
| if it doesn't have one already", but rather "set a key on the bio unless |
| the bio has the bi_skip_dm_default_key flag set". Filesystems set this |
| flag on *all* bios for encrypted file contents, regardless of whether |
| they are encrypting/decrypting the file using inline encryption or the |
| traditional filesystem-layer encryption, or moving the raw data. |
| |
| For the bi_skip_dm_default_key flag, a new field in struct bio is used |
| rather than a bit in bi_opf so that fscrypt_set_bio_crypt_ctx() can set |
| the flag, minimizing the changes needed to filesystems. (bi_opf is |
| usually overwritten after fscrypt_set_bio_crypt_ctx() is called.) |
| |
| [CPNOTE: 20/07/21] Lee: Has been submitted upstream - no current assignee |
| |
| Bug: 137270441 |
| Bug: 147814592 |
| Bug: 160885805 |
| Link: https://lkml.kernel.org/linux-f2fs-devel/20201217150435.1505269-1-satyat@google.com/T/#u |
| Change-Id: I69c9cd1e968ccf990e4ad96e5115b662237f5095 |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Lee Jones <joneslee@google.com> |
| --- |
| block/bio.c | 3 + |
| block/blk-crypto-fallback.c | 2 + |
| drivers/md/Kconfig | 21 ++ |
| drivers/md/Makefile | 1 + |
| drivers/md/dm-default-key.c | 413 ++++++++++++++++++++++++++++++++++++ |
| fs/crypto/inline_crypt.c | 14 +- |
| fs/f2fs/data.c | 6 +- |
| include/linux/blk-crypto.h | 36 ++++ |
| include/linux/blk_types.h | 3 + |
| include/linux/fscrypt.h | 14 ++ |
| 10 files changed, 511 insertions(+), 2 deletions(-) |
| create mode 100644 drivers/md/dm-default-key.c |
| |
| diff --git a/block/bio.c b/block/bio.c |
| --- a/block/bio.c |
| +++ b/block/bio.c |
| @@ -270,6 +270,9 @@ void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table, |
| #endif |
| #ifdef CONFIG_BLK_INLINE_ENCRYPTION |
| bio->bi_crypt_context = NULL; |
| +#if IS_ENABLED(CONFIG_DM_DEFAULT_KEY) |
| + bio->bi_skip_dm_default_key = false; |
| +#endif |
| #endif |
| #ifdef CONFIG_BLK_DEV_INTEGRITY |
| bio->bi_integrity = NULL; |
| diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c |
| --- a/block/blk-crypto-fallback.c |
| +++ b/block/blk-crypto-fallback.c |
| @@ -180,6 +180,8 @@ static struct bio *blk_crypto_fallback_clone_bio(struct bio *bio_src) |
| |
| bio_clone_blkg_association(bio, bio_src); |
| |
| + bio_clone_skip_dm_default_key(bio, bio_src); |
| + |
| return bio; |
| } |
| |
| diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig |
| --- a/drivers/md/Kconfig |
| +++ b/drivers/md/Kconfig |
| @@ -290,6 +290,27 @@ config DM_CRYPT |
| |
| If unsure, say N. |
| |
| +config DM_DEFAULT_KEY |
| + tristate "Default-key target support" |
| + depends on BLK_DEV_DM |
| + depends on BLK_INLINE_ENCRYPTION |
| + # dm-default-key doesn't require -o inlinecrypt, but it does currently |
| + # rely on the inline encryption hooks being built into the kernel. |
| + depends on FS_ENCRYPTION_INLINE_CRYPT |
| + help |
| + This device-mapper target allows you to create a device that |
| + assigns a default encryption key to bios that aren't for the |
| + contents of an encrypted file. |
| + |
| + This ensures that all blocks on-disk will be encrypted with |
| + some key, without the performance hit of file contents being |
| + encrypted twice when fscrypt (File-Based Encryption) is used. |
| + |
| + It is only appropriate to use dm-default-key when key |
| + configuration is tightly controlled, like it is in Android, |
| + such that all fscrypt keys are at least as hard to compromise |
| + as the default key. |
| + |
| config DM_SNAPSHOT |
| tristate "Snapshot target" |
| depends on BLK_DEV_DM |
| diff --git a/drivers/md/Makefile b/drivers/md/Makefile |
| --- a/drivers/md/Makefile |
| +++ b/drivers/md/Makefile |
| @@ -57,6 +57,7 @@ obj-$(CONFIG_DM_UNSTRIPED) += dm-unstripe.o |
| obj-$(CONFIG_DM_BUFIO) += dm-bufio.o |
| obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o |
| obj-$(CONFIG_DM_CRYPT) += dm-crypt.o |
| +obj-$(CONFIG_DM_DEFAULT_KEY) += dm-default-key.o |
| obj-$(CONFIG_DM_DELAY) += dm-delay.o |
| obj-$(CONFIG_DM_DUST) += dm-dust.o |
| obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o |
| diff --git a/drivers/md/dm-default-key.c b/drivers/md/dm-default-key.c |
| new file mode 100644 |
| --- /dev/null |
| +++ b/drivers/md/dm-default-key.c |
| @@ -0,0 +1,413 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| +/* |
| + * Copyright (C) 2017 Google, Inc. |
| + */ |
| + |
| +#include <linux/blk-crypto.h> |
| +#include <linux/device-mapper.h> |
| +#include <linux/module.h> |
| + |
| +#define DM_MSG_PREFIX "default-key" |
| + |
| +#define DM_DEFAULT_KEY_MAX_KEY_SIZE 64 |
| + |
| +static const struct dm_default_key_cipher { |
| + const char *name; |
| + enum blk_crypto_mode_num mode_num; |
| + int key_size; |
| +} dm_default_key_ciphers[] = { |
| + { |
| + .name = "aes-xts-plain64", |
| + .mode_num = BLK_ENCRYPTION_MODE_AES_256_XTS, |
| + .key_size = 64, |
| + }, { |
| + .name = "xchacha12,aes-adiantum-plain64", |
| + .mode_num = BLK_ENCRYPTION_MODE_ADIANTUM, |
| + .key_size = 32, |
| + }, |
| +}; |
| + |
| +/** |
| + * struct dm_default_c - private data of a default-key target |
| + * @dev: the underlying device |
| + * @start: starting sector of the range of @dev which this target actually maps. |
| + * For this purpose a "sector" is 512 bytes. |
| + * @cipher_string: the name of the encryption algorithm being used |
| + * @iv_offset: starting offset for IVs. IVs are generated as if the target were |
| + * preceded by @iv_offset 512-byte sectors. |
| + * @sector_size: crypto sector size in bytes (usually 4096) |
| + * @sector_bits: log2(sector_size) |
| + * @key: the encryption key to use |
| + * @max_dun: the maximum DUN that may be used (computed from other params) |
| + */ |
| +struct default_key_c { |
| + struct dm_dev *dev; |
| + sector_t start; |
| + const char *cipher_string; |
| + u64 iv_offset; |
| + unsigned int sector_size; |
| + unsigned int sector_bits; |
| + struct blk_crypto_key key; |
| + u64 max_dun; |
| +}; |
| + |
| +static const struct dm_default_key_cipher * |
| +lookup_cipher(const char *cipher_string) |
| +{ |
| + int i; |
| + |
| + for (i = 0; i < ARRAY_SIZE(dm_default_key_ciphers); i++) { |
| + if (strcmp(cipher_string, dm_default_key_ciphers[i].name) == 0) |
| + return &dm_default_key_ciphers[i]; |
| + } |
| + return NULL; |
| +} |
| + |
| +static void default_key_dtr(struct dm_target *ti) |
| +{ |
| + struct default_key_c *dkc = ti->private; |
| + int err; |
| + |
| + if (dkc->dev) { |
| + err = blk_crypto_evict_key(dkc->dev->bdev, &dkc->key); |
| + if (err && err != -ENOKEY) |
| + DMWARN("Failed to evict crypto key: %d", err); |
| + dm_put_device(ti, dkc->dev); |
| + } |
| + kfree_sensitive(dkc->cipher_string); |
| + kfree_sensitive(dkc); |
| +} |
| + |
| +static int default_key_ctr_optional(struct dm_target *ti, |
| + unsigned int argc, char **argv) |
| +{ |
| + struct default_key_c *dkc = ti->private; |
| + struct dm_arg_set as; |
| + static const struct dm_arg _args[] = { |
| + {0, 3, "Invalid number of feature args"}, |
| + }; |
| + unsigned int opt_params; |
| + const char *opt_string; |
| + bool iv_large_sectors = false; |
| + char dummy; |
| + int err; |
| + |
| + as.argc = argc; |
| + as.argv = argv; |
| + |
| + err = dm_read_arg_group(_args, &as, &opt_params, &ti->error); |
| + if (err) |
| + return err; |
| + |
| + while (opt_params--) { |
| + opt_string = dm_shift_arg(&as); |
| + if (!opt_string) { |
| + ti->error = "Not enough feature arguments"; |
| + return -EINVAL; |
| + } |
| + if (!strcmp(opt_string, "allow_discards")) { |
| + ti->num_discard_bios = 1; |
| + } else if (sscanf(opt_string, "sector_size:%u%c", |
| + &dkc->sector_size, &dummy) == 1) { |
| + if (dkc->sector_size < SECTOR_SIZE || |
| + dkc->sector_size > 4096 || |
| + !is_power_of_2(dkc->sector_size)) { |
| + ti->error = "Invalid sector_size"; |
| + return -EINVAL; |
| + } |
| + } else if (!strcmp(opt_string, "iv_large_sectors")) { |
| + iv_large_sectors = true; |
| + } else { |
| + ti->error = "Invalid feature arguments"; |
| + return -EINVAL; |
| + } |
| + } |
| + |
| + /* dm-default-key doesn't implement iv_large_sectors=false. */ |
| + if (dkc->sector_size != SECTOR_SIZE && !iv_large_sectors) { |
| + ti->error = "iv_large_sectors must be specified"; |
| + return -EINVAL; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +/* |
| + * Construct a default-key mapping: |
| + * <cipher> <key> <iv_offset> <dev_path> <start> |
| + * |
| + * This syntax matches dm-crypt's, but lots of unneeded functionality has been |
| + * removed. Also, dm-default-key requires that the "iv_large_sectors" option be |
| + * given whenever a non-default sector size is used. |
| + */ |
| +static int default_key_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
| +{ |
| + struct default_key_c *dkc; |
| + const struct dm_default_key_cipher *cipher; |
| + u8 raw_key[DM_DEFAULT_KEY_MAX_KEY_SIZE]; |
| + unsigned int dun_bytes; |
| + unsigned long long tmpll; |
| + char dummy; |
| + int err; |
| + |
| + if (argc < 5) { |
| + ti->error = "Not enough arguments"; |
| + return -EINVAL; |
| + } |
| + |
| + dkc = kzalloc(sizeof(*dkc), GFP_KERNEL); |
| + if (!dkc) { |
| + ti->error = "Out of memory"; |
| + return -ENOMEM; |
| + } |
| + ti->private = dkc; |
| + |
| + /* <cipher> */ |
| + dkc->cipher_string = kstrdup(argv[0], GFP_KERNEL); |
| + if (!dkc->cipher_string) { |
| + ti->error = "Out of memory"; |
| + err = -ENOMEM; |
| + goto bad; |
| + } |
| + cipher = lookup_cipher(dkc->cipher_string); |
| + if (!cipher) { |
| + ti->error = "Unsupported cipher"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + |
| + /* <key> */ |
| + if (strlen(argv[1]) != 2 * cipher->key_size) { |
| + ti->error = "Incorrect key size for cipher"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + if (hex2bin(raw_key, argv[1], cipher->key_size) != 0) { |
| + ti->error = "Malformed key string"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + |
| + /* <iv_offset> */ |
| + if (sscanf(argv[2], "%llu%c", &dkc->iv_offset, &dummy) != 1) { |
| + ti->error = "Invalid iv_offset sector"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + |
| + /* <dev_path> */ |
| + err = dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), |
| + &dkc->dev); |
| + if (err) { |
| + ti->error = "Device lookup failed"; |
| + goto bad; |
| + } |
| + |
| + /* <start> */ |
| + if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1 || |
| + tmpll != (sector_t)tmpll) { |
| + ti->error = "Invalid start sector"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + dkc->start = tmpll; |
| + |
| + /* optional arguments */ |
| + dkc->sector_size = SECTOR_SIZE; |
| + if (argc > 5) { |
| + err = default_key_ctr_optional(ti, argc - 5, &argv[5]); |
| + if (err) |
| + goto bad; |
| + } |
| + dkc->sector_bits = ilog2(dkc->sector_size); |
| + if (ti->len & ((dkc->sector_size >> SECTOR_SHIFT) - 1)) { |
| + ti->error = "Device size is not a multiple of sector_size"; |
| + err = -EINVAL; |
| + goto bad; |
| + } |
| + |
| + dkc->max_dun = (dkc->iv_offset + ti->len - 1) >> |
| + (dkc->sector_bits - SECTOR_SHIFT); |
| + dun_bytes = DIV_ROUND_UP(fls64(dkc->max_dun), 8); |
| + |
| + err = blk_crypto_init_key(&dkc->key, raw_key, cipher->mode_num, |
| + dun_bytes, dkc->sector_size); |
| + if (err) { |
| + ti->error = "Error initializing blk-crypto key"; |
| + goto bad; |
| + } |
| + |
| + err = blk_crypto_start_using_key(dkc->dev->bdev, &dkc->key); |
| + if (err) { |
| + ti->error = "Error starting to use blk-crypto"; |
| + goto bad; |
| + } |
| + |
| + ti->num_flush_bios = 1; |
| + |
| + err = 0; |
| + goto out; |
| + |
| +bad: |
| + default_key_dtr(ti); |
| +out: |
| + memzero_explicit(raw_key, sizeof(raw_key)); |
| + return err; |
| +} |
| + |
| +static int default_key_map(struct dm_target *ti, struct bio *bio) |
| +{ |
| + const struct default_key_c *dkc = ti->private; |
| + sector_t sector_in_target; |
| + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { 0 }; |
| + |
| + bio_set_dev(bio, dkc->dev->bdev); |
| + |
| + /* |
| + * If the bio is a device-level request which doesn't target a specific |
| + * sector, there's nothing more to do. |
| + */ |
| + if (bio_sectors(bio) == 0) |
| + return DM_MAPIO_REMAPPED; |
| + |
| + /* Map the bio's sector to the underlying device. (512-byte sectors) */ |
| + sector_in_target = dm_target_offset(ti, bio->bi_iter.bi_sector); |
| + bio->bi_iter.bi_sector = dkc->start + sector_in_target; |
| + |
| + /* |
| + * If the bio should skip dm-default-key (i.e. if it's for an encrypted |
| + * file's contents), or if it doesn't have any data (e.g. if it's a |
| + * DISCARD request), there's nothing more to do. |
| + */ |
| + if (bio_should_skip_dm_default_key(bio) || !bio_has_data(bio)) |
| + return DM_MAPIO_REMAPPED; |
| + |
| + /* |
| + * Else, dm-default-key needs to set this bio's encryption context. |
| + * It must not already have one. |
| + */ |
| + if (WARN_ON_ONCE(bio_has_crypt_ctx(bio))) |
| + return DM_MAPIO_KILL; |
| + |
| + /* Calculate the DUN and enforce data-unit (crypto sector) alignment. */ |
| + dun[0] = dkc->iv_offset + sector_in_target; /* 512-byte sectors */ |
| + if (dun[0] & ((dkc->sector_size >> SECTOR_SHIFT) - 1)) |
| + return DM_MAPIO_KILL; |
| + dun[0] >>= dkc->sector_bits - SECTOR_SHIFT; /* crypto sectors */ |
| + |
| + /* |
| + * This check isn't necessary as we should have calculated max_dun |
| + * correctly, but be safe. |
| + */ |
| + if (WARN_ON_ONCE(dun[0] > dkc->max_dun)) |
| + return DM_MAPIO_KILL; |
| + |
| + bio_crypt_set_ctx(bio, &dkc->key, dun, GFP_NOIO); |
| + |
| + return DM_MAPIO_REMAPPED; |
| +} |
| + |
| +static void default_key_status(struct dm_target *ti, status_type_t type, |
| + unsigned int status_flags, char *result, |
| + unsigned int maxlen) |
| +{ |
| + const struct default_key_c *dkc = ti->private; |
| + unsigned int sz = 0; |
| + int num_feature_args = 0; |
| + |
| + switch (type) { |
| + case STATUSTYPE_INFO: |
| + case STATUSTYPE_IMA: |
| + result[0] = '\0'; |
| + break; |
| + |
| + case STATUSTYPE_TABLE: |
| + /* Omit the key for now. */ |
| + DMEMIT("%s - %llu %s %llu", dkc->cipher_string, dkc->iv_offset, |
| + dkc->dev->name, (unsigned long long)dkc->start); |
| + |
| + num_feature_args += !!ti->num_discard_bios; |
| + if (dkc->sector_size != SECTOR_SIZE) |
| + num_feature_args += 2; |
| + if (num_feature_args != 0) { |
| + DMEMIT(" %d", num_feature_args); |
| + if (ti->num_discard_bios) |
| + DMEMIT(" allow_discards"); |
| + if (dkc->sector_size != SECTOR_SIZE) { |
| + DMEMIT(" sector_size:%u", dkc->sector_size); |
| + DMEMIT(" iv_large_sectors"); |
| + } |
| + } |
| + break; |
| + } |
| +} |
| + |
| +static int default_key_prepare_ioctl(struct dm_target *ti, |
| + struct block_device **bdev) |
| +{ |
| + const struct default_key_c *dkc = ti->private; |
| + const struct dm_dev *dev = dkc->dev; |
| + |
| + *bdev = dev->bdev; |
| + |
| + /* Only pass ioctls through if the device sizes match exactly. */ |
| + if (dkc->start != 0 || |
| + ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) |
| + return 1; |
| + return 0; |
| +} |
| + |
| +static int default_key_iterate_devices(struct dm_target *ti, |
| + iterate_devices_callout_fn fn, |
| + void *data) |
| +{ |
| + const struct default_key_c *dkc = ti->private; |
| + |
| + return fn(ti, dkc->dev, dkc->start, ti->len, data); |
| +} |
| + |
| +static void default_key_io_hints(struct dm_target *ti, |
| + struct queue_limits *limits) |
| +{ |
| + const struct default_key_c *dkc = ti->private; |
| + const unsigned int sector_size = dkc->sector_size; |
| + |
| + limits->logical_block_size = |
| + max_t(unsigned int, limits->logical_block_size, sector_size); |
| + limits->physical_block_size = |
| + max_t(unsigned int, limits->physical_block_size, sector_size); |
| + limits->io_min = max_t(unsigned int, limits->io_min, sector_size); |
| +} |
| + |
| +static struct target_type default_key_target = { |
| + .name = "default-key", |
| + .version = {2, 0, 0}, |
| + .features = DM_TARGET_PASSES_CRYPTO, |
| + .module = THIS_MODULE, |
| + .ctr = default_key_ctr, |
| + .dtr = default_key_dtr, |
| + .map = default_key_map, |
| + .status = default_key_status, |
| + .prepare_ioctl = default_key_prepare_ioctl, |
| + .iterate_devices = default_key_iterate_devices, |
| + .io_hints = default_key_io_hints, |
| +}; |
| + |
| +static int __init dm_default_key_init(void) |
| +{ |
| + return dm_register_target(&default_key_target); |
| +} |
| + |
| +static void __exit dm_default_key_exit(void) |
| +{ |
| + dm_unregister_target(&default_key_target); |
| +} |
| + |
| +module_init(dm_default_key_init); |
| +module_exit(dm_default_key_exit); |
| + |
| +MODULE_AUTHOR("Paul Lawrence <paullawrence@google.com>"); |
| +MODULE_AUTHOR("Paul Crowley <paulcrowley@google.com>"); |
| +MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>"); |
| +MODULE_DESCRIPTION(DM_NAME " target for encrypting filesystem metadata"); |
| +MODULE_LICENSE("GPL"); |
| diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c |
| --- a/fs/crypto/inline_crypt.c |
| +++ b/fs/crypto/inline_crypt.c |
| @@ -297,6 +297,8 @@ static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num, |
| * otherwise fscrypt_mergeable_bio() won't work as intended. |
| * |
| * The encryption context will be freed automatically when the bio is freed. |
| + * |
| + * This function also handles setting bi_skip_dm_default_key when needed. |
| */ |
| void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, |
| u64 first_lblk, gfp_t gfp_mask) |
| @@ -304,6 +306,9 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, |
| const struct fscrypt_info *ci; |
| u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; |
| |
| + if (fscrypt_inode_should_skip_dm_default_key(inode)) |
| + bio_set_skip_dm_default_key(bio); |
| + |
| if (!fscrypt_inode_uses_inline_crypto(inode)) |
| return; |
| ci = inode->i_crypt_info; |
| @@ -378,6 +383,9 @@ EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_bh); |
| * another way, such as I/O targeting only a single file (and thus a single key) |
| * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity. |
| * |
| + * This function also returns false if the next part of the I/O would need to |
| + * have a different value for the bi_skip_dm_default_key flag. |
| + * |
| * Return: true iff the I/O is mergeable |
| */ |
| bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, |
| @@ -388,6 +396,9 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, |
| |
| if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) |
| return false; |
| + if (bio_should_skip_dm_default_key(bio) != |
| + fscrypt_inode_should_skip_dm_default_key(inode)) |
| + return false; |
| if (!bc) |
| return true; |
| |
| @@ -421,7 +432,8 @@ bool fscrypt_mergeable_bio_bh(struct bio *bio, |
| u64 next_lblk; |
| |
| if (!bh_get_inode_and_lblk_num(next_bh, &inode, &next_lblk)) |
| - return !bio->bi_crypt_context; |
| + return !bio->bi_crypt_context && |
| + !bio_should_skip_dm_default_key(bio); |
| |
| return fscrypt_mergeable_bio(bio, inode, next_lblk); |
| } |
| diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c |
| --- a/fs/f2fs/data.c |
| +++ b/fs/f2fs/data.c |
| @@ -493,6 +493,8 @@ static void f2fs_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, |
| */ |
| if (!fio || !fio->encrypted_page) |
| fscrypt_set_bio_crypt_ctx(bio, inode, first_idx, gfp_mask); |
| + else if (fscrypt_inode_should_skip_dm_default_key(inode)) |
| + bio_set_skip_dm_default_key(bio); |
| } |
| |
| static bool f2fs_crypt_mergeable_bio(struct bio *bio, const struct inode *inode, |
| @@ -504,7 +506,9 @@ static bool f2fs_crypt_mergeable_bio(struct bio *bio, const struct inode *inode, |
| * read/write raw data without encryption. |
| */ |
| if (fio && fio->encrypted_page) |
| - return !bio_has_crypt_ctx(bio); |
| + return !bio_has_crypt_ctx(bio) && |
| + (bio_should_skip_dm_default_key(bio) == |
| + fscrypt_inode_should_skip_dm_default_key(inode)); |
| |
| return fscrypt_mergeable_bio(bio, inode, next_idx); |
| } |
| diff --git a/include/linux/blk-crypto.h b/include/linux/blk-crypto.h |
| --- a/include/linux/blk-crypto.h |
| +++ b/include/linux/blk-crypto.h |
| @@ -172,6 +172,9 @@ static inline bool bio_has_crypt_ctx(struct bio *bio) |
| |
| #endif /* CONFIG_BLK_INLINE_ENCRYPTION */ |
| |
| +static inline void bio_clone_skip_dm_default_key(struct bio *dst, |
| + const struct bio *src); |
| + |
| int __bio_crypt_clone(struct bio *dst, struct bio *src, gfp_t gfp_mask); |
| /** |
| * bio_crypt_clone - clone bio encryption context |
| @@ -187,9 +190,42 @@ int __bio_crypt_clone(struct bio *dst, struct bio *src, gfp_t gfp_mask); |
| static inline int bio_crypt_clone(struct bio *dst, struct bio *src, |
| gfp_t gfp_mask) |
| { |
| + bio_clone_skip_dm_default_key(dst, src); |
| if (bio_has_crypt_ctx(src)) |
| return __bio_crypt_clone(dst, src, gfp_mask); |
| return 0; |
| } |
| |
| +#if IS_ENABLED(CONFIG_DM_DEFAULT_KEY) |
| +static inline void bio_set_skip_dm_default_key(struct bio *bio) |
| +{ |
| + bio->bi_skip_dm_default_key = true; |
| +} |
| + |
| +static inline bool bio_should_skip_dm_default_key(const struct bio *bio) |
| +{ |
| + return bio->bi_skip_dm_default_key; |
| +} |
| + |
| +static inline void bio_clone_skip_dm_default_key(struct bio *dst, |
| + const struct bio *src) |
| +{ |
| + dst->bi_skip_dm_default_key = src->bi_skip_dm_default_key; |
| +} |
| +#else /* CONFIG_DM_DEFAULT_KEY */ |
| +static inline void bio_set_skip_dm_default_key(struct bio *bio) |
| +{ |
| +} |
| + |
| +static inline bool bio_should_skip_dm_default_key(const struct bio *bio) |
| +{ |
| + return false; |
| +} |
| + |
| +static inline void bio_clone_skip_dm_default_key(struct bio *dst, |
| + const struct bio *src) |
| +{ |
| +} |
| +#endif /* !CONFIG_DM_DEFAULT_KEY */ |
| + |
| #endif /* __LINUX_BLK_CRYPTO_H */ |
| diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h |
| --- a/include/linux/blk_types.h |
| +++ b/include/linux/blk_types.h |
| @@ -281,6 +281,9 @@ struct bio { |
| |
| #ifdef CONFIG_BLK_INLINE_ENCRYPTION |
| struct bio_crypt_ctx *bi_crypt_context; |
| +#if IS_ENABLED(CONFIG_DM_DEFAULT_KEY) |
| + bool bi_skip_dm_default_key; |
| +#endif |
| #endif |
| |
| union { |
| diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h |
| --- a/include/linux/fscrypt.h |
| +++ b/include/linux/fscrypt.h |
| @@ -809,6 +809,20 @@ static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, |
| } |
| #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ |
| |
| +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_DM_DEFAULT_KEY) |
| +static inline bool |
| +fscrypt_inode_should_skip_dm_default_key(const struct inode *inode) |
| +{ |
| + return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); |
| +} |
| +#else |
| +static inline bool |
| +fscrypt_inode_should_skip_dm_default_key(const struct inode *inode) |
| +{ |
| + return false; |
| +} |
| +#endif |
| + |
| /** |
| * fscrypt_inode_uses_inline_crypto() - test whether an inode uses inline |
| * encryption |