Skip to content

Commit 3578b8e

Browse files
various: Update to latest composefs-rs API
Update composefs-rs from rev b636e0e9 to e9008489, adapting to API changes: - merge_splitstream now takes 4 arguments instead of 3 - import_layer takes digest as string directly - pull/seal return (digest, verity) as (String, ObjectID) - SplitStreamWriter::new and write_stream have new signatures - initialize_composefs_repository returns String instead of Sha256Digest Co-authored-by: Allison Karlitskaya <[email protected]> Assisted-by: OpenCode (Claude claude-sonnet-4-20250514) Signed-off-by: Colin Walters <[email protected]>
1 parent 72259a0 commit 3578b8e

File tree

8 files changed

+269
-259
lines changed

8 files changed

+269
-259
lines changed

Cargo.lock

Lines changed: 205 additions & 178 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ clap = "4.5.4"
4242
clap_mangen = { version = "0.2.20" }
4343
# Reviewers (including AI tools): The composefs-rs git revision is duplicated for each crate.
4444
# If adding/removing crates here, also update docs/Dockerfile.mdbook and docs/src/internals.md.
45-
composefs = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs", features = ["rhel9"] }
46-
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs-boot" }
47-
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "1498349e11d0e4bd545feab533f0b7ab5294ae9a", package = "composefs-oci" }
45+
composefs = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs", features = ["rhel9"] }
46+
composefs-boot = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs-boot" }
47+
composefs-oci = { git = "https://github.com/containers/composefs-rs", rev = "e9008489375044022e90d26656960725a76f4620", package = "composefs-oci" }
4848
fn-error-context = "0.2.1"
4949
hex = "0.4.3"
5050
indicatif = "0.18.0"

crates/lib/src/bootc_composefs/export.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@ use std::{fs::File, os::fd::AsRawFd};
33
use anyhow::{Context, Result};
44
use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
55
use composefs::splitstream::SplitStreamData;
6+
use composefs_oci::open_config;
67
use ocidir::{oci_spec::image::Platform, OciDir};
78
use ostree_ext::container::skopeo;
8-
use ostree_ext::{container::Transport, oci_spec::image::ImageConfiguration};
9+
use ostree_ext::container::Transport;
910
use tar::EntryType;
1011

1112
use crate::image::get_imgrefs_for_copy;
1213
use crate::{
13-
bootc_composefs::{
14-
status::{get_composefs_status, get_imginfo},
15-
update::str_to_sha256digest,
16-
},
14+
bootc_composefs::status::{get_composefs_status, get_imginfo},
1715
store::{BootedComposefs, Storage},
1816
};
1917

@@ -52,21 +50,17 @@ pub async fn export_repo_to_image(
5250

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

55-
let config_name = &imginfo.manifest.config().digest().digest();
56-
let config_name = str_to_sha256digest(config_name)?;
53+
let config_digest = imginfo.manifest.config().digest().digest();
5754

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

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

64-
let mut config_stream = booted_cfs
65-
.repo
66-
.open_stream(&hex::encode(config_name), None)
67-
.context("Opening config stream")?;
68-
69-
let config = ImageConfiguration::from_reader(&mut config_stream)?;
61+
// Use composefs_oci::open_config to get the config and layer map
62+
let (config, layer_map) =
63+
open_config(&*booted_cfs.repo, config_digest, None).context("Opening config")?;
7064

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

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

88-
let mut layer_stream = booted_cfs
89-
.repo
90-
.open_stream(&hex::encode(layer_sha256), Some(layer_verity))?;
84+
let mut layer_stream = booted_cfs.repo.open_stream("", Some(layer_verity), None)?;
9185

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

121115
let size = header.entry_size()?;
122116

123-
match layer_stream.read_exact(size as usize, ((size + 511) & !511) as usize)? {
117+
match layer_stream.read_exact(size as usize, ((size as usize) + 511) & !511)? {
124118
SplitStreamData::External(obj_id) => match header.entry_type() {
125119
EntryType::Regular | EntryType::Continuous => {
126120
let file = File::from(booted_cfs.repo.open_object(&obj_id)?);

crates/lib/src/bootc_composefs/repo.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ use std::sync::Arc;
33

44
use anyhow::{Context, Result};
55

6-
use ostree_ext::composefs::{
7-
fsverity::{FsVerityHashValue, Sha512HashValue},
8-
util::Sha256Digest,
9-
};
10-
use ostree_ext::composefs_boot::{bootloader::BootEntry as ComposefsBootEntry, BootOps};
6+
use ostree_ext::composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
7+
use ostree_ext::composefs_boot::{BootOps, bootloader::BootEntry as ComposefsBootEntry};
118
use ostree_ext::composefs_oci::{
129
image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
1310
};
@@ -26,7 +23,7 @@ pub(crate) fn open_composefs_repo(rootfs_dir: &Dir) -> Result<crate::store::Comp
2623
pub(crate) async fn initialize_composefs_repository(
2724
state: &State,
2825
root_setup: &RootSetup,
29-
) -> Result<(Sha256Digest, impl FsVerityHashValue)> {
26+
) -> Result<(String, impl FsVerityHashValue)> {
3027
let rootfs_dir = &root_setup.physical_root;
3128

3229
rootfs_dir
@@ -94,11 +91,11 @@ pub(crate) async fn pull_composefs_repo(
9491
.await
9592
.context("Pulling composefs repo")?;
9693

97-
tracing::info!("ID: {}, Verity: {}", hex::encode(id), verity.to_hex());
94+
tracing::info!("ID: {id}, Verity: {}", verity.to_hex());
9895

9996
let repo = open_composefs_repo(&rootfs_dir)?;
10097
let mut fs: crate::store::ComposefsFilesystem =
101-
create_composefs_filesystem(&repo, &hex::encode(id), None)
98+
create_composefs_filesystem(&repo, &id, None)
10299
.context("Failed to create composefs filesystem")?;
103100

104101
let entries = fs.transform_for_boot(&repo)?;

crates/lib/src/bootc_composefs/update.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use anyhow::{Context, Result};
22
use camino::Utf8PathBuf;
33
use cap_std_ext::cap_std::fs::Dir;
4-
use composefs::{
5-
fsverity::{FsVerityHashValue, Sha512HashValue},
6-
util::{parse_sha256, Sha256Digest},
7-
};
4+
use composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
85
use composefs_boot::BootOps;
96
use composefs_oci::image::create_filesystem;
107
use fn_error_context::context;
@@ -28,12 +25,6 @@ use crate::{
2825
store::{BootedComposefs, ComposefsRepository, Storage},
2926
};
3027

31-
#[context("Getting SHA256 Digest for {id}")]
32-
pub fn str_to_sha256digest(id: &str) -> Result<Sha256Digest> {
33-
let id = id.strip_prefix("sha256:").unwrap_or(id);
34-
Ok(parse_sha256(&id)?)
35-
}
36-
3728
/// Checks if a container image has been pulled to the local composefs repository.
3829
///
3930
/// This function verifies whether the specified container image exists in the local
@@ -61,10 +52,9 @@ pub(crate) async fn is_image_pulled(
6152
let img_config_manifest = get_container_manifest_and_config(&imgref_repr).await?;
6253

6354
let img_digest = img_config_manifest.manifest.config().digest().digest();
64-
let img_sha256 = str_to_sha256digest(&img_digest)?;
6555

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

6959
Ok((container_pulled, img_config_manifest))
7060
}

crates/lib/src/cfsctl.rs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct App {
4545
enum OciCommand {
4646
/// Stores a tar file as a splitstream in the repository.
4747
ImportLayer {
48-
sha256: String,
48+
digest: String,
4949
name: Option<String>,
5050
},
5151
/// Lists the contents of a tar stream
@@ -102,7 +102,7 @@ enum Command {
102102
Transaction,
103103
/// Reconstitutes a split stream and writes it to stdout
104104
Cat {
105-
/// the name of the stream to cat, either a sha256 digest or prefixed with 'ref/'
105+
/// the name of the stream to cat, either a content identifier or prefixed with 'ref/'
106106
name: String,
107107
},
108108
/// Perform garbage collection
@@ -118,7 +118,7 @@ enum Command {
118118
},
119119
/// Mounts a composefs, possibly enforcing fsverity of the image
120120
Mount {
121-
/// the name of the image to mount, either a sha256 digest or prefixed with 'ref/'
121+
/// the name of the image to mount, either an fs-verity hash or prefixed with 'ref/'
122122
name: String,
123123
/// the mountpoint
124124
mountpoint: String,
@@ -191,32 +191,32 @@ where
191191
}
192192
}
193193
Command::Cat { name } => {
194-
repo.merge_splitstream(&name, None, &mut std::io::stdout())?;
194+
repo.merge_splitstream(&name, None, None, &mut std::io::stdout())?;
195195
}
196196
Command::ImportImage { reference } => {
197197
let image_id = repo.import_image(&reference, &mut std::io::stdin())?;
198198
println!("{}", image_id.to_id());
199199
}
200200
Command::Oci { cmd: oci_cmd } => match oci_cmd {
201-
OciCommand::ImportLayer { name, sha256 } => {
201+
OciCommand::ImportLayer { name, digest } => {
202202
let object_id = composefs_oci::import_layer(
203-
&repo,
204-
&composefs::util::parse_sha256(sha256)?,
203+
repo,
204+
&digest,
205205
name.as_deref(),
206206
&mut std::io::stdin(),
207207
)?;
208208
println!("{}", object_id.to_id());
209209
}
210210
OciCommand::LsLayer { name } => {
211-
composefs_oci::ls_layer(&repo, &name)?;
211+
composefs_oci::ls_layer(repo, &name)?;
212212
}
213213
OciCommand::Dump {
214214
ref config_name,
215215
ref config_verity,
216216
} => {
217217
let verity = verity_opt(config_verity)?;
218218
let mut fs =
219-
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
219+
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
220220
fs.print_dumpfile()?;
221221
}
222222
OciCommand::ComputeId {
@@ -226,9 +226,9 @@ where
226226
} => {
227227
let verity = verity_opt(config_verity)?;
228228
let mut fs =
229-
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
229+
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
230230
if bootable {
231-
fs.transform_for_boot(&repo)?;
231+
fs.transform_for_boot(repo)?;
232232
}
233233
let id = fs.compute_image_id();
234234
println!("{}", id.to_hex());
@@ -241,34 +241,34 @@ where
241241
} => {
242242
let verity = verity_opt(config_verity)?;
243243
let mut fs =
244-
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
244+
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
245245
if bootable {
246-
fs.transform_for_boot(&repo)?;
246+
fs.transform_for_boot(repo)?;
247247
}
248-
let image_id = fs.commit_image(&repo, image_name.as_deref())?;
248+
let image_id = fs.commit_image(repo, image_name.as_deref())?;
249249
println!("{}", image_id.to_id());
250250
}
251251
OciCommand::Pull { ref image, name } => {
252-
let (sha256, verity) =
253-
composefs_oci::pull(&repo, image, name.as_deref(), None).await?;
252+
let (digest, verity) =
253+
composefs_oci::pull(repo, image, name.as_deref(), None).await?;
254254

255-
println!("sha256 {}", hex::encode(sha256));
255+
println!("config {digest}");
256256
println!("verity {}", verity.to_hex());
257257
}
258258
OciCommand::Seal {
259259
ref config_name,
260260
ref config_verity,
261261
} => {
262262
let verity = verity_opt(config_verity)?;
263-
let (sha256, verity) = composefs_oci::seal(&repo, config_name, verity.as_ref())?;
264-
println!("sha256 {}", hex::encode(sha256));
263+
let (digest, verity) = composefs_oci::seal(repo, config_name, verity.as_ref())?;
264+
println!("config {digest}");
265265
println!("verity {}", verity.to_id());
266266
}
267267
OciCommand::Mount {
268268
ref name,
269269
ref mountpoint,
270270
} => {
271-
composefs_oci::mount(&repo, name, mountpoint, None)?;
271+
composefs_oci::mount(repo, name, mountpoint, None)?;
272272
}
273273
OciCommand::PrepareBoot {
274274
ref config_name,
@@ -279,17 +279,17 @@ where
279279
} => {
280280
let verity = verity_opt(config_verity)?;
281281
let mut fs =
282-
composefs_oci::image::create_filesystem(&repo, config_name, verity.as_ref())?;
283-
let entries = fs.transform_for_boot(&repo)?;
284-
let id = fs.commit_image(&repo, None)?;
282+
composefs_oci::image::create_filesystem(repo, config_name, verity.as_ref())?;
283+
let entries = fs.transform_for_boot(repo)?;
284+
let id = fs.commit_image(repo, None)?;
285285

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

290290
let cmdline_refs: Vec<&str> = cmdline.iter().map(String::as_str).collect();
291291
write_boot::write_boot_simple(
292-
&repo,
292+
repo,
293293
entry,
294294
&id,
295295
args.insecure,
@@ -302,7 +302,7 @@ where
302302
let state = args
303303
.repo
304304
.as_ref()
305-
.map(|p: &PathBuf| p.parent().unwrap_or(p))
305+
.map(|p: &PathBuf| p.parent().unwrap())
306306
.unwrap_or(Path::new("/sysroot"))
307307
.join("state/deploy")
308308
.join(id.to_hex());
@@ -318,9 +318,9 @@ where
318318
bootable,
319319
stat_root,
320320
} => {
321-
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
321+
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
322322
if bootable {
323-
fs.transform_for_boot(&repo)?;
323+
fs.transform_for_boot(repo)?;
324324
}
325325
let id = fs.compute_image_id();
326326
println!("{}", id.to_hex());
@@ -337,21 +337,21 @@ where
337337
stat_root,
338338
ref image_name,
339339
} => {
340-
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
340+
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
341341
if bootable {
342-
fs.transform_for_boot(&repo)?;
342+
fs.transform_for_boot(repo)?;
343343
}
344-
let id = fs.commit_image(&repo, image_name.as_deref())?;
344+
let id = fs.commit_image(repo, image_name.as_deref())?;
345345
println!("{}", id.to_id());
346346
}
347347
Command::CreateDumpfile {
348348
ref path,
349349
bootable,
350350
stat_root,
351351
} => {
352-
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(&repo), stat_root)?;
352+
let mut fs = composefs::fs::read_filesystem(CWD, path, Some(repo.as_ref()), stat_root)?;
353353
if bootable {
354-
fs.transform_for_boot(&repo)?;
354+
fs.transform_for_boot(repo)?;
355355
}
356356
fs.print_dumpfile()?;
357357
}

crates/lib/src/cli.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,13 +1710,15 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
17101710
let storage = get_storage().await?;
17111711
let cfs = storage.get_ensure_composefs()?;
17121712
let testdata = b"some test data";
1713-
let testdata_digest = openssl::sha::sha256(testdata);
1714-
let mut w = SplitStreamWriter::new(&cfs, None, Some(testdata_digest));
1713+
let testdata_digest = hex::encode(openssl::sha::sha256(testdata));
1714+
let mut w = SplitStreamWriter::new(&cfs, 0);
17151715
w.write_inline(testdata);
1716-
let object = cfs.write_stream(w, Some("testobject"))?.to_hex();
1716+
let object = cfs
1717+
.write_stream(w, &testdata_digest, Some("testobject"))?
1718+
.to_hex();
17171719
assert_eq!(
17181720
object,
1719-
"5d94ceb0b2bb3a78237e0a74bc030a262239ab5f47754a5eb2e42941056b64cb21035d64a8f7c2f156e34b820802fa51884de2b1f7dc3a41b9878fc543cd9b07"
1721+
"dc31ae5d2f637e98d2171821d60d2fcafb8084d6a4bb3bd9cdc7ad41decce6e48f85d5413d22371d36b223945042f53a2a6ab449b8e45d8896ba7d8694a16681"
17201722
);
17211723
Ok(())
17221724
}

crates/lib/src/install.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,9 +1729,9 @@ async fn install_to_filesystem_impl(
17291729
// Load a fd for the mounted target physical root
17301730

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

1734-
setup_composefs_boot(rootfs, state, &hex::encode(id)).await?;
1734+
setup_composefs_boot(rootfs, state, &id).await?;
17351735
} else {
17361736
ostree_install(state, rootfs, cleanup).await?;
17371737
}

0 commit comments

Comments
 (0)