@@ -19,6 +19,7 @@ use rustix::fd::BorrowedFd;
19
19
use walkdir:: WalkDir ;
20
20
use widestring:: U16CString ;
21
21
22
+ use crate :: blockdev;
22
23
use crate :: filetree;
23
24
use crate :: model:: * ;
24
25
use crate :: ostreeutil;
@@ -57,28 +58,7 @@ pub(crate) struct Efi {
57
58
}
58
59
59
60
impl 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
-
61
+ // Get esp device via legacy
82
62
fn get_esp_device ( & self ) -> Option < PathBuf > {
83
63
let esp_devices = [ COREOS_ESP_PART_LABEL , ANACONDA_ESP_PART_LABEL ]
84
64
. into_iter ( )
@@ -93,11 +73,26 @@ impl Efi {
93
73
return esp_device;
94
74
}
95
75
96
- pub ( crate ) fn ensure_mounted_esp ( & self , root : & Path ) -> Result < PathBuf > {
97
- let mut mountpoint = self . mountpoint . borrow_mut ( ) ;
76
+ // Get esp device list on all devices
77
+ fn get_esp_devices ( & self ) -> Option < Vec < String > > {
78
+ let mut esp_devices = vec ! [ ] ;
79
+ if let Some ( esp_device) = self . get_esp_device ( ) {
80
+ esp_devices. push ( esp_device. to_string_lossy ( ) . into_owned ( ) ) ;
81
+ } else {
82
+ esp_devices = blockdev:: find_colocated_esps ( "/" ) . expect ( "get esp devices" ) ;
83
+ } ;
84
+ if !esp_devices. is_empty ( ) {
85
+ return Some ( esp_devices) ;
86
+ }
87
+ return None ;
88
+ }
89
+
90
+ fn check_mounted_esp < P : AsRef < Path > > ( & self , root : P ) -> Result < Option < PathBuf > > {
91
+ let mountpoint = self . mountpoint . borrow_mut ( ) ;
98
92
if let Some ( mountpoint) = mountpoint. as_deref ( ) {
99
- return Ok ( mountpoint. to_owned ( ) ) ;
93
+ return Ok ( Some ( mountpoint. to_owned ( ) ) ) ;
100
94
}
95
+ let root = root. as_ref ( ) ;
101
96
for & mnt in ESP_MOUNTS {
102
97
let mnt = root. join ( mnt) ;
103
98
if !mnt. exists ( ) {
@@ -109,13 +104,23 @@ impl Efi {
109
104
continue ;
110
105
}
111
106
util:: ensure_writable_mount ( & mnt) ?;
112
- log:: debug!( "Reusing existing {mnt:?}" ) ;
113
- return Ok ( mnt) ;
107
+ log:: debug!( "Reusing existing mount point {mnt:?}" ) ;
108
+ return Ok ( Some ( mnt) ) ;
109
+ }
110
+ Ok ( None )
111
+ }
112
+
113
+ pub ( crate ) fn ensure_mounted_esp < P : AsRef < Path > > (
114
+ & self ,
115
+ root : P ,
116
+ esp_device : & str ,
117
+ ) -> Result < PathBuf > {
118
+ let mut mountpoint = self . mountpoint . borrow_mut ( ) ;
119
+ if let Some ( mountpoint) = mountpoint. as_deref ( ) {
120
+ return Ok ( mountpoint. to_owned ( ) ) ;
114
121
}
115
122
116
- let esp_device = self
117
- . get_esp_device ( )
118
- . ok_or_else ( || anyhow:: anyhow!( "Failed to find ESP device" ) ) ?;
123
+ let root = root. as_ref ( ) ;
119
124
for & mnt in ESP_MOUNTS . iter ( ) {
120
125
let mnt = root. join ( mnt) ;
121
126
if !mnt. exists ( ) {
@@ -137,6 +142,7 @@ impl Efi {
137
142
if let Some ( mount) = self . mountpoint . borrow_mut ( ) . take ( ) {
138
143
Command :: new ( "umount" )
139
144
. arg ( & mount)
145
+ . arg ( "-l" )
140
146
. run ( )
141
147
. with_context ( || format ! ( "Failed to unmount {mount:?}" ) ) ?;
142
148
log:: trace!( "Unmounted" ) ;
@@ -243,8 +249,7 @@ impl Component for Efi {
243
249
}
244
250
245
251
fn query_adopt ( & self ) -> Result < Option < Adoptable > > {
246
- let esp = self . open_esp_optional ( ) ?;
247
- if esp. is_none ( ) {
252
+ if self . get_esp_devices ( ) . is_none ( ) {
248
253
log:: trace!( "No ESP detected" ) ;
249
254
return Ok ( None ) ;
250
255
} ;
@@ -267,16 +272,32 @@ impl Component for Efi {
267
272
anyhow:: bail!( "Failed to find adoptable system" )
268
273
} ;
269
274
270
- let esp = self . open_esp ( ) ?;
271
- validate_esp ( & esp) ?;
272
275
let updated = sysroot
273
276
. sub_dir ( & component_updatedirname ( self ) )
274
277
. context ( "opening update dir" ) ?;
275
278
let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
276
- // For adoption, we should only touch files that we know about.
277
- let diff = updatef. relative_diff_to ( & esp) ?;
278
- log:: trace!( "applying adoption diff: {}" , & diff) ;
279
- filetree:: apply_diff ( & updated, & esp, & diff, None ) . context ( "applying filesystem changes" ) ?;
279
+ let esp_devices = self
280
+ . get_esp_devices ( )
281
+ . expect ( "get esp devices before adopt" ) ;
282
+ let sysroot = sysroot. recover_path ( ) ?;
283
+
284
+ for esp_dev in esp_devices {
285
+ let dest_path = if let Some ( dest_path) = self . check_mounted_esp ( & sysroot) ? {
286
+ dest_path. join ( "EFI" )
287
+ } else {
288
+ self . ensure_mounted_esp ( & sysroot, & esp_dev) ?. join ( "EFI" )
289
+ } ;
290
+
291
+ let esp = openat:: Dir :: open ( & dest_path) . context ( "opening EFI dir" ) ?;
292
+ validate_esp ( & esp) ?;
293
+
294
+ // For adoption, we should only touch files that we know about.
295
+ let diff = updatef. relative_diff_to ( & esp) ?;
296
+ log:: trace!( "applying adoption diff: {}" , & diff) ;
297
+ filetree:: apply_diff ( & updated, & esp, & diff, None )
298
+ . context ( "applying filesystem changes" ) ?;
299
+ self . unmount ( ) . context ( "unmount after adopt" ) ?;
300
+ }
280
301
Ok ( InstalledContent {
281
302
meta : updatemeta. clone ( ) ,
282
303
filetree : Some ( updatef) ,
@@ -298,9 +319,17 @@ impl Component for Efi {
298
319
log:: debug!( "Found metadata {}" , meta. version) ;
299
320
let srcdir_name = component_updatedirname ( self ) ;
300
321
let ft = crate :: filetree:: FileTree :: new_from_dir ( & src_root. sub_dir ( & srcdir_name) ?) ?;
301
- let destdir = & self . ensure_mounted_esp ( Path :: new ( dest_root) ) ?;
322
+ let destdir = if let Some ( destdir) = self . check_mounted_esp ( dest_root) ? {
323
+ destdir
324
+ } else {
325
+ let esp_device = self
326
+ . get_esp_device ( )
327
+ . ok_or_else ( || anyhow:: anyhow!( "Failed to find ESP device" ) ) ?;
328
+ let esp_device = esp_device. to_str ( ) . unwrap ( ) ;
329
+ self . ensure_mounted_esp ( dest_root, esp_device) ?
330
+ } ;
302
331
303
- let destd = & openat:: Dir :: open ( destdir)
332
+ let destd = & openat:: Dir :: open ( & destdir)
304
333
. with_context ( || format ! ( "opening dest dir {}" , destdir. display( ) ) ) ?;
305
334
validate_esp ( destd) ?;
306
335
@@ -339,12 +368,25 @@ impl Component for Efi {
339
368
. context ( "opening update dir" ) ?;
340
369
let updatef = filetree:: FileTree :: new_from_dir ( & updated) . context ( "reading update dir" ) ?;
341
370
let diff = currentf. diff ( & updatef) ?;
342
- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
343
- let destdir = self . open_esp ( ) . context ( "opening EFI dir" ) ?;
344
- validate_esp ( & destdir) ?;
345
- log:: trace!( "applying diff: {}" , & diff) ;
346
- filetree:: apply_diff ( & updated, & destdir, & diff, None )
347
- . context ( "applying filesystem changes" ) ?;
371
+ let esp_devices = self
372
+ . get_esp_devices ( )
373
+ . context ( "get esp devices when running update" ) ?;
374
+ let sysroot = sysroot. recover_path ( ) ?;
375
+
376
+ for esp in esp_devices {
377
+ let dest_path = if let Some ( dest_path) = self . check_mounted_esp ( & sysroot) ? {
378
+ dest_path. join ( "EFI" )
379
+ } else {
380
+ self . ensure_mounted_esp ( & sysroot, & esp) ?. join ( "EFI" )
381
+ } ;
382
+
383
+ let destdir = openat:: Dir :: open ( & dest_path) . context ( "opening EFI dir" ) ?;
384
+ validate_esp ( & destdir) ?;
385
+ log:: trace!( "applying diff: {}" , & diff) ;
386
+ filetree:: apply_diff ( & updated, & destdir, & diff, None )
387
+ . context ( "applying filesystem changes" ) ?;
388
+ self . unmount ( ) . context ( "unmount after update" ) ?;
389
+ }
348
390
let adopted_from = None ;
349
391
Ok ( InstalledContent {
350
392
meta : updatemeta,
@@ -392,24 +434,37 @@ impl Component for Efi {
392
434
}
393
435
394
436
fn validate ( & self , current : & InstalledContent ) -> Result < ValidationResult > {
395
- if !is_efi_booted ( ) ? && self . get_esp_device ( ) . is_none ( ) {
437
+ let esp_devices = self . get_esp_devices ( ) ;
438
+ if !is_efi_booted ( ) ? && esp_devices. is_none ( ) {
396
439
return Ok ( ValidationResult :: Skip ) ;
397
440
}
398
441
let currentf = current
399
442
. filetree
400
443
. as_ref ( )
401
444
. ok_or_else ( || anyhow:: anyhow!( "No filetree for installed EFI found!" ) ) ?;
402
- self . ensure_mounted_esp ( Path :: new ( "/" ) ) ?;
403
- let efidir = self . open_esp ( ) ?;
404
- let diff = currentf. relative_diff_to ( & efidir) ?;
445
+
405
446
let mut errs = Vec :: new ( ) ;
406
- for f in diff. changes . iter ( ) {
407
- errs. push ( format ! ( "Changed: {}" , f) ) ;
408
- }
409
- for f in diff. removals . iter ( ) {
410
- errs. push ( format ! ( "Removed: {}" , f) ) ;
447
+ let esps = esp_devices. ok_or_else ( || anyhow:: anyhow!( "No esp device found!" ) ) ?;
448
+ let dest_root = Path :: new ( "/" ) ;
449
+ for esp_dev in esps. iter ( ) {
450
+ let dest_path = if let Some ( dest_path) = self . check_mounted_esp ( dest_root) ? {
451
+ dest_path. join ( "EFI" )
452
+ } else {
453
+ self . ensure_mounted_esp ( dest_root, & esp_dev) ?. join ( "EFI" )
454
+ } ;
455
+
456
+ let efidir = openat:: Dir :: open ( dest_path. as_path ( ) ) ?;
457
+ let diff = currentf. relative_diff_to ( & efidir) ?;
458
+
459
+ for f in diff. changes . iter ( ) {
460
+ errs. push ( format ! ( "Changed: {}" , f) ) ;
461
+ }
462
+ for f in diff. removals . iter ( ) {
463
+ errs. push ( format ! ( "Removed: {}" , f) ) ;
464
+ }
465
+ assert_eq ! ( diff. additions. len( ) , 0 ) ;
466
+ self . unmount ( ) . context ( "unmount after validate" ) ?;
411
467
}
412
- assert_eq ! ( diff. additions. len( ) , 0 ) ;
413
468
if !errs. is_empty ( ) {
414
469
Ok ( ValidationResult :: Errors ( errs) )
415
470
} else {
0 commit comments