diff --git a/TODO b/TODO
index 3307ce1992..8a3861c3e0 100644
--- a/TODO
+++ b/TODO
@@ -117,6 +117,8 @@ Deprecations and removals:
Features:
+* Add support for extra verity configuration options to systemd-reart (FEC, hash type, etc)
+
* measure credentials picked up from SMBIOS to some suitable PCR
* measure GPT and LUKS headers somewhere when we use them (i.e. in
diff --git a/man/repart.d.xml b/man/repart.d.xml
index 6a0a6b32b2..bdbf054fcf 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -580,6 +580,38 @@
This option has no effect if the partition already exists.
+
+ Verity=
+
+ Takes one of off, data or
+ hash. Defaults to off. If set to off or
+ data, the partition is populated with content as specified by
+ CopyBlocks= or CopyFiles=. If set to hash,
+ the partition will be populated with verity hashes from a matching verity data partition. A matching
+ data partition is a partition with Verity= set to data and the
+ same verity match key (as configured with VerityMatchKey=). If not explicitly
+ configured, the data partition's UUID will be set to the first 128 bits of the verity root hash.
+ Similarly, if not configured, the hash partition's UUID will be set to the final 128 bits of the
+ verity root hash. The verity root hash itself will be included in the output of
+ systemd-repart.
+
+ This option has no effect if the partition already exists.
+
+ Usage of this option in combination with Encrypt= is not supported.
+
+ For each unique VerityMatchKey= value, a single verity data partition
+ (Verity=data) and a single verity hash partition (Verity=hash)
+ must be defined.
+
+
+
+ VerityMatchKey=
+
+ Takes a short, user-chosen identifier string. This setting is used to find sibling
+ verity partitions for the current verity partition. See the description for
+ Verity=.
+
+
FactoryReset=
@@ -746,6 +778,28 @@ SizeMaxBytes=64M
# ln -s 50-root.conf /usr/lib/repart.d/70-root-b.conf
# ln -s 60-root-verity.conf /usr/lib/repart.d/80-root-verity-b.conf
+
+
+
+
+ Create a data and verity partition from a OS tree
+
+ Assuming we have an OS tree at /var/tmp/os-tree that we want to package in a root partition
+ together with a matching verity partition, we can do so as follows:
+
+ # 50-root.conf
+[Partition]
+Type=root
+CopyFiles=/var/tmp/os-tree
+Verity=data
+VerityMatchKey=root
+
+
+ # 60-root-verity.conf
+[Partition]
+Type=root-verity
+Verity=hash
+VerityMatchKey=root
diff --git a/src/partition/repart.c b/src/partition/repart.c
index ea8468dc29..889de712e2 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -132,6 +132,14 @@ typedef enum EncryptMode {
_ENCRYPT_MODE_INVALID = -EINVAL,
} EncryptMode;
+typedef enum VerityMode {
+ VERITY_OFF,
+ VERITY_DATA,
+ VERITY_HASH,
+ _VERITY_MODE_MAX,
+ _VERITY_MODE_INVALID = -EINVAL,
+} VerityMode;
+
struct Partition {
char *definition_path;
char **drop_in_files;
@@ -170,12 +178,19 @@ struct Partition {
char **copy_files;
char **make_directories;
EncryptMode encrypt;
+ VerityMode verity;
+ char *verity_match_key;
uint64_t gpt_flags;
int no_auto;
int read_only;
int growfs;
+ uint8_t *roothash;
+ size_t roothash_size;
+
+ Partition *siblings[_VERITY_MODE_MAX];
+
LIST_FIELDS(Partition, partitions);
};
@@ -211,10 +226,18 @@ static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
[ENCRYPT_KEY_FILE_TPM2] = "key-file+tpm2",
};
+static const char *verity_mode_table[_VERITY_MODE_MAX] = {
+ [VERITY_OFF] = "off",
+ [VERITY_DATA] = "data",
+ [VERITY_HASH] = "hash",
+};
+
#if HAVE_LIBCRYPTSETUP
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
#else
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(verity_mode, VerityMode);
#endif
@@ -282,6 +305,9 @@ static Partition* partition_free(Partition *p) {
free(p->format);
strv_free(p->copy_files);
strv_free(p->make_directories);
+ free(p->verity_match_key);
+
+ free(p->roothash);
return mfree(p);
}
@@ -407,6 +433,21 @@ static bool context_drop_one_priority(Context *context) {
p->dropped = true;
log_info("Can't fit partition %s of priority %" PRIi32 ", dropping.", p->definition_path, p->priority);
+
+ /* We ensure that all verity sibling partitions have the same priority, so it's safe
+ * to drop all siblings here as well. */
+
+ for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) {
+ if (!p->siblings[mode])
+ continue;
+
+ if (p->siblings[mode]->dropped)
+ continue;
+
+ p->siblings[mode]->dropped = true;
+ log_info("Also dropping sibling verity %s partition %s",
+ verity_mode_to_string(mode), p->siblings[mode]->definition_path);
+ }
}
return true;
@@ -1352,6 +1393,8 @@ static int config_parse_uuid(
return 0;
}
+static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
+
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
ConfigTableItem table[] = {
@@ -1371,6 +1414,8 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "CopyFiles", config_parse_copy_files, 0, p },
{ "Partition", "MakeDirectories", config_parse_make_dirs, 0, p },
{ "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
+ { "Partition", "Verity", config_parse_verity, 0, &p->verity },
+ { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
{ "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
{ "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
{ "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
@@ -1428,6 +1473,31 @@ static int partition_read_definition(Partition *p, const char *path, const char
return log_oom();
}
+ if (p->verity != VERITY_OFF || p->encrypt != ENCRYPT_OFF) {
+ r = dlopen_cryptsetup();
+ if (r < 0)
+ return log_syntax(NULL, LOG_ERR, path, 1, r,
+ "libcryptsetup not found, Verity=/Encrypt= are not supported: %m");
+ }
+
+ if (p->verity != VERITY_OFF && !p->verity_match_key)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "VerityMatchKey= must be set if Verity=%s", verity_mode_to_string(p->verity));
+
+ if (p->verity == VERITY_OFF && p->verity_match_key)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "VerityMatchKey= can only be set if Verity= is not \"%s\"",
+ verity_mode_to_string(p->verity));
+
+ if (p->verity == VERITY_HASH && (p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories))
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "CopyBlocks=/CopyFiles=/Format=/MakeDirectories= cannot be used with Verity=%s",
+ verity_mode_to_string(p->verity));
+
+ if (p->verity != VERITY_OFF && p->encrypt != ENCRYPT_OFF)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Encrypting verity hash/data partitions is not supported");
+
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((gpt_partition_type_is_root_verity(p->type_uuid) ||
gpt_partition_type_is_usr_verity(p->type_uuid)) &&
@@ -1442,6 +1512,47 @@ static int partition_read_definition(Partition *p, const char *path, const char
return 0;
}
+static int find_verity_sibling(Context *context, Partition *p, VerityMode mode, Partition **ret) {
+ Partition *s = NULL;
+
+ assert(p);
+ assert(p->verity != VERITY_OFF);
+ assert(p->verity_match_key);
+ assert(mode != VERITY_OFF);
+ assert(p->verity != mode);
+ assert(ret);
+
+ /* Try to find the matching sibling partition of the given type for a verity partition. For a data
+ * partition, this is the corresponding hash partiton with the same verity name (and vice versa for
+ * the hash partition).
+ */
+
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (p == q)
+ continue;
+
+ if (q->verity != mode)
+ continue;
+
+ assert(q->verity_match_key);
+
+ if (!streq(p->verity_match_key, q->verity_match_key))
+ continue;
+
+ if (s)
+ return -ENOTUNIQ;
+
+ s = q;
+ }
+
+ if (!s)
+ return -ENXIO;
+
+ *ret = s;
+
+ return 0;
+}
+
static int context_read_definitions(
Context *context,
char **directories,
@@ -1480,6 +1591,42 @@ static int context_read_definitions(
context->n_partitions++;
}
+ /* Check that each configured verity hash/data partition has a matching verity data/hash partition. */
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->verity == VERITY_OFF)
+ continue;
+
+ for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) {
+ Partition *q;
+
+ if (p->verity == mode)
+ continue;
+
+ if (p->siblings[mode])
+ continue;
+
+ r = find_verity_sibling(context, p, mode, &q);
+ if (r == -ENXIO)
+ return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Missing verity %s partition for verity %s partition with VerityMatchKey=%s",
+ verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
+ if (r == -ENOTUNIQ)
+ return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Multiple verity %s partitions found for verity %s partition with VerityMatchKey=%s",
+ verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
+ if (r < 0)
+ return r;
+
+ if (q->priority != p->priority)
+ return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
+ p->priority, q->priority, p->verity_match_key);
+
+ p->siblings[mode] = q;
+ }
+ }
+
return 0;
}
@@ -2042,26 +2189,26 @@ static int context_dump_partitions(Context *context, const char *node) {
_cleanup_(table_unrefp) Table *t = NULL;
uint64_t sum_padding = 0, sum_size = 0;
int r;
- const size_t dropin_files_col = 13;
- bool no_dropin_files = true;
+ const size_t roothash_col = 13, dropin_files_col = 14;
+ bool has_roothash = false, has_dropin_files = false;
if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
log_info("Empty partition table.");
return 0;
}
- t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "drop-in files");
+ t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "roothash", "drop-in files");
if (!t)
return log_oom();
if (!DEBUG_LOGGING) {
if (arg_json_format_flags & JSON_FORMAT_OFF)
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 8, (size_t) 11, dropin_files_col);
+ (size_t) 8, (size_t) 11, roothash_col, dropin_files_col);
else
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12,
- dropin_files_col);
+ (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10,
+ (size_t) 12, roothash_col, dropin_files_col);
}
(void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
@@ -2073,7 +2220,7 @@ static int context_dump_partitions(Context *context, const char *node) {
(void) table_set_align_percent(t, table_get_cell(t, 0, 11), 100);
LIST_FOREACH(partitions, p, context->partitions) {
- _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
+ _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL, *rh = NULL;
char uuid_buffer[SD_ID128_UUID_STRING_MAX];
const char *label, *activity = NULL;
@@ -2101,6 +2248,12 @@ static int context_dump_partitions(Context *context, const char *node) {
if (p->new_padding != UINT64_MAX)
sum_padding += p->new_padding;
+ if (p->verity == VERITY_HASH) {
+ rh = p->roothash ? hexmem(p->roothash, p->roothash_size) : strdup("TBD");
+ if (!rh)
+ return log_oom();
+ }
+
r = table_add_many(
t,
TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type_uuid, uuid_buffer),
@@ -2116,11 +2269,13 @@ static int context_dump_partitions(Context *context, const char *node) {
TABLE_UINT64, p->new_padding,
TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
TABLE_STRING, activity ?: "unchanged",
+ TABLE_STRING, rh,
TABLE_STRV, p->drop_in_files);
if (r < 0)
return table_log_add_error(r);
- no_dropin_files = no_dropin_files && strv_isempty(p->drop_in_files);
+ has_roothash = has_roothash || !isempty(rh);
+ has_dropin_files = has_dropin_files || !strv_isempty(p->drop_in_files);
}
if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
@@ -2144,12 +2299,19 @@ static int context_dump_partitions(Context *context, const char *node) {
TABLE_EMPTY,
TABLE_STRING, b,
TABLE_EMPTY,
+ TABLE_EMPTY,
TABLE_EMPTY);
if (r < 0)
return table_log_add_error(r);
}
- if (no_dropin_files) {
+ if (!has_roothash) {
+ r = table_hide_column_from_display(t, roothash_col);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set columns to display: %m");
+ }
+
+ if (!has_dropin_files) {
r = table_hide_column_from_display(t, dropin_files_col);
if (r < 0)
return log_error_errno(r, "Failed to set columns to display: %m");
@@ -2353,6 +2515,55 @@ static int context_dump_partition_bar(Context *context, const char *node) {
return 0;
}
+static bool context_has_roothash(Context *context) {
+ LIST_FOREACH(partitions, p, context->partitions)
+ if (p->roothash)
+ return true;
+
+ return false;
+}
+
+static int context_dump(Context *context, const char *node, bool late) {
+ int r;
+
+ assert(context);
+ assert(node);
+
+ if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
+ return 0;
+
+ /* If we're outputting JSON, only dump after doing all operations so we can include the roothashes
+ * in the output. */
+ if (!late && !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
+ return 0;
+
+ /* If we're not outputting JSON, only dump again after doing all operations if there are any
+ * roothashes that we need to communicate to the user. */
+ if (late && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !context_has_roothash(context))
+ return 0;
+
+ r = context_dump_partitions(context, node);
+ if (r < 0)
+ return r;
+
+ /* Make sure we only write the partition bar once, even if we're writing the partition table twice to
+ * communicate roothashes. */
+ if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
+ putc('\n', stdout);
+
+ r = context_dump_partition_bar(context, node);
+ if (r < 0)
+ return r;
+
+ putc('\n', stdout);
+ }
+
+ fflush(stdout);
+
+ return 0;
+}
+
+
static bool context_changed(const Context *context) {
assert(context);
@@ -3143,6 +3354,130 @@ static int context_mkfs(Context *context) {
return 0;
}
+static int do_verity_format(
+ LoopDevice *data_device,
+ LoopDevice *hash_device,
+ uint64_t sector_size,
+ uint8_t **ret_roothash,
+ size_t *ret_roothash_size) {
+
+#if HAVE_LIBCRYPTSETUP
+ _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_free_ uint8_t *rh = NULL;
+ size_t rhs;
+ int r;
+
+ assert(data_device);
+ assert(hash_device);
+ assert(sector_size > 0);
+ assert(ret_roothash);
+ assert(ret_roothash_size);
+
+ r = dlopen_cryptsetup();
+ if (r < 0)
+ return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
+
+ r = sym_crypt_init(&cd, hash_device->node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+
+ r = sym_crypt_format(
+ cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0,
+ &(struct crypt_params_verity){
+ .data_device = data_device->node,
+ .flags = CRYPT_VERITY_CREATE_HASH,
+ .hash_name = "sha256",
+ .hash_type = 1,
+ .data_block_size = sector_size,
+ .hash_block_size = sector_size,
+ .salt_size = 32,
+ });
+ if (r < 0)
+ return log_error_errno(r, "Failed to setup verity hash data: %m");
+
+ r = sym_crypt_get_volume_key_size(cd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine verity root hash size: %m");
+ rhs = (size_t) r;
+
+ rh = malloc(rhs);
+ if (!rh)
+ return log_oom();
+
+ r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get verity root hash: %m");
+
+ *ret_roothash = TAKE_PTR(rh);
+ *ret_roothash_size = rhs;
+
+ return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity: %m");
+#endif
+}
+
+static int context_verity(Context *context) {
+ int fd = -1, r;
+
+ assert(context);
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ Partition *dp;
+ _cleanup_(loop_device_unrefp) LoopDevice *hash_device = NULL, *data_device = NULL;
+ _cleanup_free_ uint8_t *rh = NULL;
+ size_t rhs = 0; /* Initialize to work around for GCC false positive. */
+
+ if (p->dropped)
+ continue;
+
+ if (PARTITION_EXISTS(p)) /* Never format existing partitions */
+ continue;
+
+ if (p->verity != VERITY_HASH)
+ continue;
+
+ assert_se(dp = p->siblings[VERITY_DATA]);
+ assert(!dp->dropped);
+
+ if (fd < 0)
+ assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+
+ r = loop_device_make(fd, O_RDONLY, dp->offset, dp->new_size, 0, LOCK_EX, &data_device);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to make loopback device of verity data partition %" PRIu64 ": %m",
+ p->partno);
+
+ r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &hash_device);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to make loopback device of verity hash partition %" PRIu64 ": %m",
+ p->partno);
+
+ r = do_verity_format(data_device, hash_device, context->sector_size, &rh, &rhs);
+ if (r < 0)
+ return r;
+
+ assert(rhs >= sizeof(sd_id128_t) * 2);
+
+ if (!dp->new_uuid_is_set) {
+ memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
+ dp->new_uuid_is_set = true;
+ }
+
+ if (!p->new_uuid_is_set) {
+ memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
+ p->new_uuid_is_set = true;
+ }
+
+ p->roothash = TAKE_PTR(rh);
+ p->roothash_size = rhs;
+ }
+
+ return 0;
+}
+
static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
struct {
sd_id128_t type_uuid;
@@ -3288,7 +3623,7 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
if (!sd_id128_is_null(p->current_uuid))
p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
- else if (!p->new_uuid_is_set) {
+ else if (!p->new_uuid_is_set && p->verity == VERITY_OFF) {
/* Not explicitly set by user! */
r = partition_acquire_uuid(context, p, &p->new_uuid);
if (r < 0)
@@ -3512,21 +3847,6 @@ static int context_write_partition_table(
assert(context);
- if (arg_pretty > 0 ||
- (arg_pretty < 0 && isatty(STDOUT_FILENO) > 0) ||
- !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
-
- (void) context_dump_partitions(context, node);
-
- if (arg_json_format_flags & JSON_FORMAT_OFF) {
- putc('\n', stdout);
- (void) context_dump_partition_bar(context, node);
- putc('\n', stdout);
- }
-
- fflush(stdout);
- }
-
if (!from_scratch && !context_changed(context)) {
log_info("No changes.");
return 0;
@@ -3573,6 +3893,10 @@ static int context_write_partition_table(
if (r < 0)
return r;
+ r = context_verity(context);
+ if (r < 0)
+ return r;
+
r = context_mangle_partitions(context);
if (r < 0)
return r;
@@ -4420,6 +4744,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_tpm2_pcr_mask == UINT32_MAX)
arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
+ if (arg_pretty < 0 && isatty(STDOUT_FILENO))
+ arg_pretty = true;
+
return 1;
}
@@ -5022,10 +5349,14 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ (void) context_dump(context, node, /*late=*/ false);
+
r = context_write_partition_table(context, node, from_scratch);
if (r < 0)
return r;
+ (void) context_dump(context, node, /*late=*/ true);
+
return 0;
}
diff --git a/test/TEST-58-REPART/test.sh b/test/TEST-58-REPART/test.sh
index 4aff2788e4..063ce9425d 100755
--- a/test/TEST-58-REPART/test.sh
+++ b/test/TEST-58-REPART/test.sh
@@ -10,6 +10,8 @@ TEST_DESCRIPTION="test systemd-repart"
test_append_files() {
if ! get_bool "${TEST_NO_QEMU:=}"; then
install_dmevent
+ image_install jq
+ instmods dm_verity =md
generate_module_dependencies
fi
}
diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh
index fddcda3872..fd972a38b9 100755
--- a/test/units/testsuite-58.sh
+++ b/test/units/testsuite-58.sh
@@ -598,6 +598,51 @@ EOF
assert_in "$imgs/zero1 : start= 2048, size= 20480, type=${root_guid}, uuid=00000000-0000-0000-0000-000000000000" "$output"
}
+test_verity() {
+ local defs imgs output
+
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping verity test in container."
+ return
+ fi
+
+ defs="$(mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.XXXXXXXXXX")"
+ # shellcheck disable=SC2064
+ trap "rm -rf '$defs' '$imgs'" RETURN
+
+ cat >"$defs/root.conf" <"$defs/verity.conf" <= 512 and <= PAGE_SIZE, and
# must be powers of 2. Which leaves exactly four different ones to test on