From: Jonas Dreßler Date: Fri, 29 May 2026 13:32:07 +0000 (+0200) Subject: repart: Place new partitions at beginning of free area rather than at end X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=959c3286d00fcbd59ed0501e7c990685dbbeb87a;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/repart/repart.c b/src/repart/repart.c index 92e1e6ef45e..c7814798ad1 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 e2530169ca3..bc0edbf206f 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" <