Repository navigation
install mount: Select the deployment from the default boot entry - #2515
cgwalters-bot wants to merge 1 commit into
Conversation
`--latest` counted deployments and refused anything but exactly one. That fails on any installation that has been upgraded, and it can't choose when a sysroot holds both OSTree and composefs deployments (e.g. mid-migration). A booted system answers "which deployment" from its kernel command line. Offline, the equivalent is the command line of the boot entry the bootloader would pick. So read the entries from /boot and the ESP and order them as `bootc status` does, sharing its bootloader probe (now taking the boot directories rather than looking at the host's /boot) and entry sorting. A composefs default entry names its deployment by digest (or UKI name). For an `ostree=` one, OSTree's own deployment order, which it loads from the same entries, already has the default first, so use that rather than re-sorting the entries: GRUB's file name order differs from it at ten or more deployments. /boot has to be at SYSROOT/boot, since OSTree loads its deployments from there. The ESP comes from a new `--esp`, defaulting to a mounted SYSROOT/efi, then SYSROOT/boot/efi, matching what systemd-gpt-auto-generator would set up, and otherwise to the ESP discovered from the disks backing the sysroot, as install finds it. Architectures without an ESP skip all that. With no entries at all, e.g. when no ESP is found on a systemd-boot install, a sole deployment is still selected as before. Generated-by: AI
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Bootloader ordering and cross-backend deployment selection are sufficiently operationally sensitive to require final maintainer review.
Review effort: Balanced
Findings: None
What changed in this PR
Updates offline deployment mounting to follow the bootloader’s default entry.
Changes:
- Selects OSTree or composefs deployments from GRUB/systemd-boot entries.
- Adds
--espand documents boot partition requirements. - Adds unit and integration coverage for ordering and selection.
| File | Description |
|---|---|
crates/lib/src/mount.rs |
Implements boot-entry-based deployment selection. |
crates/lib/src/bootc_composefs/status.rs |
Generalizes offline bootloader probing and entry sorting. |
docs/src/man/bootc-install-mount.8.md |
Documents selection semantics and --esp. |
tmt/tests/booted/test-install-outside-container.nu |
Tests multi-deployment and missing-deployment behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// Where `/boot` is expected in the sysroot, as OSTree requires. | ||
| const BOOT: &str = "boot"; | ||
| /// Where the ESP is looked for in the sysroot when `--esp` is not given. | ||
| const DEFAULT_ESP: &str = "boot/efi"; |
There was a problem hiding this comment.
Prefer /efi actually first - the ideal path is a DPS system matching what systemd-gpt-auto-generator would do.
But we definitely need boot/efi as fallback
|
|
||
| /// The mounted EFI System Partition, where systemd-boot keeps its boot entries. | ||
| /// | ||
| /// Defaults to SYSROOT/boot/efi, if it exists. |
There was a problem hiding this comment.
See above but also we must honor the same ESP discovery paths we do for install. Let's make that clear.
Defaults to `/efi` or `/boot/efi` if either are mounted, otherwise the ESP will be discovered (if applicable) from the block device(s) backing the target root.
Hmm btw in theory we should make this variable also conditional on whether the arch supports an ESP
There was a problem hiding this comment.
Done in a3c55bf (your sign-off kept): --esp now defaults to a mounted SYSROOT/efi, then SYSROOT/boot/efi, then the ESP found from the backing block devices via the same find_colocated_esps() + mount_esp_readonly() install/status use, all gated on ARCH_USES_EFI (the flag is hidden elsewhere). Your doc text is on the flag, and the man page is updated to match.
Tests: a new unit test covers the preference order and discovery fallback, and plan-23 now also exercises the discovered, /boot/efi and /efi cases on composefs. just validate, the unit tests, and plan-23 on ostree/grub and on composefs systemd-boot/UKI all passed on a 16-core devspace.
Generated-by: https://github.com/cgwalters/#llms
b298c49 to
a3c55bf
Compare
a3c55bf to
f36cbb0
Compare
|
@cgwalters The grub-cc failures were a test bug: like systemd-boot, grub-cc keeps its entries on the ESP, but the empty-/boot/efi case assumed only systemd-boot does. The test now checks whether the ESP holds entries. f36cbb0 is that fix, rebased onto main, and it drops your sign-off (it's a test change you didn't ask for), so it needs your re-approval. Tested on Fedora 44 devspaces: plan-23 passes on composefs grub-cc bls (ext4 and xfs) and on ostree; Generated-by: https://github.com/cgwalters/#llms |
|
@cgwalters one open question, separate from the CI fix: on a grub-cc system the offline bootloader probe in this PR reports systemd-boot, so default-entry selection orders BLS entries by sort-key rather than GRUB's reverse filename order (#2221). With a single deployment it can't matter; with several it could pick a different default than GRUB boots. Follow up in this PR, or separately? Generated-by: https://github.com/cgwalters/#llms |
I don't like the way we probe the bootloader. Here's an idea...what if for grub we changed how we write https://uapi-group.org/specifications/specs/boot_loader_specification/#standard-conformance-marker-file so it said And that's what we dispatch on - bootupd at install time should know if the bootloader implements the spec in a conformant way or not, and then we query it. For now...needing to touch the bootloader stuff in a new way I think is adding more risk to the install mount which we don't need right now. Let's take this one as draft and move it off the p0 composefs stabilization list. |
| /// `/boot/grub2/` *and* `/boot/loader/entries/`, and is unambiguously | ||
| /// GRUB. Only a BLS layout with no GRUB directory implies a BLS-native | ||
| /// bootloader. | ||
| pub(crate) fn bootloader(&self) -> Bootloader { |
There was a problem hiding this comment.
Missing grub-cc here. Should be the same as systemd-boot, so I think this is fine, but a comment here would be good
| efi_loader_info: Result<String, EfiError>, | ||
| bls_entries_dir_present: bool, | ||
| grub_dir_present: bool, | ||
| probe: impl FnOnce() -> Result<BootDirProbe>, |
There was a problem hiding this comment.
I don't understand why this needs to be a function as an argument? Can't we just a standalone function?
| /// Where `/boot` is expected in the sysroot, as OSTree requires. | ||
| const BOOT: &str = "boot"; | ||
| /// Where a mounted ESP is looked for in the sysroot when `--esp` is not | ||
| /// given, in order: `/efi` as systemd-gpt-auto-generator mounts it, then the |
There was a problem hiding this comment.
systemd-gpt-auto-generator mounts ESP at /boot
From https://www.freedesktop.org/software/systemd/man/latest/systemd-gpt-auto-generator.html
The ESP is mounted to /boot/ if that directory is not used for XBOOTLDR, and otherwise to /efi/
| is_mountpoint: impl Fn(&Dir) -> Result<bool>, | ||
| discover: impl FnOnce() -> Result<Option<T>>, |
There was a problem hiding this comment.
why do these need to be a functions as arguments?
| else { | ||
| continue; | ||
| }; | ||
| if is_mountpoint(&dir).with_context(|| format!("Checking whether {path} is mounted"))? { |
There was a problem hiding this comment.
is_mountpoint is already a function on cap_std::fs::Dir
|
Fix round 1 of 2 for Generated-by: https://github.com/cgwalters/#llms |
|
✅ dispatch ended well and handed back no result. Its reason:
|
f36cbb0 to
9273143
Compare
|
Fix round 2 of 2 for Generated-by: https://github.com/cgwalters/#llms |
|
Failed checks: one rerun reserved for these checks on this head. Generated-by: https://github.com/cgwalters/#llms |
bootc install mount --latestnow mounts the deployment that the default boot entry boots, instead of requiring the sysroot to contain exactly one deployment. This is the follow-up to the review on #2483 (mount.rs:90). Design notes: https://gist.github.com/cgwalters-bot/3d295a3185c75c5a24989dda21018573A booted system knows its deployment from the kernel command line. Offline, the equivalent is the command line of the entry the bootloader would pick. The entries come from SYSROOT/boot and the ESP, and they are ordered with the same code
bootc statususes: for GRUB, theuser.cfgUKI entries or else the Type 1 entries sorted by file name; otherwise the Type 1 entries in BLS order. For a composefs entry, its digest (or UKI name) picks the deployment. For anostree=entry, it is OSTree's own default (deployments()[0], which OSTree orders from the same entries, asbootc statusshows it), rather than re-sorting the entries: GRUB's file name order differs from OSTree's at ten or more deployments. The bootloader probeget_bootloaderuses without EFI variables now takes the boot directories, so mount shares it. A new--espoption names the ESP, and defaults to SYSROOT/boot/efi.Caveats:
--boot. OSTree loads its deployments from SYSROOT/boot, so /boot has to be mounted there anyway, and the man page says so.loader.conf'sdefaultand grubenv'ssaved_entryare not considered.bootc statusdoesn't consider them either.Tested on a 16-core RHEL 10 devspace:
install-outside-container) is extended. On ostree it deploys the same commit ten more times (11 deployments, so file-name order would pick the wrong one) and checks that--latestfollows OSTree's default, also afterostree admin set-default. On composefs it rewrites the installed boot entries to name a missing deployment and checks that the mount fails naming it, then restores them. An earlier two-deployment version of the test, run with main's bootc (683bb05), failed withtarget must contain exactly one deployment (found 2 OSTree, 0 composefs).just validateandjust unit-testspass. The new unit tests cover entry ordering for each bootloader layout, the boot directory probe, and choosing the deployment.The
Signed-off-by: Colin Walters <walters@verbum.org>on these commits was added on cgwalters's approval of the review draft: cgwalters-forge#32 (review)Generated-by: https://github.com/cgwalters/#llms