aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/device-mapper/verity.txt25
-rw-r--r--drivers/md/dm-verity.c102
2 files changed, 89 insertions, 38 deletions
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index 2929f6b1c..e15bc1a0f 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -10,7 +10,8 @@ Construction Parameters
<version> <dev> <hash_dev>
<data_block_size> <hash_block_size>
<num_data_blocks> <hash_start_block>
- <algorithm> <digest> <salt> <mode>
+ <algorithm> <digest> <salt>
+ [<#opt_params> <opt_params>]
<version>
This is the type of the on-disk hash format.
@@ -62,15 +63,21 @@ Construction Parameters
<salt>
The hexadecimal encoding of the salt value.
-<mode>
- Optional. The mode of operation.
+<#opt_params>
+ Number of optional parameters. If there are no optional parameters,
+ the optional paramaters section can be skipped or #opt_params can be zero.
+ Otherwise #opt_params is the number of following arguments.
- 0 is the normal mode of operation where a corrupted block will result in an
- I/O error.
+ Example of optional parameters section:
+ 1 ignore_corruption
- 1 is logging mode where corrupted blocks are logged and a uevent is sent to
- notify user space.
+ignore_corruption
+ Log corrupted blocks, but allow read operations to proceed normally.
+restart_on_corruption
+ Restart the system when a corrupted block is discovered. This option is
+ not compatible with ignore_corruption and requires user space support to
+ avoid restart loops.
Theory of operation
===================
@@ -135,7 +142,7 @@ block boundary) are the hash blocks which are stored a depth at a time
The full specification of kernel parameters and on-disk metadata format
is available at the cryptsetup project's wiki page
- http://code.google.com/p/cryptsetup/wiki/DMVerity
+ https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
Status
======
@@ -152,7 +159,7 @@ Set up a device:
A command line tool veritysetup is available to compute or verify
the hash tree or activate the kernel device. This is available from
-the cryptsetup upstream repository http://code.google.com/p/cryptsetup/
+the cryptsetup upstream repository https://gitlab.com/cryptsetup/cryptsetup/
(as a libcryptsetup extension).
Create hash on the device:
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index fe07d915e..03f0c2bb9 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -24,7 +24,7 @@
#define DM_MSG_PREFIX "verity"
#define DM_VERITY_ENV_LENGTH 42
-#define DM_VERITY_ENV_VAR_NAME "VERITY_ERR_BLOCK_NR"
+#define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
#define DM_VERITY_IO_VEC_INLINE 16
#define DM_VERITY_MEMPOOL_SIZE 4
@@ -33,14 +33,17 @@
#define DM_VERITY_MAX_LEVELS 63
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
+#define DM_VERITY_OPT_LOGGING "ignore_corruption"
+#define DM_VERITY_OPT_RESTART "restart_on_corruption"
+
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
enum verity_mode {
- DM_VERITY_MODE_EIO = 0,
- DM_VERITY_MODE_LOGGING = 1,
- DM_VERITY_MODE_RESTART = 2
+ DM_VERITY_MODE_EIO,
+ DM_VERITY_MODE_LOGGING,
+ DM_VERITY_MODE_RESTART
};
enum verity_block_type {
@@ -201,17 +204,20 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
* Handle verification errors.
*/
static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
- unsigned long long block)
+ unsigned long long block)
{
char verity_env[DM_VERITY_ENV_LENGTH];
char *envp[] = { verity_env, NULL };
const char *type_str = "";
struct mapped_device *md = dm_table_get_md(v->ti->table);
+ /* Corruption should be visible in device status in all modes */
+ v->hash_failed = 1;
+
if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
goto out;
- ++v->corrupted_errs;
+ v->corrupted_errs++;
switch (type) {
case DM_VERITY_BLOCK_TYPE_DATA:
@@ -224,8 +230,8 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
BUG();
}
- DMERR_LIMIT("%s: %s block %llu is corrupted", v->data_dev->name,
- type_str, block);
+ DMERR("%s: %s block %llu is corrupted", v->data_dev->name, type_str,
+ block);
if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
DMERR("%s: reached maximum errors", v->data_dev->name);
@@ -270,7 +276,7 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
verity_hash_at_level(v, block, level, &hash_block, &offset);
data = dm_bufio_read(v->bufio, hash_block, &buf);
- if (unlikely(IS_ERR(data)))
+ if (IS_ERR(data))
return PTR_ERR(data);
aux = dm_bufio_get_aux_data(buf);
@@ -322,8 +328,6 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
goto release_ret_r;
}
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- v->hash_failed = 1;
-
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
hash_block)) {
r = -EIO;
@@ -418,6 +422,7 @@ test_block_hash:
r = crypto_shash_update(desc,
page + bv->bv_offset + offset, len);
kunmap_atomic(page);
+
if (r < 0) {
DMERR("crypto_shash_update failed: %d", r);
return r;
@@ -445,8 +450,6 @@ test_block_hash:
return r;
}
if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- v->hash_failed = 1;
-
if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
io->block + b))
return -EIO;
@@ -520,7 +523,7 @@ static void verity_prefetch_io(struct work_struct *work)
goto no_prefetch_cluster;
if (unlikely(cluster & (cluster - 1)))
- cluster = 1 << (fls(cluster) - 1);
+ cluster = 1 << __fls(cluster);
hash_block_start &= ~(sector_t)(cluster - 1);
hash_block_end |= cluster - 1;
@@ -636,6 +639,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
else
for (x = 0; x < v->salt_size; x++)
DMEMIT("%02x", v->salt[x]);
+ if (v->mode != DM_VERITY_MODE_EIO) {
+ DMEMIT(" 1 ");
+ switch (v->mode) {
+ case DM_VERITY_MODE_LOGGING:
+ DMEMIT(DM_VERITY_OPT_LOGGING);
+ break;
+ case DM_VERITY_MODE_RESTART:
+ DMEMIT(DM_VERITY_OPT_RESTART);
+ break;
+ default:
+ BUG();
+ }
+ }
break;
}
}
@@ -737,13 +753,19 @@ static void verity_dtr(struct dm_target *ti)
static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
- unsigned num;
+ struct dm_arg_set as;
+ const char *opt_string;
+ unsigned int num, opt_params;
unsigned long long num_ll;
int r;
int i;
sector_t hash_position;
char dummy;
+ static struct dm_arg _args[] = {
+ {0, 1, "Invalid number of feature args"},
+ };
+
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
if (!v) {
ti->error = "Cannot allocate verity structure";
@@ -758,14 +780,14 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
- if (argc < 10 || argc > 11) {
- ti->error = "Invalid argument count: 10-11 arguments required";
+ if (argc < 10) {
+ ti->error = "Not enough arguments";
r = -EINVAL;
goto bad;
}
- if (sscanf(argv[0], "%d%c", &num, &dummy) != 1 ||
- num < 0 || num > 1) {
+ if (sscanf(argv[0], "%u%c", &num, &dummy) != 1 ||
+ num > 1) {
ti->error = "Invalid version";
r = -EINVAL;
goto bad;
@@ -792,7 +814,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -EINVAL;
goto bad;
}
- v->data_dev_block_bits = ffs(num) - 1;
+ v->data_dev_block_bits = __ffs(num);
if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
!num || (num & (num - 1)) ||
@@ -802,7 +824,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -EINVAL;
goto bad;
}
- v->hash_dev_block_bits = ffs(num) - 1;
+ 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))
@@ -880,19 +902,41 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
}
- if (argc > 10) {
- if (sscanf(argv[10], "%d%c", &num, &dummy) != 1 ||
- num < DM_VERITY_MODE_EIO ||
- num > DM_VERITY_MODE_RESTART) {
- ti->error = "Invalid mode";
- r = -EINVAL;
+ argv += 10;
+ argc -= 10;
+
+ /* Optional parameters */
+ if (argc) {
+ as.argc = argc;
+ as.argv = argv;
+
+ r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+ if (r)
goto bad;
+
+ while (opt_params) {
+ opt_params--;
+ opt_string = dm_shift_arg(&as);
+ if (!opt_string) {
+ ti->error = "Not enough feature arguments";
+ r = -EINVAL;
+ goto bad;
+ }
+
+ if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
+ v->mode = DM_VERITY_MODE_LOGGING;
+ else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
+ v->mode = DM_VERITY_MODE_RESTART;
+ else {
+ ti->error = "Invalid feature arguments";
+ r = -EINVAL;
+ goto bad;
+ }
}
- v->mode = num;
}
v->hash_per_block_bits =
- fls((1 << v->hash_dev_block_bits) / v->digest_size) - 1;
+ __fls((1 << v->hash_dev_block_bits) / v->digest_size);
v->levels = 0;
if (v->data_blocks)