@@ -13,7 +13,6 @@ use anyhow::{bail, Context, Result};
13
13
use fn_error_context:: context;
14
14
use openat_ext:: OpenatDirExt ;
15
15
use os_release:: OsRelease ;
16
- use rustix:: fd:: BorrowedFd ;
17
16
use walkdir:: WalkDir ;
18
17
use widestring:: U16CString ;
19
18
@@ -26,6 +25,9 @@ use crate::{component::*, packagesystem};
26
25
/// Well-known paths to the ESP that may have been mounted external to us.
27
26
pub ( crate ) const ESP_MOUNTS : & [ & str ] = & [ "boot/efi" , "efi" , "boot" ] ;
28
27
28
+ /// Backup EFI dir
29
+ const TEMP_EFI_DIR : & str = ".EFI.tmp" ;
30
+
29
31
/// The binary to change EFI boot ordering
30
32
const EFIBOOTMGR : & str = "efibootmgr" ;
31
33
#[ cfg( target_arch = "aarch64" ) ]
@@ -83,9 +85,9 @@ impl Efi {
83
85
if !mnt. exists ( ) {
84
86
continue ;
85
87
}
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 {
89
91
continue ;
90
92
}
91
93
log:: debug!( "Reusing existing {mnt:?}" ) ;
@@ -349,12 +351,39 @@ impl Component for Efi {
349
351
. context ( "opening update dir" ) ?;
350
352
let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
351
353
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) ?;
355
370
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
+ }
358
387
let adopted_from = None ;
359
388
Ok ( InstalledContent {
360
389
meta : updatemeta,
@@ -455,13 +484,10 @@ impl Drop for Efi {
455
484
}
456
485
457
486
fn 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) ;
465
491
} ;
466
492
Ok ( ( ) )
467
493
}
@@ -584,6 +610,18 @@ fn find_file_recursive<P: AsRef<Path>>(dir: P, target_file: &str) -> Result<Vec<
584
610
Ok ( result)
585
611
}
586
612
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
+
587
625
#[ cfg( test) ]
588
626
mod tests {
589
627
use super :: * ;
0 commit comments