@@ -81,9 +81,9 @@ use crate::bootc_composefs::state::copy_etc_to_state;
8181use crate :: boundimage:: { BoundImage , ResolvedBoundImage } ;
8282use crate :: composefs_consts:: {
8383 BOOT_LOADER_ENTRIES , COMPOSEFS_CMDLINE , COMPOSEFS_STAGED_DEPLOYMENT_FNAME ,
84- COMPOSEFS_TRANSIENT_STATE_DIR , ORIGIN_KEY_BOOT , ORIGIN_KEY_BOOT_DIGEST , ORIGIN_KEY_BOOT_TYPE ,
85- SHARED_VAR_PATH , STAGED_BOOT_LOADER_ENTRIES , STATE_DIR_ABS , STATE_DIR_RELATIVE , USER_CFG ,
86- USER_CFG_STAGED ,
84+ COMPOSEFS_TRANSIENT_STATE_DIR , ORIGIN_KEY_BOOT , ORIGIN_KEY_BOOTLOADER , ORIGIN_KEY_BOOT_DIGEST ,
85+ ORIGIN_KEY_BOOT_TYPE , SHARED_VAR_PATH , STAGED_BOOT_LOADER_ENTRIES , STATE_DIR_ABS ,
86+ STATE_DIR_RELATIVE , USER_CFG , USER_CFG_STAGED ,
8787} ;
8888use crate :: containerenv:: ContainerExecutionInfo ;
8989use crate :: deploy:: {
@@ -94,7 +94,7 @@ use crate::lsm;
9494use crate :: parsers:: bls_config:: { parse_bls_config, BLSConfig } ;
9595use crate :: parsers:: grub_menuconfig:: MenuEntry ;
9696use crate :: progress_jsonl:: ProgressWriter ;
97- use crate :: spec:: ImageReference ;
97+ use crate :: spec:: { Bootloader , Host , ImageReference } ;
9898use crate :: store:: Storage ;
9999use crate :: task:: Task ;
100100use crate :: utils:: { path_relative_to, sigpolicy_from_opt} ;
@@ -311,6 +311,10 @@ pub(crate) struct InstallComposefsOpts {
311311 #[ clap( long, default_value_t) ]
312312 #[ serde( default ) ]
313313 pub ( crate ) insecure : bool ,
314+
315+ #[ clap( long, default_value_t) ]
316+ #[ serde( default ) ]
317+ pub ( crate ) bootloader : Bootloader ,
314318}
315319
316320#[ cfg( feature = "install-to-disk" ) ]
@@ -1581,7 +1585,7 @@ pub(crate) enum BootSetupType<'a> {
15811585 /// For initial setup, i.e. install to-disk
15821586 Setup ( ( & ' a RootSetup , & ' a State , & ' a FileSystem < Sha256HashValue > ) ) ,
15831587 /// For `bootc upgrade`
1584- Upgrade ( & ' a FileSystem < Sha256HashValue > ) ,
1588+ Upgrade ( ( & ' a FileSystem < Sha256HashValue > , & ' a Host ) ) ,
15851589}
15861590
15871591/// Compute SHA256Sum of VMlinuz + Initrd
@@ -1707,6 +1711,18 @@ fn write_bls_boot_entries_to_disk(
17071711 Ok ( ( ) )
17081712}
17091713
1714+ struct BLSEntryPath < ' a > {
1715+ /// Where to write vmlinuz/initrd
1716+ entries_path : Utf8PathBuf ,
1717+ /// The absolute path, with reference to the partition's root, where the vmlinuz/initrd are written to
1718+ /// We need this as when installing, the mounted path will not
1719+ abs_entries_path : & ' a str ,
1720+ /// Where to write the .conf files
1721+ config_path : Utf8PathBuf ,
1722+ /// If we mounted EFI, the target path
1723+ mount_path : Option < Utf8PathBuf > ,
1724+ }
1725+
17101726/// Sets up and writes BLS entries and binaries (VMLinuz + Initrd) to disk
17111727///
17121728/// # Returns
@@ -1721,7 +1737,7 @@ pub(crate) fn setup_composefs_bls_boot(
17211737) -> Result < String > {
17221738 let id_hex = id. to_hex ( ) ;
17231739
1724- let ( esp_device, cmdline_refs, fs) = match setup_type {
1740+ let ( root_path , esp_device, cmdline_refs, fs, bootloader ) = match setup_type {
17251741 BootSetupType :: Setup ( ( root_setup, state, fs) ) => {
17261742 // root_setup.kargs has [root=UUID=<UUID>, "rw"]
17271743 let mut cmdline_options = String :: from ( root_setup. kargs . join ( " " ) ) ;
@@ -1743,10 +1759,20 @@ pub(crate) fn setup_composefs_bls_boot(
17431759 . find ( |p| p. parttype . as_str ( ) == ESP_GUID )
17441760 . ok_or_else ( || anyhow:: anyhow!( "ESP partition not found" ) ) ?;
17451761
1746- ( esp_part. node . clone ( ) , cmdline_options, fs)
1762+ (
1763+ root_setup. physical_root_path . clone ( ) ,
1764+ esp_part. node . clone ( ) ,
1765+ cmdline_options,
1766+ fs,
1767+ state
1768+ . composefs_options
1769+ . as_ref ( )
1770+ . map ( |opts| opts. bootloader . clone ( ) )
1771+ . unwrap_or ( Bootloader :: default ( ) ) ,
1772+ )
17471773 }
17481774
1749- BootSetupType :: Upgrade ( fs ) => {
1775+ BootSetupType :: Upgrade ( ( fs , host ) ) => {
17501776 let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ;
17511777
17521778 let fsinfo = inspect_filesystem ( & sysroot) ?;
@@ -1756,7 +1782,10 @@ pub(crate) fn setup_composefs_bls_boot(
17561782 anyhow:: bail!( "Could not find parent device for mountpoint /sysroot" ) ;
17571783 } ;
17581784
1785+ let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
1786+
17591787 (
1788+ Utf8PathBuf :: from ( "/sysroot" ) ,
17601789 get_esp_partition ( & parent) ?. 0 ,
17611790 vec ! [
17621791 format!( "root=UUID={DPS_UUID}" ) ,
@@ -1765,24 +1794,45 @@ pub(crate) fn setup_composefs_bls_boot(
17651794 ]
17661795 . join ( " " ) ,
17671796 fs,
1797+ bootloader,
17681798 )
17691799 }
17701800 } ;
17711801
1772- let temp_efi_dir = tempfile:: tempdir ( )
1773- . map_err ( |e| anyhow:: anyhow!( "Failed to create temporary directory for EFI mount: {e}" ) ) ?;
1774- let mounted_efi = temp_efi_dir. path ( ) . to_path_buf ( ) ;
1802+ let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
17751803
1776- Command :: new ( "mount" )
1777- . args ( [ & PathBuf :: from ( & esp_device) , & mounted_efi] )
1778- . log_debug ( )
1779- . run_inherited_with_cmd_context ( )
1780- . context ( "Mounting EFI" ) ?;
1804+ let entry_paths = match bootloader {
1805+ Bootloader :: Grub => BLSEntryPath {
1806+ entries_path : root_path. join ( "boot" ) ,
1807+ config_path : root_path. join ( "boot" ) ,
1808+ abs_entries_path : "boot" ,
1809+ mount_path : None ,
1810+ } ,
17811811
1782- let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
1812+ Bootloader :: Systemd => {
1813+ let temp_efi_dir = tempfile:: tempdir ( ) . map_err ( |e| {
1814+ anyhow:: anyhow!( "Failed to create temporary directory for EFI mount: {e}" )
1815+ } ) ?;
1816+
1817+ let mounted_efi = Utf8PathBuf :: from_path_buf ( temp_efi_dir. path ( ) . to_path_buf ( ) )
1818+ . map_err ( |_| anyhow:: anyhow!( "EFI dir is not valid UTF-8" ) ) ?;
17831819
1784- let efi_dir = Utf8PathBuf :: from_path_buf ( mounted_efi. join ( EFI_LINUX ) )
1785- . map_err ( |_| anyhow:: anyhow!( "EFI dir is not valid UTF-8" ) ) ?;
1820+ Command :: new ( "mount" )
1821+ . args ( [ & PathBuf :: from ( & esp_device) , mounted_efi. as_std_path ( ) ] )
1822+ . log_debug ( )
1823+ . run_inherited_with_cmd_context ( )
1824+ . context ( "Mounting EFI" ) ?;
1825+
1826+ let efi_linux_dir = mounted_efi. join ( EFI_LINUX ) ;
1827+
1828+ BLSEntryPath {
1829+ entries_path : efi_linux_dir,
1830+ config_path : mounted_efi. clone ( ) ,
1831+ abs_entries_path : EFI_LINUX ,
1832+ mount_path : Some ( mounted_efi) ,
1833+ }
1834+ }
1835+ } ;
17861836
17871837 let ( bls_config, boot_digest) = match & entry {
17881838 ComposefsBootEntry :: Type1 ( ..) => unimplemented ! ( ) ,
@@ -1831,44 +1881,66 @@ pub(crate) fn setup_composefs_bls_boot(
18311881 . with_title ( id_hex. clone ( ) )
18321882 . with_sort_key ( default_sort_key. into ( ) )
18331883 . with_version ( version. unwrap_or ( default_sort_key. into ( ) ) )
1834- . with_linux ( format ! ( "/{EFI_LINUX}/{id_hex}/vmlinuz" ) )
1835- . with_initrd ( vec ! [ format!( "/{EFI_LINUX}/{id_hex}/initrd" ) ] )
1884+ . with_linux ( format ! (
1885+ "/{}/{id_hex}/vmlinuz" ,
1886+ entry_paths. abs_entries_path
1887+ ) )
1888+ . with_initrd ( vec ! [ format!(
1889+ "/{}/{id_hex}/initrd" ,
1890+ entry_paths. abs_entries_path
1891+ ) ] )
18361892 . with_options ( cmdline_refs) ;
18371893
18381894 if let Some ( symlink_to) = find_vmlinuz_initrd_duplicates ( & boot_digest) ? {
1839- bls_config. linux = format ! ( "/{EFI_LINUX}/{symlink_to}/vmlinuz" ) ;
1840- bls_config. initrd = vec ! [ format!( "/{EFI_LINUX}/{symlink_to}/initrd" ) ] ;
1895+ bls_config. linux =
1896+ format ! ( "/{}/{symlink_to}/vmlinuz" , entry_paths. abs_entries_path) ;
1897+
1898+ bls_config. initrd = vec ! [ format!(
1899+ "/{}/{symlink_to}/initrd" ,
1900+ entry_paths. abs_entries_path
1901+ ) ] ;
18411902 } else {
1842- write_bls_boot_entries_to_disk ( & efi_dir, id, usr_lib_modules_vmlinuz, & repo) ?;
1903+ write_bls_boot_entries_to_disk (
1904+ & entry_paths. entries_path ,
1905+ id,
1906+ usr_lib_modules_vmlinuz,
1907+ & repo,
1908+ ) ?;
18431909 }
18441910
18451911 ( bls_config, boot_digest)
18461912 }
18471913 } ;
18481914
1849- let ( entries_path , booted_bls) = if is_upgrade {
1915+ let ( config_path , booted_bls) = if is_upgrade {
18501916 let mut booted_bls = get_booted_bls ( ) ?;
18511917 booted_bls. sort_key = Some ( "0" . into ( ) ) ; // entries are sorted by their filename in reverse order
18521918
18531919 // This will be atomically renamed to 'loader/entries' on shutdown/reboot
18541920 (
1855- mounted_efi. join ( format ! ( "loader/{STAGED_BOOT_LOADER_ENTRIES}" ) ) ,
1921+ entry_paths
1922+ . config_path
1923+ . join ( "loader" )
1924+ . join ( STAGED_BOOT_LOADER_ENTRIES ) ,
18561925 Some ( booted_bls) ,
18571926 )
18581927 } else {
18591928 (
1860- mounted_efi. join ( format ! ( "loader/{BOOT_LOADER_ENTRIES}" ) ) ,
1929+ entry_paths
1930+ . config_path
1931+ . join ( "loader" )
1932+ . join ( BOOT_LOADER_ENTRIES ) ,
18611933 None ,
18621934 )
18631935 } ;
18641936
1865- create_dir_all ( & entries_path ) . with_context ( || format ! ( "Creating {:?}" , entries_path ) ) ?;
1937+ create_dir_all ( & config_path ) . with_context ( || format ! ( "Creating {:?}" , config_path ) ) ?;
18661938
18671939 // Scope to allow for proper unmounting
18681940 {
18691941 let loader_entries_dir =
1870- cap_std:: fs:: Dir :: open_ambient_dir ( & entries_path , cap_std:: ambient_authority ( ) )
1871- . with_context ( || format ! ( "Opening {entries_path :?}" ) ) ?;
1942+ cap_std:: fs:: Dir :: open_ambient_dir ( & config_path , cap_std:: ambient_authority ( ) )
1943+ . with_context ( || format ! ( "Opening {config_path :?}" ) ) ?;
18721944
18731945 loader_entries_dir. atomic_write (
18741946 // SAFETY: We set sort_key above
@@ -1893,14 +1965,17 @@ pub(crate) fn setup_composefs_bls_boot(
18931965 let owned_loader_entries_fd = loader_entries_dir
18941966 . reopen_as_ownedfd ( )
18951967 . context ( "Reopening as owned fd" ) ?;
1968+
18961969 rustix:: fs:: fsync ( owned_loader_entries_fd) . context ( "fsync" ) ?;
18971970 }
18981971
1899- Command :: new ( "umount" )
1900- . arg ( & mounted_efi)
1901- . log_debug ( )
1902- . run_inherited_with_cmd_context ( )
1903- . context ( "Unmounting EFI" ) ?;
1972+ if let Some ( mounted_efi) = entry_paths. mount_path {
1973+ Command :: new ( "umount" )
1974+ . arg ( mounted_efi)
1975+ . log_debug ( )
1976+ . run_inherited_with_cmd_context ( )
1977+ . context ( "Unmounting EFI" ) ?;
1978+ }
19041979
19051980 Ok ( boot_digest)
19061981}
@@ -2231,6 +2306,11 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
22312306 false ,
22322307 boot_type,
22332308 boot_digest,
2309+ state
2310+ . composefs_options
2311+ . as_ref ( )
2312+ . map ( |opt| opt. bootloader . clone ( ) )
2313+ . unwrap_or ( Bootloader :: default ( ) ) ,
22342314 ) ?;
22352315
22362316 Ok ( ( ) )
@@ -2245,6 +2325,7 @@ pub(crate) fn write_composefs_state(
22452325 staged : bool ,
22462326 boot_type : BootType ,
22472327 boot_digest : Option < String > ,
2328+ bootloader : Bootloader ,
22482329) -> Result < ( ) > {
22492330 let state_path = root_path. join ( format ! ( "{STATE_DIR_RELATIVE}/{}" , deployment_id. to_hex( ) ) ) ;
22502331
@@ -2283,6 +2364,10 @@ pub(crate) fn write_composefs_state(
22832364 . item ( ORIGIN_KEY_BOOT_DIGEST , boot_digest) ;
22842365 }
22852366
2367+ config = config
2368+ . section ( ORIGIN_KEY_BOOT )
2369+ . item ( ORIGIN_KEY_BOOTLOADER , bootloader) ;
2370+
22862371 let state_dir = cap_std:: fs:: Dir :: open_ambient_dir ( & state_path, cap_std:: ambient_authority ( ) )
22872372 . context ( "Opening state dir" ) ?;
22882373
0 commit comments