| Index: drivers/md/dm-verity.c
|
| diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
|
| index b9797f9097fe7ff13d67f003361469d12aac941a..8c302a6e33ac88db018508a9825d4124e29b110b 100644
|
| --- a/drivers/md/dm-verity.c
|
| +++ b/drivers/md/dm-verity.c
|
| @@ -11,13 +11,19 @@
|
| * Implements a verifying transparent block device.
|
| * See Documentation/device-mapper/dm-verity.txt
|
| */
|
| +#include <linux/async.h>
|
| #include <linux/backing-dev.h>
|
| #include <linux/bio.h>
|
| #include <linux/blkdev.h>
|
| #include <linux/completion.h>
|
| #include <linux/crypto.h>
|
| #include <linux/delay.h>
|
| +#include <linux/device.h>
|
| +#ifdef CONFIG_EFI_PARTITION
|
| +# include <linux/efi.h>
|
| +#endif
|
| #include <linux/err.h>
|
| +#include <linux/genhd.h>
|
| #include <linux/init.h>
|
| #include <linux/kernel.h>
|
| #include <linux/mempool.h>
|
| @@ -121,7 +127,12 @@ MODULE_PARM_DESC(error_behavior, "Behavior on error");
|
| */
|
| static int bht_readahead;
|
| module_param(bht_readahead, int, 0644);
|
| -MODULE_PARM_DESC(bht_readahead, "number of entries to readahead in the bht");
|
| +MODULE_PARM_DESC(bht_readahead, "Number of entries to readahead in the bht");
|
| +
|
| +/* Controls whether verity_get_device will wait forever for a device. */
|
| +static int dev_wait;
|
| +module_param(dev_wait, bool, 0444);
|
| +MODULE_PARM_DESC(dev_wait, "Wait forever for a backing device")
|
|
|
| /* Used for tracking pending bios as well as for exporting information via
|
| * STATUSTYPE_INFO.
|
| @@ -541,6 +552,218 @@ static void verity_error(struct verity_config *vc, struct dm_verity_io *io,
|
| }
|
| }
|
|
|
| +/**
|
| + * efi_parse_hexbyte - Convert a ASCII hex number to a byte
|
| + * @src: Pointer to at least 2 characters to convert.
|
| + *
|
| + * Convert a two character ASCII hex string to a number.
|
| + * From ldm.c.
|
| + *
|
| + * Return: 0-255 Success, the byte was parsed correctly
|
| + * -1 Error, an invalid character was supplied
|
| + */
|
| +static int efi_hexbyte (const u8 *src)
|
| +{
|
| + unsigned int x; /* For correct wrapping */
|
| + int h;
|
| +
|
| + /* high part */
|
| + if ((x = src[0] - '0') <= '9'-'0') h = x;
|
| + else if ((x = src[0] - 'a') <= 'f'-'a') h = x+10;
|
| + else if ((x = src[0] - 'A') <= 'F'-'A') h = x+10;
|
| + else return -1;
|
| + h <<= 4;
|
| +
|
| + /* low part */
|
| + if ((x = src[1] - '0') <= '9'-'0') return h | x;
|
| + if ((x = src[1] - 'a') <= 'f'-'a') return h | (x+10);
|
| + if ((x = src[1] - 'A') <= 'F'-'A') return h | (x+10);
|
| + return -1;
|
| +}
|
| +
|
| +/**
|
| + * efi_parse_guid - Convert GUID from ASCII to binary
|
| + * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
| + * @dest: Memory block to hold binary GUID (16 bytes)
|
| + *
|
| + * N.B. The GUID need not be NULL terminated and no safety checks are performed.
|
| + *
|
| + * Return: 'true' @dest contains binary GUID
|
| + * 'false' @dest contents are undefined
|
| + */
|
| +static bool efi_parse_guid(const u8 *src, u8 *dest)
|
| +{
|
| + dest[3] = efi_hexbyte(src); src += 2;
|
| + dest[2] = efi_hexbyte(src); src += 2;
|
| + dest[1] = efi_hexbyte(src); src += 2;
|
| + dest[0] = efi_hexbyte(src); src += 2;
|
| + src += 1; /* - */
|
| + dest[5] = efi_hexbyte(src); src += 2;
|
| + dest[4] = efi_hexbyte(src); src += 2;
|
| + src += 1; /* - */
|
| + dest[7] = efi_hexbyte(src); src += 2;
|
| + dest[6] = efi_hexbyte(src); src += 2;
|
| + src += 1; /* - */
|
| + dest[8] = efi_hexbyte(src); src += 2;
|
| + dest[9] = efi_hexbyte(src); src += 2;
|
| + src += 1; /* - */
|
| + dest[10] = efi_hexbyte(src); src += 2;
|
| + dest[11] = efi_hexbyte(src); src += 2;
|
| + dest[12] = efi_hexbyte(src); src += 2;
|
| + dest[13] = efi_hexbyte(src); src += 2;
|
| + dest[14] = efi_hexbyte(src); src += 2;
|
| + dest[15] = efi_hexbyte(src); src += 2;
|
| + return true;
|
| +}
|
| +
|
| +/**
|
| + * dm_get_efi_device: claim a device using its unique GUID
|
| + * @ti: current dm_target
|
| + * @guid_string: 36 byte GUID
|
| + * (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
|
| + * @dev_start: offset in sectors passed to dm_get_device
|
| + * @dev_len: length in sectors passed to dm_get_device
|
| + * @dm_dev: dm_dev to populate
|
| + *
|
| + * Wraps dm_get_device allowing it to use a unique partition GUID to
|
| + * find a given partition on any drive. This code is based on
|
| + * printk_all_partitions in that it walks all of the register block devices.
|
| + *
|
| + * N.B., guid_string is not checked for safety.
|
| + */
|
| +static int dm_get_efi_device(struct dm_target *ti,
|
| + const char *guid_string,
|
| + sector_t dev_start,
|
| + sector_t dev_len,
|
| + struct dm_dev **dm_dev)
|
| +{
|
| +#ifdef CONFIG_EFI_PARTITION
|
| + int part = 0;
|
| + efi_guid_t guid;
|
| + struct class_dev_iter iter;
|
| + struct device *dev;
|
| + dev_t devt = 0;
|
| + char devt_buf[BDEVT_SIZE];
|
| +
|
| + DMDEBUG("dm_get_efi_device called");
|
| + if (!efi_parse_guid(guid_string, guid.b))
|
| + goto bad_guid;
|
| + DMDEBUG("GUID parsed");
|
| +
|
| + /* Walks the block_class. Since we can't access gendisk's
|
| + * device_type, we just filter out entries with no devnode below.
|
| + */
|
| + class_dev_iter_init(&iter, &block_class, NULL, NULL);
|
| + while ((dev = class_dev_iter_next(&iter))) {
|
| + struct gendisk *disk = dev_to_disk(dev);
|
| + struct block_device *bdev = NULL;
|
| +
|
| + DMDEBUG("Walking %u:%u", MAJOR(dev->devt), MINOR(dev->devt));
|
| +
|
| + /*
|
| + * Don't show empty devices or things that have been
|
| + * surpressed
|
| + */
|
| + if (!disk || get_capacity(disk) == 0 ||
|
| + (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
|
| + continue;
|
| +
|
| + if (!disk->part_tbl) {
|
| + DMDEBUG("No partition table for %u:%u",
|
| + MAJOR(disk_devt(disk)),
|
| + MINOR(disk_devt(disk)));
|
| + continue;
|
| + }
|
| +
|
| + /* We're walking all disk and partitions so just grab disks. */
|
| + bdev = bdget_disk(disk, 0);
|
| + if (!bdev) {
|
| + DMDEBUG("No bdev for %u:%u. Skipping",
|
| + MAJOR(dev->devt), MINOR(dev->devt));
|
| + continue;
|
| + }
|
| +
|
| + /* Ensure we have a bd_disk. If nothing else has it opened,
|
| + * there's no guarantee it'll be populated.
|
| + */
|
| + int err = blkdev_get(bdev, FMODE_READ);
|
| + if (err < 0) {
|
| + DMDEBUG("Failed to blkdev_get: %d", err);
|
| + bdput(bdev);
|
| + continue;
|
| + }
|
| +
|
| + DMDEBUG("find_efi_partition called for disk: %u:%u",
|
| + MAJOR(dev->devt), MINOR(dev->devt));
|
| + part = efi_find_partition(&guid, bdev);
|
| + /* Nb, it seems that calling bdput after blkdev_put
|
| + * isn't common. If the inode was only in use here,
|
| + * then it will end up I_CLEAR'd when bdput gets to it.
|
| + */
|
| + blkdev_put(bdev, FMODE_READ);
|
| + /* On match, create the dev node if possible.*/
|
| + if (part > 0) {
|
| + devt = MKDEV(MAJOR(dev->devt),
|
| + MINOR(dev->devt) + part);
|
| + DMDEBUG("found a part! (%d %u:%u)", part,
|
| + MAJOR(devt), MINOR(devt));
|
| + break;
|
| + }
|
| + }
|
| + class_dev_iter_exit(&iter);
|
| +
|
| + if (part <= 0)
|
| + goto found_nothing;
|
| +
|
| + /* Construct the dev name to pass to dm_get_device. This is roundabout
|
| + * EFI partition scanning isn't exactly fast so it doesn't seem like a
|
| + * bad tradeoff.
|
| + */
|
| + snprintf(devt_buf, sizeof(devt_buf), "%u:%u", MAJOR(devt), MINOR(devt));
|
| +
|
| + /* TODO(wad) to make this generic we could also pass in the mode. */
|
| + if (dm_get_device(ti, devt_buf, dev_start, dev_len,
|
| + dm_table_get_mode(ti->table), dm_dev) == 0)
|
| + return 0;
|
| +
|
| + ti->error = "Failed to acquire device";
|
| + DMDEBUG("Failed to acquire discovered device %s", devt_buf);
|
| + return -1;
|
| +bad_guid:
|
| + ti->error = "Bad GUID";
|
| + DMDEBUG("Supplied value '%s' is an invalid GUID", guid_string);
|
| + return -1;
|
| +found_nothing:
|
| + DMDEBUG("No matching partition for GUID: %s", guid_string);
|
| + ti->error = "No matching GUID";
|
| +#endif
|
| + return -1;
|
| +}
|
| +
|
| +static int verity_get_device(struct dm_target *ti, const char *devname,
|
| + sector_t dev_start, sector_t dev_len,
|
| + struct dm_dev **dm_dev)
|
| +{
|
| + do {
|
| + /* Try the normal path first since if everything is ready, it
|
| + * will be the fastest.
|
| + */
|
| + if (!dm_get_device(ti, devname, dev_start, dev_len,
|
| + dm_table_get_mode(ti->table), dm_dev))
|
| + return 0;
|
| +
|
| + /* Try the device by EFI GUID */
|
| + if (!dm_get_efi_device(ti, devname, dev_start, dev_len, dm_dev))
|
| + return 0;
|
| +
|
| + /* No need to be too aggressive since this is a slow path. */
|
| + msleep(500);
|
| + } while (dev_wait && (driver_probe_done() != 0 || *dm_dev == NULL));
|
| + async_synchronize_full();
|
| + return -1;
|
| +}
|
| +
|
| +
|
| /*-----------------------------------------------------------------
|
| * Reverse flow of requests into the device.
|
| *
|
| @@ -1256,8 +1479,7 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
| /* arg0: device to verify */
|
| vc->start = 0; /* TODO: should this support a starting offset? */
|
| /* We only ever grab the device in read-only mode. */
|
| - ret = dm_get_device(ti, argv[0], vc->start, ti->len,
|
| - dm_table_get_mode(ti->table), &vc->dev);
|
| + ret = verity_get_device(ti, argv[0], vc->start, ti->len, &vc->dev);
|
| if (ret) {
|
| DMERR("Failed to acquire device '%s': %d", argv[0], ret);
|
| ti->error = "Device lookup failed";
|
| @@ -1283,8 +1505,8 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
| * ti->len passed to device mapper does not include
|
| * the hashes.
|
| */
|
| - if (dm_get_device(ti, argv[1], vc->hash_start, dm_bht_sectors(&vc->bht),
|
| - dm_table_get_mode(ti->table), &vc->hash_dev)) {
|
| + if (verity_get_device(ti, argv[1], vc->hash_start,
|
| + dm_bht_sectors(&vc->bht), &vc->hash_dev)) {
|
| ti->error = "Hash device lookup failed";
|
| goto bad_hash_dev;
|
| }
|
|
|