Skip to content

Commit 78e9b37

Browse files
committed
install: Fix DPS support
This fixes bootc's use of the Discoverable Partition Specification (DPS) to properly support systemd-gpt-auto-generator. Previously, bootc was incorrectly setting filesystem UUIDs to the DPS partition type UUID value, which caused UUID collisions and prevented proper DPS functionality. It's still a TODO on our side to support systemd-repart in this flow. Note we go back to using random filesystem UUIDs with this, but per above we should likely reinitialize them on boot via repart. Note we remove root= parameter from kernel cmdline for composefs sealed images, allowing systemd-gpt-auto-generator to auto-discover the root partition and we test this. Fixes: #1771 Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
1 parent 71dc8e5 commit 78e9b37

File tree

7 files changed

+49
-15
lines changed

7 files changed

+49
-15
lines changed

Dockerfile.cfsuki

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ RUN --mount=type=secret,id=key \
2828
# Should be generated externally
2929
test -n "${COMPOSEFS_FSVERITY}"
3030

31-
# Inject the composefs kernel argument and specify a root with the x86_64 DPS UUID.
32-
# TODO: Discoverable partition fleshed out, or drop root UUID as systemd-stub extension
33-
# TODO: https://github.com/containers/composefs-rs/issues/183
34-
cmdline="composefs=${COMPOSEFS_FSVERITY} root=UUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 console=ttyS0,115200n8 enforcing=0 rw"
31+
cmdline="composefs=${COMPOSEFS_FSVERITY} console=ttyS0,115200n8 enforcing=0 rw"
3532

3633
# pesign uses NSS database so create it from input cert/key
3734
mkdir pesign

crates/blockdev/src/blockdev.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub struct Device {
3737
// Filesystem-related properties
3838
pub label: Option<String>,
3939
pub fstype: Option<String>,
40+
pub uuid: Option<String>,
4041
pub path: Option<String>,
4142
}
4243

crates/lib/src/bootc_composefs/boot.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ use crate::{
5151
BOOT_LOADER_ENTRIES, COMPOSEFS_CMDLINE, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST,
5252
STAGED_BOOT_LOADER_ENTRIES, STATE_DIR_ABS, USER_CFG, USER_CFG_STAGED,
5353
},
54-
discoverable_partition_specification::this_arch_root,
5554
install::RW_KARG,
5655
spec::{Bootloader, Host},
5756
};
@@ -412,10 +411,7 @@ pub(crate) fn setup_composefs_bls_boot(
412411
(
413412
Utf8PathBuf::from("/sysroot"),
414413
get_esp_partition(&sysroot_parent)?.0,
415-
Cmdline::from(format!(
416-
"root=UUID={} {RW_KARG} {COMPOSEFS_CMDLINE}={id_hex}",
417-
this_arch_root()
418-
)),
414+
Cmdline::from(format!("{RW_KARG} {COMPOSEFS_CMDLINE}={id_hex}")),
419415
fs,
420416
bootloader,
421417
)

crates/lib/src/install/baseline.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ pub(crate) const EFIPN_SIZE_MB: u32 = 512;
4141
/// We need more space than ostree as we have UKIs and UKI addons
4242
/// We might also need to store UKIs for pinned deployments
4343
pub(crate) const CFS_EFIPN_SIZE_MB: u32 = 1024;
44-
/// The GPT type for "linux"
45-
pub(crate) const LINUX_PARTTYPE: &str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
4644
#[cfg(feature = "install-to-disk")]
4745
pub(crate) const PREPBOOT_GUID: &str = "9E1A2D38-C612-4316-AA26-8B49521E5A8B";
4846
#[cfg(feature = "install-to-disk")]
@@ -113,7 +111,8 @@ fn mkfs<'a>(
113111
let devinfo = bootc_blockdev::list_dev(dev.into())?;
114112
let size = ostree_ext::glib::format_size(devinfo.size);
115113

116-
let u = uuid::Uuid::parse_str(crate::discoverable_partition_specification::this_arch_root())?;
114+
// Generate a random UUID for the filesystem
115+
let u = uuid::Uuid::new_v4();
117116

118117
let mut t = Task::new(
119118
&format!("Creating {label} filesystem ({fs}) on device {dev} (size={size})"),
@@ -315,9 +314,11 @@ pub(crate) fn install_create_rootfs(
315314
let root_size = root_size
316315
.map(|v| Cow::Owned(format!("size={v}MiB, ")))
317316
.unwrap_or_else(|| Cow::Borrowed(""));
317+
let rootpart_uuid =
318+
uuid::Uuid::parse_str(crate::discoverable_partition_specification::this_arch_root())?;
318319
writeln!(
319320
&mut partitioning_buf,
320-
r#"{root_size}type={LINUX_PARTTYPE}, name="root""#
321+
r#"{root_size}type={rootpart_uuid}, name="root""#
321322
)?;
322323
tracing::debug!("Partitioning: {partitioning_buf}");
323324
Task::new("Initializing partitions", "sfdisk")
@@ -336,9 +337,14 @@ pub(crate) fn install_create_rootfs(
336337
let base_partitions = &bootc_blockdev::partitions_of(&devpath)?;
337338

338339
let root_partition = base_partitions.find_partno(rootpn)?;
339-
if root_partition.parttype.as_str() != LINUX_PARTTYPE {
340+
// Verify the partition type matches the DPS root partition type for this architecture
341+
let expected_parttype = crate::discoverable_partition_specification::this_arch_root();
342+
if !root_partition
343+
.parttype
344+
.eq_ignore_ascii_case(expected_parttype)
345+
{
340346
anyhow::bail!(
341-
"root partition {partno} has type {}; expected {LINUX_PARTTYPE}",
347+
"root partition {rootpn} has type {}; expected {expected_parttype}",
342348
root_partition.parttype.as_str()
343349
);
344350
}

crates/tests-integration/src/composefs_bcvk.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ fn inner_tests() -> Vec<Trial> {
8080

8181
assert_eq!(verity_from_status.unwrap(), verity_from_cmdline);
8282

83+
// Verify that we booted via systemd-gpt-auto-generator by checking
84+
// that /proc/cmdline does NOT contain a root= parameter
85+
let has_root_param = cmdline.iter().any(|entry| {
86+
entry.key() == "root".into()
87+
});
88+
assert!(!has_root_param, "Sealed composefs image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS");
89+
8390
Ok(())
8491
})]
8592
.into_iter()

docs/src/man/bootc-install-to-disk.8.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ the container image, alongside any required system partitions such as
1919
the EFI system partition. Use `install to-filesystem` for anything
2020
more complex such as RAID, LVM, LUKS etc.
2121

22+
## Partitioning details
23+
24+
The default as of bootc 1.11 uses the [Discoverable Partitions Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification/)
25+
for the generated root filesystem, as well as any required system partitions
26+
such as the EFI system partition.
27+
28+
Note that by default when used with "type 1" bootloader setups (i.e. non-UKI)
29+
a kernel argument `root=UUID=<uuid of filesystem>` is injected by default.
30+
31+
When used with the composefs backend and UKIs, it's recommended that
32+
a bootloader implementing the DPS specification is used and that the root
33+
partition is auto-discovered.
34+
2235
# OPTIONS
2336

2437
<!-- BEGIN GENERATED OPTIONS -->

tmt/tests/booted/readonly/030-test-composefs.nu

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,26 @@ use tap.nu
33

44
tap begin "composefs integration smoke test"
55

6+
def parse_cmdline [] {
7+
open /proc/cmdline | str trim | split row " "
8+
}
9+
610
# Detect composefs by checking if composefs field is present
711
let st = bootc status --json | from json
812
let is_composefs = ($st.status.booted.composefs? != null)
913
let expecting_composefs = ($env.BOOTC_variant? | default "" | find "composefs") != null
1014
if $expecting_composefs {
1115
assert $is_composefs
16+
# When using systemd-boot with DPS (Discoverable Partition Specification),
17+
# /proc/cmdline should NOT contain a root= parameter because systemd-gpt-auto-generator
18+
# discovers the root partition automatically
19+
# Note that there is `bootctl --json=pretty` but it doesn't actually output JSON
20+
let bootctl_output = (bootctl)
21+
if ($bootctl_output | str contains 'Product: systemd-boot') {
22+
let cmdline = parse_cmdline
23+
let has_root_param = ($cmdline | any { |param| $param | str starts-with 'root=' })
24+
assert (not $has_root_param) "systemd-boot image should not have root= in kernel cmdline; systemd-gpt-auto-generator should discover the root partition via DPS"
25+
}
1226
}
1327

1428
if $is_composefs {

0 commit comments

Comments
 (0)