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