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
382 changes: 204 additions & 178 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ clap = "4.5.4"
clap_mangen = { version = "0.2.20" }
# Reviewers (including AI tools): The composefs-rs git revision is duplicated for each crate.
# If adding/removing crates here, also update docs/Dockerfile.mdbook and docs/src/internals.md.
composefs = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs", features = ["rhel9"] }
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs-boot" }
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs-oci" }
composefs = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs", features = ["rhel9"] }
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs-boot" }
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs-oci" }
fn-error-context = "0.2.1"
hex = "0.4.3"
indicatif = "0.18.0"
Expand Down
32 changes: 13 additions & 19 deletions crates/lib/src/bootc_composefs/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ use std::{fs::File, os::fd::AsRawFd};
use anyhow::{Context, Result};
use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
use composefs::splitstream::SplitStreamData;
use composefs_oci::open_config;
use ocidir::{OciDir, oci_spec::image::Platform};
use ostree_ext::container::Transport;
use ostree_ext::container::skopeo;
use ostree_ext::{container::Transport, oci_spec::image::ImageConfiguration};
use tar::EntryType;

use crate::image::get_imgrefs_for_copy;
use crate::{
bootc_composefs::{
status::{get_composefs_status, get_imginfo},
update::str_to_sha256digest,
},
bootc_composefs::status::{get_composefs_status, get_imginfo},
store::{BootedComposefs, Storage},
};

Expand Down Expand Up @@ -52,21 +50,17 @@ pub async fn export_repo_to_image(

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

let config_name = &imginfo.manifest.config().digest().digest();
let config_name = str_to_sha256digest(config_name)?;
let config_digest = imginfo.manifest.config().digest().digest();

let var_tmp =
Dir::open_ambient_dir("/var/tmp", ambient_authority()).context("Opening /var/tmp")?;

let tmpdir = cap_std_ext::cap_tempfile::tempdir_in(&var_tmp)?;
let oci_dir = OciDir::ensure(tmpdir.try_clone()?).context("Opening OCI")?;

let mut config_stream = booted_cfs
.repo
.open_stream(&hex::encode(config_name), None)
.context("Opening config stream")?;

let config = ImageConfiguration::from_reader(&mut config_stream)?;
// Use composefs_oci::open_config to get the config and layer map
let (config, layer_map) =
open_config(&*booted_cfs.repo, config_digest, None).context("Opening config")?;

// We can't guarantee that we'll get the same tar stream as the container image
// So we create new config and manifest
Expand All @@ -82,12 +76,12 @@ pub async fn export_repo_to_image(
let total_layers = config.rootfs().diff_ids().len();

for (idx, old_diff_id) in config.rootfs().diff_ids().iter().enumerate() {
let layer_sha256 = str_to_sha256digest(old_diff_id)?;
let layer_verity = config_stream.lookup(&layer_sha256)?;
// Look up the layer verity from the map
let layer_verity = layer_map
.get(old_diff_id.as_str())
.ok_or_else(|| anyhow::anyhow!("Layer {old_diff_id} not found in config"))?;

let mut layer_stream = booted_cfs
.repo
.open_stream(&hex::encode(layer_sha256), Some(layer_verity))?;
let mut layer_stream = booted_cfs.repo.open_stream("", Some(layer_verity), None)?;

let mut layer_writer = oci_dir.create_layer(None)?;
layer_writer.follow_symlinks(false);
Expand Down Expand Up @@ -120,7 +114,7 @@ pub async fn export_repo_to_image(

let size = header.entry_size()?;

match layer_stream.read_exact(size as usize, ((size + 511) & !511) as usize)? {
match layer_stream.read_exact(size as usize, ((size as usize) + 511) & !511)? {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

. o O ( Reminds me that I wanted to get rid of the magic handling of padding in the writer side . . )

SplitStreamData::External(obj_id) => match header.entry_type() {
EntryType::Regular | EntryType::Continuous => {
let file = File::from(booted_cfs.repo.open_object(&obj_id)?);
Expand Down
11 changes: 4 additions & 7 deletions crates/lib/src/bootc_composefs/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use std::sync::Arc;

use anyhow::{Context, Result};

use ostree_ext::composefs::{
fsverity::{FsVerityHashValue, Sha512HashValue},
util::Sha256Digest,
};
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,
Expand All @@ -26,7 +23,7 @@ pub(crate) fn open_composefs_repo(rootfs_dir: &Dir) -> Result<crate::store::Comp
pub(crate) async fn initialize_composefs_repository(
state: &State,
root_setup: &RootSetup,
) -> Result<(Sha256Digest, impl FsVerityHashValue)> {
) -> Result<(String, impl FsVerityHashValue)> {
let rootfs_dir = &root_setup.physical_root;

rootfs_dir
Expand Down Expand Up @@ -94,11 +91,11 @@ pub(crate) async fn pull_composefs_repo(
.await
.context("Pulling composefs repo")?;

tracing::info!("ID: {}, Verity: {}", hex::encode(id), verity.to_hex());
tracing::info!("ID: {id}, Verity: {}", verity.to_hex());

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

let entries = fs.transform_for_boot(&repo)?;
Expand Down
16 changes: 3 additions & 13 deletions crates/lib/src/bootc_composefs/update.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use anyhow::{Context, Result};
use camino::Utf8PathBuf;
use cap_std_ext::cap_std::fs::Dir;
use composefs::{
fsverity::{FsVerityHashValue, Sha512HashValue},
util::{Sha256Digest, parse_sha256},
};
use composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
use composefs_boot::BootOps;
use composefs_oci::image::create_filesystem;
use fn_error_context::context;
Expand All @@ -28,12 +25,6 @@ use crate::{
store::{BootedComposefs, ComposefsRepository, Storage},
};

#[context("Getting SHA256 Digest for {id}")]
pub fn str_to_sha256digest(id: &str) -> Result<Sha256Digest> {
let id = id.strip_prefix("sha256:").unwrap_or(id);
Ok(parse_sha256(&id)?)
}

/// Checks if a container image has been pulled to the local composefs repository.
///
/// This function verifies whether the specified container image exists in the local
Expand Down Expand Up @@ -61,10 +52,9 @@ pub(crate) async fn is_image_pulled(
let img_config_manifest = get_container_manifest_and_config(&imgref_repr).await?;

let img_digest = img_config_manifest.manifest.config().digest().digest();
let img_sha256 = str_to_sha256digest(&img_digest)?;

// check_stream is expensive to run, but probably a good idea
let container_pulled = repo.check_stream(&img_sha256).context("Checking stream")?;
// NB: add deep checking?
let container_pulled = repo.has_stream(img_digest).context("Checking stream")?;

Ok((container_pulled, img_config_manifest))
}
Expand Down
64 changes: 32 additions & 32 deletions crates/lib/src/cfsctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct App {
enum OciCommand {
/// Stores a tar file as a splitstream in the repository.
ImportLayer {
sha256: String,
digest: String,
name: Option<String>,
},
/// Lists the contents of a tar stream
Expand Down Expand Up @@ -102,7 +102,7 @@ enum Command {
Transaction,
/// Reconstitutes a split stream and writes it to stdout
Cat {
/// the name of the stream to cat, either a sha256 digest or prefixed with 'ref/'
/// the name of the stream to cat, either a content identifier or prefixed with 'ref/'
name: String,
},
/// Perform garbage collection
Expand All @@ -118,7 +118,7 @@ enum Command {
},
/// Mounts a composefs, possibly enforcing fsverity of the image
Mount {
/// the name of the image to mount, either a sha256 digest or prefixed with 'ref/'
/// the name of the image to mount, either an fs-verity hash or prefixed with 'ref/'
name: String,
/// the mountpoint
mountpoint: String,
Expand Down Expand Up @@ -191,32 +191,32 @@ where
}
}
Command::Cat { name } => {
repo.merge_splitstream(&name, None, &mut std::io::stdout())?;
repo.merge_splitstream(&name, None, None, &mut std::io::stdout())?;
}
Command::ImportImage { reference } => {
let image_id = repo.import_image(&reference, &mut std::io::stdin())?;
println!("{}", image_id.to_id());
}
Command::Oci { cmd: oci_cmd } => match oci_cmd {
OciCommand::ImportLayer { name, sha256 } => {
OciCommand::ImportLayer { name, digest } => {
let object_id = composefs_oci::import_layer(
&repo,
&composefs::util::parse_sha256(sha256)?,
repo,
&digest,
name.as_deref(),
&mut std::io::stdin(),
)?;
println!("{}", object_id.to_id());
}
OciCommand::LsLayer { name } => {
composefs_oci::ls_layer(&repo, &name)?;
composefs_oci::ls_layer(repo, &name)?;
}
OciCommand::Dump {
ref config_name,
ref config_verity,
} => {
let verity = verity_opt(config_verity)?;
let mut fs =
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
fs.print_dumpfile()?;
}
OciCommand::ComputeId {
Expand All @@ -226,9 +226,9 @@ where
} => {
let verity = verity_opt(config_verity)?;
let mut fs =
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
if bootable {
fs.transform_for_boot(&repo)?;
fs.transform_for_boot(repo)?;
}
let id = fs.compute_image_id();
println!("{}", id.to_hex());
Expand All @@ -241,34 +241,34 @@ where
} => {
let verity = verity_opt(config_verity)?;
let mut fs =
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
if bootable {
fs.transform_for_boot(&repo)?;
fs.transform_for_boot(repo)?;
}
let image_id = fs.commit_image(&repo, image_name.as_deref())?;
let image_id = fs.commit_image(repo, image_name.as_deref())?;
println!("{}", image_id.to_id());
}
OciCommand::Pull { ref image, name } => {
let (sha256, verity) =
composefs_oci::pull(&repo, image, name.as_deref(), None).await?;
let (digest, verity) =
composefs_oci::pull(repo, image, name.as_deref(), None).await?;

println!("sha256 {}", hex::encode(sha256));
println!("config {digest}");
println!("verity {}", verity.to_hex());
}
OciCommand::Seal {
ref config_name,
ref config_verity,
} => {
let verity = verity_opt(config_verity)?;
let (sha256, verity) = composefs_oci::seal(&repo, config_name, verity.as_ref())?;
println!("sha256 {}", hex::encode(sha256));
let (digest, verity) = composefs_oci::seal(repo, config_name, verity.as_ref())?;
println!("config {digest}");
println!("verity {}", verity.to_id());
}
OciCommand::Mount {
ref name,
ref mountpoint,
} => {
composefs_oci::mount(&repo, name, mountpoint, None)?;
composefs_oci::mount(repo, name, mountpoint, None)?;
}
OciCommand::PrepareBoot {
ref config_name,
Expand All @@ -279,17 +279,17 @@ where
} => {
let verity = verity_opt(config_verity)?;
let mut fs =
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
let entries = fs.transform_for_boot(&repo)?;
let id = fs.commit_image(&repo, None)?;
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
let entries = fs.transform_for_boot(repo)?;
let id = fs.commit_image(repo, None)?;

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

let cmdline_refs: Vec<&str> = cmdline.iter().map(String::as_str).collect();
write_boot::write_boot_simple(
&repo,
repo,
entry,
&id,
args.insecure,
Expand All @@ -302,7 +302,7 @@ where
let state = args
.repo
.as_ref()
.map(|p: &PathBuf| p.parent().unwrap_or(p))
.map(|p: &PathBuf| p.parent().unwrap())
.unwrap_or(Path::new("/sysroot"))
.join("state/deploy")
.join(id.to_hex());
Expand All @@ -318,9 +318,9 @@ where
bootable,
stat_root,
} => {
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
if bootable {
fs.transform_for_boot(&repo)?;
fs.transform_for_boot(repo)?;
}
let id = fs.compute_image_id();
println!("{}", id.to_hex());
Expand All @@ -337,21 +337,21 @@ where
stat_root,
ref image_name,
} => {
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
if bootable {
fs.transform_for_boot(&repo)?;
fs.transform_for_boot(repo)?;
}
let id = fs.commit_image(&repo, image_name.as_deref())?;
let id = fs.commit_image(repo, image_name.as_deref())?;
println!("{}", id.to_id());
}
Command::CreateDumpfile {
ref path,
bootable,
stat_root,
} => {
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
if bootable {
fs.transform_for_boot(&repo)?;
fs.transform_for_boot(repo)?;
}
fs.print_dumpfile()?;
}
Expand Down
10 changes: 6 additions & 4 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1714,13 +1714,15 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
let storage = get_storage().await?;
let cfs = storage.get_ensure_composefs()?;
let testdata = b"some test data";
let testdata_digest = openssl::sha::sha256(testdata);
let mut w = SplitStreamWriter::new(&cfs, None, Some(testdata_digest));
let testdata_digest = hex::encode(openssl::sha::sha256(testdata));
let mut w = SplitStreamWriter::new(&cfs, 0);
w.write_inline(testdata);
let object = cfs.write_stream(w, Some("testobject"))?.to_hex();
let object = cfs
.write_stream(w, &testdata_digest, Some("testobject"))?
.to_hex();
assert_eq!(
object,
"5d94ceb0b2bb3a78237e0a74bc030a262239ab5f47754a5eb2e42941056b64cb21035d64a8f7c2f156e34b820802fa51884de2b1f7dc3a41b9878fc543cd9b07"
"dc31ae5d2f637e98d2171821d60d2fcafb8084d6a4bb3bd9cdc7ad41decce6e48f85d5413d22371d36b223945042f53a2a6ab449b8e45d8896ba7d8694a16681"
);
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1735,9 +1735,9 @@ async fn install_to_filesystem_impl(
// Load a fd for the mounted target physical root

let (id, verity) = initialize_composefs_repository(state, rootfs).await?;
tracing::info!("id: {}, verity: {}", hex::encode(id), verity.to_hex());
tracing::info!("id: {id}, verity: {}", verity.to_hex());

setup_composefs_boot(rootfs, state, &hex::encode(id)).await?;
setup_composefs_boot(rootfs, state, &id).await?;
} else {
ostree_install(state, rootfs, cleanup).await?;
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion hack/provision-derived.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ fi
# for testing bootc install on Fedora CoreOS where these would conflict.
if test -z "${SKIP_CONFIGS:-}"; then
# For test-22-logically-bound-install
cp -a lbi/usr/. /usr
install -D -m 0644 -t /usr/share/containers/systemd/ lbi/*
for x in curl.container curl-base.image podman.image; do
ln -s /usr/share/containers/systemd/$x /usr/lib/bootc/bound-images.d/$x
done
Expand Down