@@ -8,7 +8,10 @@ use bootc_blockdev::find_parent_devices;
8
8
use bootc_mount:: inspect_filesystem;
9
9
use bootc_utils:: CommandRunExt ;
10
10
use camino:: { Utf8Path , Utf8PathBuf } ;
11
- use cap_std_ext:: { cap_std, dirext:: CapStdExtDirExt } ;
11
+ use cap_std_ext:: {
12
+ cap_std:: { ambient_authority, fs:: Dir } ,
13
+ dirext:: CapStdExtDirExt ,
14
+ } ;
12
15
use clap:: ValueEnum ;
13
16
use composefs:: fs:: read_file;
14
17
use composefs:: tree:: { FileSystem , RegularFile } ;
@@ -174,8 +177,7 @@ fn compute_boot_digest(
174
177
/// Returns the verity of the deployment that has a boot digest same as the one passed in
175
178
#[ context( "Checking boot entry duplicates" ) ]
176
179
fn find_vmlinuz_initrd_duplicates ( digest : & str ) -> Result < Option < String > > {
177
- let deployments =
178
- cap_std:: fs:: Dir :: open_ambient_dir ( STATE_DIR_ABS , cap_std:: ambient_authority ( ) ) ;
180
+ let deployments = Dir :: open_ambient_dir ( STATE_DIR_ABS , ambient_authority ( ) ) ;
179
181
180
182
let deployments = match deployments {
181
183
Ok ( d) => d,
@@ -231,7 +233,7 @@ fn write_bls_boot_entries_to_disk(
231
233
let path = boot_dir. join ( & id_hex) ;
232
234
create_dir_all ( & path) ?;
233
235
234
- let entries_dir = cap_std :: fs :: Dir :: open_ambient_dir ( & path, cap_std :: ambient_authority ( ) )
236
+ let entries_dir = Dir :: open_ambient_dir ( & path, ambient_authority ( ) )
235
237
. with_context ( || format ! ( "Opening {path}" ) ) ?;
236
238
237
239
entries_dir
@@ -486,9 +488,8 @@ pub(crate) fn setup_composefs_bls_boot(
486
488
487
489
// Scope to allow for proper unmounting
488
490
{
489
- let loader_entries_dir =
490
- cap_std:: fs:: Dir :: open_ambient_dir ( & config_path, cap_std:: ambient_authority ( ) )
491
- . with_context ( || format ! ( "Opening {config_path:?}" ) ) ?;
491
+ let loader_entries_dir = Dir :: open_ambient_dir ( & config_path, ambient_authority ( ) )
492
+ . with_context ( || format ! ( "Opening {config_path:?}" ) ) ?;
492
493
493
494
loader_entries_dir. atomic_write (
494
495
// SAFETY: We set sort_key above
@@ -600,7 +601,7 @@ fn write_pe_to_esp(
600
601
None => efi_linux_path,
601
602
} ;
602
603
603
- let pe_dir = cap_std :: fs :: Dir :: open_ambient_dir ( & final_pe_path, cap_std :: ambient_authority ( ) )
604
+ let pe_dir = Dir :: open_ambient_dir ( & final_pe_path, ambient_authority ( ) )
604
605
. with_context ( || format ! ( "Opening {final_pe_path:?}" ) ) ?;
605
606
606
607
let pe_name = match pe_type {
@@ -622,6 +623,94 @@ fn write_pe_to_esp(
622
623
Ok ( boot_label)
623
624
}
624
625
626
+ #[ context( "Writing Grub menuentry" ) ]
627
+ fn write_grub_uki_menuentry (
628
+ root_path : Utf8PathBuf ,
629
+ setup_type : & BootSetupType ,
630
+ boot_label : & String ,
631
+ id : & Sha256HashValue ,
632
+ esp_device : & String ,
633
+ ) -> Result < ( ) > {
634
+ let boot_dir = root_path. join ( "boot" ) ;
635
+ create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
636
+
637
+ let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
638
+
639
+ let efi_uuid_source = get_efi_uuid_source ( ) ;
640
+
641
+ let user_cfg_name = if is_upgrade {
642
+ USER_CFG_STAGED
643
+ } else {
644
+ USER_CFG
645
+ } ;
646
+
647
+ let grub_dir = Dir :: open_ambient_dir ( boot_dir. join ( "grub2" ) , ambient_authority ( ) )
648
+ . context ( "opening boot/grub2" ) ?;
649
+
650
+ // Iterate over all available deployments, and generate a menuentry for each
651
+ //
652
+ // TODO: We might find a staged deployment here
653
+ if is_upgrade {
654
+ let mut buffer = vec ! [ ] ;
655
+
656
+ // Shouldn't really fail so no context here
657
+ buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
658
+ buffer. write_all (
659
+ MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
660
+ . to_string ( )
661
+ . as_bytes ( ) ,
662
+ ) ?;
663
+
664
+ let mut str_buf = String :: new ( ) ;
665
+ let boot_dir =
666
+ Dir :: open_ambient_dir ( boot_dir, ambient_authority ( ) ) . context ( "Opening boot dir" ) ?;
667
+ let entries = get_sorted_uki_boot_entries ( & boot_dir, & mut str_buf) ?;
668
+
669
+ // Write out only the currently booted entry, which should be the very first one
670
+ // Even if we have booted into the second menuentry "boot entry", the default will be the
671
+ // first one
672
+ buffer. write_all ( entries[ 0 ] . to_string ( ) . as_bytes ( ) ) ?;
673
+
674
+ grub_dir
675
+ . atomic_write ( user_cfg_name, buffer)
676
+ . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
677
+
678
+ rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
679
+
680
+ return Ok ( ( ) ) ;
681
+ }
682
+
683
+ // Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
684
+ // This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
685
+ let esp_uuid = Task :: new ( "blkid for ESP UUID" , "blkid" )
686
+ . args ( [ "-s" , "UUID" , "-o" , "value" , & esp_device] )
687
+ . read ( ) ?;
688
+
689
+ grub_dir. atomic_write (
690
+ EFI_UUID_FILE ,
691
+ format ! ( "set EFI_PART_UUID=\" {}\" " , esp_uuid. trim( ) ) . as_bytes ( ) ,
692
+ ) ?;
693
+
694
+ // Write to grub2/user.cfg
695
+ let mut buffer = vec ! [ ] ;
696
+
697
+ // Shouldn't really fail so no context here
698
+ buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
699
+ buffer. write_all (
700
+ MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
701
+ . to_string ( )
702
+ . as_bytes ( ) ,
703
+ ) ?;
704
+
705
+ grub_dir
706
+ . atomic_write ( user_cfg_name, buffer)
707
+ . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
708
+
709
+ rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
710
+
711
+ Ok ( ( ) )
712
+ }
713
+
625
714
#[ context( "Setting up UKI boot" ) ]
626
715
pub ( crate ) fn setup_composefs_uki_boot (
627
716
setup_type : BootSetupType ,
@@ -630,7 +719,7 @@ pub(crate) fn setup_composefs_uki_boot(
630
719
id : & Sha256HashValue ,
631
720
entries : Vec < ComposefsBootEntry < Sha256HashValue > > ,
632
721
) -> Result < ( ) > {
633
- let ( root_path, esp_device, is_insecure_from_opts) = match setup_type {
722
+ let ( root_path, esp_device, bootloader , is_insecure_from_opts) = match setup_type {
634
723
BootSetupType :: Setup ( ( root_setup, state, ..) ) => {
635
724
if let Some ( v) = & state. config_opts . karg {
636
725
if v. len ( ) > 0 {
@@ -645,22 +734,37 @@ pub(crate) fn setup_composefs_uki_boot(
645
734
. find ( |p| p. parttype . as_str ( ) == ESP_GUID )
646
735
. ok_or_else ( || anyhow ! ( "ESP partition not found" ) ) ?;
647
736
737
+ let bootloader = state
738
+ . composefs_options
739
+ . as_ref ( )
740
+ . map ( |opts| opts. bootloader . clone ( ) )
741
+ . unwrap_or ( Bootloader :: default ( ) ) ;
742
+
743
+ let is_insecure = state
744
+ . composefs_options
745
+ . as_ref ( )
746
+ . map ( |x| x. insecure )
747
+ . unwrap_or ( false ) ;
748
+
648
749
(
649
750
root_setup. physical_root_path . clone ( ) ,
650
751
esp_part. node . clone ( ) ,
651
- state
652
- . composefs_options
653
- . as_ref ( )
654
- . map ( |x| x. insecure )
655
- . unwrap_or ( false ) ,
752
+ bootloader,
753
+ is_insecure,
656
754
)
657
755
}
658
756
659
- BootSetupType :: Upgrade ( .. ) => {
757
+ BootSetupType :: Upgrade ( ( _ , host ) ) => {
660
758
let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ;
661
759
let sysroot_parent = get_sysroot_parent_dev ( ) ?;
760
+ let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
662
761
663
- ( sysroot, get_esp_partition ( & sysroot_parent) ?. 0 , false )
762
+ (
763
+ sysroot,
764
+ get_esp_partition ( & sysroot_parent) ?. 0 ,
765
+ bootloader,
766
+ false ,
767
+ )
664
768
}
665
769
} ;
666
770
@@ -705,84 +809,17 @@ pub(crate) fn setup_composefs_uki_boot(
705
809
. run_inherited_with_cmd_context ( )
706
810
. context ( "Unmounting ESP" ) ?;
707
811
708
- let boot_dir = root_path. join ( "boot" ) ;
709
- create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
710
-
711
- let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
712
-
713
- let efi_uuid_source = get_efi_uuid_source ( ) ;
812
+ match bootloader {
813
+ Bootloader :: Grub => {
814
+ write_grub_uki_menuentry ( root_path, & setup_type, & boot_label, id, & esp_device) ?
815
+ }
714
816
715
- let user_cfg_name = if is_upgrade {
716
- USER_CFG_STAGED
717
- } else {
718
- USER_CFG
817
+ Bootloader :: Systemd => {
818
+ // No-op for now, but later we want to have .conf files so we can control the order of
819
+ // entries.
820
+ }
719
821
} ;
720
822
721
- let grub_dir =
722
- cap_std:: fs:: Dir :: open_ambient_dir ( boot_dir. join ( "grub2" ) , cap_std:: ambient_authority ( ) )
723
- . context ( "opening boot/grub2" ) ?;
724
-
725
- // Iterate over all available deployments, and generate a menuentry for each
726
- //
727
- // TODO: We might find a staged deployment here
728
- if is_upgrade {
729
- let mut buffer = vec ! [ ] ;
730
-
731
- // Shouldn't really fail so no context here
732
- buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
733
- buffer. write_all (
734
- MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
735
- . to_string ( )
736
- . as_bytes ( ) ,
737
- ) ?;
738
-
739
- let mut str_buf = String :: new ( ) ;
740
- let boot_dir = cap_std:: fs:: Dir :: open_ambient_dir ( boot_dir, cap_std:: ambient_authority ( ) )
741
- . context ( "Opening boot dir" ) ?;
742
- let entries = get_sorted_uki_boot_entries ( & boot_dir, & mut str_buf) ?;
743
-
744
- // Write out only the currently booted entry, which should be the very first one
745
- // Even if we have booted into the second menuentry "boot entry", the default will be the
746
- // first one
747
- buffer. write_all ( entries[ 0 ] . to_string ( ) . as_bytes ( ) ) ?;
748
-
749
- grub_dir
750
- . atomic_write ( user_cfg_name, buffer)
751
- . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
752
-
753
- rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
754
-
755
- return Ok ( ( ) ) ;
756
- }
757
-
758
- // Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
759
- // This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
760
- let esp_uuid = Task :: new ( "blkid for ESP UUID" , "blkid" )
761
- . args ( [ "-s" , "UUID" , "-o" , "value" , & esp_device] )
762
- . read ( ) ?;
763
-
764
- grub_dir. atomic_write (
765
- EFI_UUID_FILE ,
766
- format ! ( "set EFI_PART_UUID=\" {}\" " , esp_uuid. trim( ) ) . as_bytes ( ) ,
767
- ) ?;
768
-
769
- // Write to grub2/user.cfg
770
- let mut buffer = vec ! [ ] ;
771
-
772
- // Shouldn't really fail so no context here
773
- buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
774
- buffer. write_all (
775
- MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
776
- . to_string ( )
777
- . as_bytes ( ) ,
778
- ) ?;
779
-
780
- grub_dir
781
- . atomic_write ( user_cfg_name, buffer)
782
- . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
783
-
784
- rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
785
-
786
823
Ok ( ( ) )
787
824
}
788
825
0 commit comments