repart: Properly pre-calculate auto size of images

When passing --size=auto to repart, it will pre-calculate the image size and
resize the image to that size before partitioning. Currently, that fails when
passing a large grain size, complaining that the auto-sized image is too small
to fit the data.

The reason for this is that the current code simply assumes the GPT metadata
size taken away from the usable size by fdisk is static (1044KiB), when it
actually is more complicated than that:

There's two ranges of GPT metadata: One at the beginning of the image, and one
at the end of the image. And there's the first usable block that is defined by
fdisk when creating the partition table.

The static value of 1044KiB usually works, because fdisk sets the first usable
block to 1MiB (so 1024KiB), leaving 20KiB of leeway for the secondary GPT at
the end of the image.

Now as soon as the first partition starts at an offset higher than 1024KiB, we
lose the 20KiB leeway for the secondary GPT, and the partitions will no longer
fit.

What we should do, is first of all round up to the grain size instead of 4096
(as that's the minimum offset our first partition will start at), and second of
all properly subtract the secondary GPT at the end.

Also confirm we don't regress on this anymore by adding a test that uses a 2MiB
grain size, breaking the old code.
This commit is contained in:
Jonas Dreßler
2026-07-14 12:46:50 +01:00
committed by Luca Boccassi
parent 321a321787
commit 04e3d8933b
2 changed files with 63 additions and 7 deletions
+16 -5
View File
@@ -100,9 +100,6 @@
/* We know up front we're never going to put more than this in a verity sig partition. */
#define VERITY_SIG_SIZE (HARD_MIN_SIZE*4ULL)
/* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */
#define GPT_METADATA_SIZE (1044ULL*1024ULL)
/* LUKS2 takes off 16M of the partition size with its metadata by default */
#define LUKS2_METADATA_SIZE (16ULL*1024ULL*1024ULL)
@@ -11250,12 +11247,26 @@ static int determine_auto_size(
assert(c);
minimal_size = round_up_size(GPT_METADATA_SIZE, 4096);
/* At the beginning of the image, some size is reserved for:
* Protective MBR (1 block) + GPT header (1 block) +
* Primary partition table (minimum 16KiB) = (2 * c->sector_size) + (16 * 1024)
*
* Note that fdisk usually sets the first usable block to 1MiB (at least for
* disks larger than 4MiB), so we just force it to that as well. If fdisk
* decides to set it lower, we made the image a bit larger than necessary,
* if it sets it higher, we'd have a problem. */
minimal_size = 1024 * 1024;
/* Of course need to align the start to our grain size as well */
minimal_size = round_up_size(minimal_size, c->grain_size);
/* At the end of the image, there is size reserved for:
* Secondary partition table (minimum 16KiB) + GPT header (1 block) */
minimal_size += (16 * 1024) + c->sector_size;
if (c->from_scratch)
current_size = 0;
else
current_size = round_up_size(GPT_METADATA_SIZE, 4096);
current_size = minimal_size;
foreign_size = 0;
+47 -2
View File
@@ -370,7 +370,7 @@ label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
device: $imgs/zzz
unit: sectors
first-lba: 2048
last-lba: 6422494
last-lba: 6422487
$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
@@ -434,7 +434,7 @@ label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
device: $imgs/zzz
unit: sectors
first-lba: 2048
last-lba: 6553566
last-lba: 6553559
$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
@@ -568,6 +568,51 @@ EOF
assert_in "$imgs/copy_to3 : start= 32768, size= 90072, type=${esp_guid}," "$output"
}
testcase_size_auto_with_grain_size() {
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"
tee "$defs/01-esp.conf" <<EOF
[Partition]
Type=esp
SizeMinBytes=10M
EOF
tee "$defs/02-usr.conf" <<EOF
[Partition]
Type=usr-${architecture}
SizeMinBytes=10M
EOF
tee "$defs/03-root.conf" <<EOF
[Partition]
Type=root-${architecture}
SizeMinBytes=10M
EOF
systemd-repart --offline="$OFFLINE" \
--empty=create \
--size=auto \
--definitions="$defs" \
--seed="$seed" \
--dry-run=no \
--grain-size=2097152 \
"$imgs/auto"
output=$(sfdisk --dump "$imgs/auto")
assert_in "first-lba: 2048" "$output"
assert_in "last-lba: 65535" "$output"
assert_in "$imgs/auto1 : start= 4096, size= 20480, type=${esp_guid}," "$output"
assert_in "$imgs/auto2 : start= 24576, size= 20480, type=${usr_guid}," "$output"
assert_in "$imgs/auto3 : start= 45056, size= 20480, type=${root_guid}," "$output"
}
testcase_dropin() {
local defs imgs output