@@ -21,17 +21,22 @@ use ostree_ext::composefs::fsverity;
2121use ostree_ext:: composefs:: fsverity:: FsVerityHashValue ;
2222use ostree_ext:: composefs:: splitstream:: SplitStreamWriter ;
2323use ostree_ext:: container as ostree_container;
24- use ostree_ext:: container_utils:: ostree_booted;
24+ use ostree_ext:: container_utils:: { composefs_booted , ostree_booted} ;
2525use ostree_ext:: keyfileext:: KeyFileExt ;
2626use ostree_ext:: ostree;
2727use schemars:: schema_for;
2828use serde:: { Deserialize , Serialize } ;
2929
30- use crate :: deploy:: RequiredHostSpec ;
30+ use crate :: deploy:: { composefs_rollback, RequiredHostSpec } ;
31+ use crate :: install:: {
32+ pull_composefs_repo, setup_composefs_bls_boot, setup_composefs_uki_boot, write_composefs_state,
33+ BootSetupType , BootType ,
34+ } ;
3135use crate :: lints;
3236use crate :: progress_jsonl:: { ProgressWriter , RawProgressFd } ;
3337use crate :: spec:: Host ;
3438use crate :: spec:: ImageReference ;
39+ use crate :: status:: composefs_deployment_status;
3540use crate :: utils:: sigpolicy_from_opt;
3641
3742/// Shared progress options
@@ -778,6 +783,53 @@ fn prepare_for_write() -> Result<()> {
778783 Ok ( ( ) )
779784}
780785
786+ #[ context( "Upgrading composefs" ) ]
787+ async fn upgrade_composefs ( _opts : UpgradeOpts ) -> Result < ( ) > {
788+ // TODO: IMPORTANT Have all the checks here that `bootc upgrade` has for an ostree booted system
789+
790+ let host = composefs_deployment_status ( )
791+ . await
792+ . context ( "Getting composefs deployment status" ) ?;
793+
794+ // TODO: IMPORTANT We need to check if any deployment is staged and get the image from that
795+ let imgref = host
796+ . spec
797+ . image
798+ . as_ref ( )
799+ . ok_or_else ( || anyhow:: anyhow!( "No image source specified" ) ) ?;
800+
801+ // let booted_image = host
802+ // .status
803+ // .booted
804+ // .ok_or(anyhow::anyhow!("Could not find booted image"))?
805+ // .image
806+ // .ok_or(anyhow::anyhow!("Could not find booted image"))?;
807+
808+ // tracing::debug!("booted_image: {booted_image:#?}");
809+ // tracing::debug!("imgref: {imgref:#?}");
810+
811+ // let digest = booted_image
812+ // .digest()
813+ // .context("Getting digest for booted image")?;
814+
815+ let ( repo, entries, id) = pull_composefs_repo ( & imgref. transport , & imgref. image ) . await ?;
816+
817+ let Some ( entry) = entries. into_iter ( ) . next ( ) else {
818+ anyhow:: bail!( "No boot entries!" ) ;
819+ } ;
820+
821+ let boot_type = BootType :: from ( & entry) ;
822+
823+ match boot_type {
824+ BootType :: Bls => setup_composefs_bls_boot ( BootSetupType :: Upgrade , repo, & id, entry) ,
825+ BootType :: Uki => setup_composefs_uki_boot ( BootSetupType :: Upgrade , repo, & id, entry) ,
826+ } ?;
827+
828+ write_composefs_state ( & Utf8PathBuf :: from ( "/sysroot" ) , id, imgref, true , boot_type) ?;
829+
830+ Ok ( ( ) )
831+ }
832+
781833/// Implementation of the `bootc upgrade` CLI command.
782834#[ context( "Upgrading" ) ]
783835async fn upgrade ( opts : UpgradeOpts ) -> Result < ( ) > {
@@ -891,9 +943,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
891943 Ok ( ( ) )
892944}
893945
894- /// Implementation of the `bootc switch` CLI command.
895- #[ context( "Switching" ) ]
896- async fn switch ( opts : SwitchOpts ) -> Result < ( ) > {
946+ fn imgref_for_switch ( opts : & SwitchOpts ) -> Result < ImageReference > {
897947 let transport = ostree_container:: Transport :: try_from ( opts. transport . as_str ( ) ) ?;
898948 let imgref = ostree_container:: ImageReference {
899949 transport,
@@ -902,6 +952,63 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
902952 let sigverify = sigpolicy_from_opt ( opts. enforce_container_sigpolicy ) ;
903953 let target = ostree_container:: OstreeImageReference { sigverify, imgref } ;
904954 let target = ImageReference :: from ( target) ;
955+
956+ return Ok ( target) ;
957+ }
958+
959+ #[ context( "Composefs Switching" ) ]
960+ async fn switch_composefs ( opts : SwitchOpts ) -> Result < ( ) > {
961+ let target = imgref_for_switch ( & opts) ?;
962+ // TODO: Handle in-place
963+
964+ let host = composefs_deployment_status ( )
965+ . await
966+ . context ( "Getting composefs deployment status" ) ?;
967+
968+ let new_spec = {
969+ let mut new_spec = host. spec . clone ( ) ;
970+ new_spec. image = Some ( target. clone ( ) ) ;
971+ new_spec
972+ } ;
973+
974+ if new_spec == host. spec {
975+ println ! ( "Image specification is unchanged." ) ;
976+ return Ok ( ( ) ) ;
977+ }
978+
979+ let Some ( target_imgref) = new_spec. image else {
980+ anyhow:: bail!( "Target image is undefined" )
981+ } ;
982+
983+ let ( repo, entries, id) = pull_composefs_repo ( & "docker" . into ( ) , & target_imgref. image ) . await ?;
984+
985+ let Some ( entry) = entries. into_iter ( ) . next ( ) else {
986+ anyhow:: bail!( "No boot entries!" ) ;
987+ } ;
988+
989+ let boot_type = BootType :: from ( & entry) ;
990+
991+ match boot_type {
992+ BootType :: Bls => setup_composefs_bls_boot ( BootSetupType :: Upgrade , repo, & id, entry) ,
993+ BootType :: Uki => setup_composefs_uki_boot ( BootSetupType :: Upgrade , repo, & id, entry) ,
994+ } ?;
995+
996+ write_composefs_state (
997+ & Utf8PathBuf :: from ( "/sysroot" ) ,
998+ id,
999+ & target_imgref,
1000+ true ,
1001+ boot_type,
1002+ ) ?;
1003+
1004+ Ok ( ( ) )
1005+ }
1006+
1007+ /// Implementation of the `bootc switch` CLI command.
1008+ #[ context( "Switching" ) ]
1009+ async fn switch ( opts : SwitchOpts ) -> Result < ( ) > {
1010+ let target = imgref_for_switch ( & opts) ?;
1011+
9051012 let prog: ProgressWriter = opts. progress . try_into ( ) ?;
9061013
9071014 // If we're doing an in-place mutation, we shortcut most of the rest of the work here
@@ -966,8 +1073,12 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
9661073/// Implementation of the `bootc rollback` CLI command.
9671074#[ context( "Rollback" ) ]
9681075async fn rollback ( opts : RollbackOpts ) -> Result < ( ) > {
969- let sysroot = & get_storage ( ) . await ?;
970- crate :: deploy:: rollback ( sysroot) . await ?;
1076+ if composefs_booted ( ) ? {
1077+ composefs_rollback ( ) . await ?
1078+ } else {
1079+ let sysroot = & get_storage ( ) . await ?;
1080+ crate :: deploy:: rollback ( sysroot) . await ?;
1081+ } ;
9711082
9721083 if opts. apply {
9731084 crate :: reboot:: reboot ( ) ?;
@@ -1117,8 +1228,20 @@ impl Opt {
11171228async fn run_from_opt ( opt : Opt ) -> Result < ( ) > {
11181229 let root = & Dir :: open_ambient_dir ( "/" , cap_std:: ambient_authority ( ) ) ?;
11191230 match opt {
1120- Opt :: Upgrade ( opts) => upgrade ( opts) . await ,
1121- Opt :: Switch ( opts) => switch ( opts) . await ,
1231+ Opt :: Upgrade ( opts) => {
1232+ if composefs_booted ( ) ? {
1233+ upgrade_composefs ( opts) . await
1234+ } else {
1235+ upgrade ( opts) . await
1236+ }
1237+ }
1238+ Opt :: Switch ( opts) => {
1239+ if composefs_booted ( ) ? {
1240+ switch_composefs ( opts) . await
1241+ } else {
1242+ switch ( opts) . await
1243+ }
1244+ }
11221245 Opt :: Rollback ( opts) => rollback ( opts) . await ,
11231246 Opt :: Edit ( opts) => edit ( opts) . await ,
11241247 Opt :: UsrOverlay => usroverlay ( ) . await ,
@@ -1259,8 +1382,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
12591382 FsverityOpts :: Enable { path } => {
12601383 let fd =
12611384 std:: fs:: File :: open ( & path) . with_context ( || format ! ( "Reading {path}" ) ) ?;
1262- // Note this is not robust to forks, we're not using the _maybe_copy variant
1263- fsverity:: enable_verity_with_retry :: < fsverity:: Sha256HashValue > ( & fd) ?;
1385+ fsverity:: enable_verity_raw :: < fsverity:: Sha256HashValue > ( & fd) ?;
12641386 Ok ( ( ) )
12651387 }
12661388 } ,
0 commit comments