From: Michael Ferrari Date: Fri, 27 Mar 2026 21:43:27 +0000 (+0100) Subject: Support `CopyBlocks=` for `Verity={hash,sig}` X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=189d5c020b9705ccc7215dacd5bb262e96e7dcc3;p=thirdparty%2Fsystemd.git Support `CopyBlocks=` for `Verity={hash,sig}` 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. We ensure that if one of the hash uses `CopyBlocks=` that the data partition does so as well. Similarly if the signature partition does it checks that the hash and data partition do so as well. This is to minimize the chance of accidental misconfiguration of mixing `CopyBlocks=auto` and `CopyBlocks=` and of manually populating partitions while hash/sig partitions are copied from existing sources. --- diff --git a/src/repart/repart.c b/src/repart/repart.c index 7dbcfd6d582..fd1f16d4de3 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -2998,10 +2998,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), @@ -3494,6 +3504,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; @@ -5620,7 +5651,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; @@ -5794,6 +5825,10 @@ 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]); @@ -8884,6 +8919,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;