@@ -19,6 +19,7 @@ use rustix::fd::BorrowedFd;
1919use walkdir:: WalkDir ;
2020use widestring:: U16CString ;
2121
22+ use crate :: blockdev;
2223use crate :: filetree;
2324use crate :: model:: * ;
2425use crate :: ostreeutil;
@@ -54,31 +55,10 @@ pub(crate) fn is_efi_booted() -> Result<bool> {
5455#[ derive( Default ) ]
5556pub ( crate ) struct Efi {
5657 mountpoint : RefCell < Option < PathBuf > > ,
58+ esps : RefCell < Option < Vec < String > > > ,
5759}
5860
5961impl Efi {
60- fn esp_path ( & self ) -> Result < PathBuf > {
61- self . ensure_mounted_esp ( Path :: new ( "/" ) )
62- . map ( |v| v. join ( "EFI" ) )
63- }
64-
65- fn open_esp_optional ( & self ) -> Result < Option < openat:: Dir > > {
66- if !is_efi_booted ( ) ? && self . get_esp_device ( ) . is_none ( ) {
67- log:: debug!( "Skip EFI" ) ;
68- return Ok ( None ) ;
69- }
70- let sysroot = openat:: Dir :: open ( "/" ) ?;
71- let esp = sysroot. sub_dir_optional ( & self . esp_path ( ) ?) ?;
72- Ok ( esp)
73- }
74-
75- fn open_esp ( & self ) -> Result < openat:: Dir > {
76- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
77- let sysroot = openat:: Dir :: open ( "/" ) ?;
78- let esp = sysroot. sub_dir ( & self . esp_path ( ) ?) ?;
79- Ok ( esp)
80- }
81-
8262 fn get_esp_device ( & self ) -> Option < PathBuf > {
8363 let esp_devices = [ COREOS_ESP_PART_LABEL , ANACONDA_ESP_PART_LABEL ]
8464 . into_iter ( )
@@ -93,11 +73,32 @@ impl Efi {
9373 return esp_device;
9474 }
9575
96- pub ( crate ) fn ensure_mounted_esp ( & self , root : & Path ) -> Result < PathBuf > {
97- let mut mountpoint = self . mountpoint . borrow_mut ( ) ;
76+ fn get_all_esp_devices ( & self ) -> Option < Vec < String > > {
77+ let mut esps = self . esps . borrow_mut ( ) ;
78+ if let Some ( esp_devs) = esps. as_deref ( ) {
79+ log:: debug!( "Reusing existing esps {esp_devs:?}" ) ;
80+ return Some ( esp_devs. to_owned ( ) ) ;
81+ }
82+
83+ let mut esp_devices = vec ! [ ] ;
84+ if let Some ( esp_device) = self . get_esp_device ( ) {
85+ esp_devices. push ( esp_device. to_string_lossy ( ) . into_owned ( ) ) ;
86+ } else {
87+ esp_devices = blockdev:: find_colocated_esps ( "/" ) . expect ( "get esp devices" ) ;
88+ } ;
89+ if !esp_devices. is_empty ( ) {
90+ * esps = Some ( esp_devices. clone ( ) ) ;
91+ return Some ( esp_devices) ;
92+ }
93+ return None ;
94+ }
95+
96+ fn check_existing_esp < P : AsRef < Path > > ( & self , root : P ) -> Result < Option < PathBuf > > {
97+ let mountpoint = self . mountpoint . borrow_mut ( ) ;
9898 if let Some ( mountpoint) = mountpoint. as_deref ( ) {
99- return Ok ( mountpoint. to_owned ( ) ) ;
99+ return Ok ( Some ( mountpoint. to_owned ( ) ) ) ;
100100 }
101+ let root = root. as_ref ( ) ;
101102 for & mnt in ESP_MOUNTS {
102103 let mnt = root. join ( mnt) ;
103104 if !mnt. exists ( ) {
@@ -109,13 +110,23 @@ impl Efi {
109110 continue ;
110111 }
111112 util:: ensure_writable_mount ( & mnt) ?;
112- log:: debug!( "Reusing existing {mnt:?}" ) ;
113- return Ok ( mnt) ;
113+ log:: debug!( "Reusing existing mount point {mnt:?}" ) ;
114+ return Ok ( Some ( mnt) ) ;
114115 }
116+ Ok ( None )
117+ }
115118
116- let esp_device = self
117- . get_esp_device ( )
118- . ok_or_else ( || anyhow:: anyhow!( "Failed to find ESP device" ) ) ?;
119+ pub ( crate ) fn ensure_mounted_esp < P : AsRef < Path > > (
120+ & self ,
121+ root : P ,
122+ esp_device : & str ,
123+ ) -> Result < PathBuf > {
124+ let mut mountpoint = self . mountpoint . borrow_mut ( ) ;
125+ if let Some ( mountpoint) = mountpoint. as_deref ( ) {
126+ return Ok ( mountpoint. to_owned ( ) ) ;
127+ }
128+
129+ let root = root. as_ref ( ) ;
119130 for & mnt in ESP_MOUNTS . iter ( ) {
120131 let mnt = root. join ( mnt) ;
121132 if !mnt. exists ( ) {
@@ -134,10 +145,9 @@ impl Efi {
134145 }
135146 Ok ( mountpoint. as_deref ( ) . unwrap ( ) . to_owned ( ) )
136147 }
137-
138148 fn unmount ( & self ) -> Result < ( ) > {
139149 if let Some ( mount) = self . mountpoint . borrow_mut ( ) . take ( ) {
140- let status = Command :: new ( "umount" ) . arg ( & mount) . status ( ) ?;
150+ let status = Command :: new ( "umount" ) . arg ( "-l" ) . arg ( & mount) . status ( ) ?;
141151 if !status. success ( ) {
142152 anyhow:: bail!( "Failed to unmount {mount:?}: {status:?}" ) ;
143153 }
@@ -245,8 +255,7 @@ impl Component for Efi {
245255 }
246256
247257 fn query_adopt ( & self ) -> Result < Option < Adoptable > > {
248- let esp = self . open_esp_optional ( ) ?;
249- if esp. is_none ( ) {
258+ if self . get_all_esp_devices ( ) . is_none ( ) {
250259 log:: trace!( "No ESP detected" ) ;
251260 return Ok ( None ) ;
252261 } ;
@@ -269,16 +278,32 @@ impl Component for Efi {
269278 anyhow:: bail!( "Failed to find adoptable system" )
270279 } ;
271280
272- let esp = self . open_esp ( ) ?;
273- validate_esp ( & esp) ?;
274281 let updated = sysroot
275282 . sub_dir ( & component_updatedirname ( self ) )
276283 . context ( "opening update dir" ) ?;
277284 let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
278- // For adoption, we should only touch files that we know about.
279- let diff = updatef. relative_diff_to ( & esp) ?;
280- log:: trace!( "applying adoption diff: {}" , & diff) ;
281- filetree:: apply_diff ( & updated, & esp, & diff, None ) . context ( "applying filesystem changes" ) ?;
285+ let esp_devices = self
286+ . get_all_esp_devices ( )
287+ . expect ( "get esp devices before adopt" ) ;
288+ let sysroot = sysroot. recover_path ( ) ?;
289+
290+ for esp_dev in esp_devices {
291+ let dest_path = if let Some ( dest_path) = self . check_existing_esp ( & sysroot) ? {
292+ dest_path. join ( "EFI" )
293+ } else {
294+ self . ensure_mounted_esp ( & sysroot, & esp_dev) ?. join ( "EFI" )
295+ } ;
296+
297+ let esp = openat:: Dir :: open ( & dest_path) . context ( "opening EFI dir" ) ?;
298+ validate_esp ( & esp) ?;
299+
300+ // For adoption, we should only touch files that we know about.
301+ let diff = updatef. relative_diff_to ( & esp) ?;
302+ log:: trace!( "applying adoption diff: {}" , & diff) ;
303+ filetree:: apply_diff ( & updated, & esp, & diff, None )
304+ . context ( "applying filesystem changes" ) ?;
305+ self . unmount ( ) . context ( "unmount after adopt" ) ?;
306+ }
282307 Ok ( InstalledContent {
283308 meta : updatemeta. clone ( ) ,
284309 filetree : Some ( updatef) ,
@@ -300,9 +325,18 @@ impl Component for Efi {
300325 log:: debug!( "Found metadata {}" , meta. version) ;
301326 let srcdir_name = component_updatedirname ( self ) ;
302327 let ft = crate :: filetree:: FileTree :: new_from_dir ( & src_root. sub_dir ( & srcdir_name) ?) ?;
303- let destdir = & self . ensure_mounted_esp ( Path :: new ( dest_root) ) ?;
304328
305- let destd = & openat:: Dir :: open ( destdir)
329+ let destdir = if let Some ( destdir) = self . check_existing_esp ( dest_root) ? {
330+ destdir
331+ } else {
332+ let esp_device = self
333+ . get_esp_device ( )
334+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find ESP device" ) ) ?;
335+ let esp_device = esp_device. to_str ( ) . unwrap ( ) ;
336+ self . ensure_mounted_esp ( dest_root, esp_device) ?
337+ } ;
338+
339+ let destd = & openat:: Dir :: open ( & destdir)
306340 . with_context ( || format ! ( "opening dest dir {}" , destdir. display( ) ) ) ?;
307341 validate_esp ( destd) ?;
308342
@@ -344,12 +378,25 @@ impl Component for Efi {
344378 . context ( "opening update dir" ) ?;
345379 let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
346380 let diff = currentf. diff ( & updatef) ?;
347- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
348- let destdir = self . open_esp ( ) . context ( "opening EFI dir" ) ?;
349- validate_esp ( & destdir) ?;
350- log:: trace!( "applying diff: {}" , & diff) ;
351- filetree:: apply_diff ( & updated, & destdir, & diff, None )
352- . context ( "applying filesystem changes" ) ?;
381+ let esp_devices = self
382+ . get_all_esp_devices ( )
383+ . context ( "get esp devices when running update" ) ?;
384+ let sysroot = sysroot. recover_path ( ) ?;
385+
386+ for esp in esp_devices {
387+ let dest_path = if let Some ( dest_path) = self . check_existing_esp ( & sysroot) ? {
388+ dest_path. join ( "EFI" )
389+ } else {
390+ self . ensure_mounted_esp ( & sysroot, & esp) ?. join ( "EFI" )
391+ } ;
392+
393+ let destdir = openat:: Dir :: open ( & dest_path) . context ( "opening EFI dir" ) ?;
394+ validate_esp ( & destdir) ?;
395+ log:: trace!( "applying diff: {}" , & diff) ;
396+ filetree:: apply_diff ( & updated, & destdir, & diff, None )
397+ . context ( "applying filesystem changes" ) ?;
398+ self . unmount ( ) . context ( "unmount after update" ) ?;
399+ }
353400 let adopted_from = None ;
354401 Ok ( InstalledContent {
355402 meta : updatemeta,
@@ -397,24 +444,36 @@ impl Component for Efi {
397444 }
398445
399446 fn validate ( & self , current : & InstalledContent ) -> Result < ValidationResult > {
400- if !is_efi_booted ( ) ? && self . get_esp_device ( ) . is_none ( ) {
447+ let esp_devices = self . get_all_esp_devices ( ) ;
448+ if !is_efi_booted ( ) ? && esp_devices. is_none ( ) {
401449 return Ok ( ValidationResult :: Skip ) ;
402450 }
403451 let currentf = current
404452 . filetree
405453 . as_ref ( )
406454 . ok_or_else ( || anyhow:: anyhow!( "No filetree for installed EFI found!" ) ) ?;
407- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
408- let efidir = self . open_esp ( ) ?;
409- let diff = currentf. relative_diff_to ( & efidir) ?;
410455 let mut errs = Vec :: new ( ) ;
411- for f in diff. changes . iter ( ) {
412- errs. push ( format ! ( "Changed: {}" , f) ) ;
413- }
414- for f in diff. removals . iter ( ) {
415- errs. push ( format ! ( "Removed: {}" , f) ) ;
456+ let esps = esp_devices. ok_or_else ( || anyhow:: anyhow!( "No esp device found!" ) ) ?;
457+ let dest_root = Path :: new ( "/" ) ;
458+ for esp_dev in esps. iter ( ) {
459+ let dest_path = if let Some ( dest_path) = self . check_existing_esp ( dest_root) ? {
460+ dest_path. join ( "EFI" )
461+ } else {
462+ self . ensure_mounted_esp ( dest_root, & esp_dev) ?. join ( "EFI" )
463+ } ;
464+
465+ let efidir = openat:: Dir :: open ( dest_path. as_path ( ) ) ?;
466+ let diff = currentf. relative_diff_to ( & efidir) ?;
467+
468+ for f in diff. changes . iter ( ) {
469+ errs. push ( format ! ( "Changed: {}" , f) ) ;
470+ }
471+ for f in diff. removals . iter ( ) {
472+ errs. push ( format ! ( "Removed: {}" , f) ) ;
473+ }
474+ assert_eq ! ( diff. additions. len( ) , 0 ) ;
475+ self . unmount ( ) . context ( "unmount after validate" ) ?;
416476 }
417- assert_eq ! ( diff. additions. len( ) , 0 ) ;
418477 if !errs. is_empty ( ) {
419478 Ok ( ValidationResult :: Errors ( errs) )
420479 } else {
0 commit comments