Skip to content

Commit 268a7bc

Browse files
committed
image: Fix Unified/Partial semantics on non-unified-storage systems
On systems where unified storage is not enabled (no composefs/bootc.json), images in bootc's containers-storage should be reported as 'unified' — the original semantics meaning 'available via bootc storage for podman'. The composefs cross-reference check (Partial vs Unified) only makes sense when the unified-storage three-store pipeline has been opted into. Without this fix, plan-01-readonly would fail because 010-test-bootc- container-store.nu asserts pulled images appear as 'unified'. Assisted-by: OpenCode (claude-sonnet-4-6@default) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent c726e5f commit 268a7bc

1 file changed

Lines changed: 26 additions & 18 deletions

File tree

crates/lib/src/image.rs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,30 @@ async fn list_host_images_composefs(sysroot: &crate::store::Storage) -> Result<V
9696
.await
9797
.context("Listing containers-storage images")?;
9898

99-
// Build the set of config digests (= image IDs) that have a complete
100-
// composefs import.
101-
//
102-
// We match by config digest rather than manifest digest because podman may
103-
// report a different manifest digest than composefs (e.g. when layers are
104-
// recompressed during the copy into containers-storage). The config digest
105-
// is stable across such transformations and is also the image ID that
106-
// podman reports.
107-
//
108-
// For each bootc composefs tag, open the OCI image to retrieve its config
109-
// digest, then look that up against the cstorage image IDs (which are also
110-
// config sha256 hashes).
99+
// On systems where unified storage is NOT enabled, all images in bootc's
100+
// containers-storage are reported as "unified" (original semantics: available
101+
// via bootc storage for podman). The composefs cross-reference check only
102+
// applies when the unified-storage flag has been set.
103+
let unified_enabled = crate::store::BootcRepoMeta::read(sysroot_dir)?
104+
.map(|m| m.unified_storage)
105+
.unwrap_or(false);
106+
107+
if !unified_enabled {
108+
return Ok(images
109+
.into_iter()
110+
.flat_map(|entry| {
111+
entry.names.unwrap_or_default().into_iter().map(|name| ImageOutput {
112+
image: name,
113+
image_type: ImageListTypeColumn::Unified,
114+
})
115+
})
116+
.collect());
117+
}
118+
119+
// On unified-storage systems, cross-reference against composefs tags to
120+
// determine whether each image went through the full three-store pipeline.
121+
// We match by config digest (= image ID, stable across layer recompression)
122+
// rather than manifest digest (which may differ after copying).
111123
let composefs_config_digests: std::collections::HashSet<String> =
112124
match sysroot.get_ensure_composefs() {
113125
Ok(repo) => {
@@ -131,18 +143,14 @@ async fn list_host_images_composefs(sysroot: &crate::store::Storage) -> Result<V
131143
.into_iter()
132144
.flat_map(|entry| {
133145
let names = entry.names.unwrap_or_default();
134-
// The cstorage image ID is the config sha256 (e.g. "sha256:abc...").
135-
// Match against composefs config digests for stable cross-referencing.
136-
let image_type = if composefs_config_digests.is_empty() {
137-
// No composefs repo / no tags yet — conservatively mark partial.
138-
ImageListTypeColumn::Partial
139-
} else if {
146+
let image_type = if {
140147
let id = &entry.id;
141148
composefs_config_digests.contains(&format!("sha256:{id}"))
142149
|| composefs_config_digests.contains(id.as_str())
143150
} {
144151
ImageListTypeColumn::Unified
145152
} else {
153+
// In composefs storage but not composefs-tagged — partial import.
146154
ImageListTypeColumn::Partial
147155
};
148156
names.into_iter().map(move |name| ImageOutput {

0 commit comments

Comments
 (0)