Skip to content

Commit 882417d

Browse files
committed
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>
1 parent 1017a80 commit 882417d

2 files changed

Lines changed: 64 additions & 57 deletions

File tree

crates/lib/src/kernel.rs

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
use std::path::Path;
88

9-
use anyhow::Result;
9+
use anyhow::{Context, Result};
1010
use camino::Utf8PathBuf;
1111
use cap_std_ext::cap_std::fs::Dir;
1212
use cap_std_ext::dirext::CapStdExtDirExt;
@@ -26,20 +26,28 @@ pub(crate) struct Kernel {
2626
pub(crate) unified: bool,
2727
}
2828

29-
/// Internal-only kernel wrapper with extra information (paths to
30-
/// vmlinuz, initramfs) that are useful but we don't want to leak out
31-
/// via serialization to inspection.
29+
/// Path to kernel component(s)
30+
///
31+
/// UKI kernels only have the single PE binary, whereas
32+
/// traditional "vmlinuz" kernels have distinct kernel and
33+
/// initramfs.
34+
pub(crate) enum KernelPath {
35+
Uki(Utf8PathBuf),
36+
Vmlinuz {
37+
path: Utf8PathBuf,
38+
initramfs: Utf8PathBuf,
39+
},
40+
}
41+
42+
/// Internal-only kernel wrapper with extra path information that are
43+
/// useful but we don't want to leak out via serialization to
44+
/// inspection.
3245
///
3346
/// `Kernel` implements `From<KernelInternal>` so we can just `.into()`
3447
/// to get the "public" form where needed.
3548
pub(crate) struct KernelInternal {
3649
pub(crate) kernel: Kernel,
37-
/// Path to vmlinuz for traditional kernels.
38-
/// This is `None` for UKI images.
39-
pub(crate) vmlinuz: Option<Utf8PathBuf>,
40-
/// Path to initramfs.img for traditional kernels.
41-
/// This is `None` for UKI images.
42-
pub(crate) initramfs: Option<Utf8PathBuf>,
50+
pub(crate) path: KernelPath,
4351
}
4452

4553
impl From<KernelInternal> for Kernel {
@@ -57,18 +65,14 @@ impl From<KernelInternal> for Kernel {
5765
/// Returns `None` if no kernel is found.
5866
pub(crate) fn find_kernel(root: &Dir) -> Result<Option<KernelInternal>> {
5967
// First, try to find a UKI
60-
if let Some(uki_filename) = find_uki_filename(root)? {
61-
let version = uki_filename
62-
.strip_suffix(".efi")
63-
.unwrap_or(&uki_filename)
64-
.to_owned();
68+
if let Some(uki_path) = find_uki_path(root)? {
69+
let version = uki_path.file_stem().unwrap_or(uki_path.as_str()).to_owned();
6570
return Ok(Some(KernelInternal {
6671
kernel: Kernel {
6772
version,
6873
unified: true,
6974
},
70-
vmlinuz: None,
71-
initramfs: None,
75+
path: KernelPath::Uki(uki_path),
7276
}));
7377
}
7478

@@ -78,26 +82,30 @@ pub(crate) fn find_kernel(root: &Dir) -> Result<Option<KernelInternal>> {
7882
.file_name()
7983
.ok_or_else(|| anyhow::anyhow!("kernel dir should have a file name: {modules_dir}"))?
8084
.to_owned();
81-
let vmlinuz = modules_dir.join("vmlinuz");
82-
let initramfs = modules_dir.join("initramfs.img");
85+
let vmlinuz = Utf8PathBuf::try_from(modules_dir.join("vmlinuz"))
86+
.context("kernel path is not valid UTF-8")?;
87+
let initramfs = Utf8PathBuf::try_from(modules_dir.join("initramfs.img"))
88+
.context("initramfs path is not valid UTF-8")?;
8389
return Ok(Some(KernelInternal {
8490
kernel: Kernel {
8591
version,
8692
unified: false,
8793
},
88-
vmlinuz: Some(vmlinuz),
89-
initramfs: Some(initramfs),
94+
path: KernelPath::Vmlinuz {
95+
path: vmlinuz,
96+
initramfs,
97+
},
9098
}));
9199
}
92100

93101
Ok(None)
94102
}
95103

96-
/// Returns the filename of the first UKI found in the container root, if any.
104+
/// Returns the path to the first UKI found in the container root, if any.
97105
///
98106
/// Looks in `/boot/EFI/Linux/*.efi`. If multiple UKIs are present, returns
99107
/// the first one in sorted order for determinism.
100-
fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
108+
fn find_uki_path(root: &Dir) -> Result<Option<Utf8PathBuf>> {
101109
let Some(boot) = root.open_dir_optional(crate::install::BOOT)? else {
102110
return Ok(None);
103111
};
@@ -120,13 +128,15 @@ fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
120128

121129
// Sort for deterministic behavior when multiple UKIs are present
122130
uki_files.sort();
123-
Ok(uki_files.into_iter().next())
131+
Ok(uki_files
132+
.into_iter()
133+
.next()
134+
.map(|filename| Utf8PathBuf::from(format!("boot/{EFI_LINUX}/{filename}"))))
124135
}
125136

126137
#[cfg(test)]
127138
mod tests {
128139
use super::*;
129-
use camino::Utf8Path;
130140
use cap_std_ext::{cap_std, cap_tempfile, dirext::CapStdExtDirExt};
131141

132142
#[test]
@@ -148,18 +158,19 @@ mod tests {
148158
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
149159
assert_eq!(kernel_internal.kernel.version, "6.12.0-100.fc41.x86_64");
150160
assert!(!kernel_internal.kernel.unified);
151-
assert_eq!(
152-
kernel_internal.vmlinuz.as_deref(),
153-
Some(Utf8Path::new(
154-
"usr/lib/modules/6.12.0-100.fc41.x86_64/vmlinuz"
155-
))
156-
);
157-
assert_eq!(
158-
kernel_internal.initramfs.as_deref(),
159-
Some(Utf8Path::new(
160-
"usr/lib/modules/6.12.0-100.fc41.x86_64/initramfs.img"
161-
))
162-
);
161+
match &kernel_internal.path {
162+
KernelPath::Vmlinuz { path, initramfs } => {
163+
assert_eq!(
164+
path.as_str(),
165+
"usr/lib/modules/6.12.0-100.fc41.x86_64/vmlinuz"
166+
);
167+
assert_eq!(
168+
initramfs.as_str(),
169+
"usr/lib/modules/6.12.0-100.fc41.x86_64/initramfs.img"
170+
);
171+
}
172+
KernelPath::Uki(_) => panic!("Expected Vmlinuz, got Uki"),
173+
}
163174
Ok(())
164175
}
165176

@@ -172,8 +183,12 @@ mod tests {
172183
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
173184
assert_eq!(kernel_internal.kernel.version, "fedora-6.12.0");
174185
assert!(kernel_internal.kernel.unified);
175-
assert!(kernel_internal.vmlinuz.is_none());
176-
assert!(kernel_internal.initramfs.is_none());
186+
match &kernel_internal.path {
187+
KernelPath::Uki(path) => {
188+
assert_eq!(path.as_str(), "boot/EFI/Linux/fedora-6.12.0.efi");
189+
}
190+
KernelPath::Vmlinuz { .. } => panic!("Expected Uki, got Vmlinuz"),
191+
}
177192
Ok(())
178193
}
179194

@@ -197,16 +212,16 @@ mod tests {
197212
}
198213

199214
#[test]
200-
fn test_find_uki_filename_sorted() -> Result<()> {
215+
fn test_find_uki_path_sorted() -> Result<()> {
201216
let tempdir = cap_tempfile::tempdir(cap_std::ambient_authority())?;
202217
tempdir.create_dir_all("boot/EFI/Linux")?;
203218
tempdir.atomic_write("boot/EFI/Linux/zzz.efi", b"fake uki")?;
204219
tempdir.atomic_write("boot/EFI/Linux/aaa.efi", b"fake uki")?;
205220
tempdir.atomic_write("boot/EFI/Linux/mmm.efi", b"fake uki")?;
206221

207222
// Should return first in sorted order
208-
let filename = find_uki_filename(&tempdir)?.expect("should find uki");
209-
assert_eq!(filename, "aaa.efi");
223+
let path = find_uki_path(&tempdir)?.expect("should find uki");
224+
assert_eq!(path.as_str(), "boot/EFI/Linux/aaa.efi");
210225
Ok(())
211226
}
212227
}

crates/lib/src/ukify.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,13 @@ pub(crate) fn build_ukify(
5454
let kernel = crate::kernel::find_kernel(&root)?
5555
.ok_or_else(|| anyhow::anyhow!("No kernel found in {rootfs}"))?;
5656

57-
// We can only build a UKI from a traditional kernel, not from an existing UKI
58-
if kernel.kernel.unified {
59-
anyhow::bail!(
60-
"Cannot build UKI: rootfs already contains a UKI at boot/EFI/Linux/{}.efi",
61-
kernel.kernel.version
62-
);
63-
}
64-
65-
// Get paths from the kernel info
66-
let vmlinuz_path = kernel
67-
.vmlinuz
68-
.ok_or_else(|| anyhow::anyhow!("Traditional kernel should have vmlinuz path"))?;
69-
let initramfs_path = kernel
70-
.initramfs
71-
.ok_or_else(|| anyhow::anyhow!("Traditional kernel should have initramfs path"))?;
57+
// Extract vmlinuz and initramfs paths, or bail if this is already a UKI
58+
let (vmlinuz_path, initramfs_path) = match kernel.path {
59+
crate::kernel::KernelPath::Vmlinuz { path, initramfs } => (path, initramfs),
60+
crate::kernel::KernelPath::Uki(path) => {
61+
anyhow::bail!("Cannot build UKI: rootfs already contains a UKI at {path}");
62+
}
63+
};
7264

7365
// Verify kernel and initramfs exist
7466
if !root

0 commit comments

Comments
 (0)