@@ -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 } ;
@@ -161,8 +164,7 @@ fn compute_boot_digest(
161164/// Returns the verity of the deployment that has a boot digest same as the one passed in
162165#[ context( "Checking boot entry duplicates" ) ]
163166fn find_vmlinuz_initrd_duplicates ( digest : & str ) -> Result < Option < String > > {
164- let deployments =
165- cap_std:: fs:: Dir :: open_ambient_dir ( STATE_DIR_ABS , cap_std:: ambient_authority ( ) ) ;
167+ let deployments = Dir :: open_ambient_dir ( STATE_DIR_ABS , ambient_authority ( ) ) ;
166168
167169 let deployments = match deployments {
168170 Ok ( d) => d,
@@ -218,7 +220,7 @@ fn write_bls_boot_entries_to_disk(
218220 let path = boot_dir. join ( & id_hex) ;
219221 create_dir_all ( & path) ?;
220222
221- let entries_dir = cap_std :: fs :: Dir :: open_ambient_dir ( & path, cap_std :: ambient_authority ( ) )
223+ let entries_dir = Dir :: open_ambient_dir ( & path, ambient_authority ( ) )
222224 . with_context ( || format ! ( "Opening {path}" ) ) ?;
223225
224226 entries_dir
@@ -481,9 +483,8 @@ pub(crate) fn setup_composefs_bls_boot(
481483
482484 // Scope to allow for proper unmounting
483485 {
484- let loader_entries_dir =
485- cap_std:: fs:: Dir :: open_ambient_dir ( & config_path, cap_std:: ambient_authority ( ) )
486- . with_context ( || format ! ( "Opening {config_path:?}" ) ) ?;
486+ let loader_entries_dir = Dir :: open_ambient_dir ( & config_path, ambient_authority ( ) )
487+ . with_context ( || format ! ( "Opening {config_path:?}" ) ) ?;
487488
488489 loader_entries_dir. atomic_write (
489490 // SAFETY: We set sort_key above
@@ -595,7 +596,7 @@ fn write_pe_to_esp(
595596 None => efi_linux_path,
596597 } ;
597598
598- let pe_dir = cap_std :: fs :: Dir :: open_ambient_dir ( & final_pe_path, cap_std :: ambient_authority ( ) )
599+ let pe_dir = Dir :: open_ambient_dir ( & final_pe_path, ambient_authority ( ) )
599600 . with_context ( || format ! ( "Opening {final_pe_path:?}" ) ) ?;
600601
601602 let pe_name = match pe_type {
@@ -617,6 +618,94 @@ fn write_pe_to_esp(
617618 Ok ( boot_label)
618619}
619620
621+ #[ context( "Writing Grub menuentry" ) ]
622+ fn write_grub_uki_menuentry (
623+ root_path : Utf8PathBuf ,
624+ setup_type : & BootSetupType ,
625+ boot_label : & String ,
626+ id : & Sha256HashValue ,
627+ esp_device : & String ,
628+ ) -> Result < ( ) > {
629+ let boot_dir = root_path. join ( "boot" ) ;
630+ create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
631+
632+ let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
633+
634+ let efi_uuid_source = get_efi_uuid_source ( ) ;
635+
636+ let user_cfg_name = if is_upgrade {
637+ USER_CFG_STAGED
638+ } else {
639+ USER_CFG
640+ } ;
641+
642+ let grub_dir = Dir :: open_ambient_dir ( boot_dir. join ( "grub2" ) , ambient_authority ( ) )
643+ . context ( "opening boot/grub2" ) ?;
644+
645+ // Iterate over all available deployments, and generate a menuentry for each
646+ //
647+ // TODO: We might find a staged deployment here
648+ if is_upgrade {
649+ let mut buffer = vec ! [ ] ;
650+
651+ // Shouldn't really fail so no context here
652+ buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
653+ buffer. write_all (
654+ MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
655+ . to_string ( )
656+ . as_bytes ( ) ,
657+ ) ?;
658+
659+ let mut str_buf = String :: new ( ) ;
660+ let boot_dir =
661+ Dir :: open_ambient_dir ( boot_dir, ambient_authority ( ) ) . context ( "Opening boot dir" ) ?;
662+ let entries = get_sorted_uki_boot_entries ( & boot_dir, & mut str_buf) ?;
663+
664+ // Write out only the currently booted entry, which should be the very first one
665+ // Even if we have booted into the second menuentry "boot entry", the default will be the
666+ // first one
667+ buffer. write_all ( entries[ 0 ] . to_string ( ) . as_bytes ( ) ) ?;
668+
669+ grub_dir
670+ . atomic_write ( user_cfg_name, buffer)
671+ . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
672+
673+ rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
674+
675+ return Ok ( ( ) ) ;
676+ }
677+
678+ // Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
679+ // This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
680+ let esp_uuid = Task :: new ( "blkid for ESP UUID" , "blkid" )
681+ . args ( [ "-s" , "UUID" , "-o" , "value" , & esp_device] )
682+ . read ( ) ?;
683+
684+ grub_dir. atomic_write (
685+ EFI_UUID_FILE ,
686+ format ! ( "set EFI_PART_UUID=\" {}\" " , esp_uuid. trim( ) ) . as_bytes ( ) ,
687+ ) ?;
688+
689+ // Write to grub2/user.cfg
690+ let mut buffer = vec ! [ ] ;
691+
692+ // Shouldn't really fail so no context here
693+ buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
694+ buffer. write_all (
695+ MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
696+ . to_string ( )
697+ . as_bytes ( ) ,
698+ ) ?;
699+
700+ grub_dir
701+ . atomic_write ( user_cfg_name, buffer)
702+ . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
703+
704+ rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
705+
706+ Ok ( ( ) )
707+ }
708+
620709#[ context( "Setting up UKI boot" ) ]
621710pub ( crate ) fn setup_composefs_uki_boot (
622711 setup_type : BootSetupType ,
@@ -625,7 +714,7 @@ pub(crate) fn setup_composefs_uki_boot(
625714 id : & Sha256HashValue ,
626715 entries : Vec < ComposefsBootEntry < Sha256HashValue > > ,
627716) -> Result < ( ) > {
628- let ( root_path, esp_device, is_insecure_from_opts) = match setup_type {
717+ let ( root_path, esp_device, bootloader , is_insecure_from_opts) = match setup_type {
629718 BootSetupType :: Setup ( ( root_setup, state, ..) ) => {
630719 if let Some ( v) = & state. config_opts . karg {
631720 if v. len ( ) > 0 {
@@ -640,18 +729,27 @@ pub(crate) fn setup_composefs_uki_boot(
640729 . find ( |p| p. parttype . as_str ( ) == ESP_GUID )
641730 . ok_or_else ( || anyhow ! ( "ESP partition not found" ) ) ?;
642731
732+ let bootloader = state
733+ . composefs_options
734+ . as_ref ( )
735+ . map ( |opts| opts. bootloader . clone ( ) )
736+ . unwrap_or ( Bootloader :: default ( ) ) ;
737+
738+ let is_insecure = state
739+ . composefs_options
740+ . as_ref ( )
741+ . map ( |x| x. insecure )
742+ . unwrap_or ( false ) ;
743+
643744 (
644745 root_setup. physical_root_path . clone ( ) ,
645746 esp_part. node . clone ( ) ,
646- state
647- . composefs_options
648- . as_ref ( )
649- . map ( |x| x. insecure )
650- . unwrap_or ( false ) ,
747+ bootloader,
748+ is_insecure,
651749 )
652750 }
653751
654- BootSetupType :: Upgrade ( .. ) => {
752+ BootSetupType :: Upgrade ( ( _ , host ) ) => {
655753 let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ;
656754
657755 let fsinfo = inspect_filesystem ( & sysroot) ?;
@@ -661,7 +759,9 @@ pub(crate) fn setup_composefs_uki_boot(
661759 anyhow:: bail!( "Could not find parent device for mountpoint /sysroot" ) ;
662760 } ;
663761
664- ( sysroot, get_esp_partition ( & parent) ?. 0 , false )
762+ let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
763+
764+ ( sysroot, get_esp_partition ( & parent) ?. 0 , bootloader, false )
665765 }
666766 } ;
667767
@@ -706,84 +806,17 @@ pub(crate) fn setup_composefs_uki_boot(
706806 . run_inherited_with_cmd_context ( )
707807 . context ( "Unmounting ESP" ) ?;
708808
709- let boot_dir = root_path. join ( "boot" ) ;
710- create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
711-
712- let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
713-
714- let efi_uuid_source = get_efi_uuid_source ( ) ;
809+ match bootloader {
810+ Bootloader :: Grub => {
811+ write_grub_uki_menuentry ( root_path, & setup_type, & boot_label, id, & esp_device) ?
812+ }
715813
716- let user_cfg_name = if is_upgrade {
717- USER_CFG_STAGED
718- } else {
719- USER_CFG
814+ Bootloader :: Systemd => {
815+ // No-op for now, but later we want to have .conf files so we can control the order of
816+ // entries.
817+ }
720818 } ;
721819
722- let grub_dir =
723- cap_std:: fs:: Dir :: open_ambient_dir ( boot_dir. join ( "grub2" ) , cap_std:: ambient_authority ( ) )
724- . context ( "opening boot/grub2" ) ?;
725-
726- // Iterate over all available deployments, and generate a menuentry for each
727- //
728- // TODO: We might find a staged deployment here
729- if is_upgrade {
730- let mut buffer = vec ! [ ] ;
731-
732- // Shouldn't really fail so no context here
733- buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
734- buffer. write_all (
735- MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
736- . to_string ( )
737- . as_bytes ( ) ,
738- ) ?;
739-
740- let mut str_buf = String :: new ( ) ;
741- let boot_dir = cap_std:: fs:: Dir :: open_ambient_dir ( boot_dir, cap_std:: ambient_authority ( ) )
742- . context ( "Opening boot dir" ) ?;
743- let entries = get_sorted_uki_boot_entries ( & boot_dir, & mut str_buf) ?;
744-
745- // Write out only the currently booted entry, which should be the very first one
746- // Even if we have booted into the second menuentry "boot entry", the default will be the
747- // first one
748- buffer. write_all ( entries[ 0 ] . to_string ( ) . as_bytes ( ) ) ?;
749-
750- grub_dir
751- . atomic_write ( user_cfg_name, buffer)
752- . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
753-
754- rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
755-
756- return Ok ( ( ) ) ;
757- }
758-
759- // Open grub2/efiuuid.cfg and write the EFI partition fs-UUID in there
760- // This will be sourced by grub2/user.cfg to be used for `--fs-uuid`
761- let esp_uuid = Task :: new ( "blkid for ESP UUID" , "blkid" )
762- . args ( [ "-s" , "UUID" , "-o" , "value" , & esp_device] )
763- . read ( ) ?;
764-
765- grub_dir. atomic_write (
766- EFI_UUID_FILE ,
767- format ! ( "set EFI_PART_UUID=\" {}\" " , esp_uuid. trim( ) ) . as_bytes ( ) ,
768- ) ?;
769-
770- // Write to grub2/user.cfg
771- let mut buffer = vec ! [ ] ;
772-
773- // Shouldn't really fail so no context here
774- buffer. write_all ( efi_uuid_source. as_bytes ( ) ) ?;
775- buffer. write_all (
776- MenuEntry :: new ( & boot_label, & id. to_hex ( ) )
777- . to_string ( )
778- . as_bytes ( ) ,
779- ) ?;
780-
781- grub_dir
782- . atomic_write ( user_cfg_name, buffer)
783- . with_context ( || format ! ( "Writing to {user_cfg_name}" ) ) ?;
784-
785- rustix:: fs:: fsync ( grub_dir. reopen_as_ownedfd ( ) ?) . context ( "fsync" ) ?;
786-
787820 Ok ( ( ) )
788821}
789822
0 commit comments