@@ -81,9 +81,9 @@ use crate::bootc_composefs::state::copy_etc_to_state;
81
81
use crate :: boundimage:: { BoundImage , ResolvedBoundImage } ;
82
82
use crate :: composefs_consts:: {
83
83
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 ,
87
87
} ;
88
88
use crate :: containerenv:: ContainerExecutionInfo ;
89
89
use crate :: deploy:: {
@@ -94,7 +94,7 @@ use crate::lsm;
94
94
use crate :: parsers:: bls_config:: { parse_bls_config, BLSConfig } ;
95
95
use crate :: parsers:: grub_menuconfig:: MenuEntry ;
96
96
use crate :: progress_jsonl:: ProgressWriter ;
97
- use crate :: spec:: ImageReference ;
97
+ use crate :: spec:: { Bootloader , Host , ImageReference } ;
98
98
use crate :: store:: Storage ;
99
99
use crate :: task:: Task ;
100
100
use crate :: utils:: { path_relative_to, sigpolicy_from_opt} ;
@@ -311,6 +311,10 @@ pub(crate) struct InstallComposefsOpts {
311
311
#[ clap( long, default_value_t) ]
312
312
#[ serde( default ) ]
313
313
pub ( crate ) insecure : bool ,
314
+
315
+ #[ clap( long, default_value_t) ]
316
+ #[ serde( default ) ]
317
+ pub ( crate ) bootloader : Bootloader ,
314
318
}
315
319
316
320
#[ cfg( feature = "install-to-disk" ) ]
@@ -1581,7 +1585,7 @@ pub(crate) enum BootSetupType<'a> {
1581
1585
/// For initial setup, i.e. install to-disk
1582
1586
Setup ( ( & ' a RootSetup , & ' a State , & ' a FileSystem < Sha256HashValue > ) ) ,
1583
1587
/// For `bootc upgrade`
1584
- Upgrade ( & ' a FileSystem < Sha256HashValue > ) ,
1588
+ Upgrade ( ( & ' a FileSystem < Sha256HashValue > , & ' a Host ) ) ,
1585
1589
}
1586
1590
1587
1591
/// Compute SHA256Sum of VMlinuz + Initrd
@@ -1707,6 +1711,18 @@ fn write_bls_boot_entries_to_disk(
1707
1711
Ok ( ( ) )
1708
1712
}
1709
1713
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
+
1710
1726
/// Sets up and writes BLS entries and binaries (VMLinuz + Initrd) to disk
1711
1727
///
1712
1728
/// # Returns
@@ -1721,7 +1737,7 @@ pub(crate) fn setup_composefs_bls_boot(
1721
1737
) -> Result < String > {
1722
1738
let id_hex = id. to_hex ( ) ;
1723
1739
1724
- let ( esp_device, cmdline_refs, fs) = match setup_type {
1740
+ let ( root_path , esp_device, cmdline_refs, fs, bootloader ) = match setup_type {
1725
1741
BootSetupType :: Setup ( ( root_setup, state, fs) ) => {
1726
1742
// root_setup.kargs has [root=UUID=<UUID>, "rw"]
1727
1743
let mut cmdline_options = String :: from ( root_setup. kargs . join ( " " ) ) ;
@@ -1743,10 +1759,20 @@ pub(crate) fn setup_composefs_bls_boot(
1743
1759
. find ( |p| p. parttype . as_str ( ) == ESP_GUID )
1744
1760
. ok_or_else ( || anyhow:: anyhow!( "ESP partition not found" ) ) ?;
1745
1761
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
+ )
1747
1773
}
1748
1774
1749
- BootSetupType :: Upgrade ( fs ) => {
1775
+ BootSetupType :: Upgrade ( ( fs , host ) ) => {
1750
1776
let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ;
1751
1777
1752
1778
let fsinfo = inspect_filesystem ( & sysroot) ?;
@@ -1756,7 +1782,10 @@ pub(crate) fn setup_composefs_bls_boot(
1756
1782
anyhow:: bail!( "Could not find parent device for mountpoint /sysroot" ) ;
1757
1783
} ;
1758
1784
1785
+ let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
1786
+
1759
1787
(
1788
+ Utf8PathBuf :: from ( "/sysroot" ) ,
1760
1789
get_esp_partition ( & parent) ?. 0 ,
1761
1790
vec ! [
1762
1791
format!( "root=UUID={DPS_UUID}" ) ,
@@ -1765,24 +1794,51 @@ pub(crate) fn setup_composefs_bls_boot(
1765
1794
]
1766
1795
. join ( " " ) ,
1767
1796
fs,
1797
+ bootloader,
1768
1798
)
1769
1799
}
1770
1800
} ;
1771
1801
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 ( ..) ) ;
1775
1803
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, _tmpdir_guard) = match bootloader {
1805
+ Bootloader :: Grub => (
1806
+ BLSEntryPath {
1807
+ entries_path : root_path. join ( "boot" ) ,
1808
+ config_path : root_path. join ( "boot" ) ,
1809
+ abs_entries_path : "boot" ,
1810
+ mount_path : None ,
1811
+ } ,
1812
+ None ,
1813
+ ) ,
1781
1814
1782
- let is_upgrade = matches ! ( setup_type, BootSetupType :: Upgrade ( ..) ) ;
1815
+ Bootloader :: Systemd => {
1816
+ let temp_efi_dir = tempfile:: tempdir ( ) . map_err ( |e| {
1817
+ anyhow:: anyhow!( "Failed to create temporary directory for EFI mount: {e}" )
1818
+ } ) ?;
1819
+
1820
+ let mounted_efi = Utf8PathBuf :: from_path_buf ( temp_efi_dir. path ( ) . to_path_buf ( ) )
1821
+ . map_err ( |_| anyhow:: anyhow!( "EFI dir is not valid UTF-8" ) ) ?;
1822
+
1823
+ Command :: new ( "mount" )
1824
+ . args ( [ & PathBuf :: from ( & esp_device) , mounted_efi. as_std_path ( ) ] )
1825
+ . log_debug ( )
1826
+ . run_inherited_with_cmd_context ( )
1827
+ . context ( "Mounting EFI" ) ?;
1828
+
1829
+ let efi_linux_dir = mounted_efi. join ( EFI_LINUX ) ;
1783
1830
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" ) ) ?;
1831
+ (
1832
+ BLSEntryPath {
1833
+ entries_path : efi_linux_dir,
1834
+ config_path : mounted_efi. clone ( ) ,
1835
+ abs_entries_path : EFI_LINUX ,
1836
+ mount_path : Some ( mounted_efi) ,
1837
+ } ,
1838
+ Some ( temp_efi_dir) ,
1839
+ )
1840
+ }
1841
+ } ;
1786
1842
1787
1843
let ( bls_config, boot_digest) = match & entry {
1788
1844
ComposefsBootEntry :: Type1 ( ..) => unimplemented ! ( ) ,
@@ -1831,44 +1887,66 @@ pub(crate) fn setup_composefs_bls_boot(
1831
1887
. with_title ( id_hex. clone ( ) )
1832
1888
. with_sort_key ( default_sort_key. into ( ) )
1833
1889
. 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" ) ] )
1890
+ . with_linux ( format ! (
1891
+ "/{}/{id_hex}/vmlinuz" ,
1892
+ entry_paths. abs_entries_path
1893
+ ) )
1894
+ . with_initrd ( vec ! [ format!(
1895
+ "/{}/{id_hex}/initrd" ,
1896
+ entry_paths. abs_entries_path
1897
+ ) ] )
1836
1898
. with_options ( cmdline_refs) ;
1837
1899
1838
1900
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" ) ] ;
1901
+ bls_config. linux =
1902
+ format ! ( "/{}/{symlink_to}/vmlinuz" , entry_paths. abs_entries_path) ;
1903
+
1904
+ bls_config. initrd = vec ! [ format!(
1905
+ "/{}/{symlink_to}/initrd" ,
1906
+ entry_paths. abs_entries_path
1907
+ ) ] ;
1841
1908
} else {
1842
- write_bls_boot_entries_to_disk ( & efi_dir, id, usr_lib_modules_vmlinuz, & repo) ?;
1909
+ write_bls_boot_entries_to_disk (
1910
+ & entry_paths. entries_path ,
1911
+ id,
1912
+ usr_lib_modules_vmlinuz,
1913
+ & repo,
1914
+ ) ?;
1843
1915
}
1844
1916
1845
1917
( bls_config, boot_digest)
1846
1918
}
1847
1919
} ;
1848
1920
1849
- let ( entries_path , booted_bls) = if is_upgrade {
1921
+ let ( config_path , booted_bls) = if is_upgrade {
1850
1922
let mut booted_bls = get_booted_bls ( ) ?;
1851
1923
booted_bls. sort_key = Some ( "0" . into ( ) ) ; // entries are sorted by their filename in reverse order
1852
1924
1853
1925
// This will be atomically renamed to 'loader/entries' on shutdown/reboot
1854
1926
(
1855
- mounted_efi. join ( format ! ( "loader/{STAGED_BOOT_LOADER_ENTRIES}" ) ) ,
1927
+ entry_paths
1928
+ . config_path
1929
+ . join ( "loader" )
1930
+ . join ( STAGED_BOOT_LOADER_ENTRIES ) ,
1856
1931
Some ( booted_bls) ,
1857
1932
)
1858
1933
} else {
1859
1934
(
1860
- mounted_efi. join ( format ! ( "loader/{BOOT_LOADER_ENTRIES}" ) ) ,
1935
+ entry_paths
1936
+ . config_path
1937
+ . join ( "loader" )
1938
+ . join ( BOOT_LOADER_ENTRIES ) ,
1861
1939
None ,
1862
1940
)
1863
1941
} ;
1864
1942
1865
- create_dir_all ( & entries_path ) . with_context ( || format ! ( "Creating {:?}" , entries_path ) ) ?;
1943
+ create_dir_all ( & config_path ) . with_context ( || format ! ( "Creating {:?}" , config_path ) ) ?;
1866
1944
1867
1945
// Scope to allow for proper unmounting
1868
1946
{
1869
1947
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 :?}" ) ) ?;
1948
+ cap_std:: fs:: Dir :: open_ambient_dir ( & config_path , cap_std:: ambient_authority ( ) )
1949
+ . with_context ( || format ! ( "Opening {config_path :?}" ) ) ?;
1872
1950
1873
1951
loader_entries_dir. atomic_write (
1874
1952
// SAFETY: We set sort_key above
@@ -1893,14 +1971,17 @@ pub(crate) fn setup_composefs_bls_boot(
1893
1971
let owned_loader_entries_fd = loader_entries_dir
1894
1972
. reopen_as_ownedfd ( )
1895
1973
. context ( "Reopening as owned fd" ) ?;
1974
+
1896
1975
rustix:: fs:: fsync ( owned_loader_entries_fd) . context ( "fsync" ) ?;
1897
1976
}
1898
1977
1899
- Command :: new ( "umount" )
1900
- . arg ( & mounted_efi)
1901
- . log_debug ( )
1902
- . run_inherited_with_cmd_context ( )
1903
- . context ( "Unmounting EFI" ) ?;
1978
+ if let Some ( mounted_efi) = entry_paths. mount_path {
1979
+ Command :: new ( "umount" )
1980
+ . arg ( mounted_efi)
1981
+ . log_debug ( )
1982
+ . run_inherited_with_cmd_context ( )
1983
+ . context ( "Unmounting EFI" ) ?;
1984
+ }
1904
1985
1905
1986
Ok ( boot_digest)
1906
1987
}
@@ -2231,6 +2312,11 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
2231
2312
false ,
2232
2313
boot_type,
2233
2314
boot_digest,
2315
+ state
2316
+ . composefs_options
2317
+ . as_ref ( )
2318
+ . map ( |opt| opt. bootloader . clone ( ) )
2319
+ . unwrap_or ( Bootloader :: default ( ) ) ,
2234
2320
) ?;
2235
2321
2236
2322
Ok ( ( ) )
@@ -2245,6 +2331,7 @@ pub(crate) fn write_composefs_state(
2245
2331
staged : bool ,
2246
2332
boot_type : BootType ,
2247
2333
boot_digest : Option < String > ,
2334
+ bootloader : Bootloader ,
2248
2335
) -> Result < ( ) > {
2249
2336
let state_path = root_path. join ( format ! ( "{STATE_DIR_RELATIVE}/{}" , deployment_id. to_hex( ) ) ) ;
2250
2337
@@ -2283,6 +2370,10 @@ pub(crate) fn write_composefs_state(
2283
2370
. item ( ORIGIN_KEY_BOOT_DIGEST , boot_digest) ;
2284
2371
}
2285
2372
2373
+ config = config
2374
+ . section ( ORIGIN_KEY_BOOT )
2375
+ . item ( ORIGIN_KEY_BOOTLOADER , bootloader) ;
2376
+
2286
2377
let state_dir = cap_std:: fs:: Dir :: open_ambient_dir ( & state_path, cap_std:: ambient_authority ( ) )
2287
2378
. context ( "Opening state dir" ) ?;
2288
2379
0 commit comments