Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1381)

Unified Diff: drivers/md/dm-verity.c

Issue 3026039: [RFC] CHROMIUM: Two patches for comments: EFI GUID boot for dm-verity (Closed) Base URL: http://src.chromium.org/git/kernel.git
Patch Set: fix desc. dont always wait Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | fs/partitions/efi.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « no previous file | fs/partitions/efi.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698