@@ -13,7 +13,6 @@ use anyhow::{bail, Context, Result};
1313use fn_error_context:: context;
1414use openat_ext:: OpenatDirExt ;
1515use os_release:: OsRelease ;
16- use rustix:: fd:: BorrowedFd ;
1716use walkdir:: WalkDir ;
1817use widestring:: U16CString ;
1918
@@ -26,6 +25,9 @@ use crate::{component::*, packagesystem};
2625/// Well-known paths to the ESP that may have been mounted external to us.
2726pub ( crate ) const ESP_MOUNTS : & [ & str ] = & [ "boot/efi" , "efi" , "boot" ] ;
2827
28+ /// Backup EFI dir
29+ const TEMP_EFI_DIR : & str = ".EFI.tmp" ;
30+
2931/// The binary to change EFI boot ordering
3032const EFIBOOTMGR : & str = "efibootmgr" ;
3133#[ cfg( target_arch = "aarch64" ) ]
@@ -83,9 +85,9 @@ impl Efi {
8385 if !mnt. exists ( ) {
8486 continue ;
8587 }
86- let st =
87- rustix :: fs :: statfs ( & mnt ) . with_context ( || format ! ( "statfs failed for {mnt:?}" ) ) ?;
88- if st. f_type != libc :: MSDOS_SUPER_MAGIC {
88+ let st = nix :: sys :: statfs :: statfs ( & mnt )
89+ . with_context ( || format ! ( "statfs failed for {mnt:?}" ) ) ?;
90+ if st. filesystem_type ( ) != nix :: sys :: statfs :: MSDOS_SUPER_MAGIC {
8991 continue ;
9092 }
9193 log:: debug!( "Reusing existing {mnt:?}" ) ;
@@ -349,12 +351,39 @@ impl Component for Efi {
349351 . context ( "opening update dir" ) ?;
350352 let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
351353 let diff = currentf. diff ( & updatef) ?;
352- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
353- let destdir = self . open_esp ( ) . context ( "opening EFI dir" ) ?;
354- validate_esp ( & destdir) ?;
354+ let mountdir = self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
355+
356+ /* copy "lowest" directory that we need to make the change
357+ * e.g. we only affect EFI/fedora for example and not all of EFI
358+ */
359+
360+ let efipath = self . esp_path ( ) ?;
361+ let tmp_efipath = mountdir. join ( TEMP_EFI_DIR ) ;
362+ // remove a previous directory if it exists in order to handle being interrupted in the middle.
363+ if tmp_efipath. exists ( ) {
364+ std:: fs:: remove_dir_all ( & tmp_efipath) ?;
365+ }
366+ copy_dir ( & efipath, & tmp_efipath) . with_context ( || "copying existing files to temp dir" ) ?;
367+
368+ let tmpdir = sysroot. sub_dir ( & tmp_efipath) ?;
369+ validate_esp ( & tmpdir) ?;
355370 log:: trace!( "applying diff: {}" , & diff) ;
356- filetree:: apply_diff ( & updated, & destdir, & diff, None )
357- . context ( "applying filesystem changes" ) ?;
371+ filetree:: apply_diff ( & updated, & tmpdir, & diff, None )
372+ . context ( "applying filesystem changes to temp EFI" ) ?;
373+ {
374+ let esp = sysroot. sub_dir ( & mountdir) ?;
375+ log:: trace!(
376+ "doing local exchange for {} and {}" ,
377+ tmp_efipath. display( ) ,
378+ efipath. display( )
379+ ) ;
380+
381+ esp. local_exchange ( & tmp_efipath, & efipath)
382+ . with_context ( || format ! ( "exchange for {:?} and {:?}" , tmp_efipath, efipath) ) ?;
383+ // finally remove the temp dir
384+ log:: trace!( "cleanup: {}" , tmp_efipath. display( ) ) ;
385+ std:: fs:: remove_dir_all ( & tmp_efipath) . context ( "clean up temp" ) ?;
386+ }
358387 let adopted_from = None ;
359388 Ok ( InstalledContent {
360389 meta : updatemeta,
@@ -455,13 +484,10 @@ impl Drop for Efi {
455484}
456485
457486fn validate_esp ( dir : & openat:: Dir ) -> Result < ( ) > {
458- let dir = unsafe { BorrowedFd :: borrow_raw ( dir. as_raw_fd ( ) ) } ;
459- let stat = rustix:: fs:: fstatfs ( & dir) ?;
460- if stat. f_type != libc:: MSDOS_SUPER_MAGIC {
461- bail ! (
462- "EFI mount is not a msdos filesystem, but is {:?}" ,
463- stat. f_type
464- ) ;
487+ let stat = nix:: sys:: statfs:: fstatfs ( dir) ?;
488+ let fstype = stat. filesystem_type ( ) ;
489+ if fstype != nix:: sys:: statfs:: MSDOS_SUPER_MAGIC {
490+ bail ! ( "EFI mount is not a msdos filesystem, but is {:?}" , fstype) ;
465491 } ;
466492 Ok ( ( ) )
467493}
@@ -584,6 +610,18 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
584610 Ok ( result)
585611}
586612
613+ fn copy_dir ( src : & Path , dst : & Path ) -> Result < ( ) > {
614+ let r = std:: process:: Command :: new ( "cp" )
615+ . args ( [ "-a" ] )
616+ . arg ( src)
617+ . arg ( dst)
618+ . status ( ) ?;
619+ if !r. success ( ) {
620+ anyhow:: bail!( "Failed to copy" ) ;
621+ }
622+ Ok ( ( ) )
623+ }
624+
587625#[ cfg( test) ]
588626mod tests {
589627 use super :: * ;
0 commit comments