33//! Create a merged filesystem tree with the image and mounted configmaps.
44
55use std:: collections:: HashSet ;
6+ use std:: fmt:: Write as _;
67use std:: fs:: create_dir_all;
7- use std:: io:: { BufRead , Write } ;
8+ use std:: io:: { BufRead , Read , Write } ;
89use std:: path:: PathBuf ;
910
1011use anyhow:: Ok ;
@@ -25,10 +26,11 @@ use ostree_ext::tokio_util::spawn_blocking_cancellable_flatten;
2526use rustix:: fs:: { fsync, renameat_with, AtFlags , RenameFlags } ;
2627
2728use crate :: bls_config:: { parse_bls_config, BLSConfig } ;
28- use crate :: install:: { get_efi_uuid_source, get_user_config, BootType } ;
29+ use crate :: install:: { get_efi_uuid_source, BootType } ;
30+ use crate :: parsers:: grub_menuconfig:: { parse_grub_menuentry_file, MenuEntry } ;
2931use crate :: progress_jsonl:: { Event , ProgressWriter , SubTaskBytes , SubTaskStep } ;
3032use crate :: spec:: ImageReference ;
31- use crate :: spec:: { BootEntry , BootOrder , HostSpec } ;
33+ use crate :: spec:: { BootOrder , HostSpec } ;
3234use crate :: status:: { composefs_deployment_status, labels_of_config} ;
3335use crate :: store:: Storage ;
3436use crate :: utils:: async_task_with_spinner;
@@ -742,38 +744,59 @@ pub(crate) async fn stage(
742744 Ok ( ( ) )
743745}
744746
747+ /// Filename for `loader/entries`
748+ pub ( crate ) const USER_CFG : & str = "user.cfg" ;
749+ pub ( crate ) const USER_CFG_STAGED : & str = "user.cfg.staged" ;
750+ pub ( crate ) const USER_CFG_ROLLBACK : & str = USER_CFG_STAGED ;
751+
745752#[ context( "Rolling back UKI" ) ]
746- pub ( crate ) fn rollback_composefs_uki ( current : & BootEntry , rollback : & BootEntry ) -> Result < ( ) > {
747- let user_cfg_name = "grub2/user.cfg.staged" ;
748- let user_cfg_path = PathBuf :: from ( "/sysroot/boot" ) . join ( user_cfg_name) ;
753+ pub ( crate ) fn rollback_composefs_uki ( ) -> Result < ( ) > {
754+ let user_cfg_path = PathBuf :: from ( "/sysroot/boot/grub2" ) ;
749755
750- let efi_uuid_source = get_efi_uuid_source ( ) ;
756+ let mut str = String :: new ( ) ;
757+ let mut menuentries =
758+ get_sorted_uki_boot_entries ( & mut str) . context ( "Getting UKI boot entries" ) ?;
751759
752- // TODO: Need to check if user.cfg.staged exists
753- let mut usr_cfg = std:: fs:: OpenOptions :: new ( )
754- . write ( true )
755- . create ( true )
756- . truncate ( true )
757- . open ( user_cfg_path)
758- . with_context ( || format ! ( "Opening {user_cfg_name}" ) ) ?;
760+ // TODO(Johan-Liebert): Currently assuming there are only two deployments
761+ assert ! ( menuentries. len( ) == 2 ) ;
759762
760- usr_cfg. write ( efi_uuid_source. as_bytes ( ) ) ?;
763+ let ( first, second) = menuentries. split_at_mut ( 1 ) ;
764+ std:: mem:: swap ( & mut first[ 0 ] , & mut second[ 0 ] ) ;
761765
762- let verity = if let Some ( composefs) = & rollback. composefs {
763- composefs. verity . clone ( )
764- } else {
765- // Shouldn't really happen
766- anyhow:: bail!( "Verity not found for rollback deployment" )
767- } ;
768- usr_cfg. write ( get_user_config ( todo ! ( ) , & verity) . as_bytes ( ) ) ?;
766+ let mut buffer = get_efi_uuid_source ( ) ;
769767
770- let verity = if let Some ( composefs) = & current. composefs {
771- composefs. verity . clone ( )
772- } else {
773- // Shouldn't really happen
774- anyhow:: bail!( "Verity not found for booted deployment" )
775- } ;
776- usr_cfg. write ( get_user_config ( todo ! ( ) , & verity) . as_bytes ( ) ) ?;
768+ for entry in menuentries {
769+ write ! ( buffer, "{entry}" ) ?;
770+ }
771+
772+ let entries_dir =
773+ cap_std:: fs:: Dir :: open_ambient_dir ( & user_cfg_path, cap_std:: ambient_authority ( ) )
774+ . with_context ( || format ! ( "Opening {user_cfg_path:?}" ) ) ?;
775+
776+ entries_dir
777+ . atomic_write ( USER_CFG_ROLLBACK , buffer)
778+ . with_context ( || format ! ( "Writing to {USER_CFG_ROLLBACK}" ) ) ?;
779+
780+ tracing:: debug!( "Atomically exchanging for {USER_CFG_ROLLBACK} and {USER_CFG}" ) ;
781+ renameat_with (
782+ & entries_dir,
783+ USER_CFG_ROLLBACK ,
784+ & entries_dir,
785+ USER_CFG ,
786+ RenameFlags :: EXCHANGE ,
787+ )
788+ . context ( "renameat" ) ?;
789+
790+ tracing:: debug!( "Removing {USER_CFG_ROLLBACK}" ) ;
791+ rustix:: fs:: unlinkat ( & entries_dir, USER_CFG_ROLLBACK , AtFlags :: REMOVEDIR ) . context ( "unlinkat" ) ?;
792+
793+ tracing:: debug!( "Syncing to disk" ) ;
794+ fsync (
795+ entries_dir
796+ . reopen_as_ownedfd ( )
797+ . with_context ( || format ! ( "Reopening {user_cfg_path:?} as owned fd" ) ) ?,
798+ )
799+ . with_context ( || format ! ( "fsync {user_cfg_path:?}" ) ) ?;
777800
778801 Ok ( ( ) )
779802}
@@ -834,6 +857,7 @@ pub(crate) fn rollback_composefs_bls() -> Result<()> {
834857 cfg. version = idx as u32 ;
835858 }
836859
860+ // TODO(Johan-Liebert): Currently assuming there are only two deployments
837861 assert ! ( all_configs. len( ) == 2 ) ;
838862
839863 // Write these
@@ -849,7 +873,7 @@ pub(crate) fn rollback_composefs_bls() -> Result<()> {
849873 let file_name = format ! ( "bootc-composefs-{}.conf" , cfg. version) ;
850874
851875 rollback_entries_dir
852- . atomic_write ( & file_name, cfg. to_string ( ) . as_bytes ( ) )
876+ . atomic_write ( & file_name, cfg. to_string ( ) )
853877 . with_context ( || format ! ( "Writing to {file_name}" ) ) ?;
854878 }
855879
@@ -920,7 +944,7 @@ pub(crate) async fn composefs_rollback() -> Result<()> {
920944
921945 match rollback_composefs_entry. boot_type {
922946 BootType :: Bls => rollback_composefs_bls ( ) ,
923- BootType :: Uki => rollback_composefs_uki ( & host . status . booted . unwrap ( ) , & rollback_status ) ,
947+ BootType :: Uki => rollback_composefs_uki ( ) ,
924948 } ?;
925949
926950 Ok ( ( ) )
0 commit comments