From 959c3286d00fcbd59ed0501e7c990685dbbeb87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 29 May 2026 15:32:07 +0200 Subject: [PATCH] repart: Place new partitions at beginning of free area rather than at end When placing new partitions and there's space left because the new partitions aren't occupying the whole free space, context_grow_partitions_on_free_area() is supposed to distribute the free space between the new partitions. If still no partition wants the free space, the free space ends up becoming padding. Currently that padding is allocated to the partition preceding the FreeArea (ie. a->after). This obviously means that the new partitions now end up at the *end* of the free area rather than at the beginning, which is somewhat unexpected given how partition placement usually is done. Fix it by finding the last partition that belongs to the free area, and then allocating the padding to that partition, so that the new partitions end up getting aligned with the beginning of the free area, not the end. Because the span might not be rounded to grain if there's a pre-allocated a->after partition before the free area, we need to round it down ourselves (otherwise the "left >= p->new_padding" assertion in context_place_partitions() is going to fail). Also ensure the fix works as expected by adding a test. --- src/repart/repart.c | 19 +++++++++++--- test/units/TEST-58-REPART.sh | 50 +++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index 92e1e6ef45..c7814798ad 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -1719,10 +1719,21 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { break; } - /* Yuck, still no one? Then make it padding */ - if (span > 0 && a->after) { - assert(a->after->new_padding != UINT64_MAX); - a->after->new_padding += span; + /* Partitions didn't want all the space? Then make it padding */ + if (span > 0) { + Partition *last_partition = NULL; + + LIST_FOREACH(partitions, p, context->partitions) + if (p->allocated_to_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); + } } return 0; diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index e2530169ca..bc0edbf206 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -500,8 +500,8 @@ EOF "raw_size" : 33554432, "size" : "-> 32M", "old_padding" : 0, - "raw_padding" : 0, - "padding" : "-> 0B", + "raw_padding" : 70234112, + "padding" : "-> 66.9M", "activity" : "create", "drop-in_files" : [ "$defs/root.conf.d/override1.conf", @@ -577,8 +577,8 @@ EOF "raw_size" : 33554432, "size" : "-> 32M", "old_padding" : 0, - "raw_padding" : 0, - "padding" : "-> 0B", + "raw_padding" : 36679680, + "padding" : "-> 34.9M", "activity" : "create" } ] @@ -2397,6 +2397,48 @@ EOF grep -q tada "${btrfs_mntpoint_encrypted}/magic-encrypted" } +testcase_insert_into_gap() { + 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 "*** Inserting a new partition into a gap between two existing partitions ***" + + truncate -s 71M "$imgs/gap.img" + sfdisk "$imgs/gap.img" <