diff mbox

[5/5] dm verity: add support for version 0 of the on-disk format

Message ID 20170411153344.27092-6-enric.balletbo@collabora.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Enric Balletbo i Serra April 11, 2017, 3:33 p.m. UTC
From: Will Drewry <wad@chromium.org>

Version 0 of the on-disk format is compatible with the format used in the
Chromium OS. This patch adds support for this version.

Format type 0 is the original Chrome OS version, whereas the format type 1
is current version, but 0, the original format used in the Chromium OS is
still used in most devices. This patch adds support for the original
on-disk format so you can decide which want you want to use.

Signed-off-by: Will Drewry <wad@chromium.org>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/md/dm-verity-target.c | 279 ++++++++++++++++++++++++++++++++----------
 init/do_mounts_dm.c           |  16 +--
 2 files changed, 220 insertions(+), 75 deletions(-)

Comments

Enric Balletbo i Serra April 11, 2017, 7:42 p.m. UTC | #1
On 11/04/17 19:21, Milan Broz wrote:
> On 04/11/2017 05:33 PM, Enric Balletbo i Serra wrote:
>> From: Will Drewry <wad@chromium.org>
>>
>> Version 0 of the on-disk format is compatible with the format used in the
>> Chromium OS. This patch adds support for this version.
>>
>> Format type 0 is the original Chrome OS version, whereas the format type 1
>> is current version, but 0, the original format used in the Chromium OS is
>> still used in most devices. This patch adds support for the original
>> on-disk format so you can decide which want you want to use.
> 
> NACK!
> 
> What the ...  is this patch doing?
> 

Argh! Very sorry about that was *not* my intention add this patch in this
series, I was only trying to submit 1 to 4 which contains the patches already
send long time ago and the patches to move to the public header some functions
needed for init/do_mount_dm.c

I'm using this specific patch to test 1 to 4 and to boot against a chromeos
rootfs as it parses the current chromeos args, this patch also has the use of
some deprecated functions like simple_strtoull, so definitely this *shouldn't*
be here, my bad.

Besides that, with the below comments, you have made me see that I have some
lacks and I need to rethink some things that I wanted to send after this series,
so many thanks and sorry again if you spend some time looking at this.

> Isn't the format 0 already there? According to
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/device-mapper/verity.txt
> 
> <version>
>     This is the type of the on-disk hash format.
> 
>     0 is the original format used in the Chromium OS.
>       The salt is appended when hashing, digests are stored continuously and
>       the rest of the block is padded with zeroes.
> 
> Reading this patch, it does something completely different than it describes
> in header.
> 
> How is superblock format related to:
> +MODULE_PARM_DESC(dev_wait, "Wait forever for a backing device");
> ...
> not mentioning complete rewrite of verity table parsing... why?
> 
> Also please note that there is userspace (veritysetup) that have to work with
> the old format as well (I think it does already).
> I think we solved the compatibility years ago.
> 
> If not, please describe properly what is missing.
> I do not see any on-disk format changes here...
> 
> Milan
> 
>>
>> Signed-off-by: Will Drewry <wad@chromium.org>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> ---
>>  drivers/md/dm-verity-target.c | 279 ++++++++++++++++++++++++++++++++----------
>>  init/do_mounts_dm.c           |  16 +--
>>  2 files changed, 220 insertions(+), 75 deletions(-)
>>
>> diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
>> index 7335d8a..c098d22 100644
>> --- a/drivers/md/dm-verity-target.c
>> +++ b/drivers/md/dm-verity-target.c
>> @@ -17,6 +17,7 @@
>>  #include "dm-verity.h"
>>  #include "dm-verity-fec.h"
>>  
>> +#include <linux/delay.h>
>>  #include <linux/module.h>
>>  #include <linux/reboot.h>
>>  
>> @@ -28,6 +29,7 @@
>>  #define DM_VERITY_DEFAULT_PREFETCH_SIZE	262144
>>  
>>  #define DM_VERITY_MAX_CORRUPTED_ERRS	100
>> +#define DM_VERITY_NUM_POSITIONAL_ARGS	10
>>  
>>  #define DM_VERITY_OPT_LOGGING		"ignore_corruption"
>>  #define DM_VERITY_OPT_RESTART		"restart_on_corruption"
>> @@ -46,6 +48,11 @@ struct dm_verity_prefetch_work {
>>  	unsigned n_blocks;
>>  };
>>  
>> +/* Controls whether verity_get_device will wait forever for a device. */
>> +static int dev_wait;
>> +module_param(dev_wait, int, 0444);
>> +MODULE_PARM_DESC(dev_wait, "Wait forever for a backing device");
>> +
>>  /*
>>   * Auxiliary structure appended to each dm-bufio buffer. If the value
>>   * hash_verified is nonzero, hash of the block has been verified.
>> @@ -803,6 +810,183 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
>>  	return r;
>>  }
>>  
>> +static int verity_get_device(struct dm_target *ti, const char *devname,
>> +			     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, /*FMODE_READ*/
>> +				   dm_table_get_mode(ti->table), dm_dev))
>> +			return 0;
>> +
>> +		if (!dev_wait)
>> +			break;
>> +
>> +		/* No need to be too aggressive since this is a slow path. */
>> +		msleep(500);
>> +	} while (driver_probe_done() != 0 || !(*dm_dev));
>> +	return -1;
>> +}
>> +
>> +struct verity_args {
>> +	int version;
>> +	char *data_device;
>> +	char *hash_device;
>> +	int data_block_size_bits;
>> +	int hash_block_size_bits;
>> +	u64 num_data_blocks;
>> +	u64 hash_start_block;
>> +	char *algorithm;
>> +	char *digest;
>> +	char *salt;
>> +};
>> +
>> +static void pr_args(struct verity_args *args)
>> +{
>> +	printk(KERN_INFO "VERITY args: version=%d data_device=%s hash_device=%s"
>> +		" data_block_size_bits=%d hash_block_size_bits=%d"
>> +		" num_data_blocks=%lld hash_start_block=%lld"
>> +		" algorithm=%s digest=%s salt=%s\n",
>> +		args->version,
>> +		args->data_device,
>> +		args->hash_device,
>> +		args->data_block_size_bits,
>> +		args->hash_block_size_bits,
>> +		args->num_data_blocks,
>> +		args->hash_start_block,
>> +		args->algorithm,
>> +		args->digest,
>> +		args->salt);
>> +}
>> +
>> +/*
>> + * positional_args - collects the argments using the positional
>> + * parameters.
>> + * arg# - parameter
>> + *    0 - version
>> + *    1 - data device
>> + *    2 - hash device - may be same as data device
>> + *    3 - data block size log2
>> + *    4 - hash block size log2
>> + *    5 - number of data blocks
>> + *    6 - hash start block
>> + *    7 - algorithm
>> + *    8 - digest
>> + *    9 - salt
>> + */
>> +static char *positional_args(unsigned argc, char **argv,
>> +			     struct verity_args *args)
>> +{
>> +	unsigned int num;
>> +	unsigned long long num_ll;
>> +	char dummy;
>> +
>> +	if (argc < DM_VERITY_NUM_POSITIONAL_ARGS)
>> +		return "Invalid argument count: at least 10 arguments required";
>> +
>> +	if (sscanf(argv[0], "%d%c", &num, &dummy) != 1 ||
>> +	    num < 0 || num > 1)
>> +		return "Invalid version";
>> +	args->version = num;
>> +
>> +	args->data_device = argv[1];
>> +	args->hash_device = argv[2];
>> +
>> +	if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
>> +	    !num || (num & (num - 1)) ||
>> +	    num > PAGE_SIZE)
>> +		return "Invalid data device block size";
>> +	args->data_block_size_bits = ffs(num) - 1;
>> +
>> +	if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
>> +	    !num || (num & (num - 1)) ||
>> +	    num > INT_MAX)
>> +		return "Invalid hash device block size";
>> +	args->hash_block_size_bits = ffs(num) - 1;
>> +
>> +	if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
>> +	    (sector_t)(num_ll << (args->data_block_size_bits - SECTOR_SHIFT))
>> +	    >> (args->data_block_size_bits - SECTOR_SHIFT) != num_ll)
>> +		return "Invalid data blocks";
>> +	args->num_data_blocks = num_ll;
>> +
>> +	if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
>> +	    (sector_t)(num_ll << (args->hash_block_size_bits - SECTOR_SHIFT))
>> +	    >> (args->hash_block_size_bits - SECTOR_SHIFT) != num_ll)
>> +		return "Invalid hash start";
>> +	args->hash_start_block = num_ll;
>> +
>> +	args->algorithm = argv[7];
>> +	args->digest = argv[8];
>> +	args->salt = argv[9];
>> +
>> +	return NULL;
>> +}
>> +
>> +static void splitarg(char *arg, char **key, char **val)
>> +{
>> +	*key = strsep(&arg, "=");
>> +	*val = strsep(&arg, "");
>> +}
>> +
>> +static char *chromeos_args(unsigned int argc, char **argv,
>> +			   struct verity_args *args)
>> +{
>> +	char *key, *val;
>> +	unsigned long num;
>> +	int i;
>> +
>> +	args->version = 0;
>> +	args->data_block_size_bits = 12;
>> +	args->hash_block_size_bits = 12;
>> +	for (i = 0; i < argc; ++i) {
>> +		DMWARN("Argument %d: '%s'", i, argv[i]);
>> +		splitarg(argv[i], &key, &val);
>> +		if (!key) {
>> +			DMWARN("Bad argument %d: missing key?", i);
>> +			return "Bad argument: missing key";
>> +		}
>> +		if (!val) {
>> +			DMWARN("Bad argument %d='%s': missing value", i, key);
>> +			return "Bad argument: missing value";
>> +		}
>> +		if (!strcmp(key, "alg")) {
>> +			args->algorithm = val;
>> +		} else if (!strcmp(key, "payload")) {
>> +			args->data_device = val;
>> +		} else if (!strcmp(key, "hashtree")) {
>> +			args->hash_device = val;
>> +		} else if (!strcmp(key, "root_hexdigest")) {
>> +			args->digest = val;
>> +		} else if (!strcmp(key, "hashstart")) {
>> +			if (kstrtoul(val, 10, &num))
>> +				return "Invalid hashstart";
>> +			args->hash_start_block =
>> +			   num >> (args->hash_block_size_bits - SECTOR_SHIFT);
>> +			args->num_data_blocks = args->hash_start_block;
>> +		} else if (!strcmp(key, "salt")) {
>> +			args->salt = val;
>> +		}
>> +	}
>> +	if (!args->salt)
>> +		args->salt = "";
>> +
>> +#define NEEDARG(n) do { \
>> +	if (!(n)) { \
>> +		return "Missing argument: " #n; \
>> +	} } while (0)
>> +
>> +	NEEDARG(args->algorithm);
>> +	NEEDARG(args->data_device);
>> +	NEEDARG(args->hash_device);
>> +	NEEDARG(args->digest);
>> +
>> +#undef NEEDARG
>> +	return NULL;
>> +}
>> +
>>  /*
>>   * Target parameters:
>>   *	<version>	The current format is version 1.
>> @@ -819,14 +1003,21 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
>>   */
>>  static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
>>  {
>> +	struct verity_args args = { 0 };
>>  	struct dm_verity *v;
>>  	struct dm_arg_set as;
>> -	unsigned int num;
>> -	unsigned long long num_ll;
>>  	int r;
>>  	int i;
>>  	sector_t hash_position;
>> -	char dummy;
>> +
>> +	if (argc >= DM_VERITY_NUM_POSITIONAL_ARGS)
>> +		ti->error = positional_args(argc, argv, &args);
>> +	else
>> +		ti->error = chromeos_args(argc, argv, &args);
>> +	if (ti->error)
>> +		return -EINVAL;
>> +	if (0)
>> +		pr_args(&args);
>>  
>>  	v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
>>  	if (!v) {
>> @@ -840,83 +1031,46 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
>>  	if (r)
>>  		goto bad;
>>  
>> -	if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
>> -		ti->error = "Device must be readonly";
>> -		r = -EINVAL;
>> -		goto bad;
>> -	}
>> +	v->version = args.version;
>>  
>> -	if (argc < 10) {
>> -		ti->error = "Not enough arguments";
>> -		r = -EINVAL;
>> -		goto bad;
>> -	}
>> -
>> -	if (sscanf(argv[0], "%u%c", &num, &dummy) != 1 ||
>> -	    num > 1) {
>> -		ti->error = "Invalid version";
>> -		r = -EINVAL;
>> -		goto bad;
>> -	}
>> -	v->version = num;
>> -
>> -	r = dm_get_device(ti, argv[1], FMODE_READ, &v->data_dev);
>> +	r = verity_get_device(ti, args.data_device, &v->data_dev);
>>  	if (r) {
>>  		ti->error = "Data device lookup failed";
>>  		goto bad;
>>  	}
>>  
>> -	r = dm_get_device(ti, argv[2], FMODE_READ, &v->hash_dev);
>> +	r = verity_get_device(ti, args.hash_device, &v->hash_dev);
>>  	if (r) {
>>  		ti->error = "Hash device lookup failed";
>>  		goto bad;
>>  	}
>>  
>> -	if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
>> -	    !num || (num & (num - 1)) ||
>> -	    num < bdev_logical_block_size(v->data_dev->bdev) ||
>> -	    num > PAGE_SIZE) {
>> +	v->data_dev_block_bits = args.data_block_size_bits;
>> +	if ((1 << v->data_dev_block_bits) <
>> +	    bdev_logical_block_size(v->data_dev->bdev)) {
>>  		ti->error = "Invalid data device block size";
>>  		r = -EINVAL;
>>  		goto bad;
>>  	}
>> -	v->data_dev_block_bits = __ffs(num);
>>  
>> -	if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
>> -	    !num || (num & (num - 1)) ||
>> -	    num < bdev_logical_block_size(v->hash_dev->bdev) ||
>> -	    num > INT_MAX) {
>> +	v->hash_dev_block_bits = args.hash_block_size_bits;
>> +	if ((1 << v->data_dev_block_bits) <
>> +	    bdev_logical_block_size(v->hash_dev->bdev)) {
>>  		ti->error = "Invalid hash device block size";
>>  		r = -EINVAL;
>>  		goto bad;
>>  	}
>> -	v->hash_dev_block_bits = __ffs(num);
>> -
>> -	if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
>> -	    (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
>> -	    >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
>> -		ti->error = "Invalid data blocks";
>> -		r = -EINVAL;
>> -		goto bad;
>> -	}
>> -	v->data_blocks = num_ll;
>>  
>> +	v->data_blocks = args.num_data_blocks;
>>  	if (ti->len > (v->data_blocks << (v->data_dev_block_bits - SECTOR_SHIFT))) {
>>  		ti->error = "Data device is too small";
>>  		r = -EINVAL;
>>  		goto bad;
>>  	}
>>  
>> -	if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
>> -	    (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
>> -	    >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
>> -		ti->error = "Invalid hash start";
>> -		r = -EINVAL;
>> -		goto bad;
>> -	}
>> -	v->hash_start = num_ll;
>> +	v->hash_start = args.hash_start_block;
>>  
>> -	v->alg_name = kstrdup(argv[7], GFP_KERNEL);
>> +	v->alg_name = kstrdup(args.algorithm, GFP_KERNEL);
>>  	if (!v->alg_name) {
>>  		ti->error = "Cannot allocate algorithm name";
>>  		r = -ENOMEM;
>> @@ -945,36 +1099,33 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
>>  		r = -ENOMEM;
>>  		goto bad;
>>  	}
>> -	if (strlen(argv[8]) != v->digest_size * 2 ||
>> -	    hex2bin(v->root_digest, argv[8], v->digest_size)) {
>> +	if (strlen(args.digest) != v->digest_size * 2 ||
>> +	    hex2bin(v->root_digest, args.digest, v->digest_size)) {
>>  		ti->error = "Invalid root digest";
>>  		r = -EINVAL;
>>  		goto bad;
>>  	}
>>  
>> -	if (strcmp(argv[9], "-")) {
>> -		v->salt_size = strlen(argv[9]) / 2;
>> +	if (strcmp(args.salt, "-")) {
>> +		v->salt_size = strlen(args.salt) / 2;
>>  		v->salt = kmalloc(v->salt_size, GFP_KERNEL);
>>  		if (!v->salt) {
>>  			ti->error = "Cannot allocate salt";
>>  			r = -ENOMEM;
>>  			goto bad;
>>  		}
>> -		if (strlen(argv[9]) != v->salt_size * 2 ||
>> -		    hex2bin(v->salt, argv[9], v->salt_size)) {
>> +		if (strlen(args.salt) != v->salt_size * 2 ||
>> +		    hex2bin(v->salt, args.salt, v->salt_size)) {
>>  			ti->error = "Invalid salt";
>>  			r = -EINVAL;
>>  			goto bad;
>>  		}
>>  	}
>>  
>> -	argv += 10;
>> -	argc -= 10;
>> -
>>  	/* Optional parameters */
>> -	if (argc) {
>> -		as.argc = argc;
>> -		as.argv = argv;
>> +	if (argc > DM_VERITY_NUM_POSITIONAL_ARGS) {
>> +		as.argc = argc - DM_VERITY_NUM_POSITIONAL_ARGS;
>> +		as.argv = argv + DM_VERITY_NUM_POSITIONAL_ARGS;
>>  
>>  		r = verity_parse_opt_args(&as, v);
>>  		if (r < 0)
>> diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
>> index 54c3299..25a040e 100644
>> --- a/init/do_mounts_dm.c
>> +++ b/init/do_mounts_dm.c
>> @@ -187,8 +187,7 @@ static char * __init dm_parse_device(struct dm_device *dev, char *str)
>>  	if (opt.delim == DM_FIELD_SEP[0]) {
>>  		if (!get_dm_option(&opt, DM_LINE_SEP))
>>  			return NULL;
>> -		if (kstrtoul(opt.start, 10, &dev->num_targets))
>> -			dev->num_targets = 1;
>> +		dev->num_targets = simple_strtoul(opt.start, NULL, 10);
>>  	} else {
>>  		dev->num_targets = 1;
>>  	}
>> @@ -214,7 +213,7 @@ static char * __init dm_parse_targets(struct dm_device *dev, char *str)
>>  	 */
>>  	opt.next = str;
>>  	for (i = 0; i < num_targets; i++) {
>> -		*target = kzalloc(sizeof(*target), GFP_KERNEL);
>> +		*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
>>  		if (!*target) {
>>  			DMERR("failed to allocate memory for target %s<%ld>",
>>  			      dev->name, i);
>> @@ -227,18 +226,14 @@ static char * __init dm_parse_targets(struct dm_device *dev, char *str)
>>  			      " for target %s<%ld>", dev->name, i);
>>  			goto parse_fail;
>>  		}
>> -
>> -		if (kstrtoull(opt.start, 10, &(*target)->begin))
>> -			goto parse_fail;
>> +		(*target)->begin = simple_strtoull(opt.start, NULL, 10);
>>  
>>  		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
>>  			DMERR("failed to parse length for target %s<%ld>",
>>  			      dev->name, i);
>>  			goto parse_fail;
>>  		}
>> -
>> -		if (kstrtoull(opt.start, 10, &(*target)->length))
>> -			goto parse_fail;
>> +		(*target)->length = simple_strtoull(opt.start, NULL, 10);
>>  
>>  		if (get_dm_option(&opt, DM_FIELD_SEP))
>>  			(*target)->type = kstrndup(opt.start, opt.len,
>> @@ -328,8 +323,7 @@ static int __init dm_setup(char *str)
>>  	if (!get_dm_option(&opt, DM_FIELD_SEP))
>>  		goto parse_fail;
>>  	if (isdigit(opt.start[0])) {	/* Optional number field */
>> -		if (kstrtoul(opt.start, 10, &num_devices))
>> -			num_devices = 1;
>> +		num_devices = simple_strtoul(opt.start, NULL, 10);
>>  		str = opt.next;
>>  	} else {
>>  		num_devices = 1;
>>
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
diff mbox

Patch

diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 7335d8a..c098d22 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -17,6 +17,7 @@ 
 #include "dm-verity.h"
 #include "dm-verity-fec.h"
 
+#include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/reboot.h>
 
@@ -28,6 +29,7 @@ 
 #define DM_VERITY_DEFAULT_PREFETCH_SIZE	262144
 
 #define DM_VERITY_MAX_CORRUPTED_ERRS	100
+#define DM_VERITY_NUM_POSITIONAL_ARGS	10
 
 #define DM_VERITY_OPT_LOGGING		"ignore_corruption"
 #define DM_VERITY_OPT_RESTART		"restart_on_corruption"
@@ -46,6 +48,11 @@  struct dm_verity_prefetch_work {
 	unsigned n_blocks;
 };
 
+/* Controls whether verity_get_device will wait forever for a device. */
+static int dev_wait;
+module_param(dev_wait, int, 0444);
+MODULE_PARM_DESC(dev_wait, "Wait forever for a backing device");
+
 /*
  * Auxiliary structure appended to each dm-bufio buffer. If the value
  * hash_verified is nonzero, hash of the block has been verified.
@@ -803,6 +810,183 @@  static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
 	return r;
 }
 
+static int verity_get_device(struct dm_target *ti, const char *devname,
+			     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, /*FMODE_READ*/
+				   dm_table_get_mode(ti->table), dm_dev))
+			return 0;
+
+		if (!dev_wait)
+			break;
+
+		/* No need to be too aggressive since this is a slow path. */
+		msleep(500);
+	} while (driver_probe_done() != 0 || !(*dm_dev));
+	return -1;
+}
+
+struct verity_args {
+	int version;
+	char *data_device;
+	char *hash_device;
+	int data_block_size_bits;
+	int hash_block_size_bits;
+	u64 num_data_blocks;
+	u64 hash_start_block;
+	char *algorithm;
+	char *digest;
+	char *salt;
+};
+
+static void pr_args(struct verity_args *args)
+{
+	printk(KERN_INFO "VERITY args: version=%d data_device=%s hash_device=%s"
+		" data_block_size_bits=%d hash_block_size_bits=%d"
+		" num_data_blocks=%lld hash_start_block=%lld"
+		" algorithm=%s digest=%s salt=%s\n",
+		args->version,
+		args->data_device,
+		args->hash_device,
+		args->data_block_size_bits,
+		args->hash_block_size_bits,
+		args->num_data_blocks,
+		args->hash_start_block,
+		args->algorithm,
+		args->digest,
+		args->salt);
+}
+
+/*
+ * positional_args - collects the argments using the positional
+ * parameters.
+ * arg# - parameter
+ *    0 - version
+ *    1 - data device
+ *    2 - hash device - may be same as data device
+ *    3 - data block size log2
+ *    4 - hash block size log2
+ *    5 - number of data blocks
+ *    6 - hash start block
+ *    7 - algorithm
+ *    8 - digest
+ *    9 - salt
+ */
+static char *positional_args(unsigned argc, char **argv,
+			     struct verity_args *args)
+{
+	unsigned int num;
+	unsigned long long num_ll;
+	char dummy;
+
+	if (argc < DM_VERITY_NUM_POSITIONAL_ARGS)
+		return "Invalid argument count: at least 10 arguments required";
+
+	if (sscanf(argv[0], "%d%c", &num, &dummy) != 1 ||
+	    num < 0 || num > 1)
+		return "Invalid version";
+	args->version = num;
+
+	args->data_device = argv[1];
+	args->hash_device = argv[2];
+
+	if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
+	    !num || (num & (num - 1)) ||
+	    num > PAGE_SIZE)
+		return "Invalid data device block size";
+	args->data_block_size_bits = ffs(num) - 1;
+
+	if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
+	    !num || (num & (num - 1)) ||
+	    num > INT_MAX)
+		return "Invalid hash device block size";
+	args->hash_block_size_bits = ffs(num) - 1;
+
+	if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
+	    (sector_t)(num_ll << (args->data_block_size_bits - SECTOR_SHIFT))
+	    >> (args->data_block_size_bits - SECTOR_SHIFT) != num_ll)
+		return "Invalid data blocks";
+	args->num_data_blocks = num_ll;
+
+	if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
+	    (sector_t)(num_ll << (args->hash_block_size_bits - SECTOR_SHIFT))
+	    >> (args->hash_block_size_bits - SECTOR_SHIFT) != num_ll)
+		return "Invalid hash start";
+	args->hash_start_block = num_ll;
+
+	args->algorithm = argv[7];
+	args->digest = argv[8];
+	args->salt = argv[9];
+
+	return NULL;
+}
+
+static void splitarg(char *arg, char **key, char **val)
+{
+	*key = strsep(&arg, "=");
+	*val = strsep(&arg, "");
+}
+
+static char *chromeos_args(unsigned int argc, char **argv,
+			   struct verity_args *args)
+{
+	char *key, *val;
+	unsigned long num;
+	int i;
+
+	args->version = 0;
+	args->data_block_size_bits = 12;
+	args->hash_block_size_bits = 12;
+	for (i = 0; i < argc; ++i) {
+		DMWARN("Argument %d: '%s'", i, argv[i]);
+		splitarg(argv[i], &key, &val);
+		if (!key) {
+			DMWARN("Bad argument %d: missing key?", i);
+			return "Bad argument: missing key";
+		}
+		if (!val) {
+			DMWARN("Bad argument %d='%s': missing value", i, key);
+			return "Bad argument: missing value";
+		}
+		if (!strcmp(key, "alg")) {
+			args->algorithm = val;
+		} else if (!strcmp(key, "payload")) {
+			args->data_device = val;
+		} else if (!strcmp(key, "hashtree")) {
+			args->hash_device = val;
+		} else if (!strcmp(key, "root_hexdigest")) {
+			args->digest = val;
+		} else if (!strcmp(key, "hashstart")) {
+			if (kstrtoul(val, 10, &num))
+				return "Invalid hashstart";
+			args->hash_start_block =
+			   num >> (args->hash_block_size_bits - SECTOR_SHIFT);
+			args->num_data_blocks = args->hash_start_block;
+		} else if (!strcmp(key, "salt")) {
+			args->salt = val;
+		}
+	}
+	if (!args->salt)
+		args->salt = "";
+
+#define NEEDARG(n) do { \
+	if (!(n)) { \
+		return "Missing argument: " #n; \
+	} } while (0)
+
+	NEEDARG(args->algorithm);
+	NEEDARG(args->data_device);
+	NEEDARG(args->hash_device);
+	NEEDARG(args->digest);
+
+#undef NEEDARG
+	return NULL;
+}
+
 /*
  * Target parameters:
  *	<version>	The current format is version 1.
@@ -819,14 +1003,21 @@  static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
  */
 static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 {
+	struct verity_args args = { 0 };
 	struct dm_verity *v;
 	struct dm_arg_set as;
-	unsigned int num;
-	unsigned long long num_ll;
 	int r;
 	int i;
 	sector_t hash_position;
-	char dummy;
+
+	if (argc >= DM_VERITY_NUM_POSITIONAL_ARGS)
+		ti->error = positional_args(argc, argv, &args);
+	else
+		ti->error = chromeos_args(argc, argv, &args);
+	if (ti->error)
+		return -EINVAL;
+	if (0)
+		pr_args(&args);
 
 	v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
 	if (!v) {
@@ -840,83 +1031,46 @@  static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	if (r)
 		goto bad;
 
-	if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
-		ti->error = "Device must be readonly";
-		r = -EINVAL;
-		goto bad;
-	}
+	v->version = args.version;
 
-	if (argc < 10) {
-		ti->error = "Not enough arguments";
-		r = -EINVAL;
-		goto bad;
-	}
-
-	if (sscanf(argv[0], "%u%c", &num, &dummy) != 1 ||
-	    num > 1) {
-		ti->error = "Invalid version";
-		r = -EINVAL;
-		goto bad;
-	}
-	v->version = num;
-
-	r = dm_get_device(ti, argv[1], FMODE_READ, &v->data_dev);
+	r = verity_get_device(ti, args.data_device, &v->data_dev);
 	if (r) {
 		ti->error = "Data device lookup failed";
 		goto bad;
 	}
 
-	r = dm_get_device(ti, argv[2], FMODE_READ, &v->hash_dev);
+	r = verity_get_device(ti, args.hash_device, &v->hash_dev);
 	if (r) {
 		ti->error = "Hash device lookup failed";
 		goto bad;
 	}
 
-	if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
-	    !num || (num & (num - 1)) ||
-	    num < bdev_logical_block_size(v->data_dev->bdev) ||
-	    num > PAGE_SIZE) {
+	v->data_dev_block_bits = args.data_block_size_bits;
+	if ((1 << v->data_dev_block_bits) <
+	    bdev_logical_block_size(v->data_dev->bdev)) {
 		ti->error = "Invalid data device block size";
 		r = -EINVAL;
 		goto bad;
 	}
-	v->data_dev_block_bits = __ffs(num);
 
-	if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
-	    !num || (num & (num - 1)) ||
-	    num < bdev_logical_block_size(v->hash_dev->bdev) ||
-	    num > INT_MAX) {
+	v->hash_dev_block_bits = args.hash_block_size_bits;
+	if ((1 << v->data_dev_block_bits) <
+	    bdev_logical_block_size(v->hash_dev->bdev)) {
 		ti->error = "Invalid hash device block size";
 		r = -EINVAL;
 		goto bad;
 	}
-	v->hash_dev_block_bits = __ffs(num);
-
-	if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
-	    (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
-	    >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
-		ti->error = "Invalid data blocks";
-		r = -EINVAL;
-		goto bad;
-	}
-	v->data_blocks = num_ll;
 
+	v->data_blocks = args.num_data_blocks;
 	if (ti->len > (v->data_blocks << (v->data_dev_block_bits - SECTOR_SHIFT))) {
 		ti->error = "Data device is too small";
 		r = -EINVAL;
 		goto bad;
 	}
 
-	if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
-	    (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
-	    >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
-		ti->error = "Invalid hash start";
-		r = -EINVAL;
-		goto bad;
-	}
-	v->hash_start = num_ll;
+	v->hash_start = args.hash_start_block;
 
-	v->alg_name = kstrdup(argv[7], GFP_KERNEL);
+	v->alg_name = kstrdup(args.algorithm, GFP_KERNEL);
 	if (!v->alg_name) {
 		ti->error = "Cannot allocate algorithm name";
 		r = -ENOMEM;
@@ -945,36 +1099,33 @@  static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 		r = -ENOMEM;
 		goto bad;
 	}
-	if (strlen(argv[8]) != v->digest_size * 2 ||
-	    hex2bin(v->root_digest, argv[8], v->digest_size)) {
+	if (strlen(args.digest) != v->digest_size * 2 ||
+	    hex2bin(v->root_digest, args.digest, v->digest_size)) {
 		ti->error = "Invalid root digest";
 		r = -EINVAL;
 		goto bad;
 	}
 
-	if (strcmp(argv[9], "-")) {
-		v->salt_size = strlen(argv[9]) / 2;
+	if (strcmp(args.salt, "-")) {
+		v->salt_size = strlen(args.salt) / 2;
 		v->salt = kmalloc(v->salt_size, GFP_KERNEL);
 		if (!v->salt) {
 			ti->error = "Cannot allocate salt";
 			r = -ENOMEM;
 			goto bad;
 		}
-		if (strlen(argv[9]) != v->salt_size * 2 ||
-		    hex2bin(v->salt, argv[9], v->salt_size)) {
+		if (strlen(args.salt) != v->salt_size * 2 ||
+		    hex2bin(v->salt, args.salt, v->salt_size)) {
 			ti->error = "Invalid salt";
 			r = -EINVAL;
 			goto bad;
 		}
 	}
 
-	argv += 10;
-	argc -= 10;
-
 	/* Optional parameters */
-	if (argc) {
-		as.argc = argc;
-		as.argv = argv;
+	if (argc > DM_VERITY_NUM_POSITIONAL_ARGS) {
+		as.argc = argc - DM_VERITY_NUM_POSITIONAL_ARGS;
+		as.argv = argv + DM_VERITY_NUM_POSITIONAL_ARGS;
 
 		r = verity_parse_opt_args(&as, v);
 		if (r < 0)
diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
index 54c3299..25a040e 100644
--- a/init/do_mounts_dm.c
+++ b/init/do_mounts_dm.c
@@ -187,8 +187,7 @@  static char * __init dm_parse_device(struct dm_device *dev, char *str)
 	if (opt.delim == DM_FIELD_SEP[0]) {
 		if (!get_dm_option(&opt, DM_LINE_SEP))
 			return NULL;
-		if (kstrtoul(opt.start, 10, &dev->num_targets))
-			dev->num_targets = 1;
+		dev->num_targets = simple_strtoul(opt.start, NULL, 10);
 	} else {
 		dev->num_targets = 1;
 	}
@@ -214,7 +213,7 @@  static char * __init dm_parse_targets(struct dm_device *dev, char *str)
 	 */
 	opt.next = str;
 	for (i = 0; i < num_targets; i++) {
-		*target = kzalloc(sizeof(*target), GFP_KERNEL);
+		*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
 		if (!*target) {
 			DMERR("failed to allocate memory for target %s<%ld>",
 			      dev->name, i);
@@ -227,18 +226,14 @@  static char * __init dm_parse_targets(struct dm_device *dev, char *str)
 			      " for target %s<%ld>", dev->name, i);
 			goto parse_fail;
 		}
-
-		if (kstrtoull(opt.start, 10, &(*target)->begin))
-			goto parse_fail;
+		(*target)->begin = simple_strtoull(opt.start, NULL, 10);
 
 		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
 			DMERR("failed to parse length for target %s<%ld>",
 			      dev->name, i);
 			goto parse_fail;
 		}
-
-		if (kstrtoull(opt.start, 10, &(*target)->length))
-			goto parse_fail;
+		(*target)->length = simple_strtoull(opt.start, NULL, 10);
 
 		if (get_dm_option(&opt, DM_FIELD_SEP))
 			(*target)->type = kstrndup(opt.start, opt.len,
@@ -328,8 +323,7 @@  static int __init dm_setup(char *str)
 	if (!get_dm_option(&opt, DM_FIELD_SEP))
 		goto parse_fail;
 	if (isdigit(opt.start[0])) {	/* Optional number field */
-		if (kstrtoul(opt.start, 10, &num_devices))
-			num_devices = 1;
+		num_devices = simple_strtoul(opt.start, NULL, 10);
 		str = opt.next;
 	} else {
 		num_devices = 1;