diff options
| -rw-r--r-- | Documentation/device-mapper/verity.txt | 25 | ||||
| -rw-r--r-- | drivers/md/dm-verity.c | 102 |
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) |
