@@ -45,7 +45,7 @@ use ostree_ext::composefs::{
4545 util:: Sha256Digest ,
4646} ;
4747use ostree_ext:: composefs_boot:: {
48- write_boot:: write_boot_simple as composefs_write_boot_simple, BootOps ,
48+ bootloader :: BootEntry , write_boot:: write_boot_simple as composefs_write_boot_simple, BootOps ,
4949} ;
5050use ostree_ext:: composefs_oci:: {
5151 image:: create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
@@ -240,7 +240,8 @@ pub(crate) enum BootType {
240240}
241241
242242#[ derive( Debug , Clone , clap:: Parser , Serialize , Deserialize , PartialEq , Eq ) ]
243- pub ( crate ) struct InstallComposefsOptions {
243+ pub ( crate ) struct InstallComposefsOpts {
244+ #[ clap( long, value_enum, default_value_t) ]
244245 pub ( crate ) boot : BootType ,
245246}
246247
@@ -269,10 +270,10 @@ pub(crate) struct InstallToDiskOpts {
269270 pub ( crate ) via_loopback : bool ,
270271
271272 #[ clap( long) ]
272- pub ( crate ) composefs_experimental : bool ,
273+ pub ( crate ) composefs_native : bool ,
273274
274275 #[ clap( flatten) ]
275- pub ( crate ) composefs_opts : InstallComposefsOptions ,
276+ pub ( crate ) composefs_opts : InstallComposefsOpts ,
276277}
277278
278279#[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
@@ -413,6 +414,9 @@ pub(crate) struct State {
413414 /// The root filesystem of the running container
414415 pub ( crate ) container_root : Dir ,
415416 pub ( crate ) tempdir : TempDir ,
417+
418+ // If Some, then --composefs_native is passed
419+ pub ( crate ) composefs_options : Option < InstallComposefsOpts > ,
416420}
417421
418422impl State {
@@ -558,7 +562,7 @@ impl FromStr for MountSpec {
558562
559563impl InstallToDiskOpts {
560564 pub ( crate ) fn validate ( & self ) {
561- if !self . composefs_experimental {
565+ if !self . composefs_native {
562566 // Reject using --boot without --composefs
563567 if self . composefs_opts . boot != BootType :: default ( ) {
564568 panic ! ( "--boot must not be provided without --composefs" ) ;
@@ -1226,6 +1230,7 @@ async fn prepare_install(
12261230 config_opts : InstallConfigOpts ,
12271231 source_opts : InstallSourceOpts ,
12281232 target_opts : InstallTargetOpts ,
1233+ composefs_opts : Option < InstallComposefsOpts > ,
12291234) -> Result < Arc < State > > {
12301235 tracing:: trace!( "Preparing install" ) ;
12311236 let rootfs = cap_std:: fs:: Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) )
@@ -1370,6 +1375,7 @@ async fn prepare_install(
13701375 container_root : rootfs,
13711376 tempdir,
13721377 host_is_container,
1378+ composefs_options : composefs_opts,
13731379 } ) ;
13741380
13751381 Ok ( state)
@@ -1494,37 +1500,14 @@ async fn initialize_composefs_repository(
14941500 composefs_oci_pull ( & Arc :: new ( repo) , & format ! ( "{transport}{name}" , ) , None ) . await
14951501}
14961502
1497- #[ context( "Setting up composefs boot" ) ]
1498- fn setup_composefs_boot ( root_setup : & RootSetup , state : & State , image_id : & str ) -> Result < ( ) > {
1499- let boot_uuid = root_setup
1500- . get_boot_uuid ( ) ?
1501- . or ( root_setup. rootfs_uuid . as_deref ( ) )
1502- . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1503-
1504- if cfg ! ( target_arch = "s390x" ) {
1505- // TODO: Integrate s390x support into install_via_bootupd
1506- crate :: bootloader:: install_via_zipl ( & root_setup. device_info , boot_uuid) ?;
1507- } else {
1508- crate :: bootloader:: install_via_bootupd (
1509- & root_setup. device_info ,
1510- & root_setup. physical_root_path ,
1511- & state. config_opts ,
1512- ) ?;
1513- }
1514-
1515- let repo = open_composefs_repo ( & root_setup. physical_root ) ?;
1516-
1517- let mut fs = create_composefs_filesystem ( & repo, image_id, None ) ?;
1518-
1519- let entries = fs. transform_for_boot ( & repo) ?;
1520- let id = fs. commit_image ( & repo, None ) ?;
1521-
1522- println ! ( "{entries:#?}" ) ;
1523-
1524- let Some ( entry) = entries. into_iter ( ) . next ( ) else {
1525- anyhow:: bail!( "No boot entries!" ) ;
1526- } ;
1527-
1503+ #[ context( "Setting up BLS boot" ) ]
1504+ fn setup_composefs_bls_boot (
1505+ root_setup : & RootSetup ,
1506+ // TODO: Make this generic
1507+ repo : ComposefsRepository < Sha256HashValue > ,
1508+ id : & Sha256HashValue ,
1509+ entry : BootEntry < Sha256HashValue > ,
1510+ ) -> Result < ( ) > {
15281511 let rootfs_uuid = match & root_setup. rootfs_uuid {
15291512 Some ( u) => u,
15301513 None => anyhow:: bail!( "Expected rootfs to have a UUID by now" ) ,
@@ -1536,6 +1519,32 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
15361519 "rw" ,
15371520 ] ;
15381521
1522+ composefs_write_boot_simple (
1523+ & repo,
1524+ entry,
1525+ & id,
1526+ root_setup. physical_root_path . as_std_path ( ) , // /run/mounts/bootc/boot
1527+ Some ( "boot" ) ,
1528+ Some ( & format ! ( "{}" , id. to_hex( ) ) ) ,
1529+ & cmdline_refs,
1530+ ) ?;
1531+
1532+ Ok ( ( ) )
1533+ }
1534+
1535+ #[ context( "Setting up UKI boot" ) ]
1536+ fn setup_composefs_uki_boot (
1537+ root_setup : & RootSetup ,
1538+ // TODO: Make this generic
1539+ repo : ComposefsRepository < Sha256HashValue > ,
1540+ id : & Sha256HashValue ,
1541+ entry : BootEntry < Sha256HashValue > ,
1542+ ) -> Result < ( ) > {
1543+ let rootfs_uuid = match & root_setup. rootfs_uuid {
1544+ Some ( u) => u,
1545+ None => anyhow:: bail!( "Expected rootfs to have a UUID by now" ) ,
1546+ } ;
1547+
15391548 let boot_dir = root_setup. physical_root_path . join ( "boot" ) ;
15401549 create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
15411550
@@ -1544,28 +1553,71 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
15441553 entry,
15451554 & id,
15461555 boot_dir. as_std_path ( ) ,
1556+ None ,
15471557 Some ( & format ! ( "{}" , id. to_hex( ) ) ) ,
1548- Some ( "/boot" ) ,
1549- & cmdline_refs,
1558+ & [ ] ,
15501559 ) ?;
15511560
15521561 // Add the user grug cfg
15531562 // TODO: We don't need this for BLS. Have a flag for BLS vs UKI, or maybe we can figure it out
15541563 // via the boot entries above
15551564 let grub_user_config = format ! (
15561565 r#"
1557- menuentry "Some Fedora" {{
1566+ menuentry "Fedora Bootc UKI " {{
15581567 insmod fat
15591568 insmod chain
15601569 search --no-floppy --set=root --fs-uuid {rootfs_uuid}
1561- chainloader /boot/EFI/Linux/uki .efi
1570+ chainloader /boot/EFI/Linux/{uki_id} .efi
15621571}}
1563- "#
1572+ "# , uki_id=id . to_hex ( )
15641573 ) ;
15651574
15661575 std:: fs:: write ( boot_dir. join ( "grub2/user.cfg" ) , grub_user_config)
15671576 . context ( "Failed to write grub2/user.cfg" ) ?;
15681577
1578+ Ok ( ( ) )
1579+ }
1580+
1581+ #[ context( "Setting up composefs boot" ) ]
1582+ fn setup_composefs_boot ( root_setup : & RootSetup , state : & State , image_id : & str ) -> Result < ( ) > {
1583+ let boot_uuid = root_setup
1584+ . get_boot_uuid ( ) ?
1585+ . or ( root_setup. rootfs_uuid . as_deref ( ) )
1586+ . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1587+
1588+ if cfg ! ( target_arch = "s390x" ) {
1589+ // TODO: Integrate s390x support into install_via_bootupd
1590+ crate :: bootloader:: install_via_zipl ( & root_setup. device_info , boot_uuid) ?;
1591+ } else {
1592+ crate :: bootloader:: install_via_bootupd (
1593+ & root_setup. device_info ,
1594+ & root_setup. physical_root_path ,
1595+ & state. config_opts ,
1596+ ) ?;
1597+ }
1598+
1599+ let repo = open_composefs_repo ( & root_setup. physical_root ) ?;
1600+
1601+ let mut fs = create_composefs_filesystem ( & repo, image_id, None ) ?;
1602+
1603+ let entries = fs. transform_for_boot ( & repo) ?;
1604+ let id = fs. commit_image ( & repo, None ) ?;
1605+
1606+ println ! ( "{entries:#?}" ) ;
1607+
1608+ let Some ( entry) = entries. into_iter ( ) . next ( ) else {
1609+ anyhow:: bail!( "No boot entries!" ) ;
1610+ } ;
1611+
1612+ let Some ( composefs_opts) = & state. composefs_options else {
1613+ anyhow:: bail!( "Could not find options for composefs" )
1614+ } ;
1615+
1616+ match composefs_opts. boot {
1617+ BootType :: Bls => setup_composefs_bls_boot ( root_setup, repo, & id, entry) ?,
1618+ BootType :: Uki => setup_composefs_uki_boot ( root_setup, repo, & id, entry) ?,
1619+ } ;
1620+
15691621 let state_path = root_setup
15701622 . physical_root_path
15711623 . join ( format ! ( "state/{}" , id. to_hex( ) ) ) ;
@@ -1581,7 +1633,6 @@ async fn install_to_filesystem_impl(
15811633 state : & State ,
15821634 rootfs : & mut RootSetup ,
15831635 cleanup : Cleanup ,
1584- composefs : bool ,
15851636) -> Result < ( ) > {
15861637 if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
15871638 rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1610,7 +1661,7 @@ async fn install_to_filesystem_impl(
16101661
16111662 let bound_images = BoundImages :: from_state ( state) . await ?;
16121663
1613- if composefs {
1664+ if state . composefs_options . is_some ( ) {
16141665 // Load a fd for the mounted target physical root
16151666 let ( id, verity) = initialize_composefs_repository ( state, rootfs) . await ?;
16161667
@@ -1694,7 +1745,17 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
16941745 } else if !target_blockdev_meta. file_type ( ) . is_block_device ( ) {
16951746 anyhow:: bail!( "Not a block device: {}" , block_opts. device) ;
16961747 }
1697- let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts ) . await ?;
1748+ let state = prepare_install (
1749+ opts. config_opts ,
1750+ opts. source_opts ,
1751+ opts. target_opts ,
1752+ if opts. composefs_native {
1753+ Some ( opts. composefs_opts )
1754+ } else {
1755+ None
1756+ } ,
1757+ )
1758+ . await ?;
16981759
16991760 // This is all blocking stuff
17001761 let ( mut rootfs, loopback) = {
@@ -1715,7 +1776,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
17151776 ( rootfs, loopback_dev)
17161777 } ;
17171778
1718- install_to_filesystem_impl ( & state, & mut rootfs, Cleanup :: Skip , opts . composefs_experimental ) . await ?;
1779+ install_to_filesystem_impl ( & state, & mut rootfs, Cleanup :: Skip ) . await ?;
17191780
17201781 // Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
17211782 let ( root_path, luksdev) = rootfs. into_storage ( ) ;
@@ -1902,7 +1963,7 @@ pub(crate) async fn install_to_filesystem(
19021963 // IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
19031964 // IMPORTANT: In practice, we should only be gathering information before this point,
19041965 // IMPORTANT: and not performing any mutations at all.
1905- let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts ) . await ?;
1966+ let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts , None ) . await ?;
19061967 // And the last bit of state here is the fsopts, which we also destructure now.
19071968 let mut fsopts = opts. filesystem_opts ;
19081969
@@ -2101,7 +2162,7 @@ pub(crate) async fn install_to_filesystem(
21012162 skip_finalize,
21022163 } ;
21032164
2104- install_to_filesystem_impl ( & state, & mut rootfs, cleanup, false ) . await ?;
2165+ install_to_filesystem_impl ( & state, & mut rootfs, cleanup) . await ?;
21052166
21062167 // Drop all data about the root except the path to ensure any file descriptors etc. are closed.
21072168 drop ( rootfs) ;
0 commit comments