Skip to content

Commit dbcce3f

Browse files
install/composefs: Introduce flag for bls/uki boot
Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent 200d669 commit dbcce3f

File tree

1 file changed

+108
-47
lines changed

1 file changed

+108
-47
lines changed

lib/src/install.rs

Lines changed: 108 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use ostree_ext::composefs::{
4545
util::Sha256Digest,
4646
};
4747
use ostree_ext::composefs_boot::{
48-
write_boot::write_boot_simple as composefs_write_boot_simple, BootOps,
48+
bootloader::BootEntry, write_boot::write_boot_simple as composefs_write_boot_simple, BootOps,
4949
};
5050
use ostree_ext::composefs_oci::{
5151
image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
@@ -240,7 +240,8 @@ pub(crate) enum BootType {
240240
}
241241

242242
#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize, PartialEq, Eq)]
243-
pub(crate) struct InstallComposefsOptions {
243+
pub(crate) struct InstallComposefsOpts {
244+
#[clap(long, value_enum, default_value_t)]
244245
pub(crate) boot: BootType,
245246
}
246247

@@ -269,10 +270,10 @@ pub(crate) struct InstallToDiskOpts {
269270
pub(crate) via_loopback: bool,
270271

271272
#[clap(long)]
272-
pub(crate) composefs_experimental: bool,
273+
pub(crate) composefs_native: bool,
273274

274275
#[clap(flatten)]
275-
pub(crate) composefs_opts: InstallComposefsOptions,
276+
pub(crate) composefs_opts: InstallComposefsOpts,
276277
}
277278

278279
#[derive(ValueEnum, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -413,6 +414,9 @@ pub(crate) struct State {
413414
/// The root filesystem of the running container
414415
pub(crate) container_root: Dir,
415416
pub(crate) tempdir: TempDir,
417+
418+
// If Some, then --composefs_native is passed
419+
pub(crate) composefs_options: Option<InstallComposefsOpts>,
416420
}
417421

418422
impl State {
@@ -558,7 +562,7 @@ impl FromStr for MountSpec {
558562

559563
impl InstallToDiskOpts {
560564
pub(crate) fn validate(&self) {
561-
if !self.composefs_experimental {
565+
if !self.composefs_native {
562566
// Reject using --boot without --composefs
563567
if self.composefs_opts.boot != BootType::default() {
564568
panic!("--boot must not be provided without --composefs");
@@ -1226,6 +1230,7 @@ async fn prepare_install(
12261230
config_opts: InstallConfigOpts,
12271231
source_opts: InstallSourceOpts,
12281232
target_opts: InstallTargetOpts,
1233+
composefs_opts: Option<InstallComposefsOpts>,
12291234
) -> Result<Arc<State>> {
12301235
tracing::trace!("Preparing install");
12311236
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
@@ -1370,6 +1375,7 @@ async fn prepare_install(
13701375
container_root: rootfs,
13711376
tempdir,
13721377
host_is_container,
1378+
composefs_options: composefs_opts,
13731379
});
13741380

13751381
Ok(state)
@@ -1494,37 +1500,14 @@ async fn initialize_composefs_repository(
14941500
composefs_oci_pull(&Arc::new(repo), &format!("{transport}{name}",), None).await
14951501
}
14961502

1497-
#[context("Setting up composefs boot")]
1498-
fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -> Result<()> {
1499-
let boot_uuid = root_setup
1500-
.get_boot_uuid()?
1501-
.or(root_setup.rootfs_uuid.as_deref())
1502-
.ok_or_else(|| anyhow!("No uuid for boot/root"))?;
1503-
1504-
if cfg!(target_arch = "s390x") {
1505-
// TODO: Integrate s390x support into install_via_bootupd
1506-
crate::bootloader::install_via_zipl(&root_setup.device_info, boot_uuid)?;
1507-
} else {
1508-
crate::bootloader::install_via_bootupd(
1509-
&root_setup.device_info,
1510-
&root_setup.physical_root_path,
1511-
&state.config_opts,
1512-
)?;
1513-
}
1514-
1515-
let repo = open_composefs_repo(&root_setup.physical_root)?;
1516-
1517-
let mut fs = create_composefs_filesystem(&repo, image_id, None)?;
1518-
1519-
let entries = fs.transform_for_boot(&repo)?;
1520-
let id = fs.commit_image(&repo, None)?;
1521-
1522-
println!("{entries:#?}");
1523-
1524-
let Some(entry) = entries.into_iter().next() else {
1525-
anyhow::bail!("No boot entries!");
1526-
};
1527-
1503+
#[context("Setting up BLS boot")]
1504+
fn setup_composefs_bls_boot(
1505+
root_setup: &RootSetup,
1506+
// TODO: Make this generic
1507+
repo: ComposefsRepository<Sha256HashValue>,
1508+
id: &Sha256HashValue,
1509+
entry: BootEntry<Sha256HashValue>,
1510+
) -> Result<()> {
15281511
let rootfs_uuid = match &root_setup.rootfs_uuid {
15291512
Some(u) => u,
15301513
None => anyhow::bail!("Expected rootfs to have a UUID by now"),
@@ -1536,6 +1519,32 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
15361519
"rw",
15371520
];
15381521

1522+
composefs_write_boot_simple(
1523+
&repo,
1524+
entry,
1525+
&id,
1526+
root_setup.physical_root_path.as_std_path(), // /run/mounts/bootc/boot
1527+
Some("boot"),
1528+
Some(&format!("{}", id.to_hex())),
1529+
&cmdline_refs,
1530+
)?;
1531+
1532+
Ok(())
1533+
}
1534+
1535+
#[context("Setting up UKI boot")]
1536+
fn setup_composefs_uki_boot(
1537+
root_setup: &RootSetup,
1538+
// TODO: Make this generic
1539+
repo: ComposefsRepository<Sha256HashValue>,
1540+
id: &Sha256HashValue,
1541+
entry: BootEntry<Sha256HashValue>,
1542+
) -> Result<()> {
1543+
let rootfs_uuid = match &root_setup.rootfs_uuid {
1544+
Some(u) => u,
1545+
None => anyhow::bail!("Expected rootfs to have a UUID by now"),
1546+
};
1547+
15391548
let boot_dir = root_setup.physical_root_path.join("boot");
15401549
create_dir_all(&boot_dir).context("Failed to create boot dir")?;
15411550

@@ -1544,28 +1553,71 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
15441553
entry,
15451554
&id,
15461555
boot_dir.as_std_path(),
1556+
None,
15471557
Some(&format!("{}", id.to_hex())),
1548-
Some("/boot"),
1549-
&cmdline_refs,
1558+
&[],
15501559
)?;
15511560

15521561
// Add the user grug cfg
15531562
// TODO: We don't need this for BLS. Have a flag for BLS vs UKI, or maybe we can figure it out
15541563
// via the boot entries above
15551564
let grub_user_config = format!(
15561565
r#"
1557-
menuentry "Some Fedora" {{
1566+
menuentry "Fedora Bootc UKI" {{
15581567
insmod fat
15591568
insmod chain
15601569
search --no-floppy --set=root --fs-uuid {rootfs_uuid}
1561-
chainloader /boot/EFI/Linux/uki.efi
1570+
chainloader /boot/EFI/Linux/{uki_id}.efi
15621571
}}
1563-
"#
1572+
"#, uki_id=id.to_hex()
15641573
);
15651574

15661575
std::fs::write(boot_dir.join("grub2/user.cfg"), grub_user_config)
15671576
.context("Failed to write grub2/user.cfg")?;
15681577

1578+
Ok(())
1579+
}
1580+
1581+
#[context("Setting up composefs boot")]
1582+
fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -> Result<()> {
1583+
let boot_uuid = root_setup
1584+
.get_boot_uuid()?
1585+
.or(root_setup.rootfs_uuid.as_deref())
1586+
.ok_or_else(|| anyhow!("No uuid for boot/root"))?;
1587+
1588+
if cfg!(target_arch = "s390x") {
1589+
// TODO: Integrate s390x support into install_via_bootupd
1590+
crate::bootloader::install_via_zipl(&root_setup.device_info, boot_uuid)?;
1591+
} else {
1592+
crate::bootloader::install_via_bootupd(
1593+
&root_setup.device_info,
1594+
&root_setup.physical_root_path,
1595+
&state.config_opts,
1596+
)?;
1597+
}
1598+
1599+
let repo = open_composefs_repo(&root_setup.physical_root)?;
1600+
1601+
let mut fs = create_composefs_filesystem(&repo, image_id, None)?;
1602+
1603+
let entries = fs.transform_for_boot(&repo)?;
1604+
let id = fs.commit_image(&repo, None)?;
1605+
1606+
println!("{entries:#?}");
1607+
1608+
let Some(entry) = entries.into_iter().next() else {
1609+
anyhow::bail!("No boot entries!");
1610+
};
1611+
1612+
let Some(composefs_opts) = &state.composefs_options else {
1613+
anyhow::bail!("Could not find options for composefs")
1614+
};
1615+
1616+
match composefs_opts.boot {
1617+
BootType::Bls => setup_composefs_bls_boot(root_setup, repo, &id, entry)?,
1618+
BootType::Uki => setup_composefs_uki_boot(root_setup, repo, &id, entry)?,
1619+
};
1620+
15691621
let state_path = root_setup
15701622
.physical_root_path
15711623
.join(format!("state/{}", id.to_hex()));
@@ -1581,7 +1633,6 @@ async fn install_to_filesystem_impl(
15811633
state: &State,
15821634
rootfs: &mut RootSetup,
15831635
cleanup: Cleanup,
1584-
composefs: bool,
15851636
) -> Result<()> {
15861637
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
15871638
rootfs.kargs.push("selinux=0".to_string());
@@ -1610,7 +1661,7 @@ async fn install_to_filesystem_impl(
16101661

16111662
let bound_images = BoundImages::from_state(state).await?;
16121663

1613-
if composefs {
1664+
if state.composefs_options.is_some() {
16141665
// Load a fd for the mounted target physical root
16151666
let (id, verity) = initialize_composefs_repository(state, rootfs).await?;
16161667

@@ -1694,7 +1745,17 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
16941745
} else if !target_blockdev_meta.file_type().is_block_device() {
16951746
anyhow::bail!("Not a block device: {}", block_opts.device);
16961747
}
1697-
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts).await?;
1748+
let state = prepare_install(
1749+
opts.config_opts,
1750+
opts.source_opts,
1751+
opts.target_opts,
1752+
if opts.composefs_native {
1753+
Some(opts.composefs_opts)
1754+
} else {
1755+
None
1756+
},
1757+
)
1758+
.await?;
16981759

16991760
// This is all blocking stuff
17001761
let (mut rootfs, loopback) = {
@@ -1715,7 +1776,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
17151776
(rootfs, loopback_dev)
17161777
};
17171778

1718-
install_to_filesystem_impl(&state, &mut rootfs, Cleanup::Skip, opts.composefs_experimental).await?;
1779+
install_to_filesystem_impl(&state, &mut rootfs, Cleanup::Skip).await?;
17191780

17201781
// Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
17211782
let (root_path, luksdev) = rootfs.into_storage();
@@ -1902,7 +1963,7 @@ pub(crate) async fn install_to_filesystem(
19021963
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
19031964
// IMPORTANT: In practice, we should only be gathering information before this point,
19041965
// IMPORTANT: and not performing any mutations at all.
1905-
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts).await?;
1966+
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts, None).await?;
19061967
// And the last bit of state here is the fsopts, which we also destructure now.
19071968
let mut fsopts = opts.filesystem_opts;
19081969

@@ -2101,7 +2162,7 @@ pub(crate) async fn install_to_filesystem(
21012162
skip_finalize,
21022163
};
21032164

2104-
install_to_filesystem_impl(&state, &mut rootfs, cleanup, false).await?;
2165+
install_to_filesystem_impl(&state, &mut rootfs, cleanup).await?;
21052166

21062167
// Drop all data about the root except the path to ensure any file descriptors etc. are closed.
21072168
drop(rootfs);

0 commit comments

Comments
 (0)