Skip to content

repart: add FixedOffsetBytes= to pin a partition at an explicit disk offset#42418

Open
naraghavan2186 wants to merge 1 commit into
systemd:mainfrom
naraghavan2186:naraghavan/systemd-repart-first-lba
Open

repart: add FixedOffsetBytes= to pin a partition at an explicit disk offset#42418
naraghavan2186 wants to merge 1 commit into
systemd:mainfrom
naraghavan2186:naraghavan/systemd-repart-first-lba

Conversation

@naraghavan2186
Copy link
Copy Markdown
Contributor

@naraghavan2186 naraghavan2186 commented Jun 1, 2026

There are certain use cases require a partition to start at a specific byte offset rather than being placed automatically by the allocation algorithm. Add FixedOffsetBytes= to repart.con to support this so that the partition is placed at the exact configured offset without grain-size rounding, and the GPT first_usable_lba is lowered to accommodate offsets below the default 2048-sector boundary. SizeMinBytes= must be set alongside it since there is no free area to grow into. Auto-placed partitions and --grain-size= are unaffected.

Fixes 40907

@github-actions github-actions Bot added documentation util-lib repart please-review PR is ready for (re-)review by a maintainer labels Jun 1, 2026
@naraghavan2186
Copy link
Copy Markdown
Contributor Author

naraghavan2186 commented Jun 1, 2026

Tested the change with the below repart configs

$ cat > "/tmp/repart.d/00-uboot.conf" <<EOF
[Partition]
Type=linux-generic
Label=uboot
SizeMinBytes=256K
SizeMaxBytes=256K
CopyBlocks=/tmp/uboot.bin
FixedOffsetBytes=32768
EOF

$ cat > "/tmp/repart.d/10-root.conf" <<EOF
[Partition]
Type=root
SizeMinBytes=64M
SizeMaxBytes=64M
EOF

$ ./build/systemd-repart --offline=yes  --definitions="$defs"  --empty=create  --size=200M  --dry-run=no  /tmp/disk.img

$ sfdisk -d /tmp/disk.img
label: gpt
label-id: AFCDFE94-E7AF-4B58-82F7-E76CCE746641
device: /tmp/disk.img
unit: sectors
first-lba: 64
last-lba: 409566
sector-size: 512

/tmp/disk.img1 : start=          64, size=         512, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=40FC43FC-FB9C-47F7-AE7A-0F5D88BE8087, name="uboot"
/tmp/disk.img2 : start=         576, size=      131072, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CFD8264C-FB9A-4BD2-9F37-34D9191613E7, name="root-x86-64", attrs="GUID:59"

As per the output first-lba: 64 - GPT header now advertises sector 64 as the first usable LBA, down from the hardcoded 2048.

disk.img1 : start=64, size=512 - uboot at exactly sector 64 (32768 bytes), 512 sectors = 256KB. No grain-size rounding applied.

disk.img2 : start=576, size=131072 - root starts at sector 576.

uboot ends at sector 64 + 512 = 576
pinned_region_end = 32768 + 262144 = 294912 bytes = sector 576
round_up_size(294912, grain_size=4096) = 294912 (already aligned — 294912 / 4096 = 72 exactly)
So auto_start = sector 576, root lands there

Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c
Comment thread man/repart.d.xml Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c
@poettering poettering added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks and removed please-review PR is ready for (re-)review by a maintainer labels Jun 1, 2026
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from 5f953b5 to f3ac519 Compare June 2, 2026 09:41
@github-actions github-actions Bot added please-review PR is ready for (re-)review by a maintainer and removed reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks labels Jun 2, 2026
@poettering
Copy link
Copy Markdown
Member

please mark the review items you have addressed as "resolved".

in systemd we generally want that the reviewer opens the items, and the PR owener then closes them after addressing them. just leave the ones open that reuqire further discussion

Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
@poettering poettering added reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks and removed please-review PR is ready for (re-)review by a maintainer labels Jun 2, 2026
@naraghavan2186 naraghavan2186 changed the title repart: add OffsetBytes= to pin a partition at an explicit disk offset repart: add FixedOffsetBytes= to pin a partition at an explicit disk offset Jun 2, 2026
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from f3ac519 to fba0d83 Compare June 2, 2026 11:42
@github-actions github-actions Bot removed the reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks label Jun 2, 2026
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c
Comment thread man/repart.d.xml Outdated
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from fba0d83 to bba8ded Compare June 2, 2026 16:05
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from bba8ded to f61f3be Compare June 3, 2026 05:22
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c Outdated
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from f61f3be to cc27f61 Compare June 3, 2026 11:08
Comment thread src/repart/repart.c Outdated
Comment thread src/repart/repart.c
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from cc27f61 to a3b5ebe Compare June 4, 2026 09:12
Comment thread man/repart.d.xml Outdated
Comment thread man/repart.d.xml Outdated
…offset

There are certain use cases require a partition to start at a specific byte offset
rather than being placed automatically by the allocation algorithm. Add
FixedOffsetBytes= to repart.conf to support this so that the partition is placed
at the exact configured offset without grain-size rounding, and the GPT
first_usable_lba is lowered to accommodate offsets below the default
2048-sector boundary.  SizeMinBytes= is optional  when set it
is used to reserve the exact space for the fixed region, otherwise the
grain size is used as a conservative minimum. Auto-placed partitions
and --grain-size= are unaffected.
@naraghavan2186 naraghavan2186 force-pushed the naraghavan/systemd-repart-first-lba branch from a3b5ebe to 0cb5cf3 Compare June 4, 2026 13:37
Comment thread src/repart/repart.c
p->definition_path, p->fixed_offset,
sym_fdisk_get_first_lba(context->fdisk_context) * context->sector_size);

p->offset = p->fixed_offset;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: must-fix: On an existing (non-from-scratch) disk a new fixed-offset partition is pinned here (p->offset = p->fixed_offset) with no check that [fixed_offset, fixed_offset+new_size) is clear of existing on-disk partitions; context_scan_fixed_partitions() only validates fixed-vs-fixed overlap and the disk bounds. Two consequences:

  1. Data loss. In context_write_partition_table() the destructive steps — context_wipe_and_discard() (blkid wipe + BLKDISCARD), context_copy_blocks(), context_mkfs() — all run at p->offset before context_mangle_partitions() calls fdisk_add_partition(), which is the only place an overlap is rejected. So when a fixed offset overlaps an existing partition, that partition is wiped/discarded/overwritten and only then is the GPT write refused — the data is already gone. This refutes the earlier dismissal that fdisk_add_partition() returning an error makes overlap safe. The below-first-usable-LBA case is worse: it only logs "adding it will likely fail, proceeding anyway" and proceeds into the same destructive path.

  2. Overlap with auto-placed partitions. Only the leading free area is clipped by context->start = fixed_end_aligned; free areas built from an existing partition's trailing padding are not bounded by the fixed region, and fixed partitions never consume free-area budget, so a fixed offset landing inside such a gap is assigned the same range as an auto-placed partition.

Validate each fixed offset+size against existing partition extents (and refuse the below-first-usable-LBA case) before any wipe, and exclude the fixed region from every free area, not just the leading one.

Comment thread src/repart/repart.c

if (parsed == 0)
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
"FixedOffsetBytes= must not be zero.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: config_parse_offset_bytes() rejects only parsed == 0, but parse_size() accepts the literal 18446744073709551615 and returns exactly UINT64_MAX — which is the in-band "auto-placed" sentinel for p->fixed_offset (set in partition_new() and tested everywhere as == UINT64_MAX). Such an assignment is silently accepted and the partition is then treated as having no fixed offset at all, with no diagnostic and none of the downstream alignment/bounds checks ever running on it. Reject parsed == UINT64_MAX alongside the existing zero check so the sentinel value cannot be produced from input.

Comment thread src/repart/repart.c
else
p->new_size = partition_min_size(context, p);
}
if (p->new_padding == UINT64_MAX)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: suggestion: For fixed-offset new partitions this branch unconditionally sets new_padding = 0, so any PaddingMinBytes=/PaddingMaxBytes= the user configured is silently discarded (auto-placed partitions honor them via partition_min_padding()), and context_scan_fixed_partitions() reserves only [fixed_offset, fixed_offset+size) with no room for padding. A fixed-offset partition with PaddingMinBytes= set is accepted but its padding is neither applied nor reserved. Either reject PaddingMinBytes=/PaddingMaxBytes= combined with FixedOffsetBytes= in partition_read_definition() (mirroring the Priority=/SupplementFor= guards), or honor and reserve the padding.

Comment thread src/repart/repart.c
/* Verify the fixed offset is within the GPT's usable range. For from scratch
* disks this is guaranteed (we lowered first_lba to match). For existing disks
* it may not be, producing a error from libfdisk later otherwise. */
if (p->fixed_offset < sym_fdisk_get_first_lba(context->fdisk_context) * context->sector_size)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: nit: sym_fdisk_get_first_lba(context->fdisk_context) * context->sector_size is computed twice in a row — in this if condition and again in the log_warning() arguments just below. Hoist it into a single local (e.g. uint64_t first_usable = …;) and reuse it, as the identical expression is evaluated once at the other call sites.

@naraghavan2186 naraghavan2186 requested a review from poettering June 4, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

systemd-repart: first_lba / first usable sector cannot be set earlier than 2048

3 participants