composefs: Find GRUB's boot directory on a separate /boot when upgrading - #29
cgwalters-bot wants to merge 11 commits into
Conversation
0d19188 to
f2bc24f
Compare
cgwalters-bot
left a comment
There was a problem hiding this comment.
Review guide for head f2bc24f740: where to look closely and what is safe to skim. It is advice from the bot's reviewer and doesn't replace reading the diff; the review app walks it.
Upgrade and switch on composefs+GRUB used to build the BLS and GRUB-UKI paths from /sysroot, which is empty when /boot is a separate partition. The fix stores the path get_boot_dir_for_grub() picked in Storage and derives every staging path, plus the kernel prefix in new BLS entries ("/" vs "/boot"), from it. The risk is concentrated in two decisions that now affect what gets written, not just what status reads: which directory holds GRUB's entries (classify), and which prefix the kernel paths get (grub_bls_abs_entries_path). A wrong answer in either writes entries GRUB can't use, and you only find out at the next boot. The GRUB+UKI path changed too but has no test. The new tmt test rebuilds the VM's disk layout by hand; read it for what it proves and what it assumes about install.
Hotspots
- risky · logic —
crates/lib/src/bootc_composefs/boot.rs:336-354(36f1d52): Every new BLS entry's linux/initrd prefix comes from this: "/" if the boot dir is a mount point, else "/boot". That's right for a whole partition on /boot, but a bind mount or btrfs subvolume on /boot is also a mount point while the kernels sit under /boot on that filesystem, so entries get "/bootc_composefs-..." and GRUB can't find the kernel at the next boot. is_mountpoint() returning None also silently means "/boot". - look-closely · logic —
crates/lib/src/store/mod.rs:394-411(36f1d52): This decision used to pick only where status and GC read. Now it also picks where upgrade and switch write kernels and entries. A boot partition with no grub2 dir, or an ESP on /boot that really holds GRUB's config (ESP-as-/boot layouts, pinned by the ESP+grub-dir=PhysicalRoot test case), falls back to /sysroot/boot, and staging then writes entries GRUB never reads without any error. - note · api —
crates/lib/src/store/mod.rs:522-540(36f1d52): grub_boot_path is set only here, for booted composefs with GRUB; Storage::new and the ostree branch leave it None. Any Upgrade setup reached with such a Storage now fails with "Grub boot path not found" where it used to fall back to /sysroot. Check that update.rs's callers only ever pass the booted storage. - note · logic —
crates/lib/src/bootc_composefs/boot.rs:947-952(36f1d52): This is where the original bug showed: the shared kernel dir is found through storage.boot_dir (an fd), but its path prefix comes from abs_entries_path (a path check). They agree only because both now derive from one GrubBootLocation. The v2 upgrade in plan-57 (back to the base initramfs) is the only coverage of this branch. - look-closely · test-gap —
crates/lib/src/bootc_composefs/boot.rs:1619-1655(36f1d52): GRUB+UKI with a separate /boot now reads and writes /boot/grub2/user.cfg(.staged), but plan-57 skips UKI (fixme_skip_if_uki, and first_boot skips non-BLS), so nothing tests this path. If no menu entries are parsed at the new location, entries[0] panics rather than returning an error. - look-closely · test-gap —
tmt/tests/booted/test-composefs-separate-boot.nu:151-166(f2bc24f): The test writes the entries' relative kernel paths and the systemd.mount-extra karg itself, imitating install. So it checks upgrade against its own reconstruction of install's output, not install's real output (boot_mount_spec's source and options format). Install with a separate /boot stays untested. - note · test-gap —
tmt/tests/booted/test-composefs-separate-boot.nu:72-100(f2bc24f): These assertions are what catches a regression: /sysroot/boot stays empty and the kernel paths are relative to the partition. referenced_kernel_dirs reads onlylinuxlines, so a wrong initrd prefix, or GC dropping an initramfs dir that only an initrd line refers to, would pass. - note · error-handling —
tmt/tests/booted/test-composefs-separate-boot.nu:114-141(f2bc24f): This repartitions and reformats the running VM's ESP. A failure between the sfdisk and the copy back (a busy umount, partx) leaves a VM that can't boot, and the plan then fails by timeout instead of with an error. The 512-byte sector size is hard-coded where sfdisk reports sectorsize.
Safe to skim
tmt/plans/integration.fmf: generated from the test's headertmt/tests/tests.fmf: generated from the test's headercrates/lib/src/bootc_composefs/boot.rs:777-853: mechanical: drops root_path from the setup tuplecrates/lib/src/bootc_composefs/boot.rs:1778-1884: mechanical: drops root_path from the UKI setup tuplecrates/lib/src/store/mod.rs:637-646: doc comment and new field
cgwalters-bot
left a comment
There was a problem hiding this comment.
Review guide for head f2bc24f740: where to look closely and what is safe to skim. It is advice from the bot's reviewer and doesn't replace reading the diff; the review app walks it.
Upgrade and switch on composefs+GRUB used to build the BLS and GRUB-UKI paths from /sysroot, which is empty when /boot is a separate partition. The fix stores the path get_boot_dir_for_grub() picked in Storage and derives every staging path, plus the kernel prefix in new BLS entries ("/" vs "/boot"), from it. The risk is concentrated in two decisions that now affect what gets written, not just what status reads: which directory holds GRUB's entries (classify), and which prefix the kernel paths get (grub_bls_abs_entries_path). A wrong answer in either writes entries GRUB can't use, and you only find out at the next boot. The GRUB+UKI path changed too but has no test. The new tmt test rebuilds the VM's disk layout by hand; read it for what it proves and what it assumes about install.
Hotspots
- risky · logic —
crates/lib/src/bootc_composefs/boot.rs:336-354(36f1d52): Every new BLS entry's linux/initrd prefix comes from this: "/" if the boot dir is a mount point, else "/boot". That's right for a whole partition on /boot, but a bind mount or btrfs subvolume on /boot is also a mount point while the kernels sit under /boot on that filesystem, so entries get "/bootc_composefs-..." and GRUB can't find the kernel at the next boot. (The fallback to "/boot" when the mount check is inconclusive predates this PR.) - look-closely · logic —
crates/lib/src/store/mod.rs:394-411(36f1d52): Finalize, delete and the BLS compat path already wrote through this decision; now upgrade and switch stage kernels and entries through it too. A boot partition with no grub2 dir, or an ESP on /boot that really holds GRUB's config (ESP-as-/boot layouts, pinned by the ESP+grub-dir=PhysicalRoot test case), falls back to /sysroot/boot, and staging then writes entries GRUB never reads without any error. - note · api —
crates/lib/src/store/mod.rs:522-540(36f1d52): grub_boot_path is set only here, for booted composefs with GRUB; Storage::new and the ostree branch leave it None. Any Upgrade setup reached with such a Storage now fails with "Grub boot path not found" where it used to fall back to /sysroot. Check that update.rs's callers only ever pass the booted storage. - note · logic —
crates/lib/src/bootc_composefs/boot.rs:947-952(36f1d52): This is where the original bug showed: the shared kernel dir is found through storage.boot_dir (an fd), but its path prefix comes from abs_entries_path (a path check). They agree only because both now derive from one GrubBootLocation. The v2 upgrade in plan-57 (back to the base initramfs) is the only coverage of this branch. - look-closely · test-gap —
crates/lib/src/bootc_composefs/boot.rs:1619-1655(36f1d52): GRUB+UKI with a separate /boot now reads and writes /boot/grub2/user.cfg(.staged), but plan-57 skips UKI (fixme_skip_if_uki, and first_boot skips non-BLS), so nothing tests this path. If no menu entries are parsed at the new location, entries[0] panics rather than returning an error. - look-closely · test-gap —
tmt/tests/booted/test-composefs-separate-boot.nu:151-166(f2bc24f): The test writes the entries' relative kernel paths and the systemd.mount-extra karg itself, imitating install. So it checks upgrade against its own reconstruction of install's output, not install's real output (boot_mount_spec's source and options format). Install with a separate /boot stays untested. - note · test-gap —
tmt/tests/booted/test-composefs-separate-boot.nu:72-100(f2bc24f): These assertions are what catches a regression: /sysroot/boot stays empty and the kernel paths are relative to the partition. referenced_kernel_dirs reads onlylinuxlines, so a wrong initrd prefix, or GC dropping an initramfs dir that only an initrd line refers to, would pass. - note · error-handling —
tmt/tests/booted/test-composefs-separate-boot.nu:114-141(f2bc24f): This repartitions and reformats the running VM's ESP. A failure between the sfdisk and the copy back (a busy umount, partx) leaves a VM that can't boot, and the plan then fails by timeout instead of with an error. The 512-byte sector size is hard-coded where sfdisk reports sectorsize.
Safe to skim
tmt/plans/integration.fmf: generated from the test's headertmt/tests/tests.fmf: generated from the test's headercrates/lib/src/bootc_composefs/boot.rs:777-853: mechanical: drops root_path from the setup tuplecrates/lib/src/bootc_composefs/boot.rs:1778-1884: mechanical: drops root_path from the UKI setup tuplecrates/lib/src/store/mod.rs:637-646: doc comment and new field
Set manual headers from filenames, keep temporary inputs in target/man, and regenerate when version metadata changes. Escape leading apostrophes so roff does not silently drop prose. Add regression tests for headers, apostrophes, and boolean options. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Map narrative chapters to stable section 7 names and validate one-to-one coverage with SUMMARY.md. Keep existing navigation flat while exposing the previously unlisted command references. Reject missing, duplicate, empty, and unsafe entries before rendering. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Use parsed Markdown spans to preserve formatting and code examples while mapping chapter links to installed manuals. Keep external links intact and resolve generated web artifacts against bootc.dev. Reject missing or escaping local documentation links. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Convert supported Mermaid flowcharts and two-column guide tables using parsed Markdown spans, leaving fenced examples untouched. Reject unsupported diagram syntax rather than silently losing it. Wrap long command examples for terminal reading. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Render canonical narrative chapters into section 7 without creating Markdown copies. Generate bootc-docs(7) in website navigation order, include manual cross-references and version metadata, and remove stale generated guide output. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Install section 7 alongside sections 5 and 8, fail the install target if generation fails, and validate coverage during website and generated-file checks. Route reference generation through the shared inventory and point bootc(8) at the offline index. The existing RPM man* file glob already covers the new manuals. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Keep each shared explanation in one canonical source so website and installed manuals cannot drift through separately maintained copies. Keep installation configuration discovery and merge precedence in its reference manual, and soft-reboot behavior in the upgrades guide. Replace repeated explanations with links while retaining command-specific details. Align the canonical text and CLI help with the implemented behavior, including platform-specific bootloader setup and the experimental composefs soft-reboot fallback limitation. No runtime behavior changes. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Keep one naming convention for guides and reference manuals instead of maintaining a separate chapter-to-manual manifest. Rename narrative chapters to stable section-7 filenames, reuse filename parsing, and update links without changing prose or navigation. Preserve published website URLs with mdBook redirects. Generated-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Signed-off-by: bootc-bot[bot] <225049296+bootc-bot[bot]@users.noreply.github.com>
f2bc24f to
74f9054
Compare
With GRUB and a separate boot partition, `bootc switch` and `bootc upgrade` fail with "Getting sorted Type1 boot entries: No such file or directory": the boot partition is mounted on /boot (via the systemd.mount-extra karg install adds), and /sysroot/boot is just an empty directory on the physical root. bootc-dev#2440 taught the booted storage to find the right directory for status, rollback, finalization and GC, but staging a new deployment still built its paths from /sysroot, both for BLS entries and for GRUB's UKI menu entries. So have get_boot_dir_for_grub() return the path it picked along with the directory, keep that in Storage, and derive every GRUB boot path while staging from it. The decision itself moves into a pure function so it can be unit tested. That also fixes the BLS entries' kernel and initramfs paths, which must be relative to the boot partition then. Every disk image-builder makes has a separate /boot, so this blocked using those for composefs. Generated-by: AI Signed-off-by: Colin Walters <walters@verbum.org>
Disks from image-builder always have a separate /boot, but the VMs tmt runs in don't, so nothing covered switching, upgrading or rolling back with that layout on composefs. Installing to a loop device inside the VM with a separate /boot would exercise install, but we can't boot that disk. So instead, like the XBOOTLDR test does, give the VM's own disk the layout install would have made: split the ESP into a smaller ESP and an ext4 XBOOTLDR partition, move /sysroot/boot there, make the BLS entries point at the kernels relative to it and add the systemd.mount-extra karg, and point bootupd's bootuuid.cfg at the new filesystem. Then switch, upgrade and roll back, checking that everything stays on /boot and that GC keeps the kernels there in sync with the entries. Generated-by: AI Signed-off-by: Colin Walters <walters@verbum.org>
|
Signed off 2 commit(s) with |
74f9054 to
58ac509
Compare
|
Opened upstream as bootc-dev#2516. Closing this review draft. |
|
Signed off 2 commit(s) with |
|
Signed off 1 commit(s) with |
With GRUB and a separate /boot partition (as every image-builder disk has), composefs
bootc switchandbootc upgradefail with "Getting sorted Type1 boot entries: No such file or directory": staging built its paths from /sysroot, and /sysroot/boot is empty when the boot partition is mounted on /boot. bootc-dev#2440 fixed the same for status (and, through the booted storage, rollback, finalize and GC), so this keeps the path thatget_boot_dir_for_grub()picked inStorageand derives the BLS and GRUB UKI staging paths from it, including the kernel paths in BLS entries, which must then be relative to the boot partition.The second commit adds plan-57, which gives the test VM's disk a separate ext4 /boot (carved out of the ESP, like the XBOOTLDR test does) and then switches, upgrades and rolls back. bcvk's disks have no separate /boot, and a disk installed to a loop device inside the VM can't be booted, so repartitioning was the way to cover the booted paths.
Tested on a 16-core devspace (composefs, GRUB, ext4, BLS):
just unit-tests,just validate, and plans 56 (now renumbered 57), 24 and 36 passed. Without the fix, the new plan fails with the error above. The fix also passed switch + reboot + upgrade + reboot in rhel-bootc-examples' composefs e2e test on an image-builder disk: https://gist.github.com/cgwalters-bot/04b3e5503547a054ebcdf227238eaec1Related: bootc-dev#2440
Generated-by: https://github.com/cgwalters/#llms
Review draft in cgwalters-forge, not upstream yet. This section is removed when the PR is opened upstream.
bootc-dev/bootc, basemainPVTI_lAHOAQ_SPs4Bj2Gizg83mOATo review:
/promoteon a line of its own, to open it upstream, ready for review. Either covers only the commits pushed so far.Signed-off-by: Colin Walters <walters@verbum.org>to the commits lacking it (the bot's and yours; anyone else's only if you ask), with you as committer./draftline (in the same comment or before) to open it upstream as a draft (/readyundoes that).