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; |
} |