6161//! 1. **Primary**: New/upgraded deployment (default boot target)
6262//! 2. **Secondary**: Currently booted deployment (rollback option)
6363
64- use std:: ffi:: OsStr ;
6564use std:: fs:: create_dir_all;
6665use std:: io:: Write ;
6766use std:: path:: Path ;
67+ use std:: sync:: Arc ;
6868
6969use anyhow:: { Context , Result , anyhow, bail} ;
7070use bootc_kernel_cmdline:: utf8:: { Cmdline , Parameter , ParameterKey } ;
@@ -81,43 +81,27 @@ use clap::ValueEnum;
8181use composefs:: fs:: read_file;
8282use composefs:: fsverity:: { FsVerityHashValue , Sha512HashValue } ;
8383use composefs:: tree:: RegularFile ;
84- use composefs_boot:: BootOps ;
8584use composefs_boot:: bootloader:: {
8685 BootEntry as ComposefsBootEntry , EFI_ADDON_DIR_EXT , EFI_ADDON_FILE_EXT , EFI_EXT , PEType ,
87- UsrLibModulesVmlinuz ,
86+ UsrLibModulesVmlinuz , get_boot_resources ,
8887} ;
8988use composefs_boot:: { cmdline:: get_cmdline_composefs, os_release:: OsReleaseInfo , uki} ;
90- use composefs_oci:: image:: create_filesystem as create_composefs_filesystem;
9189use fn_error_context:: context;
9290use rustix:: { mount:: MountFlags , path:: Arg } ;
9391use schemars:: JsonSchema ;
9492use serde:: { Deserialize , Serialize } ;
9593
94+ use crate :: bootc_composefs:: state:: { get_booted_bls, write_composefs_state} ;
95+ use crate :: bootc_kargs:: compute_new_kargs;
96+ use crate :: composefs_consts:: { TYPE1_BOOT_DIR_PREFIX , TYPE1_ENT_PATH , TYPE1_ENT_PATH_STAGED } ;
97+ use crate :: parsers:: bls_config:: { BLSConfig , BLSConfigType } ;
9698use crate :: task:: Task ;
97- use crate :: {
98- bootc_composefs:: repo:: get_imgref,
99- composefs_consts:: { TYPE1_ENT_PATH , TYPE1_ENT_PATH_STAGED } ,
100- } ;
101- use crate :: {
102- bootc_composefs:: repo:: open_composefs_repo,
103- store:: { ComposefsFilesystem , Storage } ,
104- } ;
105- use crate :: {
106- bootc_composefs:: state:: { get_booted_bls, write_composefs_state} ,
107- composefs_consts:: TYPE1_BOOT_DIR_PREFIX ,
108- } ;
109- use crate :: {
110- bootc_composefs:: status:: get_container_manifest_and_config, bootc_kargs:: compute_new_kargs,
111- } ;
99+ use crate :: { bootc_composefs:: repo:: open_composefs_repo, store:: Storage } ;
112100use crate :: { bootc_composefs:: status:: get_sorted_grub_uki_boot_entries, install:: PostFetchState } ;
113- use crate :: {
114- composefs_consts:: UKI_NAME_PREFIX ,
115- parsers:: bls_config:: { BLSConfig , BLSConfigType } ,
116- } ;
117101use crate :: {
118102 composefs_consts:: {
119103 BOOT_LOADER_ENTRIES , COMPOSEFS_CMDLINE , ORIGIN_KEY_BOOT , ORIGIN_KEY_BOOT_DIGEST ,
120- STAGED_BOOT_LOADER_ENTRIES , STATE_DIR_ABS , USER_CFG , USER_CFG_STAGED ,
104+ STAGED_BOOT_LOADER_ENTRIES , STATE_DIR_ABS , UKI_NAME_PREFIX , USER_CFG , USER_CFG_STAGED ,
121105 } ,
122106 spec:: { Bootloader , Host } ,
123107} ;
@@ -149,23 +133,9 @@ pub(crate) const BOOTC_UKI_DIR: &str = "EFI/Linux/bootc";
149133
150134pub ( crate ) enum BootSetupType < ' a > {
151135 /// For initial setup, i.e. install to-disk
152- Setup (
153- (
154- & ' a RootSetup ,
155- & ' a State ,
156- & ' a PostFetchState ,
157- & ' a ComposefsFilesystem ,
158- ) ,
159- ) ,
136+ Setup ( ( & ' a RootSetup , & ' a State , & ' a PostFetchState ) ) ,
160137 /// For `bootc upgrade`
161- Upgrade (
162- (
163- & ' a Storage ,
164- & ' a BootedComposefs ,
165- & ' a ComposefsFilesystem ,
166- & ' a Host ,
167- ) ,
168- ) ,
138+ Upgrade ( ( & ' a Storage , & ' a BootedComposefs , & ' a Host ) ) ,
169139}
170140
171141#[ derive(
@@ -448,41 +418,20 @@ fn write_bls_boot_entries_to_disk(
448418}
449419
450420/// Parses /usr/lib/os-release and returns (id, title, version)
451- fn parse_os_release (
452- fs : & crate :: store:: ComposefsFilesystem ,
453- repo : & crate :: store:: ComposefsRepository ,
454- ) -> Result < Option < ( String , Option < String > , Option < String > ) > > {
421+ fn parse_os_release ( mounted_fs : & Dir ) -> Result < Option < ( String , Option < String > , Option < String > ) > > {
455422 // Every update should have its own /usr/lib/os-release
456- let ( dir, fname) = fs
457- . root
458- . split ( OsStr :: new ( "/usr/lib/os-release" ) )
459- . context ( "Getting /usr/lib/os-release" ) ?;
460-
461- let os_release = dir
462- . get_file_opt ( fname)
463- . context ( "Getting /usr/lib/os-release" ) ?;
464-
465- let Some ( os_rel_file) = os_release else {
466- return Ok ( None ) ;
467- } ;
468-
469- let file_contents = match read_file ( os_rel_file, repo) {
423+ let file_contents = match mounted_fs. read_to_string ( "usr/lib/os-release" ) {
470424 Ok ( c) => c,
471- Err ( e) => {
472- tracing:: warn!( "Could not read /usr/lib/os-release: {e:?}" ) ;
425+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: NotFound => {
473426 return Ok ( None ) ;
474427 }
475- } ;
476-
477- let file_contents = match std:: str:: from_utf8 ( & file_contents) {
478- Ok ( c) => c,
479428 Err ( e) => {
480- tracing:: warn!( "/usr/lib/os-release did not have valid UTF-8 : {e}" ) ;
429+ tracing:: warn!( "Could not read /usr/lib/os-release: {e:? }" ) ;
481430 return Ok ( None ) ;
482431 }
483432 } ;
484433
485- let parsed = OsReleaseInfo :: parse ( file_contents) ;
434+ let parsed = OsReleaseInfo :: parse ( & file_contents) ;
486435
487436 let os_id = parsed
488437 . get_value ( & [ "ID" ] )
@@ -518,8 +467,8 @@ pub(crate) fn setup_composefs_bls_boot(
518467) -> Result < String > {
519468 let id_hex = id. to_hex ( ) ;
520469
521- let ( root_path, esp_device, mut cmdline_refs, fs , bootloader) = match setup_type {
522- BootSetupType :: Setup ( ( root_setup, state, postfetch, fs ) ) => {
470+ let ( root_path, esp_device, mut cmdline_refs, bootloader) = match setup_type {
471+ BootSetupType :: Setup ( ( root_setup, state, postfetch) ) => {
523472 // root_setup.kargs has [root=UUID=<UUID>, "rw"]
524473 let mut cmdline_options = Cmdline :: new ( ) ;
525474
@@ -541,12 +490,11 @@ pub(crate) fn setup_composefs_bls_boot(
541490 root_setup. physical_root_path . clone ( ) ,
542491 esp_part. path ( ) ,
543492 cmdline_options,
544- fs,
545493 postfetch. detected_bootloader . clone ( ) ,
546494 )
547495 }
548496
549- BootSetupType :: Upgrade ( ( storage, booted_cfs, fs , host) ) => {
497+ BootSetupType :: Upgrade ( ( storage, booted_cfs, host) ) => {
550498 let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
551499
552500 let boot_dir = storage. require_boot_dir ( ) ?;
@@ -583,7 +531,6 @@ pub(crate) fn setup_composefs_bls_boot(
583531 Utf8PathBuf :: from ( "/sysroot" ) ,
584532 esp_dev. path ( ) ,
585533 cmdline,
586- fs,
587534 bootloader,
588535 )
589536 }
@@ -657,7 +604,7 @@ pub(crate) fn setup_composefs_bls_boot(
657604 let boot_digest = compute_boot_digest ( usr_lib_modules_vmlinuz, & repo)
658605 . context ( "Computing boot digest" ) ?;
659606
660- let osrel = parse_os_release ( fs , & repo ) ?;
607+ let osrel = parse_os_release ( mounted_erofs ) ?;
661608
662609 let ( os_id, title, version, sort_key) = match osrel {
663610 Some ( ( id_str, title_opt, version_opt) ) => (
@@ -1102,7 +1049,7 @@ pub(crate) fn setup_composefs_uki_boot(
11021049) -> Result < String > {
11031050 let ( root_path, esp_device, bootloader, missing_fsverity_allowed, uki_addons) = match setup_type
11041051 {
1105- BootSetupType :: Setup ( ( root_setup, state, postfetch, .. ) ) => {
1052+ BootSetupType :: Setup ( ( root_setup, state, postfetch) ) => {
11061053 state. require_no_kargs_for_uki ( ) ?;
11071054
11081055 let esp_part = root_setup. device_info . find_partition_of_esp ( ) ?;
@@ -1116,7 +1063,7 @@ pub(crate) fn setup_composefs_uki_boot(
11161063 )
11171064 }
11181065
1119- BootSetupType :: Upgrade ( ( storage, booted_cfs, _ , host) ) => {
1066+ BootSetupType :: Upgrade ( ( storage, booted_cfs, host) ) => {
11201067 let sysroot = Utf8PathBuf :: from ( "/sysroot" ) ; // Still needed for root_path
11211068 let bootloader = host. require_composefs_booted ( ) ?. bootloader . clone ( ) ;
11221069
@@ -1259,25 +1206,34 @@ fn get_secureboot_keys(fs: &Dir, p: &str) -> Result<Option<SecurebootKeys>> {
12591206pub ( crate ) async fn setup_composefs_boot (
12601207 root_setup : & RootSetup ,
12611208 state : & State ,
1262- image_id : & str ,
1209+ pull_result : & composefs_oci :: skopeo :: PullResult < Sha512HashValue > ,
12631210 allow_missing_fsverity : bool ,
12641211) -> Result < ( ) > {
12651212 const COMPOSEFS_BOOT_SETUP_JOURNAL_ID : & str = "1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5" ;
12661213
12671214 tracing:: info!(
12681215 message_id = COMPOSEFS_BOOT_SETUP_JOURNAL_ID ,
12691216 bootc. operation = "boot_setup" ,
1270- bootc. image_id = image_id ,
1217+ bootc. config_digest = pull_result . config_digest ,
12711218 bootc. allow_missing_fsverity = allow_missing_fsverity,
12721219 "Setting up composefs boot" ,
12731220 ) ;
12741221
12751222 let mut repo = open_composefs_repo ( & root_setup. physical_root ) ?;
12761223 repo. set_insecure ( allow_missing_fsverity) ;
12771224
1278- let mut fs = create_composefs_filesystem ( & repo, image_id, None ) ?;
1279- let entries = fs. transform_for_boot ( & repo) ?;
1280- let id = fs. commit_image ( & repo, None ) ?;
1225+ let repo = Arc :: new ( repo) ;
1226+
1227+ // Generate the bootable EROFS image (idempotent).
1228+ let id = composefs_oci:: generate_boot_image ( & repo, & pull_result. manifest_digest )
1229+ . context ( "Generating bootable EROFS image" ) ?;
1230+
1231+ // Get boot entries from the OCI filesystem (untransformed).
1232+ let fs = composefs_oci:: image:: create_filesystem ( & * repo, & pull_result. config_digest , None )
1233+ . context ( "Creating composefs filesystem for boot entry discovery" ) ?;
1234+ let entries =
1235+ get_boot_resources ( & fs, & * repo) . context ( "Extracting boot entries from OCI image" ) ?;
1236+
12811237 let mounted_fs = Dir :: reopen_dir (
12821238 & repo
12831239 . mount ( & id. to_hex ( ) )
@@ -1320,16 +1276,23 @@ pub(crate) async fn setup_composefs_boot(
13201276
13211277 let boot_type = BootType :: from ( entry) ;
13221278
1279+ // Unwrap Arc to pass owned repo to boot setup functions.
1280+ let repo = Arc :: try_unwrap ( repo) . map_err ( |_| {
1281+ anyhow:: anyhow!(
1282+ "BUG: Arc<Repository> still has other references after boot image generation"
1283+ )
1284+ } ) ?;
1285+
13231286 let boot_digest = match boot_type {
13241287 BootType :: Bls => setup_composefs_bls_boot (
1325- BootSetupType :: Setup ( ( & root_setup, & state, & postfetch, & fs ) ) ,
1288+ BootSetupType :: Setup ( ( & root_setup, & state, & postfetch) ) ,
13261289 repo,
13271290 & id,
13281291 entry,
13291292 & mounted_fs,
13301293 ) ?,
13311294 BootType :: Uki => setup_composefs_uki_boot (
1332- BootSetupType :: Setup ( ( & root_setup, & state, & postfetch, & fs ) ) ,
1295+ BootSetupType :: Setup ( ( & root_setup, & state, & postfetch) ) ,
13331296 repo,
13341297 & id,
13351298 entries,
@@ -1343,11 +1306,7 @@ pub(crate) async fn setup_composefs_boot(
13431306 None ,
13441307 boot_type,
13451308 boot_digest,
1346- & get_container_manifest_and_config ( & get_imgref (
1347- & state. source . imageref . transport . to_string ( ) ,
1348- & state. source . imageref . name ,
1349- ) )
1350- . await ?,
1309+ & pull_result. manifest_digest ,
13511310 allow_missing_fsverity,
13521311 )
13531312 . await ?;
0 commit comments