mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
Merge tag 'for-7.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper updates from Mikulas Patocka: - small cleanups in dm-vdo, dm-raid, dm-cache, dm-zoned-metadata - rework of dm-ima - introduce dm-inlinecrypt - fix wrong return value in dm-ioctl - fix rcu stall when polling * tag 'for-7.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: dm-zoned-metadata: Use strscpy() to copy device name dm cache: make smq background work limit configurable dm-inlinecrypt: add support for hardware-wrapped keys dm: limit target bio polling to one shot dm-ioctl: report an error if a device has no table dm: add documentation for dm-inlinecrypt target dm-inlinecrypt: add target for inline block device encryption block: export blk-crypto symbols required by dm-inlinecrypt dm-ima: use active table's size if available dm-ima: Fail more gracefully in dm_ima_measure_on_* dm-ima: Handle race between rename and table swap dm-ima: Fix issues with dm_ima_measure_on_device_rename dm-ima: remove new_map from dm_ima_measure_on_device_clear dm-ima: Fix UAF errors and measuring incorrect context dm-ima: don't copy the active table to the inactive table dm-ima: Remove status_flags from dm_ima_measure_on_table_load() dm-ima: remove broken last_target_measured logic dm-ima: remove dm_ima_reset_data() dm-raid: only requeue bios when dm is suspending dm vdo: use get_random_u32() where appropriate
This commit is contained in:
129
Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst
Normal file
129
Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst
Normal file
@@ -0,0 +1,129 @@
|
||||
==============
|
||||
dm-inlinecrypt
|
||||
==============
|
||||
|
||||
Device-Mapper's "inlinecrypt" target provides transparent encryption of block devices
|
||||
using the inline encryption hardware.
|
||||
|
||||
For a more detailed description of inline encryption, see:
|
||||
https://docs.kernel.org/block/inline-encryption.html
|
||||
|
||||
Parameters::
|
||||
|
||||
<cipher> <key> <iv_offset> <device path> \
|
||||
<offset> [<#opt_params> <opt_params>]
|
||||
|
||||
<cipher>
|
||||
Encryption cipher type.
|
||||
|
||||
The cipher specifications format is::
|
||||
|
||||
cipher
|
||||
|
||||
Examples::
|
||||
|
||||
aes-xts-plain64
|
||||
|
||||
The cipher type corresponds to the encryption modes supported by
|
||||
inline crypto in the block layer. Currently, only
|
||||
BLK_ENCRYPTION_MODE_AES_256_XTS (i.e. aes-xts-plain64) is supported.
|
||||
|
||||
<key>
|
||||
Key used for encryption. It is encoded either as a hexadecimal number
|
||||
or it can be passed as <key_string> prefixed with single colon
|
||||
character (':') for keys residing in kernel keyring service.
|
||||
You can only use key sizes that are valid for the selected cipher.
|
||||
Note that the size in bytes of a valid key must be in bellow range.
|
||||
|
||||
[BLK_CRYPTO_KEY_TYPE_RAW, BLK_CRYPTO_KEY_TYPE_HW_WRAPPED]
|
||||
|
||||
<key_string>
|
||||
The kernel keyring key is identified by string in following format:
|
||||
<key_size>:<keyring_type>:<key_description>.
|
||||
|
||||
<key_size>
|
||||
The encryption key size in bytes. The kernel key payload size must match
|
||||
the value passed in <key_size>.
|
||||
|
||||
<keyring_type>
|
||||
The type of the key inside the kernel keyring. It can be either 'logon',
|
||||
or 'trusted' kernel key type.
|
||||
|
||||
<key_description>
|
||||
The kernel keyring key description inlinecrypt target should look for
|
||||
when loading key of <keyring_type>.
|
||||
|
||||
<iv_offset>
|
||||
The IV offset is a sector count that is added to the sector number
|
||||
before creating the IV.
|
||||
|
||||
<device path>
|
||||
This is the device that is going to be used as backend and contains the
|
||||
encrypted data. You can specify it as a path like /dev/xxx or a device
|
||||
number <major>:<minor>.
|
||||
|
||||
<offset>
|
||||
Starting sector within the device where the encrypted data begins.
|
||||
|
||||
<#opt_params>
|
||||
Number of optional parameters. If there are no optional parameters,
|
||||
the optional parameters section can be skipped or #opt_params can be zero.
|
||||
Otherwise #opt_params is the number of following arguments.
|
||||
|
||||
Example of optional parameters section:
|
||||
keytype:raw allow_discards sector_size:4096 iv_large_sectors
|
||||
|
||||
<key_type>
|
||||
The type of the key as seen by the block layer, either standard or
|
||||
hardware-wrapped. The string is supplied in the table as <keytype:raw>
|
||||
or <keytype:hw-wrapped>.
|
||||
|
||||
allow_discards
|
||||
Block discard requests (a.k.a. TRIM) are passed through the inlinecrypt
|
||||
device. The default is to ignore discard requests.
|
||||
|
||||
WARNING: Assess the specific security risks carefully before enabling this
|
||||
option. For example, allowing discards on encrypted devices may lead to
|
||||
the leak of information about the ciphertext device (filesystem type,
|
||||
used space etc.) if the discarded blocks can be located easily on the
|
||||
device later.
|
||||
|
||||
sector_size:<bytes>
|
||||
Use <bytes> as the encryption unit instead of 512 bytes sectors.
|
||||
This option can be in range 512 - 4096 bytes and must be power of two.
|
||||
Virtual device will announce this size as a minimal IO and logical sector.
|
||||
|
||||
iv_large_sectors
|
||||
Use <sector_size>-based sector numbers for IV generation instead of
|
||||
512-byte sectors.
|
||||
|
||||
For dm-inlinecrypt, this flag must be specified when <sector_size>
|
||||
is larger than 512 bytes. The legacy 512-byte-based IV behavior is
|
||||
not supported.
|
||||
|
||||
When specified, if <sector_size> is 4096 bytes, plain64 IV for the
|
||||
second sector will be 1, and <iv_offset> must be a multiple of
|
||||
<sector_size> (in 512-byte units).
|
||||
|
||||
Example scripts
|
||||
===============
|
||||
Currently, dm-inlinecrypt devices must be set up directly using dmsetup.
|
||||
There is no userspace support yet to integrate dm-inlinecrypt with LUKS
|
||||
or cryptsetup. In particular, cryptsetup currently only supports
|
||||
dm-crypt, and cannot be used to create dm-inlinecrypt mappings.
|
||||
|
||||
The following examples demonstrate how to create dm-inlinecrypt devices
|
||||
using dmsetup
|
||||
|
||||
::
|
||||
|
||||
#!/bin/sh
|
||||
# Create a inlinecrypt device using dmsetup
|
||||
dmsetup create inlinecrypt1 --table "0 `blockdev --getsz $1` inlinecrypt aes-xts-plain64 babebabebabebabebabebabebabebabebabebabebabebabebabebabebabebabe 0 0 $1 0 1 keytype:raw"
|
||||
|
||||
::
|
||||
|
||||
#!/bin/sh
|
||||
# Create a inlinecrypt device using dmsetup when encryption key is stored in keyring service
|
||||
dmsetup create inlinecrypt2 --table "0 `blockdev --getsz $1` inlinecrypt aes-xts-plain64 :64:logon:fde:dminlinecrypt_test_key 0 0 $1 0 1 keytype:raw"
|
||||
|
||||
@@ -15,6 +15,7 @@ Device Mapper
|
||||
dm-flakey
|
||||
dm-ima
|
||||
dm-init
|
||||
dm-inlinecrypt
|
||||
dm-integrity
|
||||
dm-io
|
||||
dm-log
|
||||
|
||||
@@ -116,6 +116,7 @@ void bio_crypt_set_ctx(struct bio *bio, const struct blk_crypto_key *key,
|
||||
|
||||
bio->bi_crypt_context = bc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bio_crypt_set_ctx);
|
||||
|
||||
void __bio_crypt_free_ctx(struct bio *bio)
|
||||
{
|
||||
@@ -348,6 +349,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_crypto_init_key);
|
||||
|
||||
bool blk_crypto_config_supported_natively(struct block_device *bdev,
|
||||
const struct blk_crypto_config *cfg)
|
||||
@@ -398,6 +400,7 @@ int blk_crypto_start_using_key(struct block_device *bdev,
|
||||
}
|
||||
return blk_crypto_fallback_start_using_mode(key->crypto_cfg.crypto_mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_crypto_start_using_key);
|
||||
|
||||
/**
|
||||
* blk_crypto_evict_key() - Evict a blk_crypto_key from a block_device
|
||||
|
||||
@@ -315,6 +315,17 @@ config DM_CRYPT
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DM_INLINECRYPT
|
||||
tristate "Inline encryption target support"
|
||||
depends on BLK_DEV_DM
|
||||
depends on (KEYS || KEYS=n)
|
||||
depends on BLK_INLINE_ENCRYPTION
|
||||
help
|
||||
This device-mapper target is similar to dm-crypt, but it uses the
|
||||
blk-crypto API instead of the regular crypto API. This allows it to
|
||||
take advantage of inline encryption hardware such as that commonly
|
||||
built into UFS host controllers.
|
||||
|
||||
config DM_SNAPSHOT
|
||||
tristate "Snapshot target"
|
||||
depends on BLK_DEV_DM
|
||||
|
||||
@@ -55,6 +55,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_INLINECRYPT) += dm-inlinecrypt.o
|
||||
obj-$(CONFIG_DM_DELAY) += dm-delay.o
|
||||
obj-$(CONFIG_DM_DUST) += dm-dust.o
|
||||
obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
|
||||
|
||||
@@ -21,6 +21,16 @@
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Maximum number of concurrent background work items (promotions,
|
||||
* demotions, writebacks) that can be queued in the background tracker.
|
||||
* Tuneable via the module parameter smq_max_background_work.
|
||||
* Only affects newly created cache devices.
|
||||
*/
|
||||
static unsigned int smq_max_background_work = 4096;
|
||||
module_param(smq_max_background_work, uint, 0644);
|
||||
MODULE_PARM_DESC(smq_max_background_work, "Max concurrent background work items");
|
||||
|
||||
/*
|
||||
* Safe division functions that return zero on divide by zero.
|
||||
*/
|
||||
@@ -1820,7 +1830,7 @@ __smq_create(dm_cblock_t cache_size, sector_t origin_size, sector_t cache_block_
|
||||
mq->next_hotspot_period = jiffies;
|
||||
mq->next_cache_period = jiffies;
|
||||
|
||||
mq->bg_work = btracker_create(4096); /* FIXME: hard coded value */
|
||||
mq->bg_work = btracker_create(max(1u, smq_max_background_work));
|
||||
if (!mq->bg_work)
|
||||
goto bad_btracker;
|
||||
|
||||
|
||||
@@ -21,25 +21,32 @@
|
||||
* character, so that they don't interfere with the construction of key-value pairs,
|
||||
* and clients can split the key1=val1,key2=val2,key3=val3; pairs properly.
|
||||
*/
|
||||
static void fix_separator_chars(char **buf)
|
||||
static void fix_separator_chars(char *buf)
|
||||
{
|
||||
int l = strlen(*buf);
|
||||
int l = strlen(buf);
|
||||
int i, j, sp = 0;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
|
||||
if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
|
||||
sp++;
|
||||
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
buf[l + sp] = '\0';
|
||||
for (i = l-1, j = i+sp; i >= 0; i--) {
|
||||
(*buf)[j--] = (*buf)[i];
|
||||
if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
|
||||
(*buf)[j--] = '\\';
|
||||
buf[j--] = buf[i];
|
||||
if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
|
||||
buf[j--] = '\\';
|
||||
}
|
||||
}
|
||||
|
||||
static void fix_context_strings(struct dm_ima_context *context)
|
||||
{
|
||||
fix_separator_chars(context->dev_name);
|
||||
fix_separator_chars(context->dev_uuid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal function to allocate memory for IMA measurements.
|
||||
*/
|
||||
@@ -59,68 +66,85 @@ static void *dm_ima_alloc(size_t len, bool noio)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal function to allocate and copy name and uuid for IMA measurements.
|
||||
*/
|
||||
static int dm_ima_alloc_and_copy_name_uuid(struct mapped_device *md, char **dev_name,
|
||||
char **dev_uuid, bool noio)
|
||||
void dm_ima_init(struct mapped_device *md)
|
||||
{
|
||||
int r;
|
||||
*dev_name = dm_ima_alloc(DM_NAME_LEN*2, noio);
|
||||
if (!(*dev_name)) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
md->ima.update_idx = 0;
|
||||
md->ima.measure_idx = 0;
|
||||
init_waitqueue_head(&md->ima.ima_wq);
|
||||
spin_lock_init(&md->ima.ima_lock);
|
||||
}
|
||||
|
||||
void dm_ima_alloc_context(struct dm_ima_context **context, bool noio)
|
||||
{
|
||||
*context = dm_ima_alloc(sizeof(struct dm_ima_context), noio);
|
||||
}
|
||||
|
||||
void dm_ima_free_context(struct dm_ima_context *context)
|
||||
{
|
||||
if (likely(context)) {
|
||||
kfree(context->table.device_metadata);
|
||||
kfree(context->table.hash);
|
||||
kfree(context);
|
||||
}
|
||||
}
|
||||
|
||||
*dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, noio);
|
||||
if (!(*dev_uuid)) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
static void wait_to_measure(struct dm_ima_measurements *ima,
|
||||
unsigned int update_idx)
|
||||
{
|
||||
spin_lock_irq(&ima->ima_lock);
|
||||
wait_event_lock_irq(ima->ima_wq,
|
||||
ima->measure_idx == update_idx,
|
||||
ima->ima_lock);
|
||||
spin_unlock_irq(&ima->ima_lock);
|
||||
}
|
||||
|
||||
r = dm_copy_name_and_uuid(md, *dev_name, *dev_uuid);
|
||||
if (r)
|
||||
goto error;
|
||||
|
||||
fix_separator_chars(dev_name);
|
||||
fix_separator_chars(dev_uuid);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
kfree(*dev_name);
|
||||
kfree(*dev_uuid);
|
||||
*dev_name = NULL;
|
||||
*dev_uuid = NULL;
|
||||
return r;
|
||||
static void wake_next_measure(struct dm_ima_measurements *ima)
|
||||
{
|
||||
spin_lock_irq(&ima->ima_lock);
|
||||
ima->measure_idx++;
|
||||
spin_unlock_irq(&ima->ima_lock);
|
||||
wake_up_all(&ima->ima_wq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal function to allocate and copy device data for IMA measurements.
|
||||
* Helper function for swapping the table, to make sure that the
|
||||
* correct table metadata is saved and restored.
|
||||
*/
|
||||
static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data,
|
||||
unsigned int num_targets, bool noio)
|
||||
void dm_ima_context_table_op(struct mapped_device *md,
|
||||
struct dm_ima_context *context,
|
||||
enum dm_ima_table_op op)
|
||||
{
|
||||
char *dev_name = NULL, *dev_uuid = NULL;
|
||||
int r;
|
||||
struct dm_ima_measurements *ima = &md->ima;
|
||||
|
||||
r = dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio);
|
||||
if (r)
|
||||
return r;
|
||||
if (unlikely(!context))
|
||||
return;
|
||||
|
||||
*device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
|
||||
if (!(*device_data)) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
wait_to_measure(ima, context->update_idx);
|
||||
|
||||
if (op == DM_IMA_TABLE_SAVE) {
|
||||
context->table = ima->inactive_table;
|
||||
memset(&ima->inactive_table, 0, sizeof(ima->inactive_table));
|
||||
} else {
|
||||
ima->inactive_table = context->table;
|
||||
memset(&context->table, 0, sizeof(context->table));
|
||||
}
|
||||
|
||||
scnprintf(*device_data, DM_IMA_DEVICE_BUF_LEN,
|
||||
wake_next_measure(ima);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal function to copy device data for IMA measurements.
|
||||
*/
|
||||
static void dm_ima_copy_device_data(struct mapped_device *md, char *device_data,
|
||||
struct dm_ima_context *context,
|
||||
unsigned int num_targets)
|
||||
{
|
||||
memset(device_data, 0, DM_IMA_DEVICE_BUF_LEN);
|
||||
scnprintf(device_data, DM_IMA_DEVICE_BUF_LEN,
|
||||
"name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;",
|
||||
dev_name, dev_uuid, md->disk->major, md->disk->first_minor,
|
||||
md->disk->minors, num_targets);
|
||||
error:
|
||||
kfree(dev_name);
|
||||
kfree(dev_uuid);
|
||||
return r;
|
||||
context->dev_name, context->dev_uuid, md->disk->major,
|
||||
md->disk->first_minor, md->disk->minors, num_targets);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -141,42 +165,21 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t
|
||||
memalloc_noio_restore(noio_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal function to allocate and copy current device capacity for IMA measurements.
|
||||
*/
|
||||
static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str,
|
||||
bool noio)
|
||||
static sector_t dm_ima_capacity(struct mapped_device *md)
|
||||
{
|
||||
sector_t capacity;
|
||||
|
||||
capacity = get_capacity(md->disk);
|
||||
|
||||
*capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, noio);
|
||||
if (!(*capacity_str))
|
||||
return -ENOMEM;
|
||||
|
||||
return scnprintf(*capacity_str, DM_IMA_DEVICE_BUF_LEN, "current_device_capacity=%llu;",
|
||||
capacity);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize/reset the dm ima related data structure variables.
|
||||
*/
|
||||
void dm_ima_reset_data(struct mapped_device *md)
|
||||
{
|
||||
memset(&(md->ima), 0, sizeof(md->ima));
|
||||
md->ima.dm_version_str_len = strlen(DM_IMA_VERSION_STR);
|
||||
return (md->ima.active_table.device_metadata) ?
|
||||
md->ima.active_table.capacity : get_capacity(md->disk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build up the IMA data for each target, and finally measure.
|
||||
*/
|
||||
void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags)
|
||||
void dm_ima_measure_on_table_load(struct dm_table *table,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0;
|
||||
char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL;
|
||||
char *ima_buf = NULL, *device_data_buf = NULL;
|
||||
int last_target_measured = -1;
|
||||
status_type_t type = STATUSTYPE_IMA;
|
||||
size_t cur_total_buf_len = 0;
|
||||
unsigned int num_targets, i;
|
||||
@@ -185,9 +188,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
bool noio = false;
|
||||
char table_load_event_name[] = "dm_table_load";
|
||||
|
||||
if (unlikely(!context))
|
||||
return;
|
||||
|
||||
wait_to_measure(&table->md->ima, context->update_idx);
|
||||
|
||||
ima_buf = dm_ima_alloc(DM_IMA_MEASUREMENT_BUF_LEN, noio);
|
||||
if (!ima_buf)
|
||||
return;
|
||||
goto error;
|
||||
|
||||
target_metadata_buf = dm_ima_alloc(DM_IMA_TARGET_METADATA_BUF_LEN, noio);
|
||||
if (!target_metadata_buf)
|
||||
@@ -199,13 +207,18 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
|
||||
num_targets = table->num_targets;
|
||||
|
||||
if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf, num_targets, noio))
|
||||
device_data_buf = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
|
||||
if (!device_data_buf)
|
||||
goto error;
|
||||
|
||||
fix_context_strings(context);
|
||||
dm_ima_copy_device_data(table->md, device_data_buf, context,
|
||||
num_targets);
|
||||
|
||||
sha256_init(&hash_ctx);
|
||||
|
||||
memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
|
||||
l += table->md->ima.dm_version_str_len;
|
||||
memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
|
||||
l += strlen(DM_IMA_VERSION_STR);
|
||||
|
||||
device_data_buf_len = strlen(device_data_buf);
|
||||
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
|
||||
@@ -214,8 +227,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
for (i = 0; i < num_targets; i++) {
|
||||
struct dm_target *ti = dm_table_get_target(table, i);
|
||||
|
||||
last_target_measured = 0;
|
||||
|
||||
/*
|
||||
* First retrieve the target metadata.
|
||||
*/
|
||||
@@ -229,7 +240,7 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
* Then retrieve the actual target data.
|
||||
*/
|
||||
if (ti->type->status)
|
||||
ti->type->status(ti, type, status_flags, target_data_buf,
|
||||
ti->type->status(ti, type, 0, target_data_buf,
|
||||
DM_IMA_TARGET_DATA_BUF_LEN);
|
||||
else
|
||||
target_data_buf[0] = '\0';
|
||||
@@ -260,19 +271,11 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
* prefix, so that multiple records from the same "dm_table_load" for
|
||||
* a given device can be linked together.
|
||||
*/
|
||||
memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
|
||||
l += table->md->ima.dm_version_str_len;
|
||||
memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
|
||||
l += strlen(DM_IMA_VERSION_STR);
|
||||
|
||||
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
|
||||
l += device_data_buf_len;
|
||||
|
||||
/*
|
||||
* If this iteration of the for loop turns out to be the last target
|
||||
* in the table, dm_ima_measure_data("dm_table_load", ...) doesn't need
|
||||
* to be called again, just the hash needs to be finalized.
|
||||
* "last_target_measured" tracks this state.
|
||||
*/
|
||||
last_target_measured = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -286,11 +289,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
l += target_data_buf_len;
|
||||
}
|
||||
|
||||
if (!last_target_measured) {
|
||||
dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
|
||||
|
||||
sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
|
||||
}
|
||||
dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
|
||||
sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
|
||||
|
||||
/*
|
||||
* Finalize the table hash, and store it in table->md->ima.inactive_table.hash,
|
||||
@@ -304,17 +304,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
if (!digest_buf)
|
||||
goto error;
|
||||
|
||||
if (table->md->ima.active_table.hash != table->md->ima.inactive_table.hash)
|
||||
kfree(table->md->ima.inactive_table.hash);
|
||||
|
||||
kfree(table->md->ima.inactive_table.hash);
|
||||
table->md->ima.inactive_table.hash = digest_buf;
|
||||
table->md->ima.inactive_table.hash_len = strlen(digest_buf);
|
||||
table->md->ima.inactive_table.num_targets = num_targets;
|
||||
table->md->ima.inactive_table.capacity = dm_table_get_size(table);
|
||||
|
||||
if (table->md->ima.active_table.device_metadata !=
|
||||
table->md->ima.inactive_table.device_metadata)
|
||||
kfree(table->md->ima.inactive_table.device_metadata);
|
||||
|
||||
kfree(table->md->ima.inactive_table.device_metadata);
|
||||
table->md->ima.inactive_table.device_metadata = device_data_buf;
|
||||
table->md->ima.inactive_table.device_metadata_len = device_data_buf_len;
|
||||
|
||||
@@ -326,65 +323,54 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
|
||||
kfree(ima_buf);
|
||||
kfree(target_metadata_buf);
|
||||
kfree(target_data_buf);
|
||||
|
||||
wake_next_measure(&table->md->ima);
|
||||
}
|
||||
|
||||
/*
|
||||
* Measure IMA data on device resume.
|
||||
*/
|
||||
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
|
||||
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
|
||||
char *device_table_data = NULL;
|
||||
char active[] = "active_table_hash=";
|
||||
unsigned int active_len = strlen(active);
|
||||
unsigned int l = 0;
|
||||
bool noio = true;
|
||||
bool nodata = true;
|
||||
int capacity_len;
|
||||
|
||||
if (unlikely(!context))
|
||||
return;
|
||||
|
||||
wait_to_measure(&md->ima, context->update_idx);
|
||||
|
||||
if (swap) {
|
||||
kfree(md->ima.active_table.hash);
|
||||
kfree(md->ima.active_table.device_metadata);
|
||||
md->ima.active_table = context->table;
|
||||
memset(&context->table, 0, sizeof(context->table));
|
||||
if (md->ima.active_table.device_metadata) {
|
||||
/*
|
||||
* A rename could have happened while the swap was
|
||||
* going on. In that case, the saved table info would
|
||||
* still have the old name. Update the metadata to be
|
||||
* sure that it has the current name
|
||||
*/
|
||||
struct dm_ima_device_table_metadata *table = &md->ima.active_table;
|
||||
fix_context_strings(context);
|
||||
dm_ima_copy_device_data(md, table->device_metadata,
|
||||
context, table->num_targets);
|
||||
table->device_metadata_len = strlen(table->device_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
|
||||
if (!device_table_data)
|
||||
return;
|
||||
|
||||
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
|
||||
if (capacity_len < 0)
|
||||
goto error;
|
||||
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
|
||||
l += md->ima.dm_version_str_len;
|
||||
|
||||
if (swap) {
|
||||
if (md->ima.active_table.hash != md->ima.inactive_table.hash)
|
||||
kfree(md->ima.active_table.hash);
|
||||
|
||||
md->ima.active_table.hash = NULL;
|
||||
md->ima.active_table.hash_len = 0;
|
||||
|
||||
if (md->ima.active_table.device_metadata !=
|
||||
md->ima.inactive_table.device_metadata)
|
||||
kfree(md->ima.active_table.device_metadata);
|
||||
|
||||
md->ima.active_table.device_metadata = NULL;
|
||||
md->ima.active_table.device_metadata_len = 0;
|
||||
md->ima.active_table.num_targets = 0;
|
||||
|
||||
if (md->ima.inactive_table.hash) {
|
||||
md->ima.active_table.hash = md->ima.inactive_table.hash;
|
||||
md->ima.active_table.hash_len = md->ima.inactive_table.hash_len;
|
||||
md->ima.inactive_table.hash = NULL;
|
||||
md->ima.inactive_table.hash_len = 0;
|
||||
}
|
||||
|
||||
if (md->ima.inactive_table.device_metadata) {
|
||||
md->ima.active_table.device_metadata =
|
||||
md->ima.inactive_table.device_metadata;
|
||||
md->ima.active_table.device_metadata_len =
|
||||
md->ima.inactive_table.device_metadata_len;
|
||||
md->ima.active_table.num_targets = md->ima.inactive_table.num_targets;
|
||||
md->ima.inactive_table.device_metadata = NULL;
|
||||
md->ima.inactive_table.device_metadata_len = 0;
|
||||
md->ima.inactive_table.num_targets = 0;
|
||||
}
|
||||
}
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
|
||||
l += strlen(DM_IMA_VERSION_STR);
|
||||
|
||||
if (md->ima.active_table.device_metadata) {
|
||||
memcpy(device_table_data + l, md->ima.active_table.device_metadata,
|
||||
@@ -409,59 +395,54 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
|
||||
}
|
||||
|
||||
if (nodata) {
|
||||
if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
|
||||
goto error;
|
||||
|
||||
fix_context_strings(context);
|
||||
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
|
||||
"%sname=%s,uuid=%s;device_resume=no_data;",
|
||||
DM_IMA_VERSION_STR, dev_name, dev_uuid);
|
||||
DM_IMA_VERSION_STR, context->dev_name,
|
||||
context->dev_uuid);
|
||||
}
|
||||
|
||||
memcpy(device_table_data + l, capacity_str, capacity_len);
|
||||
l += capacity_len;
|
||||
l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
|
||||
"current_device_capacity=%llu;", dm_ima_capacity(md));
|
||||
|
||||
dm_ima_measure_data("dm_device_resume", device_table_data, l, noio);
|
||||
|
||||
kfree(dev_name);
|
||||
kfree(dev_uuid);
|
||||
error:
|
||||
kfree(capacity_str);
|
||||
kfree(device_table_data);
|
||||
|
||||
wake_next_measure(&md->ima);
|
||||
}
|
||||
|
||||
/*
|
||||
* Measure IMA data on remove.
|
||||
*/
|
||||
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
|
||||
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
|
||||
struct dm_ima_context *context,
|
||||
unsigned int idx)
|
||||
{
|
||||
char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
|
||||
char *device_table_data;
|
||||
char active_table_str[] = "active_table_hash=";
|
||||
char inactive_table_str[] = "inactive_table_hash=";
|
||||
char device_active_str[] = "device_active_metadata=";
|
||||
char device_inactive_str[] = "device_inactive_metadata=";
|
||||
char remove_all_str[] = "remove_all=";
|
||||
unsigned int active_table_len = strlen(active_table_str);
|
||||
unsigned int inactive_table_len = strlen(inactive_table_str);
|
||||
unsigned int device_active_len = strlen(device_active_str);
|
||||
unsigned int device_inactive_len = strlen(device_inactive_str);
|
||||
unsigned int remove_all_len = strlen(remove_all_str);
|
||||
unsigned int l = 0;
|
||||
bool noio = true;
|
||||
bool nodata = true;
|
||||
int capacity_len;
|
||||
|
||||
wait_to_measure(&md->ima, idx);
|
||||
|
||||
if (unlikely(!context))
|
||||
goto exit;
|
||||
|
||||
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio);
|
||||
if (!device_table_data)
|
||||
goto exit;
|
||||
|
||||
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
|
||||
if (capacity_len < 0) {
|
||||
kfree(device_table_data);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
|
||||
l += md->ima.dm_version_str_len;
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
|
||||
l += strlen(DM_IMA_VERSION_STR);
|
||||
|
||||
if (md->ima.active_table.device_metadata) {
|
||||
memcpy(device_table_data + l, device_active_str, device_active_len);
|
||||
@@ -518,68 +499,57 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
|
||||
* in IMA measurements.
|
||||
*/
|
||||
if (nodata) {
|
||||
if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
|
||||
goto error;
|
||||
|
||||
fix_context_strings(context);
|
||||
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
|
||||
"%sname=%s,uuid=%s;device_remove=no_data;",
|
||||
DM_IMA_VERSION_STR, dev_name, dev_uuid);
|
||||
DM_IMA_VERSION_STR, context->dev_name,
|
||||
context->dev_uuid);
|
||||
}
|
||||
|
||||
memcpy(device_table_data + l, remove_all_str, remove_all_len);
|
||||
l += remove_all_len;
|
||||
memcpy(device_table_data + l, remove_all ? "y;" : "n;", 2);
|
||||
l += 2;
|
||||
|
||||
memcpy(device_table_data + l, capacity_str, capacity_len);
|
||||
l += capacity_len;
|
||||
l += scnprintf(device_table_data + l, (DM_IMA_DEVICE_BUF_LEN * 2) - l,
|
||||
"remove_all=%c;current_device_capacity=%llu;",
|
||||
remove_all ? 'y' : 'n', dm_ima_capacity(md));
|
||||
|
||||
dm_ima_measure_data("dm_device_remove", device_table_data, l, noio);
|
||||
|
||||
error:
|
||||
kfree(device_table_data);
|
||||
kfree(capacity_str);
|
||||
exit:
|
||||
kfree(md->ima.active_table.device_metadata);
|
||||
|
||||
if (md->ima.active_table.device_metadata !=
|
||||
md->ima.inactive_table.device_metadata)
|
||||
kfree(md->ima.inactive_table.device_metadata);
|
||||
kfree(md->ima.inactive_table.device_metadata);
|
||||
|
||||
kfree(md->ima.active_table.hash);
|
||||
kfree(md->ima.inactive_table.hash);
|
||||
|
||||
if (md->ima.active_table.hash != md->ima.inactive_table.hash)
|
||||
kfree(md->ima.inactive_table.hash);
|
||||
memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
|
||||
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
|
||||
|
||||
dm_ima_reset_data(md);
|
||||
|
||||
kfree(dev_name);
|
||||
kfree(dev_uuid);
|
||||
wake_next_measure(&md->ima);
|
||||
}
|
||||
|
||||
/*
|
||||
* Measure ima data on table clear.
|
||||
*/
|
||||
void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
|
||||
void dm_ima_measure_on_table_clear(struct mapped_device *md,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
unsigned int l = 0;
|
||||
char *device_table_data = NULL, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
|
||||
char *device_table_data = NULL;
|
||||
char inactive_str[] = "inactive_table_hash=";
|
||||
unsigned int inactive_len = strlen(inactive_str);
|
||||
bool noio = true;
|
||||
bool nodata = true;
|
||||
int capacity_len;
|
||||
|
||||
if (unlikely(!context))
|
||||
return;
|
||||
|
||||
wait_to_measure(&md->ima, context->update_idx);
|
||||
|
||||
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
|
||||
if (!device_table_data)
|
||||
return;
|
||||
goto error;
|
||||
|
||||
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
|
||||
if (capacity_len < 0)
|
||||
goto error1;
|
||||
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
|
||||
l += md->ima.dm_version_str_len;
|
||||
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
|
||||
l += strlen(DM_IMA_VERSION_STR);
|
||||
|
||||
if (md->ima.inactive_table.device_metadata_len &&
|
||||
md->ima.inactive_table.hash_len) {
|
||||
@@ -602,101 +572,79 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
|
||||
}
|
||||
|
||||
if (nodata) {
|
||||
if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
|
||||
goto error2;
|
||||
|
||||
fix_context_strings(context);
|
||||
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
|
||||
"%sname=%s,uuid=%s;table_clear=no_data;",
|
||||
DM_IMA_VERSION_STR, dev_name, dev_uuid);
|
||||
DM_IMA_VERSION_STR, context->dev_name,
|
||||
context->dev_uuid);
|
||||
}
|
||||
|
||||
memcpy(device_table_data + l, capacity_str, capacity_len);
|
||||
l += capacity_len;
|
||||
l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
|
||||
"current_device_capacity=%llu;", dm_ima_capacity(md));
|
||||
|
||||
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
|
||||
|
||||
if (new_map) {
|
||||
if (md->ima.inactive_table.hash &&
|
||||
md->ima.inactive_table.hash != md->ima.active_table.hash)
|
||||
kfree(md->ima.inactive_table.hash);
|
||||
error:
|
||||
kfree(md->ima.inactive_table.hash);
|
||||
kfree(md->ima.inactive_table.device_metadata);
|
||||
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
|
||||
|
||||
md->ima.inactive_table.hash = NULL;
|
||||
md->ima.inactive_table.hash_len = 0;
|
||||
|
||||
if (md->ima.inactive_table.device_metadata &&
|
||||
md->ima.inactive_table.device_metadata != md->ima.active_table.device_metadata)
|
||||
kfree(md->ima.inactive_table.device_metadata);
|
||||
|
||||
md->ima.inactive_table.device_metadata = NULL;
|
||||
md->ima.inactive_table.device_metadata_len = 0;
|
||||
md->ima.inactive_table.num_targets = 0;
|
||||
|
||||
if (md->ima.active_table.hash) {
|
||||
md->ima.inactive_table.hash = md->ima.active_table.hash;
|
||||
md->ima.inactive_table.hash_len = md->ima.active_table.hash_len;
|
||||
}
|
||||
|
||||
if (md->ima.active_table.device_metadata) {
|
||||
md->ima.inactive_table.device_metadata =
|
||||
md->ima.active_table.device_metadata;
|
||||
md->ima.inactive_table.device_metadata_len =
|
||||
md->ima.active_table.device_metadata_len;
|
||||
md->ima.inactive_table.num_targets =
|
||||
md->ima.active_table.num_targets;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(dev_name);
|
||||
kfree(dev_uuid);
|
||||
error2:
|
||||
kfree(capacity_str);
|
||||
error1:
|
||||
kfree(device_table_data);
|
||||
|
||||
wake_next_measure(&md->ima);
|
||||
}
|
||||
|
||||
/*
|
||||
* Measure IMA data on device rename.
|
||||
*/
|
||||
void dm_ima_measure_on_device_rename(struct mapped_device *md)
|
||||
void dm_ima_measure_on_device_rename(struct mapped_device *md,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL;
|
||||
char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL;
|
||||
char *old_device_data = NULL;
|
||||
char *combined_device_data = NULL;
|
||||
bool noio = true;
|
||||
int len;
|
||||
struct dm_ima_device_table_metadata *table;
|
||||
|
||||
if (dm_ima_alloc_and_copy_device_data(md, &new_device_data,
|
||||
md->ima.active_table.num_targets, noio))
|
||||
if (unlikely(!context))
|
||||
return;
|
||||
|
||||
if (dm_ima_alloc_and_copy_name_uuid(md, &new_dev_name, &new_dev_uuid, noio))
|
||||
goto error;
|
||||
wait_to_measure(&md->ima, context->update_idx);
|
||||
|
||||
fix_context_strings(context);
|
||||
|
||||
combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio);
|
||||
if (!combined_device_data)
|
||||
goto error;
|
||||
|
||||
if (dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio) < 0)
|
||||
goto error;
|
||||
|
||||
old_device_data = md->ima.active_table.device_metadata;
|
||||
|
||||
md->ima.active_table.device_metadata = new_device_data;
|
||||
md->ima.active_table.device_metadata_len = strlen(new_device_data);
|
||||
goto exit;
|
||||
|
||||
if (md->ima.active_table.device_metadata)
|
||||
old_device_data = md->ima.active_table.device_metadata;
|
||||
else if (md->ima.inactive_table.device_metadata)
|
||||
old_device_data = md->ima.inactive_table.device_metadata;
|
||||
else
|
||||
old_device_data = "device_rename=no_data;";
|
||||
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
|
||||
"%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
|
||||
new_dev_name, new_dev_uuid, capacity_str);
|
||||
"%s%snew_name=%s,new_uuid=%s;current_device_capacity=%llu;",
|
||||
DM_IMA_VERSION_STR, old_device_data, context->dev_name,
|
||||
context->dev_uuid, dm_ima_capacity(md));
|
||||
|
||||
dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
|
||||
|
||||
goto exit;
|
||||
|
||||
error:
|
||||
kfree(new_device_data);
|
||||
exit:
|
||||
kfree(capacity_str);
|
||||
kfree(combined_device_data);
|
||||
kfree(old_device_data);
|
||||
kfree(new_dev_name);
|
||||
kfree(new_dev_uuid);
|
||||
|
||||
exit:
|
||||
if (md->ima.active_table.device_metadata) {
|
||||
table = &md->ima.active_table;
|
||||
dm_ima_copy_device_data(md, table->device_metadata, context,
|
||||
table->num_targets);
|
||||
table->device_metadata_len = strlen(table->device_metadata);
|
||||
}
|
||||
|
||||
if (md->ima.inactive_table.device_metadata) {
|
||||
table = &md->ima.inactive_table;
|
||||
dm_ima_copy_device_data(md, table->device_metadata, context,
|
||||
table->num_targets);
|
||||
table->device_metadata_len = strlen(table->device_metadata);
|
||||
}
|
||||
|
||||
wake_next_measure(&md->ima);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
__dm_ima_str(DM_VERSION_MINOR) "." \
|
||||
__dm_ima_str(DM_VERSION_PATCHLEVEL) ";"
|
||||
|
||||
enum dm_ima_table_op {
|
||||
DM_IMA_TABLE_SAVE,
|
||||
DM_IMA_TABLE_RESTORE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IMA
|
||||
|
||||
struct dm_ima_device_table_metadata {
|
||||
@@ -36,6 +41,7 @@ struct dm_ima_device_table_metadata {
|
||||
char *device_metadata;
|
||||
unsigned int device_metadata_len;
|
||||
unsigned int num_targets;
|
||||
sector_t capacity;
|
||||
|
||||
/*
|
||||
* Contains the sha256 hashes of the IMA measurements of the target
|
||||
@@ -45,31 +51,67 @@ struct dm_ima_device_table_metadata {
|
||||
unsigned int hash_len;
|
||||
};
|
||||
|
||||
struct dm_ima_context {
|
||||
struct dm_ima_device_table_metadata table;
|
||||
unsigned int update_idx;
|
||||
char dev_name[DM_NAME_LEN*2];
|
||||
char dev_uuid[DM_UUID_LEN*2];
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure contains device metadata, and table hash for
|
||||
* active and inactive tables for ima measurements.
|
||||
*/
|
||||
struct dm_ima_measurements {
|
||||
unsigned int update_idx;
|
||||
unsigned int measure_idx;
|
||||
struct wait_queue_head ima_wq;
|
||||
spinlock_t ima_lock;
|
||||
struct dm_ima_device_table_metadata active_table;
|
||||
struct dm_ima_device_table_metadata inactive_table;
|
||||
unsigned int dm_version_str_len;
|
||||
};
|
||||
|
||||
void dm_ima_reset_data(struct mapped_device *md);
|
||||
void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
|
||||
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
|
||||
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all);
|
||||
void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map);
|
||||
void dm_ima_measure_on_device_rename(struct mapped_device *md);
|
||||
void dm_ima_init(struct mapped_device *md);
|
||||
void dm_ima_alloc_context(struct dm_ima_context **context, bool noio);
|
||||
void dm_ima_free_context(struct dm_ima_context *context);
|
||||
void dm_ima_context_table_op(struct mapped_device *md,
|
||||
struct dm_ima_context *context,
|
||||
enum dm_ima_table_op op);
|
||||
void dm_ima_measure_on_table_load(struct dm_table *table,
|
||||
struct dm_ima_context *context);
|
||||
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
|
||||
struct dm_ima_context *context);
|
||||
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
|
||||
struct dm_ima_context *context,
|
||||
unsigned int idx);
|
||||
void dm_ima_measure_on_table_clear(struct mapped_device *md,
|
||||
struct dm_ima_context *context);
|
||||
void dm_ima_measure_on_device_rename(struct mapped_device *md,
|
||||
struct dm_ima_context *context);
|
||||
|
||||
#else
|
||||
|
||||
static inline void dm_ima_reset_data(struct mapped_device *md) {}
|
||||
static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
|
||||
static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
|
||||
static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {}
|
||||
static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) {}
|
||||
static inline void dm_ima_measure_on_device_rename(struct mapped_device *md) {}
|
||||
struct dm_ima_context;
|
||||
|
||||
static inline void dm_ima_init(struct mapped_device *md) {}
|
||||
static inline void dm_ima_alloc_context(struct dm_ima_context **context, bool noio) {}
|
||||
static inline void dm_ima_free_context(struct dm_ima_context *context) {}
|
||||
static inline void dm_ima_context_table_op(struct mapped_device *md,
|
||||
struct dm_ima_context *context,
|
||||
enum dm_ima_table_op op) {}
|
||||
static inline void dm_ima_measure_on_table_load(struct dm_table *table,
|
||||
struct dm_ima_context *context) {}
|
||||
static inline void dm_ima_measure_on_device_resume(struct mapped_device *md,
|
||||
bool swap,
|
||||
struct dm_ima_context *context) {}
|
||||
static inline void dm_ima_measure_on_device_remove(struct mapped_device *md,
|
||||
bool remove_all,
|
||||
struct dm_ima_context *context,
|
||||
unsigned int idx) {}
|
||||
static inline void dm_ima_measure_on_table_clear(struct mapped_device *md,
|
||||
struct dm_ima_context *context) {}
|
||||
static inline void dm_ima_measure_on_device_rename(struct mapped_device *md,
|
||||
struct dm_ima_context *context) {}
|
||||
|
||||
#endif /* CONFIG_IMA */
|
||||
|
||||
|
||||
618
drivers/md/dm-inlinecrypt.c
Normal file
618
drivers/md/dm-inlinecrypt.c
Normal file
@@ -0,0 +1,618 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/blk-crypto.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device-mapper.h>
|
||||
#include <linux/hex.h>
|
||||
#include <linux/module.h>
|
||||
#include <keys/user-type.h>
|
||||
|
||||
#define DM_MSG_PREFIX "inlinecrypt"
|
||||
|
||||
static const struct dm_inlinecrypt_cipher {
|
||||
const char *name;
|
||||
enum blk_crypto_mode_num mode_num;
|
||||
} dm_inlinecrypt_ciphers[] = {
|
||||
{
|
||||
.name = "aes-xts-plain64",
|
||||
.mode_num = BLK_ENCRYPTION_MODE_AES_256_XTS,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inlinecrypt_ctx - private data of an inlinecrypt 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
|
||||
* @key_size: size of the encryption key in bytes
|
||||
* @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_type: type of the key -- either raw or hardware-wrapped
|
||||
* @key: the encryption key to use
|
||||
* @max_dun: the maximum DUN that may be used (computed from other params)
|
||||
*/
|
||||
struct inlinecrypt_ctx {
|
||||
struct dm_dev *dev;
|
||||
sector_t start;
|
||||
const char *cipher_string;
|
||||
unsigned int key_size;
|
||||
u64 iv_offset;
|
||||
unsigned int sector_size;
|
||||
unsigned int sector_bits;
|
||||
enum blk_crypto_key_type key_type;
|
||||
struct blk_crypto_key key;
|
||||
u64 max_dun;
|
||||
};
|
||||
|
||||
static const struct dm_inlinecrypt_cipher *
|
||||
lookup_cipher(const char *cipher_string)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dm_inlinecrypt_ciphers); i++) {
|
||||
if (strcmp(cipher_string, dm_inlinecrypt_ciphers[i].name) == 0)
|
||||
return &dm_inlinecrypt_ciphers[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void inlinecrypt_dtr(struct dm_target *ti)
|
||||
{
|
||||
struct inlinecrypt_ctx *ctx = ti->private;
|
||||
|
||||
if (ctx->dev) {
|
||||
if (ctx->key.size)
|
||||
blk_crypto_evict_key(ctx->dev->bdev, &ctx->key);
|
||||
dm_put_device(ti, ctx->dev);
|
||||
}
|
||||
kfree_sensitive(ctx->cipher_string);
|
||||
kfree_sensitive(ctx);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
|
||||
static bool contains_whitespace(const char *str)
|
||||
{
|
||||
while (*str)
|
||||
if (isspace(*str++))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int set_key_user(struct key *key, char *key_bytes,
|
||||
const unsigned int key_bytes_size)
|
||||
{
|
||||
const struct user_key_payload *ukp;
|
||||
|
||||
ukp = user_key_payload_locked(key);
|
||||
if (!ukp)
|
||||
return -EKEYREVOKED;
|
||||
|
||||
if (key_bytes_size != ukp->datalen)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(key_bytes, ukp->data, key_bytes_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes,
|
||||
const unsigned int key_bytes_size)
|
||||
{
|
||||
char *key_desc;
|
||||
int ret;
|
||||
struct key_type *type;
|
||||
struct key *key;
|
||||
int (*set_key)(struct key *key, char *key_bytes,
|
||||
const unsigned int key_bytes_size);
|
||||
|
||||
/*
|
||||
* Reject key_string with whitespace. dm core currently lacks code for
|
||||
* proper whitespace escaping in arguments on DM_TABLE_STATUS path.
|
||||
*/
|
||||
if (contains_whitespace(key_string)) {
|
||||
DMERR("whitespace chars not allowed in key string");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* look for next ':' separating key_type from key_description */
|
||||
key_desc = strchr(key_string, ':');
|
||||
if (!key_desc || key_desc == key_string || !strlen(key_desc + 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (!strncmp(key_string, "logon:", key_desc - key_string + 1)) {
|
||||
type = &key_type_logon;
|
||||
set_key = set_key_user;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = request_key(type, key_desc + 1, NULL);
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
|
||||
down_read(&key->sem);
|
||||
|
||||
ret = set_key(key, (char *)key_bytes, key_bytes_size);
|
||||
|
||||
up_read(&key->sem);
|
||||
key_put(key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_key_size(char **key_string)
|
||||
{
|
||||
char *colon, dummy;
|
||||
int ret;
|
||||
|
||||
if (*key_string[0] != ':') {
|
||||
ret = strlen(*key_string);
|
||||
|
||||
if (ret > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE
|
||||
|| ret % 2
|
||||
|| !ret) {
|
||||
DMERR("Invalid keysize");
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret >> 1;
|
||||
}
|
||||
|
||||
/* look for next ':' in key string */
|
||||
colon = strpbrk(*key_string + 1, ":");
|
||||
if (!colon)
|
||||
return -EINVAL;
|
||||
|
||||
if (sscanf(*key_string + 1, "%u%c", &ret, &dummy) != 2 || dummy != ':')
|
||||
return -EINVAL;
|
||||
|
||||
/* remaining key string should be :<logon|user>:<key_desc> */
|
||||
*key_string = colon;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes,
|
||||
const unsigned int key_bytes_size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int get_key_size(char **key_string)
|
||||
{
|
||||
int key_hex_size = strlen(*key_string);
|
||||
|
||||
if (*key_string[0] == ':')
|
||||
return -EINVAL;
|
||||
|
||||
if (key_hex_size > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE
|
||||
|| key_hex_size % 2
|
||||
|| !key_hex_size) {
|
||||
DMERR("Invalid keysize");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return key_hex_size >> 1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
static int inlinecrypt_get_key(const char *key_string,
|
||||
u8 key[BLK_CRYPTO_MAX_ANY_KEY_SIZE],
|
||||
const unsigned int key_size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE) {
|
||||
DMERR("Invalid keysize");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* ':' means the key is in kernel keyring, short-circuit normal key processing */
|
||||
if (key_string[0] == ':') {
|
||||
/* key string should be :<logon|user>:<key_desc> */
|
||||
ret = inlinecrypt_get_keyring_key(key_string + 1, key, key_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hex2bin(key, key_string, key_size) != 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inlinecrypt_ctr_optional(struct dm_target *ti,
|
||||
unsigned int argc, char **argv)
|
||||
{
|
||||
struct inlinecrypt_ctx *ctx = ti->private;
|
||||
struct dm_arg_set as;
|
||||
static const struct dm_arg _args[] = {
|
||||
{0, 4, "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 (str_has_prefix(opt_string, "keytype:")) {
|
||||
const char *val = opt_string + strlen("keytype:");
|
||||
|
||||
if (!*val) {
|
||||
ti->error = "Invalid block key type";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!strcmp(val, "raw")) {
|
||||
ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW;
|
||||
} else if (!strcmp(val, "hw-wrapped")) {
|
||||
ctx->key_type = BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
|
||||
} else {
|
||||
ti->error = "Invalid block key type";
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(opt_string, "allow_discards")) {
|
||||
ti->num_discard_bios = 1;
|
||||
} else if (sscanf(opt_string, "sector_size:%u%c",
|
||||
&ctx->sector_size, &dummy) == 1) {
|
||||
if (ctx->sector_size < SECTOR_SIZE ||
|
||||
ctx->sector_size > 4096 ||
|
||||
!is_power_of_2(ctx->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-inlinecrypt doesn't implement iv_large_sectors=false. */
|
||||
if (ctx->sector_size != SECTOR_SIZE && !iv_large_sectors) {
|
||||
ti->error = "iv_large_sectors must be specified";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct an inlinecrypt mapping:
|
||||
* <cipher> [<key>|:<key_size>:<logon>:<key_description>] <iv_offset> <dev_path> <start>
|
||||
*
|
||||
* This syntax matches dm-crypt's, but the set of supported functionality has
|
||||
* been stripped down.
|
||||
*/
|
||||
static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||
{
|
||||
struct inlinecrypt_ctx *ctx;
|
||||
const struct dm_inlinecrypt_cipher *cipher;
|
||||
u8 key_bytes[BLK_CRYPTO_MAX_ANY_KEY_SIZE];
|
||||
unsigned int dun_bytes;
|
||||
unsigned long long tmpll;
|
||||
char dummy;
|
||||
int err;
|
||||
|
||||
if (argc < 5) {
|
||||
ti->error = "Not enough arguments";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
ti->error = "Out of memory";
|
||||
return -ENOMEM;
|
||||
}
|
||||
ti->private = ctx;
|
||||
|
||||
/* <cipher> */
|
||||
ctx->cipher_string = kstrdup(argv[0], GFP_KERNEL);
|
||||
if (!ctx->cipher_string) {
|
||||
ti->error = "Out of memory";
|
||||
err = -ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
cipher = lookup_cipher(ctx->cipher_string);
|
||||
if (!cipher) {
|
||||
ti->error = "Unsupported cipher";
|
||||
err = -EINVAL;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* <key> */
|
||||
err = get_key_size(&argv[1]);
|
||||
if (err < 0) {
|
||||
ti->error = "Cannot parse key size";
|
||||
return -EINVAL;
|
||||
}
|
||||
ctx->key_size = err;
|
||||
|
||||
err = inlinecrypt_get_key(argv[1], key_bytes, ctx->key_size);
|
||||
if (err) {
|
||||
ti->error = "Malformed key string";
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/* <iv_offset> */
|
||||
if (sscanf(argv[2], "%llu%c", &ctx->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),
|
||||
&ctx->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;
|
||||
}
|
||||
ctx->start = tmpll;
|
||||
|
||||
/* optional arguments */
|
||||
ctx->sector_size = SECTOR_SIZE;
|
||||
ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW;
|
||||
if (argc > 5) {
|
||||
err = inlinecrypt_ctr_optional(ti, argc - 5, &argv[5]);
|
||||
if (err)
|
||||
goto bad;
|
||||
}
|
||||
ctx->sector_bits = ilog2(ctx->sector_size);
|
||||
if (ti->len & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
|
||||
ti->error = "Device size is not a multiple of sector_size";
|
||||
err = -EINVAL;
|
||||
goto bad;
|
||||
}
|
||||
if (ctx->iv_offset & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
|
||||
ti->error = "Wrong alignment of iv_offset sector";
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
ctx->max_dun = (ctx->iv_offset + ti->len - 1) >>
|
||||
(ctx->sector_bits - SECTOR_SHIFT);
|
||||
dun_bytes = DIV_ROUND_UP(fls64(ctx->max_dun), 8);
|
||||
|
||||
err = blk_crypto_init_key(&ctx->key, key_bytes, ctx->key_size,
|
||||
ctx->key_type, cipher->mode_num,
|
||||
dun_bytes, ctx->sector_size);
|
||||
if (err) {
|
||||
ti->error = "Error initializing blk-crypto key";
|
||||
goto bad;
|
||||
}
|
||||
|
||||
err = blk_crypto_start_using_key(ctx->dev->bdev, &ctx->key);
|
||||
if (err) {
|
||||
ti->error = "Error starting to use blk-crypto";
|
||||
goto bad;
|
||||
}
|
||||
|
||||
ti->num_flush_bios = 1;
|
||||
|
||||
err = 0;
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
inlinecrypt_dtr(ti);
|
||||
out:
|
||||
memzero_explicit(key_bytes, sizeof(key_bytes));
|
||||
return err;
|
||||
}
|
||||
|
||||
static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = ti->private;
|
||||
sector_t sector_in_target;
|
||||
u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {};
|
||||
|
||||
bio_set_dev(bio, ctx->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;
|
||||
|
||||
/*
|
||||
* The bio should never have an encryption context already, since
|
||||
* dm-inlinecrypt doesn't pass through any inline encryption
|
||||
* capabilities to the layer above it.
|
||||
*/
|
||||
if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
|
||||
return DM_MAPIO_KILL;
|
||||
|
||||
/* 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 = ctx->start + sector_in_target;
|
||||
/*
|
||||
* If the bio doesn't have any data (e.g. if it's a DISCARD request),
|
||||
* there's nothing more to do.
|
||||
*/
|
||||
if (!bio_has_data(bio))
|
||||
return DM_MAPIO_REMAPPED;
|
||||
|
||||
/* Calculate the DUN and enforce data-unit (crypto sector) alignment. */
|
||||
dun[0] = ctx->iv_offset + sector_in_target; /* 512-byte sectors */
|
||||
if (dun[0] & ((ctx->sector_size >> SECTOR_SHIFT) - 1))
|
||||
return DM_MAPIO_KILL;
|
||||
dun[0] >>= ctx->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] > ctx->max_dun))
|
||||
return DM_MAPIO_KILL;
|
||||
|
||||
bio_crypt_set_ctx(bio, &ctx->key, dun, GFP_NOIO);
|
||||
|
||||
/*
|
||||
* Since we've added an encryption context to the bio and
|
||||
* blk-crypto-fallback may be needed to process it, it's necessary to
|
||||
* use the fallback-aware bio submission code rather than
|
||||
* unconditionally returning DM_MAPIO_REMAPPED.
|
||||
*
|
||||
* To get the correct accounting for a dm target in the case where
|
||||
* __blk_crypto_submit_bio() doesn't take ownership of the bio (returns
|
||||
* true), call __blk_crypto_submit_bio() directly and return
|
||||
* DM_MAPIO_REMAPPED in that case, rather than relying on
|
||||
* blk_crypto_submit_bio() which calls submit_bio() in that case.
|
||||
*
|
||||
* TODO: blk-crypto fallback write slow-path currently double-accounts
|
||||
* IO in vmstat, as encrypted bios are submitted via submit_bio().
|
||||
* This does not affect data correctness. Consider fixing this if
|
||||
* a cleaner accounting model for derived bios is introduced.
|
||||
*/
|
||||
if (__blk_crypto_submit_bio(bio))
|
||||
return DM_MAPIO_REMAPPED;
|
||||
return DM_MAPIO_SUBMITTED;
|
||||
}
|
||||
|
||||
static void inlinecrypt_status(struct dm_target *ti, status_type_t type,
|
||||
unsigned int status_flags, char *result,
|
||||
unsigned int maxlen)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = 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:
|
||||
/*
|
||||
* Warning: like dm-crypt, dm-inlinecrypt includes the key in
|
||||
* the returned table. Userspace is responsible for redacting
|
||||
* the key when needed.
|
||||
*/
|
||||
DMEMIT("%s %*phN %u %llu %s %llu", ctx->cipher_string,
|
||||
ctx->key.size, ctx->key.bytes,
|
||||
ctx->key_type, ctx->iv_offset,
|
||||
ctx->dev->name, ctx->start);
|
||||
num_feature_args += !!ti->num_discard_bios;
|
||||
if (ctx->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 (ctx->sector_size != SECTOR_SIZE) {
|
||||
DMEMIT(" sector_size:%u", ctx->sector_size);
|
||||
DMEMIT(" iv_large_sectors");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int inlinecrypt_prepare_ioctl(struct dm_target *ti,
|
||||
struct block_device **bdev, unsigned int cmd,
|
||||
unsigned long arg, bool *forward)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = ti->private;
|
||||
const struct dm_dev *dev = ctx->dev;
|
||||
|
||||
*bdev = dev->bdev;
|
||||
|
||||
/* Only pass ioctls through if the device sizes match exactly. */
|
||||
return ctx->start != 0 || ti->len != bdev_nr_sectors(dev->bdev);
|
||||
}
|
||||
|
||||
static int inlinecrypt_iterate_devices(struct dm_target *ti,
|
||||
iterate_devices_callout_fn fn,
|
||||
void *data)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = ti->private;
|
||||
|
||||
return fn(ti, ctx->dev, ctx->start, ti->len, data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
static int inlinecrypt_report_zones(struct dm_target *ti,
|
||||
struct dm_report_zones_args *args,
|
||||
unsigned int nr_zones)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = ti->private;
|
||||
|
||||
return dm_report_zones(ctx->dev->bdev, ctx->start,
|
||||
ctx->start + dm_target_offset(ti, args->next_sector),
|
||||
args, nr_zones);
|
||||
}
|
||||
#else
|
||||
#define inlinecrypt_report_zones NULL
|
||||
#endif
|
||||
|
||||
static void inlinecrypt_io_hints(struct dm_target *ti,
|
||||
struct queue_limits *limits)
|
||||
{
|
||||
const struct inlinecrypt_ctx *ctx = ti->private;
|
||||
const unsigned int sector_size = ctx->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);
|
||||
limits->dma_alignment = limits->logical_block_size - 1;
|
||||
}
|
||||
|
||||
static struct target_type inlinecrypt_target = {
|
||||
.name = "inlinecrypt",
|
||||
.version = {1, 0, 0},
|
||||
/*
|
||||
* Do not set DM_TARGET_PASSES_CRYPTO, since dm-inlinecrypt consumes the
|
||||
* crypto capability itself.
|
||||
*/
|
||||
.features = DM_TARGET_ZONED_HM,
|
||||
.module = THIS_MODULE,
|
||||
.ctr = inlinecrypt_ctr,
|
||||
.dtr = inlinecrypt_dtr,
|
||||
.map = inlinecrypt_map,
|
||||
.status = inlinecrypt_status,
|
||||
.prepare_ioctl = inlinecrypt_prepare_ioctl,
|
||||
.iterate_devices = inlinecrypt_iterate_devices,
|
||||
.report_zones = inlinecrypt_report_zones,
|
||||
.io_hints = inlinecrypt_io_hints,
|
||||
};
|
||||
|
||||
module_dm(inlinecrypt);
|
||||
|
||||
MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
|
||||
MODULE_AUTHOR("Linlin Zhang <linlin.zhang@oss.qualcomm.com>");
|
||||
MODULE_DESCRIPTION(DM_NAME " target for inline encryption");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -259,6 +259,80 @@ static void free_cell(struct hash_cell *hc)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IMA
|
||||
|
||||
/*
|
||||
* Called while holding to _hash_lock, to guarantee the ordering of the
|
||||
* following dm_ima_measure_on_* functions, which should be called
|
||||
* right after dropping the _hash_lock
|
||||
*/
|
||||
static unsigned int dm_ima_init_context(struct hash_cell *hc,
|
||||
struct dm_ima_context *context,
|
||||
bool need_idx)
|
||||
{
|
||||
lockdep_assert_held(&_hash_lock);
|
||||
|
||||
if (unlikely(!context))
|
||||
return need_idx ? hc->md->ima.update_idx++ : 0;
|
||||
|
||||
context->update_idx = hc->md->ima.update_idx++;
|
||||
strcpy(context->dev_name, hc->name);
|
||||
strcpy(context->dev_uuid, hc->uuid ? : "");
|
||||
|
||||
return context->update_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by do_resume() to guarantee correct ordering, since do_resume()
|
||||
* does not grab the _hash_lock when the table is not getting swapped or
|
||||
* when actually swapping the active table
|
||||
*/
|
||||
static bool dm_ima_need_measure(struct mapped_device *md,
|
||||
struct dm_table *table,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
int srcu_idx;
|
||||
struct hash_cell *hc;
|
||||
bool need_measure = false;
|
||||
|
||||
if (unlikely(!context))
|
||||
return false;
|
||||
|
||||
down_write(&_hash_lock);
|
||||
/* Check if the device has been removed */
|
||||
hc = dm_get_mdptr(md);
|
||||
if (hc) {
|
||||
/*
|
||||
* If we have a table, we need to make sure that it's the
|
||||
* active table. Otherwise we raced with another process
|
||||
* setting the active table and it will do the measurement
|
||||
*/
|
||||
if (!table || dm_get_live_table(md, &srcu_idx) == table) {
|
||||
dm_ima_init_context(hc, context, false);
|
||||
need_measure = true;
|
||||
}
|
||||
if (table)
|
||||
dm_put_live_table(md, srcu_idx);
|
||||
}
|
||||
up_write(&_hash_lock);
|
||||
|
||||
return need_measure;
|
||||
}
|
||||
#else
|
||||
static inline unsigned int dm_ima_init_context(struct hash_cell *hc,
|
||||
struct dm_ima_context *context,
|
||||
bool neex_idx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline bool dm_ima_need_measure(struct mapped_device *md,
|
||||
struct dm_table *table,
|
||||
struct dm_ima_context *context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The kdev_t and uuid of a device can never change once it is
|
||||
* initially inserted.
|
||||
@@ -344,7 +418,10 @@ static int dm_hash_remove_all(unsigned flags)
|
||||
struct hash_cell *hc;
|
||||
struct mapped_device *md;
|
||||
struct dm_table *t;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
unsigned int ima_idx;
|
||||
|
||||
dm_ima_alloc_context(&ima_context, true);
|
||||
retry:
|
||||
dev_skipped = 0;
|
||||
|
||||
@@ -353,6 +430,7 @@ static int dm_hash_remove_all(unsigned flags)
|
||||
for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
|
||||
if (flags & DM_REMOVE_INTERRUPTIBLE && fatal_signal_pending(current)) {
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
@@ -367,6 +445,7 @@ static int dm_hash_remove_all(unsigned flags)
|
||||
continue;
|
||||
}
|
||||
|
||||
ima_idx = dm_ima_init_context(hc, ima_context, true);
|
||||
t = __hash_remove(hc);
|
||||
|
||||
up_write(&_hash_lock);
|
||||
@@ -375,7 +454,7 @@ static int dm_hash_remove_all(unsigned flags)
|
||||
dm_sync_table(md);
|
||||
dm_table_destroy(t);
|
||||
}
|
||||
dm_ima_measure_on_device_remove(md, true);
|
||||
dm_ima_measure_on_device_remove(md, true, ima_context, ima_idx);
|
||||
dm_put(md);
|
||||
if (likely(flags & DM_REMOVE_KEEP_OPEN_DEVICES))
|
||||
dm_destroy(md);
|
||||
@@ -396,6 +475,7 @@ static int dm_hash_remove_all(unsigned flags)
|
||||
if (dev_skipped && !(flags & DM_REMOVE_ONLY_DEFERRED))
|
||||
DMWARN("remove_all left %d open device(s)", dev_skipped);
|
||||
|
||||
dm_ima_free_context(ima_context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -443,6 +523,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
struct mapped_device *md;
|
||||
unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
|
||||
int srcu_idx;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
|
||||
/*
|
||||
* duplicate new.
|
||||
@@ -451,6 +532,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
if (!new_data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dm_ima_alloc_context(&ima_context, true);
|
||||
down_write(&_hash_lock);
|
||||
|
||||
/*
|
||||
@@ -467,6 +549,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
param->name, new);
|
||||
dm_put(hc->md);
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
kfree(new_data);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
@@ -479,6 +562,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
DMERR("Unable to rename non-existent device, %s to %s%s",
|
||||
param->name, change_uuid ? "uuid " : "", new);
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
kfree(new_data);
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
@@ -492,6 +576,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
param->name, new, hc->uuid);
|
||||
dm_put(hc->md);
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
kfree(new_data);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@@ -514,9 +599,11 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||
|
||||
md = hc->md;
|
||||
|
||||
dm_ima_measure_on_device_rename(md);
|
||||
dm_ima_init_context(hc, ima_context, false);
|
||||
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_measure_on_device_rename(md, ima_context);
|
||||
dm_ima_free_context(ima_context);
|
||||
kfree(old_name);
|
||||
|
||||
return md;
|
||||
@@ -995,13 +1082,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
struct mapped_device *md;
|
||||
int r;
|
||||
struct dm_table *t;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
unsigned int ima_idx;
|
||||
|
||||
dm_ima_alloc_context(&ima_context, true);
|
||||
down_write(&_hash_lock);
|
||||
hc = __find_device_hash_cell(param);
|
||||
|
||||
if (!hc) {
|
||||
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
@@ -1015,14 +1106,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
|
||||
up_write(&_hash_lock);
|
||||
dm_put(md);
|
||||
dm_ima_free_context(ima_context);
|
||||
return 0;
|
||||
}
|
||||
DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
|
||||
up_write(&_hash_lock);
|
||||
dm_put(md);
|
||||
dm_ima_free_context(ima_context);
|
||||
return r;
|
||||
}
|
||||
|
||||
ima_idx = dm_ima_init_context(hc, ima_context, true);
|
||||
t = __hash_remove(hc);
|
||||
up_write(&_hash_lock);
|
||||
|
||||
@@ -1033,7 +1127,8 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
|
||||
param->flags &= ~DM_DEFERRED_REMOVE;
|
||||
|
||||
dm_ima_measure_on_device_remove(md, false);
|
||||
dm_ima_measure_on_device_remove(md, false, ima_context, ima_idx);
|
||||
dm_ima_free_context(ima_context);
|
||||
|
||||
if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
|
||||
param->flags |= DM_UEVENT_GENERATED_FLAG;
|
||||
@@ -1169,13 +1264,16 @@ static int do_resume(struct dm_ioctl *param)
|
||||
struct mapped_device *md;
|
||||
struct dm_table *new_map, *old_map = NULL;
|
||||
bool need_resize_uevent = false;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
|
||||
dm_ima_alloc_context(&ima_context, true);
|
||||
down_write(&_hash_lock);
|
||||
|
||||
hc = __find_device_hash_cell(param);
|
||||
if (!hc) {
|
||||
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
@@ -1184,13 +1282,15 @@ static int do_resume(struct dm_ioctl *param)
|
||||
new_map = hc->new_map;
|
||||
hc->new_map = NULL;
|
||||
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
|
||||
|
||||
if (new_map)
|
||||
dm_ima_init_context(hc, ima_context, false);
|
||||
up_write(&_hash_lock);
|
||||
|
||||
/* Do we need to load a new map ? */
|
||||
if (new_map) {
|
||||
sector_t old_size, new_size;
|
||||
|
||||
dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_SAVE);
|
||||
/* Suspend if it isn't already suspended */
|
||||
if (param->flags & DM_SKIP_LOCKFS_FLAG)
|
||||
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
|
||||
@@ -1204,6 +1304,8 @@ static int do_resume(struct dm_ioctl *param)
|
||||
if (hc && !hc->new_map) {
|
||||
hc->new_map = new_map;
|
||||
new_map = NULL;
|
||||
dm_ima_init_context(hc, ima_context,
|
||||
false);
|
||||
} else {
|
||||
r = -ENXIO;
|
||||
}
|
||||
@@ -1211,7 +1313,9 @@ static int do_resume(struct dm_ioctl *param)
|
||||
if (new_map) {
|
||||
dm_sync_table(md);
|
||||
dm_table_destroy(new_map);
|
||||
}
|
||||
} else
|
||||
dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_RESTORE);
|
||||
dm_ima_free_context(ima_context);
|
||||
dm_put(md);
|
||||
return r;
|
||||
}
|
||||
@@ -1222,9 +1326,12 @@ static int do_resume(struct dm_ioctl *param)
|
||||
if (IS_ERR(old_map)) {
|
||||
dm_sync_table(md);
|
||||
dm_table_destroy(new_map);
|
||||
dm_ima_free_context(ima_context);
|
||||
dm_put(md);
|
||||
return PTR_ERR(old_map);
|
||||
}
|
||||
if (dm_ima_need_measure(md, new_map, ima_context))
|
||||
dm_ima_measure_on_device_resume(md, true, ima_context);
|
||||
new_size = dm_get_size(md);
|
||||
if (old_size && new_size && old_size != new_size)
|
||||
need_resize_uevent = true;
|
||||
@@ -1238,7 +1345,10 @@ static int do_resume(struct dm_ioctl *param)
|
||||
if (dm_suspended_md(md)) {
|
||||
r = dm_resume(md);
|
||||
if (!r) {
|
||||
dm_ima_measure_on_device_resume(md, new_map ? true : false);
|
||||
if (!new_map && dm_ima_need_measure(md, NULL,
|
||||
ima_context))
|
||||
dm_ima_measure_on_device_resume(md, false,
|
||||
ima_context);
|
||||
|
||||
if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
|
||||
param->flags |= DM_UEVENT_GENERATED_FLAG;
|
||||
@@ -1255,6 +1365,7 @@ static int do_resume(struct dm_ioctl *param)
|
||||
if (!r)
|
||||
__dev_status(md, param);
|
||||
|
||||
dm_ima_free_context(ima_context);
|
||||
dm_put(md);
|
||||
return r;
|
||||
}
|
||||
@@ -1532,11 +1643,12 @@ static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
|
||||
|
||||
static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
|
||||
{
|
||||
int r;
|
||||
int r, srcu_idx;
|
||||
struct hash_cell *hc;
|
||||
struct dm_table *t, *old_map = NULL;
|
||||
struct mapped_device *md;
|
||||
struct target_type *immutable_target_type;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
|
||||
md = find_device(param);
|
||||
if (!md)
|
||||
@@ -1552,8 +1664,6 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
if (r)
|
||||
goto err_unlock_md_type;
|
||||
|
||||
dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
|
||||
|
||||
immutable_target_type = dm_get_immutable_target_type(md);
|
||||
if (immutable_target_type &&
|
||||
(immutable_target_type != dm_table_get_immutable_target_type(t)) &&
|
||||
@@ -1580,12 +1690,14 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
|
||||
dm_unlock_md_type(md);
|
||||
|
||||
dm_ima_alloc_context(&ima_context, false);
|
||||
/* stage inactive table */
|
||||
down_write(&_hash_lock);
|
||||
hc = dm_get_mdptr(md);
|
||||
if (!hc) {
|
||||
DMERR("device has been removed from the dev hash table.");
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
r = -ENXIO;
|
||||
goto err_destroy_table;
|
||||
}
|
||||
@@ -1593,8 +1705,15 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
|
||||
if (hc->new_map)
|
||||
old_map = hc->new_map;
|
||||
hc->new_map = t;
|
||||
dm_ima_init_context(hc, ima_context, false);
|
||||
/* Make sure new_map doesn't get freed before we measure it*/
|
||||
dm_get_live_table(md, &srcu_idx);
|
||||
up_write(&_hash_lock);
|
||||
|
||||
dm_ima_measure_on_table_load(t, ima_context);
|
||||
dm_ima_free_context(ima_context);
|
||||
dm_put_live_table(md, srcu_idx);
|
||||
|
||||
param->flags |= DM_INACTIVE_PRESENT_FLAG;
|
||||
__dev_status(md, param);
|
||||
|
||||
@@ -1622,25 +1741,29 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
|
||||
struct hash_cell *hc;
|
||||
struct mapped_device *md;
|
||||
struct dm_table *old_map = NULL;
|
||||
bool has_new_map = false;
|
||||
struct dm_ima_context *ima_context = NULL;
|
||||
|
||||
dm_ima_alloc_context(&ima_context, true);
|
||||
down_write(&_hash_lock);
|
||||
|
||||
hc = __find_device_hash_cell(param);
|
||||
if (!hc) {
|
||||
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_free_context(ima_context);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (hc->new_map) {
|
||||
old_map = hc->new_map;
|
||||
hc->new_map = NULL;
|
||||
has_new_map = true;
|
||||
}
|
||||
|
||||
dm_ima_init_context(hc, ima_context, false);
|
||||
md = hc->md;
|
||||
up_write(&_hash_lock);
|
||||
dm_ima_measure_on_table_clear(md, ima_context);
|
||||
dm_ima_free_context(ima_context);
|
||||
|
||||
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
|
||||
__dev_status(md, param);
|
||||
@@ -1649,7 +1772,6 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
|
||||
dm_sync_table(md);
|
||||
dm_table_destroy(old_map);
|
||||
}
|
||||
dm_ima_measure_on_table_clear(md, has_new_map);
|
||||
dm_put(md);
|
||||
|
||||
return 0;
|
||||
@@ -1816,8 +1938,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
|
||||
goto out_argv;
|
||||
|
||||
table = dm_get_live_table(md, &srcu_idx);
|
||||
if (!table)
|
||||
if (!table) {
|
||||
DMERR("The device has no table.");
|
||||
r = -EINVAL;
|
||||
goto out_table;
|
||||
}
|
||||
|
||||
if (dm_deleting_md(md)) {
|
||||
r = -ENXIO;
|
||||
|
||||
@@ -282,7 +282,7 @@ static void create_unique_nonce_data(u8 *buffer)
|
||||
u32 rand;
|
||||
size_t offset = 0;
|
||||
|
||||
get_random_bytes(&rand, sizeof(u32));
|
||||
rand = get_random_u32();
|
||||
memcpy(buffer + offset, &now, sizeof(now));
|
||||
offset += sizeof(now);
|
||||
memcpy(buffer + offset, &rand, sizeof(rand));
|
||||
|
||||
@@ -2871,7 +2871,7 @@ int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev,
|
||||
if (!zmd)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(zmd->devname, devname);
|
||||
strscpy(zmd->devname, devname);
|
||||
zmd->dev = dev;
|
||||
zmd->nr_devs = num_dev;
|
||||
zmd->mblk_rbtree = RB_ROOT;
|
||||
|
||||
@@ -2098,8 +2098,17 @@ static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob,
|
||||
WARN_ON_ONCE(!dm_tio_is_normal(&io->tio));
|
||||
|
||||
/* don't poll if the mapped io is done */
|
||||
if (atomic_read(&io->io_count) > 1)
|
||||
bio_poll(&io->tio.clone, iob, flags);
|
||||
if (atomic_read(&io->io_count) > 1) {
|
||||
/*
|
||||
* DM hides the target queues from the upper poller, which may
|
||||
* decide it is safe to spin on a single stacked queue. Do not
|
||||
* pass that spinning policy down to a target queue: one slow
|
||||
* clone could keep the task inside dm_poll_bio() for a long
|
||||
* time. Poll target bios once and let the caller decide
|
||||
* whether to keep polling, reap completions or reschedule.
|
||||
*/
|
||||
bio_poll(&io->tio.clone, iob, flags | BLK_POLL_ONESHOT);
|
||||
}
|
||||
|
||||
/* bio_poll holds the last reference */
|
||||
return atomic_read(&io->io_count) == 1;
|
||||
@@ -2546,7 +2555,7 @@ int dm_create(int minor, struct mapped_device **result)
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
dm_ima_reset_data(md);
|
||||
dm_ima_init(md);
|
||||
|
||||
*result = md;
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user