repart: Fix growing the partition preceding a FreeArea from leftover space (#42969)

The "Donate to preceding partition" logic is dead code since commit

https://github.com/jonas2515/systemd/commit/19903a433507897449c086b72abb5e133e431336
("repart: split out context_grow_partition_one()").
context_grow_partition_one() gets passed a free area and a partition,
and it has
an early-return check to ensure the partition it got passed belongs to
the free
area it got passed. That means we compare the FreeArea a to the FreeArea
a->after->allocated_to_area, which always yields FALSE.

Fix the behavior of donating any left over space to the preceding
partition
by adding that partition to the loop below (and relying on the
partitions list
being ordered according to physical partition offsets).

Since this behavior is not that easy to trigger, mention how to trigger
it in a
comment, and add a test for it as well.
This commit is contained in:
Lennart Poettering
2026-07-14 11:27:57 +02:00
committed by GitHub
2 changed files with 95 additions and 21 deletions
+9 -21
View File
@@ -1652,23 +1652,14 @@ static bool context_grow_partitions_phase(
return !try_again;
}
static void context_grow_partition_one(Context *context, FreeArea *a, Partition *p, uint64_t *span) {
static void context_grow_partition_one(Context *context, Partition *p, uint64_t *span) {
uint64_t m;
assert(context);
assert(a);
assert(p);
assert(span);
if (*span == 0)
return;
if (p->allocated_to_area != a)
return;
if (PARTITION_IS_FOREIGN(p))
return;
assert(*span > 0);
assert(p->new_size != UINT64_MAX);
/* Calculate new size and align. */
@@ -1708,15 +1699,15 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
if (context_grow_partitions_phase(context, a, phase, &span, &weight_sum))
phase++; /* go to the next phase */
/* We still have space left over? Donate to preceding partition if we have one */
if (span > 0 && a->after)
context_grow_partition_one(context, a, a->after, &span);
/* What? Even still some space left (maybe because there was no preceding partition, or it had a
* size limit), then let's donate it to whoever wants it. */
/* What? Even still some space left (because one partition had max_size < share
* and another had min_size > share), then let's donate it to whoever wants it. */
if (span > 0)
LIST_FOREACH(partitions, p, context->partitions) {
context_grow_partition_one(context, a, p, &span);
if (p->allocated_to_area != a && p->padding_area != a)
continue;
context_grow_partition_one(context, p, &span);
if (span == 0)
break;
}
@@ -1726,15 +1717,12 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
Partition *last_partition = NULL;
LIST_FOREACH(partitions, p, context->partitions)
if (p->allocated_to_area == a)
if (p->allocated_to_area == a || p->padding_area == a)
last_partition = p;
if (last_partition) {
assert(last_partition->new_padding != UINT64_MAX);
last_partition->new_padding += round_down_size(span, context->grain_size);
} else if (a->after) {
assert(a->after->new_padding != UINT64_MAX);
a->after->new_padding += round_down_size(span, context->grain_size);
}
}
+86
View File
@@ -2737,6 +2737,92 @@ EOF
assert_in "$imgs/gap.img3 : start= 22528, size= 20480, type=$usr_guid, uuid=$usr_uuid, name=\"part-b\"" "$output"
}
testcase_distribute_leftover_space() {
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod 0755 "$defs"
echo "*** Left over space after distributing according to weights should still be assigned ***"
tee "$defs/01-esp.conf" <<EOF
[Partition]
Type=esp
Weight=1
SizeMinBytes=5M
EOF
tee "$defs/02-usr.conf" <<EOF
[Partition]
Type=usr-${architecture}
Weight=1000
SizeMinBytes=1M
SizeMaxBytes=2M
EOF
truncate -s 20M "$imgs/leftover.img"
systemd-repart --offline="$OFFLINE" \
--definitions="$defs" \
--seed="$seed" \
--dry-run=no \
--empty=allow \
"$imgs/leftover.img"
output=$(sfdisk --dump "$imgs/leftover.img")
# esp should get all the leftover space
assert_in "$imgs/leftover.img1 : start= 2048, size= 34776, type=$esp_guid," "$output"
assert_in "$imgs/leftover.img2 : start= 36824, size= 4096, type=$usr_guid," "$output"
rm "$defs/01-esp.conf"
rm "$defs/02-usr.conf"
rm "$imgs/leftover.img"
tee "$defs/01-xbootldr.conf" <<EOF
[Partition]
Type=xbootldr
SizeMaxBytes=10M
EOF
tee "$defs/02-usr.conf" <<EOF
[Partition]
Type=usr-${architecture}
Weight=1000
SizeMinBytes=1M
SizeMaxBytes=2M
EOF
tee "$defs/03-esp.conf" <<EOF
[Partition]
Type=esp
Weight=1
SizeMinBytes=5M
EOF
truncate -s 25M "$imgs/leftover2.img"
sfdisk "$imgs/leftover2.img" <<EOF
label: gpt
size=3M, type=${xbootldr_guid},
EOF
systemd-repart --offline="$OFFLINE" \
--definitions="$defs" \
--seed="$seed" \
--dry-run=no \
--empty=allow \
"$imgs/leftover2.img"
output=$(sfdisk --dump "$imgs/leftover2.img")
# xbootldr should get the leftover space and grow to 10M, then esp should get the rest
assert_in "$imgs/leftover2.img1 : start= 2048, size= 20480, type=$xbootldr_guid," "$output"
assert_in "$imgs/leftover2.img2 : start= 22528, size= 4096, type=$usr_guid," "$output"
assert_in "$imgs/leftover2.img3 : start= 26624, size= 24536, type=$esp_guid," "$output"
}
OFFLINE="yes"
run_testcases