Skip to content

Commit 31934ae

Browse files
composefs-native/boot: Handle systemd-boot and grub
Fix Grub boot error caused by #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: Pragyan Poudyal <[email protected]> Add guard again tempdir drop Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent 023be10 commit 31934ae

File tree

4 files changed

+207
-53
lines changed

4 files changed

+207
-53
lines changed

crates/lib/src/cli.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -923,20 +923,6 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
923923
.as_ref()
924924
.ok_or_else(|| anyhow::anyhow!("No image source specified"))?;
925925

926-
// let booted_image = host
927-
// .status
928-
// .booted
929-
// .ok_or(anyhow::anyhow!("Could not find booted image"))?
930-
// .image
931-
// .ok_or(anyhow::anyhow!("Could not find booted image"))?;
932-
933-
// tracing::debug!("booted_image: {booted_image:#?}");
934-
// tracing::debug!("imgref: {imgref:#?}");
935-
936-
// let digest = booted_image
937-
// .digest()
938-
// .context("Getting digest for booted image")?;
939-
940926
let (repo, entries, id, fs) = pull_composefs_repo(&imgref.transport, &imgref.image).await?;
941927

942928
let Some(entry) = entries.into_iter().next() else {
@@ -949,14 +935,16 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
949935
match boot_type {
950936
BootType::Bls => {
951937
boot_digest = Some(setup_composefs_bls_boot(
952-
BootSetupType::Upgrade(&fs),
938+
BootSetupType::Upgrade((&fs, &host)),
953939
repo,
954940
&id,
955941
entry,
956942
)?)
957943
}
958944

959-
BootType::Uki => setup_composefs_uki_boot(BootSetupType::Upgrade(&fs), repo, &id, entry)?,
945+
BootType::Uki => {
946+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
947+
}
960948
};
961949

962950
write_composefs_state(
@@ -1134,13 +1122,15 @@ async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
11341122
match boot_type {
11351123
BootType::Bls => {
11361124
boot_digest = Some(setup_composefs_bls_boot(
1137-
BootSetupType::Upgrade(&fs),
1125+
BootSetupType::Upgrade((&fs, &host)),
11381126
repo,
11391127
&id,
11401128
entry,
11411129
)?)
11421130
}
1143-
BootType::Uki => setup_composefs_uki_boot(BootSetupType::Upgrade(&fs), repo, &id, entry)?,
1131+
BootType::Uki => {
1132+
setup_composefs_uki_boot(BootSetupType::Upgrade((&fs, &host)), repo, &id, entry)?
1133+
}
11441134
};
11451135

11461136
write_composefs_state(

crates/lib/src/install.rs

Lines changed: 114 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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};
@@ -119,7 +119,7 @@ const OSTREE_COMPOSEFS_SUPER: &str = ".ostree.cfs";
119119
/// The mount path for selinux
120120
const SELINUXFS: &str = "/sys/fs/selinux";
121121
/// The mount path for uefi
122-
const EFIVARFS: &str = "/sys/firmware/efi/efivars";
122+
pub(crate) const EFIVARFS: &str = "/sys/firmware/efi/efivars";
123123
pub(crate) const ARCH_USES_EFI: bool = cfg!(any(target_arch = "x86_64", target_arch = "aarch64"));
124124
pub(crate) const ESP_GUID: &str = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
125125
pub(crate) const DPS_UUID: &str = "6523f8ae-3eb1-4e2a-a05a-18b695ae656f";
@@ -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"))?;
17831822

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"))?;
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);
1830+
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
}

0 commit comments

Comments
 (0)