@@ -46,7 +46,7 @@ use ostree_ext::composefs::{
46
46
util:: Sha256Digest ,
47
47
} ;
48
48
use ostree_ext:: composefs_boot:: {
49
- write_boot:: write_boot_simple as composefs_write_boot_simple, BootOps ,
49
+ bootloader :: BootEntry , write_boot:: write_boot_simple as composefs_write_boot_simple, BootOps ,
50
50
} ;
51
51
use ostree_ext:: composefs_oci:: {
52
52
image:: create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
@@ -241,7 +241,8 @@ pub(crate) enum BootType {
241
241
}
242
242
243
243
#[ derive( Debug , Clone , clap:: Parser , Serialize , Deserialize , PartialEq , Eq ) ]
244
- pub ( crate ) struct InstallComposefsOptions {
244
+ pub ( crate ) struct InstallComposefsOpts {
245
+ #[ clap( long, value_enum, default_value_t) ]
245
246
pub ( crate ) boot : BootType ,
246
247
}
247
248
@@ -270,10 +271,10 @@ pub(crate) struct InstallToDiskOpts {
270
271
pub ( crate ) via_loopback : bool ,
271
272
272
273
#[ clap( long) ]
273
- pub ( crate ) composefs_experimental : bool ,
274
+ pub ( crate ) composefs_native : bool ,
274
275
275
276
#[ clap( flatten) ]
276
- pub ( crate ) composefs_opts : InstallComposefsOptions ,
277
+ pub ( crate ) composefs_opts : InstallComposefsOpts ,
277
278
}
278
279
279
280
#[ derive( ValueEnum , Debug , Copy , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
@@ -414,6 +415,9 @@ pub(crate) struct State {
414
415
/// The root filesystem of the running container
415
416
pub ( crate ) container_root : Dir ,
416
417
pub ( crate ) tempdir : TempDir ,
418
+
419
+ // If Some, then --composefs_native is passed
420
+ pub ( crate ) composefs_options : Option < InstallComposefsOpts > ,
417
421
}
418
422
419
423
impl State {
@@ -557,7 +561,7 @@ impl FromStr for MountSpec {
557
561
558
562
impl InstallToDiskOpts {
559
563
pub ( crate ) fn validate ( & self ) {
560
- if !self . composefs_experimental {
564
+ if !self . composefs_native {
561
565
// Reject using --boot without --composefs
562
566
if self . composefs_opts . boot != BootType :: default ( ) {
563
567
panic ! ( "--boot must not be provided without --composefs" ) ;
@@ -1225,6 +1229,7 @@ async fn prepare_install(
1225
1229
config_opts : InstallConfigOpts ,
1226
1230
source_opts : InstallSourceOpts ,
1227
1231
target_opts : InstallTargetOpts ,
1232
+ composefs_opts : Option < InstallComposefsOpts > ,
1228
1233
) -> Result < Arc < State > > {
1229
1234
tracing:: trace!( "Preparing install" ) ;
1230
1235
let rootfs = cap_std:: fs:: Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) )
@@ -1369,6 +1374,7 @@ async fn prepare_install(
1369
1374
container_root : rootfs,
1370
1375
tempdir,
1371
1376
host_is_container,
1377
+ composefs_options : composefs_opts,
1372
1378
} ) ;
1373
1379
1374
1380
Ok ( state)
@@ -1492,37 +1498,14 @@ async fn initialize_composefs_repository(
1492
1498
composefs_oci_pull ( & Arc :: new ( repo) , & format ! ( "{transport}{name}" , ) , None ) . await
1493
1499
}
1494
1500
1495
- #[ context( "Setting up composefs boot" ) ]
1496
- fn setup_composefs_boot ( root_setup : & RootSetup , state : & State , image_id : & str ) -> Result < ( ) > {
1497
- let boot_uuid = root_setup
1498
- . get_boot_uuid ( ) ?
1499
- . or ( root_setup. rootfs_uuid . as_deref ( ) )
1500
- . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1501
-
1502
- if cfg ! ( target_arch = "s390x" ) {
1503
- // TODO: Integrate s390x support into install_via_bootupd
1504
- crate :: bootloader:: install_via_zipl ( & root_setup. device_info , boot_uuid) ?;
1505
- } else {
1506
- crate :: bootloader:: install_via_bootupd (
1507
- & root_setup. device_info ,
1508
- & root_setup. physical_root_path ,
1509
- & state. config_opts ,
1510
- ) ?;
1511
- }
1512
-
1513
- let repo = open_composefs_repo ( & root_setup. physical_root ) ?;
1514
-
1515
- let mut fs = create_composefs_filesystem ( & repo, image_id, None ) ?;
1516
-
1517
- let entries = fs. transform_for_boot ( & repo) ?;
1518
- let id = fs. commit_image ( & repo, None ) ?;
1519
-
1520
- println ! ( "{entries:#?}" ) ;
1521
-
1522
- let Some ( entry) = entries. into_iter ( ) . next ( ) else {
1523
- anyhow:: bail!( "No boot entries!" ) ;
1524
- } ;
1525
-
1501
+ #[ context( "Setting up BLS boot" ) ]
1502
+ fn setup_composefs_bls_boot (
1503
+ root_setup : & RootSetup ,
1504
+ // TODO: Make this generic
1505
+ repo : ComposefsRepository < Sha256HashValue > ,
1506
+ id : & Sha256HashValue ,
1507
+ entry : BootEntry < Sha256HashValue > ,
1508
+ ) -> Result < ( ) > {
1526
1509
let rootfs_uuid = match & root_setup. rootfs_uuid {
1527
1510
Some ( u) => u,
1528
1511
None => anyhow:: bail!( "Expected rootfs to have a UUID by now" ) ,
@@ -1534,6 +1517,32 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
1534
1517
"rw" ,
1535
1518
] ;
1536
1519
1520
+ composefs_write_boot_simple (
1521
+ & repo,
1522
+ entry,
1523
+ & id,
1524
+ root_setup. physical_root_path . as_std_path ( ) , // /run/mounts/bootc/boot
1525
+ Some ( "boot" ) ,
1526
+ Some ( & format ! ( "{}" , id. to_hex( ) ) ) ,
1527
+ & cmdline_refs,
1528
+ ) ?;
1529
+
1530
+ Ok ( ( ) )
1531
+ }
1532
+
1533
+ #[ context( "Setting up UKI boot" ) ]
1534
+ fn setup_composefs_uki_boot (
1535
+ root_setup : & RootSetup ,
1536
+ // TODO: Make this generic
1537
+ repo : ComposefsRepository < Sha256HashValue > ,
1538
+ id : & Sha256HashValue ,
1539
+ entry : BootEntry < Sha256HashValue > ,
1540
+ ) -> Result < ( ) > {
1541
+ let rootfs_uuid = match & root_setup. rootfs_uuid {
1542
+ Some ( u) => u,
1543
+ None => anyhow:: bail!( "Expected rootfs to have a UUID by now" ) ,
1544
+ } ;
1545
+
1537
1546
let boot_dir = root_setup. physical_root_path . join ( "boot" ) ;
1538
1547
create_dir_all ( & boot_dir) . context ( "Failed to create boot dir" ) ?;
1539
1548
@@ -1542,28 +1551,71 @@ fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -
1542
1551
entry,
1543
1552
& id,
1544
1553
boot_dir. as_std_path ( ) ,
1554
+ None ,
1545
1555
Some ( & format ! ( "{}" , id. to_hex( ) ) ) ,
1546
- Some ( "/boot" ) ,
1547
- & cmdline_refs,
1556
+ & [ ] ,
1548
1557
) ?;
1549
1558
1550
1559
// Add the user grug cfg
1551
1560
// TODO: We don't need this for BLS. Have a flag for BLS vs UKI, or maybe we can figure it out
1552
1561
// via the boot entries above
1553
1562
let grub_user_config = format ! (
1554
1563
r#"
1555
- menuentry "Some Fedora" {{
1564
+ menuentry "Fedora Bootc UKI " {{
1556
1565
insmod fat
1557
1566
insmod chain
1558
1567
search --no-floppy --set=root --fs-uuid {rootfs_uuid}
1559
- chainloader /boot/EFI/Linux/uki .efi
1568
+ chainloader /boot/EFI/Linux/{uki_id} .efi
1560
1569
}}
1561
- "#
1570
+ "# , uki_id=id . to_hex ( )
1562
1571
) ;
1563
1572
1564
1573
std:: fs:: write ( boot_dir. join ( "grub2/user.cfg" ) , grub_user_config)
1565
1574
. context ( "Failed to write grub2/user.cfg" ) ?;
1566
1575
1576
+ Ok ( ( ) )
1577
+ }
1578
+
1579
+ #[ context( "Setting up composefs boot" ) ]
1580
+ fn setup_composefs_boot ( root_setup : & RootSetup , state : & State , image_id : & str ) -> Result < ( ) > {
1581
+ let boot_uuid = root_setup
1582
+ . get_boot_uuid ( ) ?
1583
+ . or ( root_setup. rootfs_uuid . as_deref ( ) )
1584
+ . ok_or_else ( || anyhow ! ( "No uuid for boot/root" ) ) ?;
1585
+
1586
+ if cfg ! ( target_arch = "s390x" ) {
1587
+ // TODO: Integrate s390x support into install_via_bootupd
1588
+ crate :: bootloader:: install_via_zipl ( & root_setup. device_info , boot_uuid) ?;
1589
+ } else {
1590
+ crate :: bootloader:: install_via_bootupd (
1591
+ & root_setup. device_info ,
1592
+ & root_setup. physical_root_path ,
1593
+ & state. config_opts ,
1594
+ ) ?;
1595
+ }
1596
+
1597
+ let repo = open_composefs_repo ( & root_setup. physical_root ) ?;
1598
+
1599
+ let mut fs = create_composefs_filesystem ( & repo, image_id, None ) ?;
1600
+
1601
+ let entries = fs. transform_for_boot ( & repo) ?;
1602
+ let id = fs. commit_image ( & repo, None ) ?;
1603
+
1604
+ println ! ( "{entries:#?}" ) ;
1605
+
1606
+ let Some ( entry) = entries. into_iter ( ) . next ( ) else {
1607
+ anyhow:: bail!( "No boot entries!" ) ;
1608
+ } ;
1609
+
1610
+ let Some ( composefs_opts) = & state. composefs_options else {
1611
+ anyhow:: bail!( "Could not find options for composefs" )
1612
+ } ;
1613
+
1614
+ match composefs_opts. boot {
1615
+ BootType :: Bls => setup_composefs_bls_boot ( root_setup, repo, & id, entry) ?,
1616
+ BootType :: Uki => setup_composefs_uki_boot ( root_setup, repo, & id, entry) ?,
1617
+ } ;
1618
+
1567
1619
let state_path = root_setup
1568
1620
. physical_root_path
1569
1621
. join ( format ! ( "state/{}" , id. to_hex( ) ) ) ;
@@ -1579,7 +1631,6 @@ async fn install_to_filesystem_impl(
1579
1631
state : & State ,
1580
1632
rootfs : & mut RootSetup ,
1581
1633
cleanup : Cleanup ,
1582
- composefs : bool ,
1583
1634
) -> Result < ( ) > {
1584
1635
if matches ! ( state. selinux_state, SELinuxFinalState :: ForceTargetDisabled ) {
1585
1636
rootfs. kargs . push ( "selinux=0" . to_string ( ) ) ;
@@ -1608,7 +1659,7 @@ async fn install_to_filesystem_impl(
1608
1659
1609
1660
let bound_images = BoundImages :: from_state ( state) . await ?;
1610
1661
1611
- if composefs {
1662
+ if state . composefs_options . is_some ( ) {
1612
1663
// Load a fd for the mounted target physical root
1613
1664
let ( id, verity) = initialize_composefs_repository ( state, rootfs) . await ?;
1614
1665
@@ -1692,7 +1743,17 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
1692
1743
} else if !target_blockdev_meta. file_type ( ) . is_block_device ( ) {
1693
1744
anyhow:: bail!( "Not a block device: {}" , block_opts. device) ;
1694
1745
}
1695
- let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts ) . await ?;
1746
+ let state = prepare_install (
1747
+ opts. config_opts ,
1748
+ opts. source_opts ,
1749
+ opts. target_opts ,
1750
+ if opts. composefs_native {
1751
+ Some ( opts. composefs_opts )
1752
+ } else {
1753
+ None
1754
+ } ,
1755
+ )
1756
+ . await ?;
1696
1757
1697
1758
// This is all blocking stuff
1698
1759
let ( mut rootfs, loopback) = {
@@ -1713,7 +1774,7 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
1713
1774
( rootfs, loopback_dev)
1714
1775
} ;
1715
1776
1716
- install_to_filesystem_impl ( & state, & mut rootfs, Cleanup :: Skip , opts . composefs_experimental ) . await ?;
1777
+ install_to_filesystem_impl ( & state, & mut rootfs, Cleanup :: Skip ) . await ?;
1717
1778
1718
1779
// Drop all data about the root except the bits we need to ensure any file descriptors etc. are closed.
1719
1780
let ( root_path, luksdev) = rootfs. into_storage ( ) ;
@@ -1900,7 +1961,7 @@ pub(crate) async fn install_to_filesystem(
1900
1961
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
1901
1962
// IMPORTANT: In practice, we should only be gathering information before this point,
1902
1963
// IMPORTANT: and not performing any mutations at all.
1903
- let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts ) . await ?;
1964
+ let state = prepare_install ( opts. config_opts , opts. source_opts , opts. target_opts , None ) . await ?;
1904
1965
// And the last bit of state here is the fsopts, which we also destructure now.
1905
1966
let mut fsopts = opts. filesystem_opts ;
1906
1967
@@ -2099,7 +2160,7 @@ pub(crate) async fn install_to_filesystem(
2099
2160
skip_finalize,
2100
2161
} ;
2101
2162
2102
- install_to_filesystem_impl ( & state, & mut rootfs, cleanup, false ) . await ?;
2163
+ install_to_filesystem_impl ( & state, & mut rootfs, cleanup) . await ?;
2103
2164
2104
2165
// Drop all data about the root except the path to ensure any file descriptors etc. are closed.
2105
2166
drop ( rootfs) ;
0 commit comments