Skip to content

disk: systemd-repart compatibility#2407

Closed
supakeen wants to merge 3 commits into
osbuild:mainfrom
supakeen:repart-compatibility
Closed

disk: systemd-repart compatibility#2407
supakeen wants to merge 3 commits into
osbuild:mainfrom
supakeen:repart-compatibility

Conversation

@supakeen

@supakeen supakeen commented Jun 9, 2026

Copy link
Copy Markdown
Member

So; bit funky and a bit all over the place. This PR introduces 4 new options on the partition table struct. When combined together they make our partition tables identical to those created by systemd-repart with the caveat that partition tables that have start offsets (for example; the aarch64 partition tables for Fedora) are not compatible, but that would have to come from the other side (e.g. systemd-repart would need to support padding before the first partition).

I've implemented each of these options separately behind toggles so we can decide if we want them 'by default' or not. Especially the absolute_start_offset + start_offset: "1 MiB" feels funky. Perhaps someone has better ideas for that.

We could also decide we don't want to be compatible at all and instead opt for a future approach (that I still plan to PR) which allows partitioning through systemd-repart instead of through sfdisk.

A partition table with:

grain_size: 4096                                                                                                                              
start_offset: "1 MiB"                                                                                                                         
absolute_start_offset: true                                                                                                                   
align_footer: true

Creates the exact same partition table as systemd-repart would create with the following inputs:

€ cat 00-esp.conf 
[Partition]
Type=esp
SizeMinBytes=200M
repart € cat 10-root.conf 
[Partition]
Type=root
SizeMinBytes=2G
€ sudo systemd-repart --definitions=. --empty=create --size=auto repart.raw
Automatically determined minimal disk image size as 2.1G.
Sized 'repart.raw' to 2.1G.
TYPE        LABEL       UUID                                 PART FILE           RAW SIZE     SIZE PADDING
esp         esp         2e7b5217-05b1-4404-a392-9759d36ea12d    0 00-esp.conf   209715200   → 200M    → 0B
root-x86-64 root-x86-64 00fc3f3e-25cd-47d4-be90-f6f417e6351b    1 10-root.conf 2147483648     → 2G    → 0B
                                                                                          Σ = 2.1G  Σ = 0B

 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░
 │           └─ 10-root.conf                                                                                                                     
 └─ 00-esp.conf                                                                                                                                  

Applying changes to repart.raw.
Adding new partition 0 to partition table.
Adding new partition 1 to partition table.
Writing new partition table.
All done.
€ fdisk -l repart.raw 
Disk repart.raw: 2.2 GiB, 2358267904 bytes, 4605992 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: DF796A0B-5A11-4BAB-A693-2C8925D4F98F

Device       Start     End Sectors  Size Type
repart.raw1   2048  411647  409600  200M EFI System
repart.raw2 411648 4605951 4194304    2G Linux root (x86-64)

Let's compare that to a partition table created by images with the default settings; a 200 MiB ESP and a 2 GiB root:

€ fdisk -l 1M.raw 
Disk 1M.raw: 2.2 GiB, 2359296000 bytes, 4608000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: A07BA799-6E25-4F45-A84F-CEC5C8C7C10B

Device      Start     End Sectors  Size Type
1M.raw1      2048  411647  409600  200M EFI System
1M.raw2    411648 4607966 4196319    2G Linux root (x86-64)

Note the difference in end sector and total sectors for the root partition.

When building that same partition table but adding all the toggles introduced in this PR we get:

artifacts € fdisk -l 4k.raw 
Disk 4k.raw: 2.2 GiB, 2358267904 bytes, 4605992 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3721BFEE-0D51-4EC1-803E-1AEB924D4126

Device      Start     End Sectors  Size Type
4k.raw1      2048  411647  409600  200M EFI System
4k.raw2    411648 4605951 4194304    2G Linux root (x86-64)

Booting these disk images most importantly gets rid of the failing systemd-repart service saying that it can't fit the partitions inside the disk 🙂

supakeen added 2 commits June 9, 2026 14:18
We had a hardcoded default grain size of 1 MiB. This was based on the
default grain size in `sfdisk` (2048 sectors, 512 bytes for the first
LBA).

Other partitioning tooling such as `systemd-repart` uses a different
grain size by default leading to potential mismatches when rounding
partitions or padding up or down to grain sizes.

Let's allow this value to be configured through the image definitions
YAML.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
Some partitioning tools (`systemd-repart`) round down the usable end of
the disk to the grain boundary (made configurable in the previous
commit). This ensures that the *end* of the last partition on disk is
grain aligned.

`images` instead grows the last partition to take up all remaining size
on the disk. When the footer is not aligned to sector size that means
that the last partitions gets slightly larger than one would expect it
to be.

To figure out the size for the last partition we subtract the raw
non-aligned header size (16,896 bytes) from the total disk size and then
grow the last partition to that. This means that the last partition
gains 3,584 bytes (with a 4096 byte grain size).

Aligning the footer size to the grain size has the end result that the
last partition becomes exactly the requested size.

This new behavior is gated behind an `AlignFooter` flag in the partition
table as it's new behavior and it would change all pre-existing
partition tables.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
@supakeen supakeen requested review from a team and achilleas-k as code owners June 9, 2026 12:40
@supakeen supakeen requested review from croissanne and thozza June 9, 2026 12:40
Some partitioning tools (`systemd-repart`) use the convention that the
first usable sector is the first LBA. The convention for this is sector
2048 (1 MiB). This is then rounded up to the grain size.

`images` computes the start as `AlignUp(header + StartOffset)`. With the
previous default grain size of 1 MiB this means that the first partition
always started at 1 MiB when no `StartOffset` was provided.

When the grain is configured to be smaller (4096 bytes) it instead
produces sector 40 (20,480 bytes) which falls inside the reserved range
`sfdisk` claims which then makes `sfdisk` reject the partition table
because we don't pass `--first-lba` in the stage (and we probably don't
want to either).

Adding an `AbsoluteStartOffset` toggle allows us to set the offset to 1
MiB exactly; mimicking `systemd-repart`'s behavior.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
@supakeen supakeen force-pushed the repart-compatibility branch from 25c8f33 to fedc243 Compare June 9, 2026 12:44
@achilleas-k

Copy link
Copy Markdown
Member

Neat!!

Before I read everything (not that it's a lot, this is surprisingly small), what would be the downside to changing our defaults to be the same as systemd-repart?

Ignoring the fact that some users might rely on specific alignments (which sounds horrifying), are there are other problems or side effects?

@supakeen

supakeen commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Neat!!

Before I read everything (not that it's a lot, this is surprisingly small), what would be the downside to changing our defaults to be the same as systemd-repart?

Ignoring the fact that some users might rely on specific alignments (which sounds horrifying), are there are other problems or side effects?

  1. I think the configurable grain size is safe to land.
  2. I think the alignment of the footer (and thus the alignment of the last sector of the last partition) is safe to apply by default.

As far as the start of the first partition; currently we align it up to the grain size (which is by default 1 MiB and thus 2048 sectors).

I would like to make the standard 'offset' 2048 (have it in a constant called FirstLBA or such) regardless of grain size (we can't set it lower without passing additional arguments to sfdisk but we might want that in the future?) and add the StartOffset on top of that (and then align it to grainsize). That means we get slightly more room in that case which also shouldn't break anything.

If we do it that way there are in my opinion no harmful results though yes if people expect very specific alignments in the StartOffset (e.g. they set it to 2 MiB which was 4096 sectors but would now be 6144 sectors and then dd something onto their first partition which they assume to be at a specific offset?

I don't think that happens, usually when people dd firmware onto their device it's at sector 64 and they just expect room after it for which there is now slightly more room.


Want me to just make the above the default behavior?

@achilleas-k

Copy link
Copy Markdown
Member

Want me to just make the above the default behavior?

Let's talk about it after I've read through everything. Could be a follow-up.

@brlane-rht

Copy link
Copy Markdown
Contributor

I think this is generally ok, but I think adding the bool for absolute start offset just makes things confusing. Just change it to behave as expected by other tools.

@supakeen supakeen closed this by deleting the head repository Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants