+ if (r < 0)
+ _dm_remove(name, 1, 0);
+
+out:
+ if (cookie && _dm_use_udev())
+ (void)_dm_udev_wait(cookie);
+
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ dm_task_update_nodes();
+
+ /* If code just loaded target module, update versions */
+ _dm_check_versions(cd, dmd->segment.type);
+
+ _destroy_dm_targets_params(dmd);
+
+ return r;
+}
+
+static int _dm_resume_device(const char *name, uint32_t dmflags)
+{
+ struct dm_task *dmt;
+ int r = -EINVAL;
+ uint32_t cookie = 0;
+ uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+
+ if (dmflags & DM_RESUME_PRIVATE)
+ udev_flags |= CRYPT_TEMP_UDEV_FLAGS;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
+ return r;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if ((dmflags & DM_SUSPEND_SKIP_LOCKFS) && !dm_task_skip_lockfs(dmt))
+ goto out;
+
+ if ((dmflags & DM_SUSPEND_NOFLUSH) && !dm_task_no_flush(dmt))
+ goto out;
+
+ if (_dm_use_udev() && !_dm_task_set_cookie(dmt, &cookie, udev_flags))
+ goto out;
+
+ if (dm_task_run(dmt))
+ r = 0;
+out:
+ if (cookie && _dm_use_udev())
+ (void)_dm_udev_wait(cookie);
+
+ dm_task_destroy(dmt);
+
+ dm_task_update_nodes();
+
+ return r;
+}
+
+static int _dm_reload_device(struct crypt_device *cd, const char *name,
+ struct crypt_dm_active_device *dmd)
+{
+ int r = -EINVAL;
+ struct dm_task *dmt = NULL;
+ uint32_t read_ahead = 0;
+
+ /* All devices must have DM_UUID, only resize on old device is exception */
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+ goto out;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_secure_data(dmt))
+ goto out;
+ if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt))
+ goto out;
+
+ r = _create_dm_targets_params(dmd);
+ if (r)
+ goto out;
+
+ r = _add_dm_targets(dmt, dmd);
+ if (r)
+ goto out;
+
+ r = -EINVAL;
+
+#ifdef DM_READ_AHEAD_MINIMUM_FLAG
+ if (device_read_ahead(dmd->segment.data_device, &read_ahead) &&
+ !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG))
+ goto out;
+#endif
+
+ if (dm_task_run(dmt))
+ r = 0;
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ /* If code just loaded target module, update versions */
+ _dm_check_versions(cd, dmd->segment.type);
+
+ _destroy_dm_targets_params(dmd);
+
+ return r;
+}
+
+static void crypt_free_verity_params(struct crypt_params_verity *vp)
+{
+ if (!vp)
+ return;
+
+ free(CONST_CAST(void*)vp->hash_name);
+ free(CONST_CAST(void*)vp->data_device);
+ free(CONST_CAST(void*)vp->hash_device);
+ free(CONST_CAST(void*)vp->fec_device);
+ free(CONST_CAST(void*)vp->salt);
+ free(vp);
+}
+
+static void _dm_target_free_query_path(struct crypt_device *cd, struct dm_target *tgt)
+{
+ switch(tgt->type) {
+ case DM_CRYPT:
+ crypt_free_volume_key(tgt->u.crypt.vk);
+ free(CONST_CAST(void*)tgt->u.crypt.cipher);
+ break;
+ case DM_INTEGRITY:
+ free(CONST_CAST(void*)tgt->u.integrity.integrity);
+ crypt_free_volume_key(tgt->u.integrity.vk);
+
+ free(CONST_CAST(void*)tgt->u.integrity.journal_integrity);
+ crypt_free_volume_key(tgt->u.integrity.journal_integrity_key);
+
+ free(CONST_CAST(void*)tgt->u.integrity.journal_crypt);
+ crypt_free_volume_key(tgt->u.integrity.journal_crypt_key);
+
+ device_free(cd, tgt->u.integrity.meta_device);
+ break;
+ case DM_VERITY:
+ crypt_free_verity_params(tgt->u.verity.vp);
+ device_free(cd, tgt->u.verity.hash_device);
+ free(CONST_CAST(void*)tgt->u.verity.root_hash);
+ free(CONST_CAST(void*)tgt->u.verity.root_hash_sig_key_desc);
+ /* fall through */
+ case DM_LINEAR:
+ /* fall through */
+ case DM_ERROR:
+ /* fall through */
+ case DM_ZERO:
+ break;
+ default:
+ log_err(cd, _("Unknown dm target type."));
+ return;
+ }
+
+ device_free(cd, tgt->data_device);
+}
+
+static void _dm_target_erase(struct crypt_device *cd, struct dm_target *tgt)
+{
+ if (tgt->direction == TARGET_QUERY)
+ _dm_target_free_query_path(cd, tgt);
+
+ if (tgt->type == DM_CRYPT)
+ free(CONST_CAST(void*)tgt->u.crypt.integrity);
+}
+
+void dm_targets_free(struct crypt_device *cd, struct crypt_dm_active_device *dmd)
+{
+ struct dm_target *t = &dmd->segment, *next = t->next;
+
+ _dm_target_erase(cd, t);
+
+ while (next) {
+ t = next;
+ next = t->next;
+ _dm_target_erase(cd, t);
+ free(t);
+ }
+
+ memset(&dmd->segment, 0, sizeof(dmd->segment));
+}
+
+int dm_targets_allocate(struct dm_target *first, unsigned count)
+{
+ if (!first || first->next || !count)
+ return -EINVAL;
+
+ while (--count) {
+ first->next = crypt_zalloc(sizeof(*first));
+ if (!first->next)
+ return -ENOMEM;
+ first = first->next;
+ }
+
+ return 0;
+}
+
+static int check_retry(struct crypt_device *cd, uint32_t *dmd_flags, uint32_t dmt_flags)
+{
+ int ret = 0;
+
+ /* If discard not supported try to load without discard */
+ if ((*dmd_flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
+ !(dmt_flags & DM_DISCARDS_SUPPORTED)) {
+ log_dbg(cd, "Discard/TRIM is not supported");
+ *dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ ret = 1;
+ }
+
+ /* If kernel keyring is not supported load key directly in dm-crypt */
+ if ((*dmd_flags & CRYPT_ACTIVATE_KEYRING_KEY) &&
+ !(dmt_flags & DM_KERNEL_KEYRING_SUPPORTED)) {
+ log_dbg(cd, "dm-crypt does not support kernel keyring");
+ *dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_KEYRING_KEY;
+ ret = 1;
+ }
+
+ /* Drop performance options if not supported */
+ if ((*dmd_flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)) &&
+ !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED | DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) {
+ log_dbg(cd, "dm-crypt does not support performance options");
+ *dmd_flags = *dmd_flags & ~(CRYPT_ACTIVATE_SAME_CPU_CRYPT | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+int dm_create_device(struct crypt_device *cd, const char *name,
+ const char *type,
+ struct crypt_dm_active_device *dmd)
+{
+ uint32_t dmt_flags = 0;
+ int r = -EINVAL;
+
+ if (!type || !dmd)
+ return -EINVAL;
+
+ if (dm_init_context(cd, dmd->segment.type))
+ return -ENOTSUP;
+
+ r = _dm_create_device(cd, name, type, dmd->uuid, dmd);
+
+ if (r < 0 && dm_flags(cd, dmd->segment.type, &dmt_flags))
+ goto out;
+
+ if (r && (dmd->segment.type == DM_CRYPT || dmd->segment.type == DM_LINEAR || dmd->segment.type == DM_ZERO) &&
+ check_retry(cd, &dmd->flags, dmt_flags))
+ r = _dm_create_device(cd, name, type, dmd->uuid, dmd);
+
+ if (r == -EINVAL &&
+ dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) &&
+ !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED)))
+ log_err(cd, _("Requested dm-crypt performance options are not supported."));
+
+ if (r == -EINVAL && dmd->flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION|
+ CRYPT_ACTIVATE_RESTART_ON_CORRUPTION|
+ CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS|
+ CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) &&
+ !(dmt_flags & DM_VERITY_ON_CORRUPTION_SUPPORTED))
+ log_err(cd, _("Requested dm-verity data corruption handling options are not supported."));
+
+ if (r == -EINVAL && dmd->segment.type == DM_VERITY &&
+ dmd->segment.u.verity.fec_device && !(dmt_flags & DM_VERITY_FEC_SUPPORTED))
+ log_err(cd, _("Requested dm-verity FEC options are not supported."));
+
+ if (r == -EINVAL && dmd->segment.type == DM_CRYPT) {
+ if (dmd->segment.u.crypt.integrity && !(dmt_flags & DM_INTEGRITY_SUPPORTED))
+ log_err(cd, _("Requested data integrity options are not supported."));
+ if (dmd->segment.u.crypt.sector_size != SECTOR_SIZE && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED))
+ log_err(cd, _("Requested sector_size option is not supported."));
+ }
+
+ if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) &&
+ !(dmt_flags & DM_INTEGRITY_RECALC_SUPPORTED))
+ log_err(cd, _("Requested automatic recalculation of integrity tags is not supported."));
+
+ if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
+ !(dmt_flags & DM_INTEGRITY_DISCARDS_SUPPORTED))
+ log_err(cd, _("Discard/TRIM is not supported."));
+
+ if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) &&
+ !(dmt_flags & DM_INTEGRITY_BITMAP_SUPPORTED))
+ log_err(cd, _("Requested dm-integrity bitmap mode is not supported."));
+out:
+ dm_exit_context();
+ return r;
+}
+
+int dm_reload_device(struct crypt_device *cd, const char *name,
+ struct crypt_dm_active_device *dmd, uint32_t dmflags, unsigned resume)
+{
+ int r;
+ uint32_t dmt_flags;
+
+ if (!dmd)
+ return -EINVAL;
+
+ if (dm_init_context(cd, dmd->segment.type))
+ return -ENOTSUP;
+
+ if (dm_flags(cd, DM_INTEGRITY, &dmt_flags) || !(dmt_flags & DM_INTEGRITY_RECALC_SUPPORTED))
+ dmd->flags &= ~CRYPT_ACTIVATE_RECALCULATE;
+
+ r = _dm_reload_device(cd, name, dmd);
+
+ if (r == -EINVAL && (dmd->segment.type == DM_CRYPT || dmd->segment.type == DM_LINEAR)) {
+ if ((dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)) &&
+ !dm_flags(cd, DM_CRYPT, &dmt_flags) && !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED)))
+ log_err(cd, _("Requested dm-crypt performance options are not supported."));
+ if ((dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
+ !dm_flags(cd, DM_CRYPT, &dmt_flags) && !(dmt_flags & DM_DISCARDS_SUPPORTED))
+ log_err(cd, _("Discard/TRIM is not supported."));
+ if ((dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) &&
+ !dm_flags(cd, DM_INTEGRITY, &dmt_flags) && !(dmt_flags & DM_INTEGRITY_DISCARDS_SUPPORTED))
+ log_err(cd, _("Discard/TRIM is not supported."));
+ }
+
+ if (!r && resume)
+ r = _dm_resume_device(name, dmflags | act2dmflags(dmd->flags));
+
+ dm_exit_context();
+ return r;
+}
+
+static int dm_status_dmi(const char *name, struct dm_info *dmi,
+ const char *target, char **status_line)
+{
+ struct dm_task *dmt;
+ uint64_t start, length;
+ char *target_type, *params = NULL;
+ int r = -EINVAL;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+ goto out;
+
+ if (!dm_task_no_flush(dmt))
+ goto out;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, dmi))
+ goto out;
+
+ if (!dmi->exists) {
+ r = -ENODEV;
+ goto out;
+ }
+
+ dm_get_next_target(dmt, NULL, &start, &length,
+ &target_type, ¶ms);
+
+ if (!target_type || start != 0)
+ goto out;
+
+ if (target && strcmp(target_type, target))
+ goto out;
+
+ /* for target == NULL check all supported */
+ if (!target && (strcmp(target_type, DM_CRYPT_TARGET) &&
+ strcmp(target_type, DM_VERITY_TARGET) &&
+ strcmp(target_type, DM_INTEGRITY_TARGET) &&
+ strcmp(target_type, DM_LINEAR_TARGET) &&
+ strcmp(target_type, DM_ZERO_TARGET) &&
+ strcmp(target_type, DM_ERROR_TARGET)))
+ goto out;
+ r = 0;
+out:
+ if (!r && status_line && !(*status_line = strdup(params)))
+ r = -ENOMEM;
+
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int dm_status_device(struct crypt_device *cd, const char *name)
+{
+ int r;
+ struct dm_info dmi;
+ struct stat st;
+
+ /* libdevmapper is too clever and handles
+ * path argument differently with error.
+ * Fail early here if parameter is non-existent path.
+ */
+ if (strchr(name, '/') && stat(name, &st) < 0)
+ return -ENODEV;
+
+ if (dm_init_context(cd, DM_UNKNOWN))
+ return -ENOTSUP;
+ r = dm_status_dmi(name, &dmi, NULL, NULL);
+ dm_exit_context();
+
+ if (r < 0)
+ return r;
+
+ return (dmi.open_count > 0) ? 1 : 0;
+}
+
+int dm_status_suspended(struct crypt_device *cd, const char *name)
+{
+ int r;
+ struct dm_info dmi;
+
+ if (dm_init_context(cd, DM_UNKNOWN))
+ return -ENOTSUP;
+ r = dm_status_dmi(name, &dmi, NULL, NULL);
+ dm_exit_context();
+
+ if (r < 0)
+ return r;
+
+ return dmi.suspended ? 1 : 0;
+}
+
+static int _dm_status_verity_ok(struct crypt_device *cd, const char *name)
+{
+ int r;
+ struct dm_info dmi;
+ char *status_line = NULL;
+
+ r = dm_status_dmi(name, &dmi, DM_VERITY_TARGET, &status_line);
+ if (r < 0 || !status_line) {
+ free(status_line);
+ return r;
+ }
+
+ log_dbg(cd, "Verity volume %s status is %s.", name, status_line ?: "");
+ r = status_line[0] == 'V' ? 1 : 0;
+ free(status_line);
+
+ return r;
+}
+
+int dm_status_verity_ok(struct crypt_device *cd, const char *name)
+{
+ int r;
+
+ if (dm_init_context(cd, DM_VERITY))
+ return -ENOTSUP;
+ r = _dm_status_verity_ok(cd, name);
+ dm_exit_context();
+ return r;
+}
+
+int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint64_t *count)
+{
+ int r;
+ struct dm_info dmi;
+ char *status_line = NULL;
+
+ if (dm_init_context(cd, DM_INTEGRITY))
+ return -ENOTSUP;
+
+ r = dm_status_dmi(name, &dmi, DM_INTEGRITY_TARGET, &status_line);
+ if (r < 0 || !status_line) {
+ free(status_line);
+ dm_exit_context();
+ return r;
+ }
+
+ log_dbg(cd, "Integrity volume %s failure status is %s.", name, status_line ?: "");
+ *count = strtoull(status_line, NULL, 10);
+ free(status_line);
+ dm_exit_context();
+
+ return 0;
+}
+
+/* FIXME use hex wrapper, user val wrappers for line parsing */
+static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags,
+ char *params, struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ uint64_t val64;
+ char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc;
+ unsigned int i, val;
+ int r;
+ size_t key_size;
+ struct device *data_device = NULL;
+ char *cipher = NULL, *integrity = NULL;
+ struct volume_key *vk = NULL;
+
+ tgt->type = DM_CRYPT;
+ tgt->direction = TARGET_QUERY;
+ tgt->u.crypt.sector_size = SECTOR_SIZE;
+
+ r = -EINVAL;
+
+ rcipher = strsep(¶ms, " ");
+ rintegrity = NULL;
+
+ /* skip */
+ key_ = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ tgt->u.crypt.iv_offset = val64;
+
+ /* device */
+ rdevice = strsep(¶ms, " ");
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ arg = crypt_lookup_dev(rdevice);
+ r = device_alloc(cd, &data_device, arg);
+ free(arg);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ /*offset */
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ tgt->u.crypt.offset = val64;
+
+ tgt->u.crypt.tag_size = 0;
+
+ /* Features section, available since crypt target version 1.11 */
+ if (*params) {
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ for (i = 0; i < val64; i++) {
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (!strcasecmp(arg, "allow_discards"))
+ *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ else if (!strcasecmp(arg, "same_cpu_crypt"))
+ *act_flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT;
+ else if (!strcasecmp(arg, "submit_from_crypt_cpus"))
+ *act_flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
+ else if (!strcasecmp(arg, "iv_large_sectors"))
+ *act_flags |= CRYPT_ACTIVATE_IV_LARGE_SECTORS;
+ else if (sscanf(arg, "integrity:%u:", &val) == 1) {
+ tgt->u.crypt.tag_size = val;
+ rintegrity = strchr(arg + strlen("integrity:"), ':');
+ if (!rintegrity)
+ goto err;
+ rintegrity++;
+ } else if (sscanf(arg, "sector_size:%u", &val) == 1) {
+ tgt->u.crypt.sector_size = val;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params)
+ goto err;
+ }
+
+ /* cipher */
+ if (get_flags & DM_ACTIVE_CRYPT_CIPHER) {
+ r = cipher_dm2c(CONST_CAST(char**)&cipher,
+ CONST_CAST(char**)&integrity,
+ rcipher, rintegrity);
+ if (r < 0)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ if (key_[0] == ':')
+ *act_flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+
+ if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
+ /* we will trust kernel the key_string is in expected format */
+ if (key_[0] == ':') {
+ if (sscanf(key_ + 1, "%zu", &key_size) != 1)
+ goto err;
+ } else
+ key_size = strlen(key_) / 2;
+
+ vk = crypt_alloc_volume_key(key_size, NULL);
+ if (!vk) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ if (get_flags & DM_ACTIVE_CRYPT_KEY) {
+ if (key_[0] == ':') {
+ /* :<key_size>:<key_type>:<key_description> */
+ key_desc = NULL;
+ endp = strpbrk(key_ + 1, ":");
+ if (endp)
+ key_desc = strpbrk(endp + 1, ":");
+ if (!key_desc) {
+ r = -ENOMEM;
+ goto err;
+ }
+ key_desc++;
+ crypt_volume_key_set_description(vk, key_desc);
+ } else {
+ buffer[2] = '\0';
+ for(i = 0; i < vk->keylength; i++) {
+ memcpy(buffer, &key_[i * 2], 2);
+ vk->key[i] = strtoul(buffer, &endp, 16);
+ if (endp != &buffer[2]) {
+ r = -EINVAL;
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ memset(key_, 0, strlen(key_));
+
+ if (cipher)
+ tgt->u.crypt.cipher = cipher;
+ if (integrity)
+ tgt->u.crypt.integrity = integrity;
+ if (data_device)
+ tgt->data_device = data_device;
+ if (vk)
+ tgt->u.crypt.vk = vk;
+ return 0;
+err:
+ free(cipher);
+ free(integrity);
+ device_free(cd, data_device);
+ crypt_free_volume_key(vk);
+ return r;
+}
+
+static int _dm_target_query_verity(struct crypt_device *cd,
+ uint32_t get_flags,
+ char *params,
+ struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ struct crypt_params_verity *vp = NULL;
+ uint32_t val32;
+ uint64_t val64;
+ ssize_t len;
+ char *str, *str2, *arg;
+ unsigned int i, features;
+ int r;
+ struct device *data_device = NULL, *hash_device = NULL, *fec_device = NULL;
+ char *hash_name = NULL, *root_hash = NULL, *salt = NULL, *fec_dev_str = NULL;
+ char *root_hash_sig_key_desc = NULL;
+
+ if (get_flags & DM_ACTIVE_VERITY_PARAMS) {
+ vp = crypt_zalloc(sizeof(*vp));
+ if (!vp)
+ return -ENOMEM;
+ }
+
+ tgt->type = DM_VERITY;
+ tgt->direction = TARGET_QUERY;
+ tgt->u.verity.vp = vp;
+
+ /* version */
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ return -EINVAL;
+ if (vp)
+ vp->hash_type = val32;
+ params++;
+
+ /* data device */
+ str = strsep(¶ms, " ");
+ if (!params)
+ return -EINVAL;
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &data_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ return r;
+ }
+
+ r = -EINVAL;
+
+ /* hash device */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &hash_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ /* data block size*/
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->data_block_size = val32;
+ params++;
+
+ /* hash block size */
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->hash_block_size = val32;
+ params++;
+
+ /* data blocks */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->data_size = val64;
+ params++;
+
+ /* hash start */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ tgt->u.verity.hash_offset = val64;
+ params++;
+
+ /* hash algorithm */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ if (vp) {
+ hash_name = strdup(str);
+ if (!hash_name) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+
+ /* root digest */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ len = crypt_hex_to_bytes(str, &str2, 0);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+ tgt->u.verity.root_hash_size = len;
+ if (get_flags & DM_ACTIVE_VERITY_ROOT_HASH)
+ root_hash = str2;
+ else
+ free(str2);
+
+ /* salt */
+ str = strsep(¶ms, " ");
+ if (vp) {
+ if (!strcmp(str, "-")) {
+ vp->salt_size = 0;
+ vp->salt = NULL;
+ } else {
+ len = crypt_hex_to_bytes(str, &str2, 0);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+ vp->salt_size = len;
+ salt = str2;
+ }
+ }
+
+ r = -EINVAL;
+
+ /* Features section, available since verity target version 1.3 */
+ if (params) {
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ features = (int)val64;
+ for (i = 0; i < features; i++) {
+ r = -EINVAL;
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (!strcasecmp(arg, "ignore_corruption"))
+ *act_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
+ else if (!strcasecmp(arg, "restart_on_corruption"))
+ *act_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
+ else if (!strcasecmp(arg, "ignore_zero_blocks"))
+ *act_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;
+ else if (!strcasecmp(arg, "check_at_most_once"))
+ *act_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
+ else if (!strcasecmp(arg, "use_fec_from_device")) {
+ str = strsep(¶ms, " ");
+ str2 = crypt_lookup_dev(str);
+ if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) {
+ r = device_alloc(cd, &fec_device, str2);
+ if (r < 0 && r != -ENOTBLK) {
+ free(str2);
+ goto err;
+ }
+ }
+ if (vp) {
+ free(fec_dev_str);
+ fec_dev_str = str2;
+ } else
+ free(str2);
+ i++;
+ } else if (!strcasecmp(arg, "fec_start")) {
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params)
+ params++;
+ tgt->u.verity.fec_offset = val64;
+ if (vp)
+ vp->fec_area_offset = val64 * vp->hash_block_size;
+ i++;
+ } else if (!strcasecmp(arg, "fec_blocks")) {
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params)
+ params++;
+ tgt->u.verity.fec_blocks = val64;
+ i++;
+ } else if (!strcasecmp(arg, "fec_roots")) {
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params)
+ params++;
+ if (vp)
+ vp->fec_roots = val32;
+ i++;
+ } else if (!strcasecmp(arg, "root_hash_sig_key_desc")) {
+ str = strsep(¶ms, " ");
+ if (!str)
+ goto err;
+ if (!root_hash_sig_key_desc)
+ root_hash_sig_key_desc = strdup(str);
+ i++;
+ if (vp)
+ vp->flags |= CRYPT_VERITY_ROOT_HASH_SIGNATURE;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params && *params) {
+ r = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (data_device)
+ tgt->data_device = data_device;
+ if (hash_device)
+ tgt->u.verity.hash_device = hash_device;
+ if (fec_device)
+ tgt->u.verity.fec_device = fec_device;
+ if (root_hash)
+ tgt->u.verity.root_hash = root_hash;
+ if (vp && hash_name)
+ vp->hash_name = hash_name;
+ if (vp && salt)
+ vp->salt = salt;
+ if (vp && fec_dev_str)
+ vp->fec_device = fec_dev_str;
+ if (root_hash_sig_key_desc)
+ tgt->u.verity.root_hash_sig_key_desc = root_hash_sig_key_desc;
+
+ return 0;
+err:
+ device_free(cd, data_device);
+ device_free(cd, hash_device);
+ device_free(cd, fec_device);
+ free(root_hash_sig_key_desc);
+ free(root_hash);
+ free(hash_name);
+ free(salt);
+ free(fec_dev_str);
+ free(vp);
+ return r;
+}
+
+static int _dm_target_query_integrity(struct crypt_device *cd,
+ uint32_t get_flags,
+ char *params,
+ struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ uint32_t val32;
+ uint64_t val64;
+ char c, *str, *str2, *arg;
+ unsigned int i, features, val;
+ ssize_t len;
+ int r;
+ struct device *data_device = NULL, *meta_device = NULL;
+ char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL;
+ struct volume_key *vk = NULL;
+
+ tgt->type = DM_INTEGRITY;
+ tgt->direction = TARGET_QUERY;
+
+ /* data device */
+ str = strsep(¶ms, " ");
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &data_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ return r;
+ }
+
+ r = -EINVAL;
+
+ /*offset */
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ if (!*params || *params != ' ')
+ goto err;
+ tgt->u.integrity.offset = val64;
+
+ /* tag size*/
+ val32 = strtoul(params, ¶ms, 10);
+ tgt->u.integrity.tag_size = val32;
+ if (!*params || *params != ' ')
+ goto err;
+
+ /* journal */
+ c = toupper(*(++params));
+ if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R' && c != 'B'))
+ goto err;
+ if (c == 'D')
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ if (c == 'R')
+ *act_flags |= CRYPT_ACTIVATE_RECOVERY;
+ if (c == 'B') {
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
+ }
+
+ tgt->u.integrity.sector_size = SECTOR_SIZE;
+
+ /* Features section */
+ if (params) {
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ features = (int)val64;
+ for (i = 0; i < features; i++) {
+ r = -EINVAL;
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (sscanf(arg, "journal_sectors:%u", &val) == 1)
+ tgt->u.integrity.journal_size = val * SECTOR_SIZE;
+ else if (sscanf(arg, "journal_watermark:%u", &val) == 1)
+ tgt->u.integrity.journal_watermark = val;
+ else if (sscanf(arg, "sectors_per_bit:%" PRIu64, &val64) == 1) {
+ if (val64 > UINT_MAX)
+ goto err;
+ /* overloaded value for bitmap mode */
+ tgt->u.integrity.journal_watermark = (unsigned int)val64;
+ } else if (sscanf(arg, "commit_time:%u", &val) == 1)
+ tgt->u.integrity.journal_commit_time = val;
+ else if (sscanf(arg, "bitmap_flush_interval:%u", &val) == 1)
+ /* overloaded value for bitmap mode */
+ tgt->u.integrity.journal_commit_time = val;
+ else if (sscanf(arg, "interleave_sectors:%u", &val) == 1)
+ tgt->u.integrity.interleave_sectors = val;
+ else if (sscanf(arg, "block_size:%u", &val) == 1)
+ tgt->u.integrity.sector_size = val;
+ else if (sscanf(arg, "buffer_sectors:%u", &val) == 1)
+ tgt->u.integrity.buffer_sectors = val;
+ else if (!strncmp(arg, "internal_hash:", 14) && !integrity) {
+ str = &arg[14];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ integrity = strdup(arg);
+ if (!integrity) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+
+ if (str) {
+ len = crypt_hex_to_bytes(str, &str2, 1);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+
+ r = 0;
+ if (get_flags & DM_ACTIVE_CRYPT_KEY) {
+ vk = crypt_alloc_volume_key(len, str2);
+ if (!vk)
+ r = -ENOMEM;
+ } else if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
+ vk = crypt_alloc_volume_key(len, NULL);
+ if (!vk)
+ r = -ENOMEM;
+ }
+ crypt_safe_free(str2);
+ if (r < 0)
+ goto err;
+ }
+ } else if (!strncmp(arg, "meta_device:", 12) && !meta_device) {
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str = crypt_lookup_dev(&arg[12]);
+ r = device_alloc(cd, &meta_device, str);
+ free(str);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+ } else if (!strncmp(arg, "journal_crypt:", 14) && !journal_crypt) {
+ str = &arg[14];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ journal_crypt = strdup(arg);
+ if (!journal_crypt) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+ } else if (!strncmp(arg, "journal_mac:", 12) && !journal_integrity) {
+ str = &arg[12];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ journal_integrity = strdup(arg);
+ if (!journal_integrity) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+ } else if (!strcmp(arg, "recalculate")) {
+ *act_flags |= CRYPT_ACTIVATE_RECALCULATE;
+ } else if (!strcmp(arg, "fix_padding")) {
+ tgt->u.integrity.fix_padding = true;
+ } else if (!strcmp(arg, "allow_discards")) {
+ *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params && *params) {
+ r = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (data_device)
+ tgt->data_device = data_device;
+ if (meta_device)
+ tgt->u.integrity.meta_device = meta_device;
+ if (integrity)
+ tgt->u.integrity.integrity = integrity;
+ if (journal_crypt)
+ tgt->u.integrity.journal_crypt = journal_crypt;
+ if (journal_integrity)
+ tgt->u.integrity.journal_integrity = journal_integrity;
+ if (vk)
+ tgt->u.integrity.vk = vk;
+ return 0;
+err:
+ device_free(cd, data_device);
+ device_free(cd, meta_device);
+ free(integrity);
+ free(journal_crypt);
+ free(journal_integrity);
+ crypt_free_volume_key(vk);
+ return r;
+}
+
+static int _dm_target_query_linear(struct crypt_device *cd, struct dm_target *tgt,
+ uint32_t get_flags, char *params)
+{
+ uint64_t val64;
+ char *rdevice, *arg;
+ int r;
+ struct device *device = NULL;