Skip to content
Draft
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
21 changes: 6 additions & 15 deletions crates/lib/src/bootc_composefs/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,14 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::bootc_composefs::state::{get_booted_bls, write_composefs_state};
use crate::bootc_kargs::compute_new_kargs;
use crate::composefs_consts::{TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED};
use crate::parsers::bls_config::{BLSConfig, BLSConfigType};
use crate::task::Task;
use crate::{
bootc_composefs::repo::get_imgref,
composefs_consts::{TYPE1_ENT_PATH, TYPE1_ENT_PATH_STAGED},
};
use crate::{
bootc_composefs::repo::open_composefs_repo,
store::{ComposefsFilesystem, Storage},
};
use crate::{
bootc_composefs::status::get_container_manifest_and_config, bootc_kargs::compute_new_kargs,
};
use crate::{bootc_composefs::status::get_sorted_grub_uki_boot_entries, install::PostFetchState};
use crate::{
composefs_consts::{
Expand Down Expand Up @@ -1221,23 +1216,23 @@ fn get_secureboot_keys(fs: &Dir, p: &str) -> Result<Option<SecurebootKeys>> {
pub(crate) async fn setup_composefs_boot(
root_setup: &RootSetup,
state: &State,
image_id: &str,
pull_result: &composefs_oci::PullResult<Sha512HashValue>,
allow_missing_fsverity: bool,
) -> Result<()> {
const COMPOSEFS_BOOT_SETUP_JOURNAL_ID: &str = "1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5";

tracing::info!(
message_id = COMPOSEFS_BOOT_SETUP_JOURNAL_ID,
bootc.operation = "boot_setup",
bootc.image_id = image_id,
bootc.config_digest = pull_result.config_digest,
bootc.allow_missing_fsverity = allow_missing_fsverity,
"Setting up composefs boot",
);

let mut repo = open_composefs_repo(&root_setup.physical_root)?;
repo.set_insecure(allow_missing_fsverity);

let mut fs = create_composefs_filesystem(&repo, image_id, None)?;
let mut fs = create_composefs_filesystem(&repo, &pull_result.config_digest, None)?;
let entries = fs.transform_for_boot(&repo)?;
let id = fs.commit_image(&repo, None)?;
let mounted_fs = Dir::reopen_dir(
Expand Down Expand Up @@ -1302,11 +1297,7 @@ pub(crate) async fn setup_composefs_boot(
None,
boot_type,
boot_digest,
&get_container_manifest_and_config(&get_imgref(
&state.source.imageref.transport.to_string(),
&state.source.imageref.name,
))
.await?,
&pull_result.manifest_digest,
allow_missing_fsverity,
)
.await?;
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/bootc_composefs/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub async fn export_repo_to_image(

let depl_verity = depl_verity.ok_or_else(|| anyhow::anyhow!("Image {source} not found"))?;

let imginfo = get_imginfo(storage, &depl_verity, None).await?;
let imginfo = get_imginfo(storage, &depl_verity)?;

// We want the digest in the form of "sha256:abc123"
let config_digest = format!("{}", imginfo.manifest.config().digest());
Expand Down
59 changes: 43 additions & 16 deletions crates/lib/src/bootc_composefs/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use anyhow::{Context, Result};
use ostree_ext::composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
use ostree_ext::composefs_boot::{BootOps, bootloader::BootEntry as ComposefsBootEntry};
use ostree_ext::composefs_oci::{
image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
image::create_filesystem as create_composefs_filesystem, pull_image as composefs_oci_pull_image,
};

use ostree_ext::container::ImageReference as OstreeExtImgRef;
Expand All @@ -24,7 +24,7 @@ pub(crate) async fn initialize_composefs_repository(
state: &State,
root_setup: &RootSetup,
allow_missing_fsverity: bool,
) -> Result<(String, impl FsVerityHashValue)> {
) -> Result<composefs_oci::PullResult<Sha512HashValue>> {
const COMPOSEFS_REPO_INIT_JOURNAL_ID: &str = "5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9";

let rootfs_dir = &root_setup.physical_root;
Expand Down Expand Up @@ -53,13 +53,25 @@ pub(crate) async fn initialize_composefs_repository(
} = &state.source.imageref;

// transport's display is already of type "<transport_type>:"
composefs_oci_pull(
let pull_result = composefs_oci_pull_image(
&Arc::new(repo),
&format!("{transport}{image_name}"),
None,
None,
)
.await
.await?;

tracing::info!(
message_id = COMPOSEFS_REPO_INIT_JOURNAL_ID,
bootc.operation = "repository_init",
bootc.manifest_digest = pull_result.manifest_digest,
bootc.manifest_verity = pull_result.manifest_verity.to_hex(),
bootc.config_digest = pull_result.config_digest,
bootc.config_verity = pull_result.config_verity.to_hex(),
"Pulled image into composefs repository",
);

Ok(pull_result)
}

/// skopeo (in composefs-rs) doesn't understand "registry:"
Expand All @@ -82,19 +94,25 @@ pub(crate) fn get_imgref(transport: &str, image: &str) -> String {
}
}

/// Result of pulling a composefs repository, including the OCI manifest digest
/// needed to reconstruct image metadata from the local composefs repo.
pub(crate) struct PullRepoResult {
pub(crate) repo: crate::store::ComposefsRepository,
pub(crate) entries: Vec<ComposefsBootEntry<Sha512HashValue>>,
pub(crate) id: Sha512HashValue,
pub(crate) fs: crate::store::ComposefsFilesystem,
/// The OCI manifest content digest (e.g. "sha256:abc...")
pub(crate) manifest_digest: String,
}

/// Pulls the `image` from `transport` into a composefs repository at /sysroot
/// Checks for boot entries in the image and returns them
#[context("Pulling composefs repository")]
pub(crate) async fn pull_composefs_repo(
transport: &String,
image: &String,
allow_missing_fsverity: bool,
) -> Result<(
crate::store::ComposefsRepository,
Vec<ComposefsBootEntry<Sha512HashValue>>,
Sha512HashValue,
crate::store::ComposefsFilesystem,
)> {
) -> Result<PullRepoResult> {
const COMPOSEFS_PULL_JOURNAL_ID: &str = "4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8";

tracing::info!(
Expand All @@ -117,28 +135,37 @@ pub(crate) async fn pull_composefs_repo(

tracing::debug!("Image to pull {final_imgref}");

let (id, verity) = composefs_oci_pull(&Arc::new(repo), &final_imgref, None, None)
let pull_result = composefs_oci_pull_image(&Arc::new(repo), &final_imgref, None, None)
.await
.context("Pulling composefs repo")?;

tracing::info!(
message_id = COMPOSEFS_PULL_JOURNAL_ID,
id = id,
verity = verity.to_hex(),
"Pulled image into repository"
bootc.operation = "pull",
bootc.manifest_digest = pull_result.manifest_digest,
bootc.manifest_verity = pull_result.manifest_verity.to_hex(),
bootc.config_digest = pull_result.config_digest,
bootc.config_verity = pull_result.config_verity.to_hex(),
"Pulled image into composefs repository",
);

let mut repo = open_composefs_repo(&rootfs_dir)?;
repo.set_insecure(allow_missing_fsverity);
Comment on lines 152 to 153
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The repository is being opened and configured for a second time here. The first time is before the composefs_oci_pull_image call on line 131. This is inefficient as it involves redundant I/O and setup.

It would be better to open the repository only once at the beginning of the function. You can wrap it in an Arc for the pull operation, and then continue to use the same Arc<repo> for subsequent operations like create_composefs_filesystem. At the end of the function, you can use Arc::try_unwrap to get back the owned repo to be returned in PullRepoResult.


let mut fs: crate::store::ComposefsFilesystem =
create_composefs_filesystem(&repo, &id, None)
create_composefs_filesystem(&repo, &pull_result.config_digest, None)
.context("Failed to create composefs filesystem")?;

let entries = fs.transform_for_boot(&repo)?;
let id = fs.commit_image(&repo, None)?;

Ok((repo, entries, id, fs))
Ok(PullRepoResult {
repo,
entries,
id,
fs,
manifest_digest: pull_result.manifest_digest,
})
}

#[cfg(test)]
Expand Down
31 changes: 13 additions & 18 deletions crates/lib/src/bootc_composefs/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@ use rustix::{

use crate::bootc_composefs::boot::BootType;
use crate::bootc_composefs::repo::get_imgref;
use crate::bootc_composefs::status::{
ImgConfigManifest, StagedDeployment, get_sorted_type1_boot_entries,
};
use crate::bootc_composefs::status::{StagedDeployment, 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,
ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST, ORIGIN_KEY_BOOT_TYPE, SHARED_VAR_PATH,
STATE_DIR_RELATIVE,
ORIGIN_KEY_BOOT, ORIGIN_KEY_BOOT_DIGEST, ORIGIN_KEY_BOOT_TYPE, ORIGIN_KEY_IMAGE,
ORIGIN_KEY_MANIFEST_DIGEST, SHARED_VAR_PATH, STATE_DIR_RELATIVE,
},
parsers::bls_config::BLSConfig,
spec::ImageReference,
Expand Down Expand Up @@ -221,15 +219,15 @@ pub(crate) fn update_boot_digest_in_origin(
/// * `staged` - Whether this is a staged deployment (writes to transient state dir)
/// * `boot_type` - Boot loader type (`Bls` or `Uki`)
/// * `boot_digest` - Optional boot digest for verification
/// * `container_details` - Container manifest and config used to create this deployment
/// * `manifest_digest` - OCI manifest content digest, stored in the origin file so the
/// manifest+config can be retrieved from the composefs repo later
///
/// # State Directory Structure
///
/// Creates the following structure under `/sysroot/state/deploy/{deployment_id}/`:
/// * `etc/` - Copy of system configuration files
/// * `var` - Symlink to shared `/var` directory
/// * `{deployment_id}.origin` - OSTree-style origin configuration
/// * `{deployment_id}.imginfo` - Container image manifest and config as JSON
/// * `{deployment_id}.origin` - Origin configuration with image ref, boot, and image metadata
///
/// For staged deployments, also writes to `/run/composefs/staged-deployment`.
#[context("Writing composefs state")]
Expand All @@ -240,7 +238,7 @@ pub(crate) async fn write_composefs_state(
staged: Option<StagedDeployment>,
boot_type: BootType,
boot_digest: String,
container_details: &ImgConfigManifest,
manifest_digest: &str,
allow_missing_fsverity: bool,
) -> Result<()> {
let state_path = root_path
Expand Down Expand Up @@ -289,18 +287,15 @@ pub(crate) async fn write_composefs_state(
.section(ORIGIN_KEY_BOOT)
.item(ORIGIN_KEY_BOOT_DIGEST, boot_digest);

// Store the OCI manifest digest so we can retrieve the manifest+config
// from the composefs repository later (composefs-rs stores them as splitstreams).
config = config
.section(ORIGIN_KEY_IMAGE)
.item(ORIGIN_KEY_MANIFEST_DIGEST, manifest_digest);

let state_dir =
Dir::open_ambient_dir(&state_path, ambient_authority()).context("Opening state dir")?;

// NOTE: This is only supposed to be temporary until we decide on where to store
// the container manifest/config
state_dir
.atomic_write(
format!("{}.imginfo", deployment_id.to_hex()),
serde_json::to_vec(&container_details)?,
)
.context("Failed to write to .imginfo file")?;

state_dir
.atomic_write(
format!("{}.origin", deployment_id.to_hex()),
Expand Down
Loading