Repository navigation
composefs: follow-ups to #2448 (three regressions) #2598
Description
These are three regressions from #2448, found while reviewing its last revision; @jmarrero chose to merge it and fix them as follow-ups. All three come from reading the code at c153cda and haven't been reproduced.
1. A subdirectory in the ESP's loader/addons fails every UKI upgrade/switch
Every bootc upgrade/switch that stages a deployment on a composefs UKI system now copies the ESP's loader/addons into loader/addons.staged with Dir::copy, which rejects anything that isn't a regular file. So a subdirectory there aborts staging, including on systems that never used the addon options.
Reproduce: on a composefs UKI system, mkdir <ESP>/loader/addons/foo, then bootc upgrade or bootc switch to a new image. The error should end with Staging Global UKI Addons: Copying addon foo: the source path is not an existing regular file.
Fix: skip non-regular entries with a warning, or copy directories recursively.
2. An image with more than one UKI fails install, upgrade and switch
setup_composefs_uki_boot now parses every UKI's cmdline into one UKIInfo, and parse_uki_cmdline bails on the second composefs= it sees, even when the digests match. So an image with two UKIs in /boot/EFI/Linux (for example two kernel flavors sealed against the same rootfs) fails with Already had cmdline ..., found another .... Before #2448 each UKI was validated and written to the same ESP file name, so the last one won.
Reproduce: build a sealed-UKI image with two UKIs that carry the same composefs= digest, then install or switch to it.
Code: the check, the loop that feeds every UKI into it. Nothing rejects such an image earlier: the image-level digest check reads only the first UKI, and kernel discovery picks the first of several.
Fix: refuse a second composefs= only when it comes from an addon. If more than one UKI per image isn't supported, reject it up front with a clear error and document that.
3. Pruning GC can fail on a live deployment's /boot, aboot included
The pruning GC (run by upgrade, switch and delete-deployment) now dumps /boot from the non-bootable image of every live deployment, to keep objects that a pruned deployment shares. composefs_ctl::dump_files fails if the image has no /boot (/boot not found), and each line goes through Entry::parse(line).unwrap(). Before #2448 these could only fail for a deployment being pruned. Now any live deployment, the booted one included, can fail GC or panic it, and upgrade/switch then exit with an error after staging. Unlike the orphan loop, the live loop isn't skipped on aboot, so aboot systems run it too wherever a non-bootable image is linked. This is unlikely in practice, since bootc images normally ship /boot.
Reproduce: deploy a composefs image that has no /boot directory, then run bootc internals composefs-gc --prune-repo or bootc upgrade.
Relatedly, get_boot_dump returns None with a warning when a live deployment's linked images can't be resolved, and that deployment's objects then aren't protected (fail-open).
Code: get_boot_dump, live loop, orphan loop, skipped on aboot.
Fix: skip the live scan on aboot, as the orphan loop does. When a live root's /boot can't be dumped or parsed, warn and skip the /boot object cleanup for that run. Replace the unwrap with an error.
Generated-by: https://github.com/jmarrero-bot#llms
Activity
Johan-Liebert1 commentedon Oct 29, 2026
I don't think we should support this anyway. As even with only global addons we have a lot of quirks and edge cases
We don't allow multiple UKIs and never install more than one, so this is expected
though unlikely, this can happen. I'll open a PR
We always save a non-bootable image, so this shouldn't happen