Support CopyBlocks= for Verity={hash,sig} (#41393)

This enables deriving the minimum size of the `Verity=hash` partition
using the `Verity=` logic when the size of the `Verity=data` partition
is bigger than the `CopyBlocks=` target.

This enables using `Minimize=true` for an "installer image" and later
using sd-repart to install to a system with reserve space for future
updates by specifying `Size{Min,Max}Bytes=` only in the `Verity=data`
partition, without needing to hardcode the corresponding size for the
`Verity=hash` partition.

While not strictly necessary for `Verity=signature` partitions (since
they have a fixed size) there isn't too much reason to not support it,
since then you can still specify `VerityMatchKey=` to indicate that the
partition is logically still part of that group of partitions.

---

Alternative to: https://github.com/systemd/systemd/pull/41156
Fixes https://github.com/systemd/systemd/issues/40995
This commit is contained in:
Lennart Poettering
2026-04-02 16:18:14 +02:00
committed by GitHub
+77 -11
View File
@@ -3012,10 +3012,20 @@ static int partition_read_definition(
"VerityMatchKey= can only be set if Verity= is not \"%s\".",
verity_mode_to_string(p->verity));
if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) && (p->copy_blocks_path || p->copy_blocks_auto || p->format || partition_needs_populate(p)))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"CopyBlocks=/CopyFiles=/Format=/MakeDirectories=/MakeSymlinks= cannot be used with Verity=%s.",
verity_mode_to_string(p->verity));
if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG)) {
if (p->format || partition_needs_populate(p))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"CopyFiles=/Format=/MakeDirectories=/MakeSymlinks= cannot be used with Verity=%s.",
verity_mode_to_string(p->verity));
/* Later we check that the same CopyBlocks= type (auto vs path) is used for the entire verity set.
* So we assume that CopyBlocks=auto is going to be correct and just path based blocks might result in
* a broken setup */
if (p->copy_blocks_path)
log_syntax(NULL, LOG_DEBUG, path, 1, 0,
"CopyBlocks= with Verity=%s bypasses dm-verity hash/signature computation; repart cannot verify the resulting setup is correct.",
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),
@@ -3516,6 +3526,27 @@ static int context_read_definitions(Context *context) {
}
}
LIST_FOREACH(partitions, p, context->partitions) {
if (!IN_SET(p->verity, VERITY_HASH, VERITY_SIG))
continue;
/* We check all verity siblings up until our current type and ensure that if we are using CopyBlocks=
* the previous ones are using the same type of CopyBlocks=. */
for (VerityMode mode = VERITY_DATA; mode < p->verity; mode++) {
Partition *q = ASSERT_PTR(p->siblings[mode]);
if (p->copy_blocks_auto && !q->copy_blocks_auto)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"CopyBlocks=auto set with Verity=%s but Verity=%s partition does not set CopyBlocks=auto.",
verity_mode_to_string(p->verity), verity_mode_to_string(mode));
if (p->copy_blocks_path && !q->copy_blocks_path)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"CopyBlocks= set with Verity=%s but Verity=%s partition does not set CopyBlocks=.",
verity_mode_to_string(p->verity), verity_mode_to_string(mode));
}
}
LIST_FOREACH(partitions, p, context->partitions) {
Partition *dp;
@@ -5658,7 +5689,7 @@ static int partition_format_verity_hash(
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
return 0;
/* Minimized partitions will use the copy blocks logic so skip those here. */
/* Either we are minimizing the partition or we were instructed to copy an existing hash block directly. */
if (p->copy_blocks_fd >= 0)
return 0;
@@ -5795,21 +5826,41 @@ static int sign_verity_roothash(
#endif
}
static const VeritySettings *lookup_verity_settings_by_uuid_pair(sd_id128_t data_uuid, sd_id128_t hash_uuid) {
uint8_t root_hash_key[sizeof(sd_id128_t) * 2];
static int iovec_roothash_from_uuid_pair(
sd_id128_t data_uuid,
sd_id128_t hash_uuid,
struct iovec *ret_roothash) {
uint8_t roothash_bytes[sizeof(sd_id128_t) * 2];
assert(ret_roothash);
if (sd_id128_is_null(data_uuid) || sd_id128_is_null(hash_uuid))
return NULL;
return -EINVAL;
/* As per the https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ the
* UUIDs of the data and verity partitions are respectively the first and second halves of the
* dm-verity roothash, so we can use them to match the signature to the right partition. */
memcpy(root_hash_key, data_uuid.bytes, sizeof(sd_id128_t));
memcpy(root_hash_key + sizeof(sd_id128_t), hash_uuid.bytes, sizeof(sd_id128_t));
memcpy(roothash_bytes, data_uuid.bytes, sizeof(sd_id128_t));
memcpy(roothash_bytes + sizeof(sd_id128_t), hash_uuid.bytes, sizeof(sd_id128_t));
if (!iovec_memdup(&IOVEC_MAKE(roothash_bytes, sizeof(roothash_bytes)), ret_roothash))
return -ENOMEM;
return 0;
}
static const VeritySettings *lookup_verity_settings_by_uuid_pair(sd_id128_t data_uuid, sd_id128_t hash_uuid) {
_cleanup_(iovec_done) struct iovec roothash = {};
int r;
r = iovec_roothash_from_uuid_pair(data_uuid, hash_uuid, &roothash);
if (r < 0)
return NULL;
VeritySettings key = {
.root_hash = IOVEC_MAKE(root_hash_key, sizeof(root_hash_key)),
.root_hash = roothash,
};
return set_get(arg_verity_settings, &key);
@@ -5832,11 +5883,23 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
if (PARTITION_EXISTS(p))
return 0;
/* We were instructed to copy an existing signature block directly */
if (p->copy_blocks_fd >= 0)
return 0;
assert_se(hp = p->siblings[VERITY_HASH]);
assert(!hp->dropped);
assert_se(rp = p->siblings[VERITY_DATA]);
assert(!rp->dropped);
/* Currently only set while formatting the hash partition. But if this is skipped via CopyBlocks=
* we just derive the roothash from the UUIDs from the data + hash partition. */
if (!iovec_is_set(&hp->roothash)) {
r = iovec_roothash_from_uuid_pair(rp->new_uuid, hp->new_uuid, &hp->roothash);
if (r < 0)
return log_error_errno(r, "Unable to derive roothash: %m");
}
verity_settings = lookup_verity_settings_by_uuid_pair(rp->current_uuid, hp->current_uuid);
if (!verity_settings) {
@@ -9259,6 +9322,9 @@ static int context_minimize(Context *context) {
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
continue;
if (p->copy_blocks_fd >= 0)
continue;
if (p->minimize == MINIMIZE_OFF)
continue;