@@ -45,7 +45,7 @@ use ostree_ext::composefs::{
45
45
util:: Sha256Digest ,
46
46
} ;
47
47
use 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 ,
49
49
} ;
50
50
use ostree_ext:: composefs_oci:: {
51
51
image:: create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
@@ -240,7 +240,8 @@ pub(crate) enum BootType {
240
240
}
241
241
242
242
#[ 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) ]
244
245
pub ( crate ) boot : BootType ,
245
246
}
246
247
@@ -269,10 +270,10 @@ pub(crate) struct InstallToDiskOpts {
269
270
pub ( crate ) via_loopback : bool ,
270
271
271
272
#[ clap( long) ]
272
- pub ( crate ) composefs_experimental : bool ,
273
+ pub ( crate ) composefs_native : bool ,
273
274
274
275
#[ clap( flatten) ]
275
- pub ( crate ) composefs_opts : InstallComposefsOptions ,
276
+ pub ( crate ) composefs_opts : InstallComposefsOpts ,
276
277
}
277
278
278
279
#[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
@@ -413,6 +414,9 @@ pub(crate) struct State {
413
414
/// The root filesystem of the running container
414
415
pub ( crate ) container_root : Dir ,
415
416
pub ( crate ) tempdir : TempDir ,
417
+
418
+ // If Some, then --composefs_native is passed
419
+ pub ( crate ) composefs_options : Option < InstallComposefsOpts > ,
416
420
}
417
421
418
422
impl State {
@@ -558,7 +562,7 @@ impl FromStr for MountSpec {
558
562
559
563
impl InstallToDiskOpts {
560
564
pub ( crate ) fn validate ( & self ) {
561
- if !self . composefs_experimental {
565
+ if !self . composefs_native {
562
566
// Reject using --boot without --composefs
563
567
if self . composefs_opts . boot != BootType :: default ( ) {
564
568
panic ! ( "--boot must not be provided without --composefs" ) ;
@@ -1226,6 +1230,7 @@ async fn prepare_install(
1226
1230
config_opts : InstallConfigOpts ,
1227
1231
source_opts : InstallSourceOpts ,
1228
1232
target_opts : InstallTargetOpts ,
1233
+ composefs_opts : Option < InstallComposefsOpts > ,
1229
1234
) -> Result < Arc < State > > {
1230
1235
tracing:: trace!( "Preparing install" ) ;
1231
1236
let rootfs = cap_std:: fs:: Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) )
@@ -1370,6 +1375,7 @@ async fn prepare_install(
1370
1375
container_root : rootfs,
1371
1376
tempdir,
1372
1377
host_is_container,
1378
+ composefs_options : composefs_opts,
1373
1379
} ) ;
1374
1380
1375
1381
Ok ( state)
@@ -1494,37 +1500,14 @@ async fn initialize_composefs_repository(
1494
1500
composefs_oci_pull ( & Arc :: new ( repo) , & format ! ( "{transport}{name}" , ) , None ) . await
1495
1501
}
1496
1502
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 < ( ) > {
1528
1511
let rootfs_uuid = match & root_setup. rootfs_uuid {
1529
1512
Some ( u) => u,
1530
1513
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) -
1536
1519
"rw" ,
1537
1520
] ;
1538
1521
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
+
1539
1548
let boot_dir = root_setup. physical_root_path . join ( "boot" ) ;
1540
1549
create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
1541
1550
@@ -1544,28 +1553,71 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
1544
1553
entry,
1545
1554
& id,
1546
1555
boot_dir. as_std_path ( ) ,
1556
+ None ,
1547
1557
Some ( & format ! ( "{}" , id. to_hex( ) ) ) ,
1548
- Some ( "/boot" ) ,
1549
- & cmdline_refs,
1558
+ & [ ] ,
1550
1559
) ?;
1551
1560
1552
1561
// Add the user grug cfg
1553
1562
// TODO: We don't need this for BLS. Have a flag for BLS vs UKI, or maybe we can figure it out
1554
1563
// via the boot entries above
1555
1564
let grub_user_config = format ! (
1556
1565
r#"
1557
- menuentry "Some Fedora" {{
1566
+ menuentry "Fedora Bootc UKI " {{
1558
1567
insmod fat
1559
1568
insmod chain
1560
1569
search --no-floppy --set=root --fs-uuid {rootfs_uuid}
1561
- chainloader /boot/EFI/Linux/uki .efi
1570
+ chainloader /boot/EFI/Linux/{uki_id} .efi
1562
1571
}}
1563
- "#
1572
+ "# , uki_id=id . to_hex ( )
1564
1573
) ;
1565
1574
1566
1575
std:: fs:: write ( boot_dir. join ( "grub2/user.cfg" ) , grub_user_config)
1567
1576
. context ( "Failed to write grub2/user.cfg" ) ?;
1568
1577
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
+
1569
1621
let state_path = root_setup
1570
1622
. physical_root_path
1571
1623
. join ( format ! ( "state/{}" , id. to_hex( ) ) ) ;
@@ -1581,7 +1633,6 @@ async fn install_to_filesystem_impl(
1581
1633
state : & State ,
1582
1634
rootfs : & mut RootSetup ,
1583
1635
cleanup : Cleanup ,
1584
- composefs : bool ,
1585
1636
) -> Result < ( ) > {
1586
1637
if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
1587
1638
rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1610,7 +1661,7 @@ async fn install_to_filesystem_impl(
1610
1661
1611
1662
let bound_images = BoundImages :: from_state ( state) . await ?;
1612
1663
1613
- if composefs {
1664
+ if state . composefs_options . is_some ( ) {
1614
1665
// Load a fd for the mounted target physical root
1615
1666
let ( id, verity) = initialize_composefs_repository ( state, rootfs) . await ?;
1616
1667
@@ -1694,7 +1745,17 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
1694
1745
} else if !target_blockdev_meta. file_type ( ) . is_block_device ( ) {
1695
1746
anyhow:: bail!( "Not a block device: {}" , block_opts. device) ;
1696
1747
}
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 ?;
1698
1759
1699
1760
// This is all blocking stuff
1700
1761
let ( mut rootfs, loopback) = {
@@ -1715,7 +1776,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
1715
1776
( rootfs, loopback_dev)
1716
1777
} ;
1717
1778
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 ?;
1719
1780
1720
1781
// Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
1721
1782
let ( root_path, luksdev) = rootfs. into_storage ( ) ;
@@ -1902,7 +1963,7 @@ pub(crate) async fn install_to_filesystem(
1902
1963
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
1903
1964
// IMPORTANT: In practice, we should only be gathering information before this point,
1904
1965
// 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 ?;
1906
1967
// And the last bit of state here is the fsopts, which we also destructure now.
1907
1968
let mut fsopts = opts. filesystem_opts ;
1908
1969
@@ -2101,7 +2162,7 @@ pub(crate) async fn install_to_filesystem(
2101
2162
skip_finalize,
2102
2163
} ;
2103
2164
2104
- install_to_filesystem_impl ( & state, & mut rootfs, cleanup, false ) . await ?;
2165
+ install_to_filesystem_impl ( & state, & mut rootfs, cleanup) . await ?;
2105
2166
2106
2167
// Drop all data about the root except the path to ensure any file descriptors etc. are closed.
2107
2168
drop ( rootfs) ;
0 commit comments