Skip to content

Commit 1b89875

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 534fb40 commit 1b89875

2 files changed

Lines changed: 59 additions & 54 deletions

File tree

crates/lib/src/kernel.rs

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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

@@ -85,19 +89,21 @@ pub(crate) fn find_kernel(root: &Dir) -> Result<Option<KernelInternal>> {
8589
version,
8690
unified: false,
8791
},
88-
vmlinuz: Some(vmlinuz),
89-
initramfs: Some(initramfs),
92+
path: KernelPath::Vmlinuz {
93+
path: vmlinuz,
94+
initramfs,
95+
},
9096
}));
9197
}
9298

9399
Ok(None)
94100
}
95101

96-
/// Returns the filename of the first UKI found in the container root, if any.
102+
/// Returns the path to the first UKI found in the container root, if any.
97103
///
98104
/// Looks in `/boot/EFI/Linux/*.efi`. If multiple UKIs are present, returns
99105
/// the first one in sorted order for determinism.
100-
fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
106+
fn find_uki_path(root: &Dir) -> Result<Option<Utf8PathBuf>> {
101107
let Some(boot) = root.open_dir_optional(crate::install::BOOT)? else {
102108
return Ok(None);
103109
};
@@ -120,13 +126,15 @@ fn find_uki_filename(root: &Dir) -> Result<Option<String>> {
120126

121127
// Sort for deterministic behavior when multiple UKIs are present
122128
uki_files.sort();
123-
Ok(uki_files.into_iter().next())
129+
Ok(uki_files
130+
.into_iter()
131+
.next()
132+
.map(|filename| Utf8PathBuf::from(format!("boot/{EFI_LINUX}/{filename}"))))
124133
}
125134

126135
#[cfg(test)]
127136
mod tests {
128137
use super::*;
129-
use camino::Utf8Path;
130138
use cap_std_ext::{cap_std, cap_tempfile, dirext::CapStdExtDirExt};
131139

132140
#[test]
@@ -148,18 +156,19 @@ mod tests {
148156
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
149157
assert_eq!(kernel_internal.kernel.version, "6.12.0-100.fc41.x86_64");
150158
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-
);
159+
match &kernel_internal.path {
160+
KernelPath::Vmlinuz { path, initramfs } => {
161+
assert_eq!(
162+
path.as_str(),
163+
"usr/lib/modules/6.12.0-100.fc41.x86_64/vmlinuz"
164+
);
165+
assert_eq!(
166+
initramfs.as_str(),
167+
"usr/lib/modules/6.12.0-100.fc41.x86_64/initramfs.img"
168+
);
169+
}
170+
KernelPath::Uki(_) => panic!("Expected Vmlinuz, got Uki"),
171+
}
163172
Ok(())
164173
}
165174

@@ -172,8 +181,12 @@ mod tests {
172181
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
173182
assert_eq!(kernel_internal.kernel.version, "fedora-6.12.0");
174183
assert!(kernel_internal.kernel.unified);
175-
assert!(kernel_internal.vmlinuz.is_none());
176-
assert!(kernel_internal.initramfs.is_none());
184+
match &kernel_internal.path {
185+
KernelPath::Uki(path) => {
186+
assert_eq!(path.as_str(), "boot/EFI/Linux/fedora-6.12.0.efi");
187+
}
188+
KernelPath::Vmlinuz { .. } => panic!("Expected Uki, got Vmlinuz"),
189+
}
177190
Ok(())
178191
}
179192

@@ -197,16 +210,16 @@ mod tests {
197210
}
198211

199212
#[test]
200-
fn test_find_uki_filename_sorted() -> Result<()> {
213+
fn test_find_uki_path_sorted() -> Result<()> {
201214
let tempdir = cap_tempfile::tempdir(cap_std::ambient_authority())?;
202215
tempdir.create_dir_all("boot/EFI/Linux")?;
203216
tempdir.atomic_write("boot/EFI/Linux/zzz.efi", b"fake uki")?;
204217
tempdir.atomic_write("boot/EFI/Linux/aaa.efi", b"fake uki")?;
205218
tempdir.atomic_write("boot/EFI/Linux/mmm.efi", b"fake uki")?;
206219

207220
// Should return first in sorted order
208-
let filename = find_uki_filename(&tempdir)?.expect("should find uki");
209-
assert_eq!(filename, "aaa.efi");
221+
let path = find_uki_path(&tempdir)?.expect("should find uki");
222+
assert_eq!(path.as_str(), "boot/EFI/Linux/aaa.efi");
210223
Ok(())
211224
}
212225
}

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)