Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion crates/lib/src/bootc_composefs/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::io::Write;
use std::ops::Deref;
use std::os::unix::fs::symlink;
use std::path::Path;
use std::{fs::create_dir_all, process::Command};

use anyhow::{Context, Result};
Expand All @@ -8,7 +11,7 @@ use bootc_mount::tempmount::TempMount;
use bootc_utils::CommandRunExt;
use camino::Utf8PathBuf;
use cap_std_ext::cap_std::ambient_authority;
use cap_std_ext::cap_std::fs::Dir;
use cap_std_ext::cap_std::fs::{Dir, Permissions, PermissionsExt};
use cap_std_ext::dirext::CapStdExtDirExt;
use composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
use fn_error_context::context;
Expand All @@ -23,6 +26,7 @@ use crate::bootc_composefs::boot::BootType;
use crate::bootc_composefs::repo::get_imgref;
use crate::bootc_composefs::status::get_sorted_type1_boot_entries;
use crate::parsers::bls_config::BLSConfigType;
use crate::store::{BootedComposefs, Storage};
use crate::{
composefs_consts::{
COMPOSEFS_CMDLINE, COMPOSEFS_STAGED_DEPLOYMENT_FNAME, COMPOSEFS_TRANSIENT_STATE_DIR,
Expand Down Expand Up @@ -104,6 +108,49 @@ pub(crate) fn copy_etc_to_state(
cp_ret
}

/// Updates the currently booted image's target imgref
pub(crate) fn update_target_imgref_in_origin(
storage: &Storage,
booted_cfs: &BootedComposefs,
imgref: &ImageReference,
) -> Result<()> {
let path = Path::new(STATE_DIR_RELATIVE).join(booted_cfs.cmdline.digest.deref());

let state_dir = storage
.physical_root
.open_dir(path)
.context("Opening state dir")?;

let origin_filename = format!("{}.origin", booted_cfs.cmdline.digest.deref());

let origin_file = state_dir
.read_to_string(&origin_filename)
.context("Reading origin file")?;

let mut ini =
tini::Ini::from_string(&origin_file).context("Failed to parse file origin file as ini")?;

// Replace the origin
ini = ini.section("origin").item(
ORIGIN_CONTAINER,
format!("ostree-unverified-image:{imgref}"),
);

state_dir
.atomic_replace_with(origin_filename, move |f| -> std::io::Result<_> {
f.write_all(ini.to_string().as_bytes())?;
f.flush()?;

let perms = Permissions::from_mode(0o644);
f.get_mut().as_file_mut().set_permissions(perms)?;

Ok(())
})
.context("Writing to origin file")?;

Ok(())
}

/// Creates and populates /sysroot/state/deploy/image_id
#[context("Writing composefs state")]
pub(crate) fn write_composefs_state(
Expand Down
75 changes: 29 additions & 46 deletions crates/lib/src/bootc_composefs/switch.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
use anyhow::{Context, Result};
use camino::Utf8PathBuf;
use cap_std_ext::cap_std::fs::Dir;
use composefs::fsverity::FsVerityHashValue;
use fn_error_context::context;

use crate::{
bootc_composefs::{
boot::{setup_composefs_bls_boot, setup_composefs_uki_boot, BootSetupType, BootType},
repo::pull_composefs_repo,
service::start_finalize_stated_svc,
state::write_composefs_state,
state::update_target_imgref_in_origin,
status::get_composefs_status,
update::{do_upgrade, is_image_pulled, validate_update, UpdateAction},
},
cli::{imgref_for_switch, SwitchOpts},
store::{BootedComposefs, Storage},
Expand Down Expand Up @@ -44,51 +39,39 @@ pub(crate) async fn switch_composefs(
anyhow::bail!("Target image is undefined")
};

start_finalize_stated_svc()?;
let repo = &*booted_cfs.repo;
let (image, manifest, _) = is_image_pulled(repo, &target_imgref).await?;

let (repo, entries, id, fs) =
pull_composefs_repo(&target_imgref.transport, &target_imgref.image).await?;
if let Some(cfg_verity) = image {
let action = validate_update(
storage,
booted_cfs,
&host,
manifest.config().digest().digest(),
&cfg_verity,
true,
)?;

let Some(entry) = entries.iter().next() else {
anyhow::bail!("No boot entries!");
};

let boot_type = BootType::from(entry);
let mut boot_digest = None;
match action {
UpdateAction::Skip => {
println!("No changes in image: {target_imgref:#}");
return Ok(());
}

let mounted_fs = Dir::reopen_dir(
&repo
.mount(&id.to_hex())
.context("Failed to mount composefs image")?,
)?;
UpdateAction::Proceed => {
return do_upgrade(storage, &host, &target_imgref).await;
}

match boot_type {
BootType::Bls => {
boot_digest = Some(setup_composefs_bls_boot(
BootSetupType::Upgrade((storage, &fs, &host)),
repo,
&id,
entry,
&mounted_fs,
)?)
UpdateAction::UpdateOrigin => {
// The staged image will never be the current image's verity digest
println!("Image already in compoesfs repository");
println!("Updating target image reference");
return update_target_imgref_in_origin(storage, booted_cfs, &target_imgref);
}
}
BootType::Uki => setup_composefs_uki_boot(
BootSetupType::Upgrade((storage, &fs, &host)),
repo,
&id,
entries,
)?,
};
}

// TODO: Remove this hardcoded path when write_composefs_state accepts a Dir
write_composefs_state(
&Utf8PathBuf::from("/sysroot"),
id,
&target_imgref,
true,
boot_type,
boot_digest,
)?;
do_upgrade(storage, &host, &target_imgref).await?;

Ok(())
}
Loading