@@ -8,7 +8,10 @@ use bootc_blockdev::find_parent_devices;
88use bootc_mount:: inspect_filesystem;
99use bootc_utils:: CommandRunExt ;
1010use 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+ } ;
1215use clap:: ValueEnum ;
1316use composefs:: fs:: read_file;
1417use composefs:: tree:: { FileSystem , RegularFile } ;
@@ -174,8 +177,7 @@ fn compute_boot_digest(
174177/// Returns the verity of the deployment that has a boot digest same as the one passed in
175178#[ context( "Checking boot entry duplicates" ) ]
176179fn 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 ( ) ) ;
179181
180182 let deployments = match deployments {
181183 Ok ( d) => d,
@@ -231,7 +233,7 @@ fn write_bls_boot_entries_to_disk(
231233 let path = boot_dir. join ( & id_hex) ;
232234 create_dir_all ( & path) ?;
233235
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 ( ) )
235237 . with_context ( || format ! ( "Opening {path}" ) ) ?;
236238
237239 entries_dir
@@ -486,9 +488,8 @@ pub(crate) fn setup_composefs_bls_boot(
486488
487489 // Scope to allow for proper unmounting
488490 {
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:?}" ) ) ?;
492493
493494 loader_entries_dir. atomic_write (
494495 // SAFETY: We set sort_key above
@@ -600,7 +601,7 @@ fn write_pe_to_esp(
600601 None => efi_linux_path,
601602 } ;
602603
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 ( ) )
604605 . with_context ( || format ! ( "Opening {final_pe_path:?}" ) ) ?;
605606
606607 let pe_name = match pe_type {
@@ -622,6 +623,94 @@ fn write_pe_to_esp(
622623 Ok ( boot_label)
623624}
624625
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+
625714#[ context( "Setting up UKI boot" ) ]
626715pub ( crate ) fn setup_composefs_uki_boot (
627716 setup_type : BootSetupType ,
@@ -630,7 +719,7 @@ pub(crate) fn setup_composefs_uki_boot(
630719 id : & Sha256HashValue ,
631720 entries : Vec < ComposefsBootEntry < Sha256HashValue > > ,
632721) -> 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 {
634723 BootSetupType :: Setup ( ( root_setup, state, ..) ) => {
635724 if let Some ( v) = & state. config_opts . karg {
636725 if v. len ( ) > 0 {
@@ -645,22 +734,37 @@ pub(crate) fn setup_composefs_uki_boot(
645734 . find ( |p| p. parttype . as_str ( ) == ESP_GUID )
646735 . ok_or_else ( || anyhow ! ( "ESP partition not found" ) ) ?;
647736
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+
648749 (
649750 root_setup. physical_root_path . clone ( ) ,
650751 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,
656754 )
657755 }
658756
659- BootSetupType :: Upgrade ( .. ) => {
757+ BootSetupType :: Upgrade ( ( _ , host ) ) => {
660758 let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ;
661759 let sysroot_parent = get_sysroot_parent_dev ( ) ?;
760+ let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
662761
663- ( sysroot, get_esp_partition ( & sysroot_parent) ?. 0 , false )
762+ (
763+ sysroot,
764+ get_esp_partition ( & sysroot_parent) ?. 0 ,
765+ bootloader,
766+ false ,
767+ )
664768 }
665769 } ;
666770
@@ -705,84 +809,17 @@ pub(crate) fn setup_composefs_uki_boot(
705809 . run_inherited_with_cmd_context ( )
706810 . context ( "Unmounting ESP" ) ?;
707811
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+ }
714816
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+ }
719821 } ;
720822
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-
786823 Ok ( ( ) )
787824}
788825
0 commit comments