66
77use std:: path:: Path ;
88
9- use anyhow:: Result ;
9+ use anyhow:: { Context , Result } ;
1010use camino:: Utf8PathBuf ;
1111use cap_std_ext:: cap_std:: fs:: Dir ;
1212use 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.
3548pub ( 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
4553impl From < KernelInternal > for Kernel {
@@ -57,18 +65,14 @@ impl From<KernelInternal> for Kernel {
5765/// Returns `None` if no kernel is found.
5866pub ( 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) ]
127138mod 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}
0 commit comments