Skip to content

Commit 9275348

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

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
@@ -46,7 +46,7 @@ use ostree_ext::composefs::{
4646
util::Sha256Digest,
4747
};
4848
use ostree_ext::composefs_boot::{
49-
write_boot::write_boot_simple as composefs_write_boot_simple, BootOps,
49+
bootloader::BootEntry, write_boot::write_boot_simple as composefs_write_boot_simple, BootOps,
5050
};
5151
use ostree_ext::composefs_oci::{
5252
image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
@@ -241,7 +241,8 @@ pub(crate) enum BootType {
241241
}
242242

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

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

272273
#[clap(long)]
273-
pub(crate) composefs_experimental: bool,
274+
pub(crate) composefs_native: bool,
274275

275276
#[clap(flatten)]
276-
pub(crate) composefs_opts: InstallComposefsOptions,
277+
pub(crate) composefs_opts: InstallComposefsOpts,
277278
}
278279

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

419423
impl State {
@@ -557,7 +561,7 @@ impl FromStr for MountSpec {
557561

558562
impl InstallToDiskOpts {
559563
pub(crate) fn validate(&self) {
560-
if !self.composefs_experimental {
564+
if !self.composefs_native {
561565
// Reject using --boot without --composefs
562566
if self.composefs_opts.boot != BootType::default() {
563567
panic!("--boot must not be provided without --composefs");
@@ -1225,6 +1229,7 @@ async fn prepare_install(
12251229
config_opts: InstallConfigOpts,
12261230
source_opts: InstallSourceOpts,
12271231
target_opts: InstallTargetOpts,
1232+
composefs_opts: Option<InstallComposefsOpts>,
12281233
) -> Result<Arc<State>> {
12291234
tracing::trace!("Preparing install");
12301235
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
@@ -1369,6 +1374,7 @@ async fn prepare_install(
13691374
container_root: rootfs,
13701375
tempdir,
13711376
host_is_container,
1377+
composefs_options: composefs_opts,
13721378
});
13731379

13741380
Ok(state)
@@ -1492,37 +1498,14 @@ async fn initialize_composefs_repository(
14921498
composefs_oci_pull(&Arc::new(repo), &format!("{transport}{name}",), None).await
14931499
}
14941500

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

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

@@ -1542,28 +1551,71 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
15421551
entry,
15431552
&id,
15441553
boot_dir.as_std_path(),
1554+
None,
15451555
Some(&format!("{}", id.to_hex())),
1546-
Some("/boot"),
1547-
&cmdline_refs,
1556+
&[],
15481557
)?;
15491558

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

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

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

16091660
let bound_images = BoundImages::from_state(state).await?;
16101661

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

@@ -1692,7 +1743,17 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
16921743
} else if !target_blockdev_meta.file_type().is_block_device() {
16931744
anyhow::bail!("Not a block device: {}", block_opts.device);
16941745
}
1695-
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts).await?;
1746+
let state = prepare_install(
1747+
opts.config_opts,
1748+
opts.source_opts,
1749+
opts.target_opts,
1750+
if opts.composefs_native {
1751+
Some(opts.composefs_opts)
1752+
} else {
1753+
None
1754+
},
1755+
)
1756+
.await?;
16961757

16971758
// This is all blocking stuff
16981759
let (mut rootfs, loopback) = {
@@ -1713,7 +1774,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
17131774
(rootfs, loopback_dev)
17141775
};
17151776

1716-
install_to_filesystem_impl(&state, &mut rootfs, Cleanup::Skip, opts.composefs_experimental).await?;
1777+
install_to_filesystem_impl(&state, &mut rootfs, Cleanup::Skip).await?;
17171778

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

@@ -2099,7 +2160,7 @@ pub(crate) async fn install_to_filesystem(
20992160
skip_finalize,
21002161
};
21012162

2102-
install_to_filesystem_impl(&state, &mut rootfs, cleanup, false).await?;
2163+
install_to_filesystem_impl(&state, &mut rootfs, cleanup).await?;
21032164

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

0 commit comments

Comments
 (0)