@@ -7,10 +7,12 @@ use composefs_ctl::composefs;
77use composefs_ctl:: composefs_boot;
88use composefs_ctl:: composefs_oci;
99use composefs_oci:: image:: create_filesystem;
10+ use etc_merge:: print_unmergable_paths;
1011use fn_error_context:: context;
1112use ocidir:: cap_std:: ambient_authority;
1213use ostree_ext:: container:: ManifestDiff ;
1314
15+ use crate :: bootc_composefs:: finalize:: get_etc_diff;
1416use crate :: bootc_composefs:: gc:: GCOpts ;
1517use crate :: spec:: BootloaderKind ;
1618use crate :: {
@@ -288,6 +290,18 @@ pub(crate) async fn do_upgrade(
288290 . context ( "Failed to mount composefs image" ) ?,
289291 ) ?;
290292
293+ // Check if three way etc merge is possible without conflicts
294+ let new_etc = mounted_fs
295+ . open_dir ( "etc" )
296+ . context ( "Opening deployment's etc" ) ?;
297+
298+ let diff = get_etc_diff ( storage, booted_cfs, Some ( & new_etc) ) . await ?;
299+
300+ if !diff. unmergable_paths . is_empty ( ) {
301+ print_unmergable_paths ( & diff, & mut std:: io:: stderr ( ) ) ;
302+ anyhow:: bail!( "Merge conflicts found in etc" ) ;
303+ }
304+
291305 let boot_type = BootType :: from ( entry) ;
292306
293307 let boot_digest = match boot_type {
@@ -307,14 +321,16 @@ pub(crate) async fn do_upgrade(
307321 ) ?,
308322 } ;
309323
324+ let staged_state = StagedDeployment {
325+ depl_id : id. to_hex ( ) ,
326+ finalization_locked : opts. download_only ,
327+ } ;
328+
310329 write_composefs_state (
311330 & Utf8PathBuf :: from ( "/sysroot" ) ,
312331 & id,
313332 imgref,
314- Some ( StagedDeployment {
315- depl_id : id. to_hex ( ) ,
316- finalization_locked : opts. download_only ,
317- } ) ,
333+ Some ( staged_state) ,
318334 boot_type,
319335 boot_digest,
320336 & manifest_digest,
@@ -393,16 +409,20 @@ pub(crate) async fn upgrade_composefs(
393409
394410 start_finalize_stated_svc ( ) ?;
395411
396- // Make the staged deployment not download_only
397- let new_staged = StagedDeployment {
398- depl_id : staged. require_composefs ( ) ?. verity . clone ( ) ,
399- finalization_locked : false ,
400- } ;
401-
402412 let staged_depl_dir =
403413 Dir :: open_ambient_dir ( COMPOSEFS_TRANSIENT_STATE_DIR , ambient_authority ( ) )
404414 . context ( "Opening transient state directory" ) ?;
405415
416+ let current = staged_depl_dir
417+ . read_to_string ( COMPOSEFS_STAGED_DEPLOYMENT_FNAME )
418+ . context ( "Reading staged file" ) ?;
419+
420+ let mut new_staged: StagedDeployment =
421+ serde_json:: from_str ( & current) . context ( "Deserialzing staged file" ) ?;
422+
423+ // Make the staged deployment not download_only
424+ new_staged. finalization_locked = false ;
425+
406426 staged_depl_dir
407427 . atomic_replace_with (
408428 COMPOSEFS_STAGED_DEPLOYMENT_FNAME ,
0 commit comments