Skip to content

Commit 6362c43

Browse files
composefs-native/boot: Handle systemd-boot and grub
Fix Grub boot error caused by bootc-dev#1541. Introduce a `--bootloader` cli option to `--composefs-native`. Depending upon the type of bootloader passed in we write BLS configs respectively Signed-off-by: Johan-Liebert1 <[email protected]> Add guard again tempdir drop Signed-off-by: Johan-Liebert1 <[email protected]>
1 parent fd703ec commit 6362c43

File tree

5 files changed

+202
-53
lines changed

5 files changed

+202
-53
lines changed

crates/lib/src/cli.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -929,19 +929,7 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
929929
.as_ref()
930930
.ok_or_else(|| anyhow::anyhow!("No image source specified"))?;
931931

932-
// let booted_image = host
933-
// .status
934-
// .booted
935-
// .ok_or(anyhow::anyhow!("Could not find booted image"))?
936-
// .image
937-
// .ok_or(anyhow::anyhow!("Could not find booted image"))?;
938-
939-
// tracing::debug!("booted_image: {booted_image:#?}");
940-
// tracing::debug!("imgref: {imgref:#?}");
941-
942-
// let digest = booted_image
943-
// .digest()
944-
// .context("Getting digest for booted image")?;
932+
let booted_cfs = host.require_composefs_booted()?;
945933

946934
let (repo, entries, id, fs) = pull_composefs_repo(&imgref.transport, &imgref.image).await?;
947935

@@ -955,14 +943,16 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
955943
match boot_type {
956944
BootType::Bls => {
957945
boot_digest = Some(setup_composefs_bls_boot(
958-
BootSetupType::Upgrade(&fs),
946+
BootSetupType::Upgrade((&fs, &host)),
959947
repo,
960948
&id,
961949
entry,
962950
)?)
963951
}
964952

965-
BootType::Uki => setup_composefs_uki_boot(BootSetupType::Upgrade(&fs), repo, &id, entry)?,
953+
BootType::Uki => {
954+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
955+
}
966956
};
967957

968958
write_composefs_state(
@@ -972,6 +962,7 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
972962
true,
973963
boot_type,
974964
boot_digest,
965+
booted_cfs.bootloader.clone(),
975966
)?;
976967

977968
Ok(())
@@ -1112,6 +1103,8 @@ async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
11121103
.await
11131104
.context("Getting composefs deployment status")?;
11141105

1106+
let booted_cfs = host.require_composefs_booted()?;
1107+
11151108
let new_spec = {
11161109
let mut new_spec = host.spec.clone();
11171110
new_spec.image = Some(target.clone());
@@ -1140,13 +1133,15 @@ async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
11401133
match boot_type {
11411134
BootType::Bls => {
11421135
boot_digest = Some(setup_composefs_bls_boot(
1143-
BootSetupType::Upgrade(&fs),
1136+
BootSetupType::Upgrade((&fs, &host)),
11441137
repo,
11451138
&id,
11461139
entry,
11471140
)?)
11481141
}
1149-
BootType::Uki => setup_composefs_uki_boot(BootSetupType::Upgrade(&fs), repo, &id, entry)?,
1142+
BootType::Uki => {
1143+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
1144+
}
11501145
};
11511146

11521147
write_composefs_state(
@@ -1156,6 +1151,7 @@ async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
11561151
true,
11571152
boot_type,
11581153
boot_digest,
1154+
booted_cfs.bootloader.clone(),
11591155
)?;
11601156

11611157
Ok(())

crates/lib/src/composefs_consts.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub(crate) const ORIGIN_KEY_BOOT: &str = "boot";
1919
pub(crate) const ORIGIN_KEY_BOOT_TYPE: &str = "boot_type";
2020
/// Key to store the SHA256 sum of vmlinuz + initrd for a deployment
2121
pub(crate) const ORIGIN_KEY_BOOT_DIGEST: &str = "digest";
22+
/// Bootloader, Grub or Systemd
23+
pub(crate) const ORIGIN_KEY_BOOTLOADER: &str = "bootloader";
2224

2325
/// Filename for `loader/entries`
2426
pub(crate) const BOOT_LOADER_ENTRIES: &str = "entries";

crates/lib/src/install.rs

Lines changed: 126 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ use crate::bootc_composefs::state::copy_etc_to_state;
8181
use crate::boundimage::{BoundImage, ResolvedBoundImage};
8282
use crate::composefs_consts::{
8383
BOOT_LOADER_ENTRIES, COMPOSEFS_CMDLINE, COMPOSEFS_STAGED_DEPLOYMENT_FNAME,
84-
COMPOSEFS_TRANSIENT_STATE_DIR, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST, ORIGIN_KEY_BOOT_TYPE,
85-
SHARED_VAR_PATH, STAGED_BOOT_LOADER_ENTRIES, STATE_DIR_ABS, STATE_DIR_RELATIVE, USER_CFG,
86-
USER_CFG_STAGED,
84+
COMPOSEFS_TRANSIENT_STATE_DIR, ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOTLOADER, ORIGIN_KEY_BOOT_DIGEST,
85+
ORIGIN_KEY_BOOT_TYPE, SHARED_VAR_PATH, STAGED_BOOT_LOADER_ENTRIES, STATE_DIR_ABS,
86+
STATE_DIR_RELATIVE, USER_CFG, USER_CFG_STAGED,
8787
};
8888
use crate::containerenv::ContainerExecutionInfo;
8989
use crate::deploy::{
@@ -94,7 +94,7 @@ use crate::lsm;
9494
use crate::parsers::bls_config::{parse_bls_config, BLSConfig};
9595
use crate::parsers::grub_menuconfig::MenuEntry;
9696
use crate::progress_jsonl::ProgressWriter;
97-
use crate::spec::ImageReference;
97+
use crate::spec::{Bootloader, Host, ImageReference};
9898
use crate::store::Storage;
9999
use crate::task::Task;
100100
use crate::utils::{path_relative_to, sigpolicy_from_opt};
@@ -311,6 +311,10 @@ pub(crate) struct InstallComposefsOpts {
311311
#[clap(long, default_value_t)]
312312
#[serde(default)]
313313
pub(crate) insecure: bool,
314+
315+
#[clap(long, default_value_t)]
316+
#[serde(default)]
317+
pub(crate) bootloader: Bootloader,
314318
}
315319

316320
#[cfg(feature = "install-to-disk")]
@@ -1581,7 +1585,7 @@ pub(crate) enum BootSetupType<'a> {
15811585
/// For initial setup, i.e. install to-disk
15821586
Setup((&'a RootSetup, &'a State, &'a FileSystem<Sha256HashValue>)),
15831587
/// For `bootc upgrade`
1584-
Upgrade(&'a FileSystem<Sha256HashValue>),
1588+
Upgrade((&'a FileSystem<Sha256HashValue>, &'a Host)),
15851589
}
15861590

15871591
/// Compute SHA256Sum of VMlinuz + Initrd
@@ -1707,6 +1711,18 @@ fn write_bls_boot_entries_to_disk(
17071711
Ok(())
17081712
}
17091713

1714+
struct BLSEntryPath<'a> {
1715+
/// Where to write vmlinuz/initrd
1716+
entries_path: Utf8PathBuf,
1717+
/// The absolute path, with reference to the partition's root, where the vmlinuz/initrd are written to
1718+
/// We need this as when installing, the mounted path will not
1719+
abs_entries_path: &'a str,
1720+
/// Where to write the .conf files
1721+
config_path: Utf8PathBuf,
1722+
/// If we mounted EFI, the target path
1723+
mount_path: Option<Utf8PathBuf>,
1724+
}
1725+
17101726
/// Sets up and writes BLS entries and binaries (VMLinuz + Initrd) to disk
17111727
///
17121728
/// # Returns
@@ -1721,7 +1737,7 @@ pub(crate) fn setup_composefs_bls_boot(
17211737
) -> Result<String> {
17221738
let id_hex = id.to_hex();
17231739

1724-
let (esp_device, cmdline_refs, fs) = match setup_type {
1740+
let (root_path, esp_device, cmdline_refs, fs, bootloader) = match setup_type {
17251741
BootSetupType::Setup((root_setup, state, fs)) => {
17261742
// root_setup.kargs has [root=UUID=<UUID>, "rw"]
17271743
let mut cmdline_options = String::from(root_setup.kargs.join(" "));
@@ -1743,10 +1759,20 @@ pub(crate) fn setup_composefs_bls_boot(
17431759
.find(|p| p.parttype.as_str() == ESP_GUID)
17441760
.ok_or_else(|| anyhow::anyhow!("ESP partition not found"))?;
17451761

1746-
(esp_part.node.clone(), cmdline_options, fs)
1762+
(
1763+
root_setup.physical_root_path.clone(),
1764+
esp_part.node.clone(),
1765+
cmdline_options,
1766+
fs,
1767+
state
1768+
.composefs_options
1769+
.as_ref()
1770+
.map(|opts| opts.bootloader.clone())
1771+
.unwrap_or(Bootloader::default()),
1772+
)
17471773
}
17481774

1749-
BootSetupType::Upgrade(fs) => {
1775+
BootSetupType::Upgrade((fs, host)) => {
17501776
let sysroot = Utf8PathBuf::from("/sysroot");
17511777

17521778
let fsinfo = inspect_filesystem(&sysroot)?;
@@ -1756,7 +1782,10 @@ pub(crate) fn setup_composefs_bls_boot(
17561782
anyhow::bail!("Could not find parent device for mountpoint /sysroot");
17571783
};
17581784

1785+
let bootloader = host.require_composefs_booted()?.bootloader.clone();
1786+
17591787
(
1788+
Utf8PathBuf::from("/sysroot"),
17601789
get_esp_partition(&parent)?.0,
17611790
vec![
17621791
format!("root=UUID={DPS_UUID}"),
@@ -1765,24 +1794,51 @@ pub(crate) fn setup_composefs_bls_boot(
17651794
]
17661795
.join(" "),
17671796
fs,
1797+
bootloader,
17681798
)
17691799
}
17701800
};
17711801

1772-
let temp_efi_dir = tempfile::tempdir()
1773-
.map_err(|e| anyhow::anyhow!("Failed to create temporary directory for EFI mount: {e}"))?;
1774-
let mounted_efi = temp_efi_dir.path().to_path_buf();
1802+
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
17751803

1776-
Command::new("mount")
1777-
.args([&PathBuf::from(&esp_device), &mounted_efi])
1778-
.log_debug()
1779-
.run_inherited_with_cmd_context()
1780-
.context("Mounting EFI")?;
1804+
let (entry_paths, _tmpdir_guard) = match bootloader {
1805+
Bootloader::Grub => (
1806+
BLSEntryPath {
1807+
entries_path: root_path.join("boot"),
1808+
config_path: root_path.join("boot"),
1809+
abs_entries_path: "boot",
1810+
mount_path: None,
1811+
},
1812+
None,
1813+
),
17811814

1782-
let is_upgrade = matches!(setup_type, BootSetupType::Upgrade(..));
1815+
Bootloader::Systemd => {
1816+
let temp_efi_dir = tempfile::tempdir().map_err(|e| {
1817+
anyhow::anyhow!("Failed to create temporary directory for EFI mount: {e}")
1818+
})?;
1819+
1820+
let mounted_efi = Utf8PathBuf::from_path_buf(temp_efi_dir.path().to_path_buf())
1821+
.map_err(|_| anyhow::anyhow!("EFI dir is not valid UTF-8"))?;
1822+
1823+
Command::new("mount")
1824+
.args([&PathBuf::from(&esp_device), mounted_efi.as_std_path()])
1825+
.log_debug()
1826+
.run_inherited_with_cmd_context()
1827+
.context("Mounting EFI")?;
1828+
1829+
let efi_linux_dir = mounted_efi.join(EFI_LINUX);
17831830

1784-
let efi_dir = Utf8PathBuf::from_path_buf(mounted_efi.join(EFI_LINUX))
1785-
.map_err(|_| anyhow::anyhow!("EFI dir is not valid UTF-8"))?;
1831+
(
1832+
BLSEntryPath {
1833+
entries_path: efi_linux_dir,
1834+
config_path: mounted_efi.clone(),
1835+
abs_entries_path: EFI_LINUX,
1836+
mount_path: Some(mounted_efi),
1837+
},
1838+
Some(temp_efi_dir),
1839+
)
1840+
}
1841+
};
17861842

17871843
let (bls_config, boot_digest) = match &entry {
17881844
ComposefsBootEntry::Type1(..) => unimplemented!(),
@@ -1831,44 +1887,66 @@ pub(crate) fn setup_composefs_bls_boot(
18311887
.with_title(id_hex.clone())
18321888
.with_sort_key(default_sort_key.into())
18331889
.with_version(version.unwrap_or(default_sort_key.into()))
1834-
.with_linux(format!("/{EFI_LINUX}/{id_hex}/vmlinuz"))
1835-
.with_initrd(vec![format!("/{EFI_LINUX}/{id_hex}/initrd")])
1890+
.with_linux(format!(
1891+
"/{}/{id_hex}/vmlinuz",
1892+
entry_paths.abs_entries_path
1893+
))
1894+
.with_initrd(vec![format!(
1895+
"/{}/{id_hex}/initrd",
1896+
entry_paths.abs_entries_path
1897+
)])
18361898
.with_options(cmdline_refs);
18371899

18381900
if let Some(symlink_to) = find_vmlinuz_initrd_duplicates(&boot_digest)? {
1839-
bls_config.linux = format!("/{EFI_LINUX}/{symlink_to}/vmlinuz");
1840-
bls_config.initrd = vec![format!("/{EFI_LINUX}/{symlink_to}/initrd")];
1901+
bls_config.linux =
1902+
format!("/{}/{symlink_to}/vmlinuz", entry_paths.abs_entries_path);
1903+
1904+
bls_config.initrd = vec![format!(
1905+
"/{}/{symlink_to}/initrd",
1906+
entry_paths.abs_entries_path
1907+
)];
18411908
} else {
1842-
write_bls_boot_entries_to_disk(&efi_dir, id, usr_lib_modules_vmlinuz, &repo)?;
1909+
write_bls_boot_entries_to_disk(
1910+
&entry_paths.entries_path,
1911+
id,
1912+
usr_lib_modules_vmlinuz,
1913+
&repo,
1914+
)?;
18431915
}
18441916

18451917
(bls_config, boot_digest)
18461918
}
18471919
};
18481920

1849-
let (entries_path, booted_bls) = if is_upgrade {
1921+
let (config_path, booted_bls) = if is_upgrade {
18501922
let mut booted_bls = get_booted_bls()?;
18511923
booted_bls.sort_key = Some("0".into()); // entries are sorted by their filename in reverse order
18521924

18531925
// This will be atomically renamed to 'loader/entries' on shutdown/reboot
18541926
(
1855-
mounted_efi.join(format!("loader/{STAGED_BOOT_LOADER_ENTRIES}")),
1927+
entry_paths
1928+
.config_path
1929+
.join("loader")
1930+
.join(STAGED_BOOT_LOADER_ENTRIES),
18561931
Some(booted_bls),
18571932
)
18581933
} else {
18591934
(
1860-
mounted_efi.join(format!("loader/{BOOT_LOADER_ENTRIES}")),
1935+
entry_paths
1936+
.config_path
1937+
.join("loader")
1938+
.join(BOOT_LOADER_ENTRIES),
18611939
None,
18621940
)
18631941
};
18641942

1865-
create_dir_all(&entries_path).with_context(|| format!("Creating {:?}", entries_path))?;
1943+
create_dir_all(&config_path).with_context(|| format!("Creating {:?}", config_path))?;
18661944

18671945
// Scope to allow for proper unmounting
18681946
{
18691947
let loader_entries_dir =
1870-
cap_std::fs::Dir::open_ambient_dir(&entries_path, cap_std::ambient_authority())
1871-
.with_context(|| format!("Opening {entries_path:?}"))?;
1948+
cap_std::fs::Dir::open_ambient_dir(&config_path, cap_std::ambient_authority())
1949+
.with_context(|| format!("Opening {config_path:?}"))?;
18721950

18731951
loader_entries_dir.atomic_write(
18741952
// SAFETY: We set sort_key above
@@ -1893,14 +1971,17 @@ pub(crate) fn setup_composefs_bls_boot(
18931971
let owned_loader_entries_fd = loader_entries_dir
18941972
.reopen_as_ownedfd()
18951973
.context("Reopening as owned fd")?;
1974+
18961975
rustix::fs::fsync(owned_loader_entries_fd).context("fsync")?;
18971976
}
18981977

1899-
Command::new("umount")
1900-
.arg(&mounted_efi)
1901-
.log_debug()
1902-
.run_inherited_with_cmd_context()
1903-
.context("Unmounting EFI")?;
1978+
if let Some(mounted_efi) = entry_paths.mount_path {
1979+
Command::new("umount")
1980+
.arg(mounted_efi)
1981+
.log_debug()
1982+
.run_inherited_with_cmd_context()
1983+
.context("Unmounting EFI")?;
1984+
}
19041985

19051986
Ok(boot_digest)
19061987
}
@@ -2231,6 +2312,11 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
22312312
false,
22322313
boot_type,
22332314
boot_digest,
2315+
state
2316+
.composefs_options
2317+
.as_ref()
2318+
.map(|opt| opt.bootloader.clone())
2319+
.unwrap_or(Bootloader::default()),
22342320
)?;
22352321

22362322
Ok(())
@@ -2245,6 +2331,7 @@ pub(crate) fn write_composefs_state(
22452331
staged: bool,
22462332
boot_type: BootType,
22472333
boot_digest: Option<String>,
2334+
bootloader: Bootloader,
22482335
) -> Result<()> {
22492336
let state_path = root_path.join(format!("{STATE_DIR_RELATIVE}/{}", deployment_id.to_hex()));
22502337

@@ -2283,6 +2370,10 @@ pub(crate) fn write_composefs_state(
22832370
.item(ORIGIN_KEY_BOOT_DIGEST, boot_digest);
22842371
}
22852372

2373+
config = config
2374+
.section(ORIGIN_KEY_BOOT)
2375+
.item(ORIGIN_KEY_BOOTLOADER, bootloader);
2376+
22862377
let state_dir = cap_std::fs::Dir::open_ambient_dir(&state_path, cap_std::ambient_authority())
22872378
.context("Opening state dir")?;
22882379

0 commit comments

Comments
 (0)