mirror of
https://github.com/containers/bootc.git
synced 2026-02-05 06:45:13 +01:00
kernel: Add KernelPath enum
This just clarifies things in a few places to distinguish between a UKI which has just a single path versus a traditional kernel with separate vmlinuz and initramfs. Also renames `find_uki_filename` to `find_uki_path` and updates the return type to use `Utf8PathBuf` instead of just `String`. Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This commit is contained in:
@@ -26,20 +26,28 @@ pub(crate) struct Kernel {
|
||||
pub(crate) unified: bool,
|
||||
}
|
||||
|
||||
/// Internal-only kernel wrapper with extra information (paths to
|
||||
/// vmlinuz, initramfs) that are useful but we don't want to leak out
|
||||
/// via serialization to inspection.
|
||||
/// Path to kernel component(s)
|
||||
///
|
||||
/// UKI kernels only have the single PE binary, whereas
|
||||
/// traditional "vmlinuz" kernels have distinct kernel and
|
||||
/// initramfs.
|
||||
pub(crate) enum KernelPath {
|
||||
Uki(Utf8PathBuf),
|
||||
Vmlinuz {
|
||||
path: Utf8PathBuf,
|
||||
initramfs: Utf8PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
/// Internal-only kernel wrapper with extra path information that are
|
||||
/// useful but we don't want to leak out via serialization to
|
||||
/// inspection.
|
||||
///
|
||||
/// `Kernel` implements `From<KernelInternal>` so we can just `.into()`
|
||||
/// to get the "public" form where needed.
|
||||
pub(crate) struct KernelInternal {
|
||||
pub(crate) kernel: Kernel,
|
||||
/// Path to vmlinuz for traditional kernels.
|
||||
/// This is `None` for UKI images.
|
||||
pub(crate) vmlinuz: Option<Utf8PathBuf>,
|
||||
/// Path to initramfs.img for traditional kernels.
|
||||
/// This is `None` for UKI images.
|
||||
pub(crate) initramfs: Option<Utf8PathBuf>,
|
||||
pub(crate) path: KernelPath,
|
||||
}
|
||||
|
||||
impl From<KernelInternal> for Kernel {
|
||||
@@ -57,18 +65,14 @@ impl From<KernelInternal> for Kernel {
|
||||
/// Returns `None` if no kernel is found.
|
||||
pub(crate) fn find_kernel(root: &Dir) -> Result<Option<KernelInternal>> {
|
||||
// First, try to find a UKI
|
||||
if let Some(uki_filename) = find_uki_filename(root)? {
|
||||
let version = uki_filename
|
||||
.strip_suffix(".efi")
|
||||
.unwrap_or(&uki_filename)
|
||||
.to_owned();
|
||||
if let Some(uki_path) = find_uki_path(root)? {
|
||||
let version = uki_path.file_stem().unwrap_or(uki_path.as_str()).to_owned();
|
||||
return Ok(Some(KernelInternal {
|
||||
kernel: Kernel {
|
||||
version,
|
||||
unified: true,
|
||||
},
|
||||
vmlinuz: None,
|
||||
initramfs: None,
|
||||
path: KernelPath::Uki(uki_path),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -85,19 +89,21 @@ pub(crate) fn find_kernel(root: &Dir) -> Result<Option<KernelInternal>> {
|
||||
version,
|
||||
unified: false,
|
||||
},
|
||||
vmlinuz: Some(vmlinuz),
|
||||
initramfs: Some(initramfs),
|
||||
path: KernelPath::Vmlinuz {
|
||||
path: vmlinuz,
|
||||
initramfs,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Returns the filename of the first UKI found in the container root, if any.
|
||||
/// Returns the path to the first UKI found in the container root, if any.
|
||||
///
|
||||
/// Looks in `/boot/EFI/Linux/*.efi`. If multiple UKIs are present, returns
|
||||
/// the first one in sorted order for determinism.
|
||||
fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
|
||||
fn find_uki_path(root: &Dir) -> Result<Option<Utf8PathBuf>> {
|
||||
let Some(boot) = root.open_dir_optional(crate::install::BOOT)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -120,13 +126,15 @@ fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
|
||||
|
||||
// Sort for deterministic behavior when multiple UKIs are present
|
||||
uki_files.sort();
|
||||
Ok(uki_files.into_iter().next())
|
||||
Ok(uki_files
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|filename| Utf8PathBuf::from(format!("boot/{EFI_LINUX}/{filename}"))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use camino::Utf8Path;
|
||||
use cap_std_ext::{cap_std, cap_tempfile, dirext::CapStdExtDirExt};
|
||||
|
||||
#[test]
|
||||
@@ -148,18 +156,19 @@ mod tests {
|
||||
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
|
||||
assert_eq!(kernel_internal.kernel.version, "6.12.0-100.fc41.x86_64");
|
||||
assert!(!kernel_internal.kernel.unified);
|
||||
assert_eq!(
|
||||
kernel_internal.vmlinuz.as_deref(),
|
||||
Some(Utf8Path::new(
|
||||
"usr/lib/modules/6.12.0-100.fc41.x86_64/vmlinuz"
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
kernel_internal.initramfs.as_deref(),
|
||||
Some(Utf8Path::new(
|
||||
"usr/lib/modules/6.12.0-100.fc41.x86_64/initramfs.img"
|
||||
))
|
||||
);
|
||||
match &kernel_internal.path {
|
||||
KernelPath::Vmlinuz { path, initramfs } => {
|
||||
assert_eq!(
|
||||
path.as_str(),
|
||||
"usr/lib/modules/6.12.0-100.fc41.x86_64/vmlinuz"
|
||||
);
|
||||
assert_eq!(
|
||||
initramfs.as_str(),
|
||||
"usr/lib/modules/6.12.0-100.fc41.x86_64/initramfs.img"
|
||||
);
|
||||
}
|
||||
KernelPath::Uki(_) => panic!("Expected Vmlinuz, got Uki"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -172,8 +181,12 @@ mod tests {
|
||||
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
|
||||
assert_eq!(kernel_internal.kernel.version, "fedora-6.12.0");
|
||||
assert!(kernel_internal.kernel.unified);
|
||||
assert!(kernel_internal.vmlinuz.is_none());
|
||||
assert!(kernel_internal.initramfs.is_none());
|
||||
match &kernel_internal.path {
|
||||
KernelPath::Uki(path) => {
|
||||
assert_eq!(path.as_str(), "boot/EFI/Linux/fedora-6.12.0.efi");
|
||||
}
|
||||
KernelPath::Vmlinuz { .. } => panic!("Expected Uki, got Vmlinuz"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -197,7 +210,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_uki_filename_sorted() -> Result<()> {
|
||||
fn test_find_uki_path_sorted() -> Result<()> {
|
||||
let tempdir = cap_tempfile::tempdir(cap_std::ambient_authority())?;
|
||||
tempdir.create_dir_all("boot/EFI/Linux")?;
|
||||
tempdir.atomic_write("boot/EFI/Linux/zzz.efi", b"fake uki")?;
|
||||
@@ -205,8 +218,8 @@ mod tests {
|
||||
tempdir.atomic_write("boot/EFI/Linux/mmm.efi", b"fake uki")?;
|
||||
|
||||
// Should return first in sorted order
|
||||
let filename = find_uki_filename(&tempdir)?.expect("should find uki");
|
||||
assert_eq!(filename, "aaa.efi");
|
||||
let path = find_uki_path(&tempdir)?.expect("should find uki");
|
||||
assert_eq!(path.as_str(), "boot/EFI/Linux/aaa.efi");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,21 +54,13 @@ pub(crate) fn build_ukify(
|
||||
let kernel = crate::kernel::find_kernel(&root)?
|
||||
.ok_or_else(|| anyhow::anyhow!("No kernel found in {rootfs}"))?;
|
||||
|
||||
// We can only build a UKI from a traditional kernel, not from an existing UKI
|
||||
if kernel.kernel.unified {
|
||||
anyhow::bail!(
|
||||
"Cannot build UKI: rootfs already contains a UKI at boot/EFI/Linux/{}.efi",
|
||||
kernel.kernel.version
|
||||
);
|
||||
}
|
||||
|
||||
// Get paths from the kernel info
|
||||
let vmlinuz_path = kernel
|
||||
.vmlinuz
|
||||
.ok_or_else(|| anyhow::anyhow!("Traditional kernel should have vmlinuz path"))?;
|
||||
let initramfs_path = kernel
|
||||
.initramfs
|
||||
.ok_or_else(|| anyhow::anyhow!("Traditional kernel should have initramfs path"))?;
|
||||
// Extract vmlinuz and initramfs paths, or bail if this is already a UKI
|
||||
let (vmlinuz_path, initramfs_path) = match kernel.path {
|
||||
crate::kernel::KernelPath::Vmlinuz { path, initramfs } => (path, initramfs),
|
||||
crate::kernel::KernelPath::Uki(path) => {
|
||||
anyhow::bail!("Cannot build UKI: rootfs already contains a UKI at {path}");
|
||||
}
|
||||
};
|
||||
|
||||
// Verify kernel and initramfs exist
|
||||
if !root
|
||||
|
||||
Reference in New Issue
Block a user