Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 92 additions & 54 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ use bootc_mount::{inspect_filesystem, Filesystem};

/// The toplevel boot directory
const BOOT: &str = "boot";
/// The EFI Linux directory
const EFI_LINUX: &str = "EFI/Linux";
/// Directory for transient runtime state
#[cfg(feature = "install-to-disk")]
const RUN_BOOTC: &str = "/run/bootc";
Expand Down Expand Up @@ -1691,7 +1693,7 @@ pub(crate) fn setup_composefs_bls_boot(
) -> Result<String> {
let id_hex = id.to_hex();

let (root_path, cmdline_refs) = match setup_type {
let (esp_device, cmdline_refs) = match setup_type {
BootSetupType::Setup((root_setup, state)) => {
// root_setup.kargs has [root=UUID=<UUID>, "rw"]
let mut cmdline_options = String::from(root_setup.kargs.join(" "));
Expand All @@ -1705,23 +1707,53 @@ pub(crate) fn setup_composefs_bls_boot(
}
};

(root_setup.physical_root_path.clone(), cmdline_options)
// Locate ESP partition device
let esp_part = root_setup
.device_info
.partitions
.iter()
.find(|p| p.parttype.as_str() == ESP_GUID)
.ok_or_else(|| anyhow::anyhow!("ESP partition not found"))?;

(esp_part.node.clone(), cmdline_options)
}

BootSetupType::Upgrade => (
Utf8PathBuf::from("/sysroot"),
vec![
format!("root=UUID={DPS_UUID}"),
RW_KARG.to_string(),
format!("{COMPOSEFS_CMDLINE}={id_hex}"),
]
.join(" "),
),
BootSetupType::Upgrade => {
let sysroot = Utf8PathBuf::from("/sysroot");

let fsinfo = inspect_filesystem(&sysroot)?;
let parent_devices = find_parent_devices(&fsinfo.source)?;

let Some(parent) = parent_devices.into_iter().next() else {
anyhow::bail!("Could not find parent device for mountpoint /sysroot");
};

(
get_esp_partition(&parent)?.0,
vec![
format!("root=UUID={DPS_UUID}"),
RW_KARG.to_string(),
format!("{COMPOSEFS_CMDLINE}={id_hex}"),
]
.join(" "),
)
}
};

let boot_dir = root_path.join("boot");
let temp_efi_dir = tempfile::tempdir()
.map_err(|e| anyhow::anyhow!("Failed to create temporary directory for EFI mount: {e}"))?;
let mounted_efi = temp_efi_dir.path().to_path_buf();

Command::new("mount")
.args([&PathBuf::from(&esp_device), &mounted_efi])
.log_debug()
.run_inherited_with_cmd_context()
.context("Mounting EFI")?;

let is_upgrade = matches!(setup_type, BootSetupType::Upgrade);

let efi_dir = Utf8PathBuf::from_path_buf(mounted_efi.join(EFI_LINUX))
.map_err(|_| anyhow::anyhow!("EFI dir is not valid UTF-8"))?;
let (bls_config, boot_digest) = match &entry {
ComposefsBootEntry::Type1(..) => unimplemented!(),
ComposefsBootEntry::Type2(..) => unimplemented!(),
Expand All @@ -1735,16 +1767,16 @@ pub(crate) fn setup_composefs_bls_boot(
bls_config.title = Some(id_hex.clone());
bls_config.sort_key = Some("1".into());
bls_config.machine_id = None;
bls_config.linux = format!("/boot/{id_hex}/vmlinuz");
bls_config.initrd = vec![format!("/boot/{id_hex}/initrd")];
bls_config.linux = format!("/{EFI_LINUX}/{id_hex}/vmlinuz");
bls_config.initrd = vec![format!("/{EFI_LINUX}/{id_hex}/initrd")];
bls_config.options = Some(cmdline_refs);
bls_config.extra = HashMap::new();

if let Some(symlink_to) = find_vmlinuz_initrd_duplicates(&boot_digest)? {
bls_config.linux = format!("/boot/{symlink_to}/vmlinuz");
bls_config.initrd = vec![format!("/boot/{symlink_to}/initrd")];
bls_config.linux = format!("/{EFI_LINUX}/{symlink_to}/vmlinuz");
bls_config.initrd = vec![format!("/{EFI_LINUX}/{symlink_to}/initrd")];
} else {
write_bls_boot_entries_to_disk(&boot_dir, id, usr_lib_modules_vmlinuz, &repo)?;
write_bls_boot_entries_to_disk(&efi_dir, id, usr_lib_modules_vmlinuz, &repo)?;
}

(bls_config, boot_digest)
Expand All @@ -1757,43 +1789,55 @@ pub(crate) fn setup_composefs_bls_boot(

// This will be atomically renamed to 'loader/entries' on shutdown/reboot
(
boot_dir.join(format!("loader/{STAGED_BOOT_LOADER_ENTRIES}")),
mounted_efi.join(format!("loader/{STAGED_BOOT_LOADER_ENTRIES}")),
Some(booted_bls),
)
} else {
(boot_dir.join(format!("loader/{BOOT_LOADER_ENTRIES}")), None)
(
mounted_efi.join(format!("loader/{BOOT_LOADER_ENTRIES}")),
None,
)
};

create_dir_all(&entries_path).with_context(|| format!("Creating {:?}", entries_path))?;

let loader_entries_dir =
cap_std::fs::Dir::open_ambient_dir(&entries_path, cap_std::ambient_authority())
.with_context(|| format!("Opening {entries_path}"))?;

loader_entries_dir.atomic_write(
// SAFETY: We set sort_key above
format!(
"bootc-composefs-{}.conf",
bls_config.sort_key.as_ref().unwrap()
),
bls_config.to_string().as_bytes(),
)?;
// Scope to allow for proper unmounting
{
let loader_entries_dir =
cap_std::fs::Dir::open_ambient_dir(&entries_path, cap_std::ambient_authority())
.with_context(|| format!("Opening {entries_path:?}"))?;

if let Some(booted_bls) = booted_bls {
loader_entries_dir.atomic_write(
// SAFETY: We set sort_key above
format!(
"bootc-composefs-{}.conf",
booted_bls.sort_key.as_ref().unwrap()
bls_config.sort_key.as_ref().unwrap()
),
booted_bls.to_string().as_bytes(),
bls_config.to_string().as_bytes(),
)?;

if let Some(booted_bls) = booted_bls {
loader_entries_dir.atomic_write(
// SAFETY: We set sort_key above
format!(
"bootc-composefs-{}.conf",
booted_bls.sort_key.as_ref().unwrap()
),
booted_bls.to_string().as_bytes(),
)?;
}

let owned_loader_entries_fd = loader_entries_dir
.reopen_as_ownedfd()
.context("Reopening as owned fd")?;
rustix::fs::fsync(owned_loader_entries_fd).context("fsync")?;
}

let owned_loader_entries_fd = loader_entries_dir
.reopen_as_ownedfd()
.context("Reopening as owned fd")?;
rustix::fs::fsync(owned_loader_entries_fd).context("fsync")?;
Command::new("umount")
.arg(&mounted_efi)
.log_debug()
.run_inherited_with_cmd_context()
.context("Unmounting EFI")?;

Ok(boot_digest)
}
Expand Down Expand Up @@ -1868,13 +1912,12 @@ pub(crate) fn setup_composefs_uki_boot(
}
};

let mounted_esp: PathBuf = root_path.join("esp").into();
let esp_mount_point_existed = mounted_esp.exists();

create_dir_all(&mounted_esp).context("Failed to create dir {mounted_esp:?}")?;
let temp_efi_dir = tempfile::tempdir()
.map_err(|e| anyhow::anyhow!("Failed to create temporary directory for EFI mount: {e}"))?;
let mounted_efi = temp_efi_dir.path().to_path_buf();

Task::new("Mounting ESP", "mount")
.args([&PathBuf::from(&esp_device), &mounted_esp.clone()])
.args([&PathBuf::from(&esp_device), &mounted_efi.clone()])
.run()?;

let boot_label = match entry {
Expand Down Expand Up @@ -1916,7 +1959,7 @@ pub(crate) fn setup_composefs_uki_boot(
}

// Write the UKI to ESP
let efi_linux_path = mounted_esp.join("EFI/Linux");
let efi_linux_path = mounted_efi.join(EFI_LINUX);
create_dir_all(&efi_linux_path).context("Creating EFI/Linux")?;

let efi_linux =
Expand All @@ -1938,16 +1981,11 @@ pub(crate) fn setup_composefs_uki_boot(
}
};

Task::new("Unmounting ESP", "umount")
.arg(&mounted_esp)
.run()?;

if !esp_mount_point_existed {
// This shouldn't be a fatal error
if let Err(e) = std::fs::remove_dir(&mounted_esp) {
tracing::error!("Failed to remove mount point '{mounted_esp:?}': {e}");
}
}
Command::new("umount")
.arg(&mounted_efi)
.log_debug()
.run_inherited_with_cmd_context()
.context("Unmounting ESP")?;

let boot_dir = root_path.join("boot");
create_dir_all(&boot_dir).context("Failed to create boot dir")?;
Expand Down