ostree_ext/container/
update_detachedmeta.rs1use super::ImageReference;
2use crate::container::{DIFFID_LABEL, skopeo};
3use crate::container::{Transport, store as container_store};
4use anyhow::{Context, Result, anyhow};
5use camino::Utf8Path;
6use cap_std::fs::Dir;
7use cap_std_ext::cap_std;
8use containers_image_proxy::oci_spec::image as oci_image;
9use std::io::{BufReader, BufWriter};
10
11pub async fn update_detached_metadata(
16 src: &ImageReference,
17 dest: &ImageReference,
18 detached_buf: Option<&[u8]>,
19) -> Result<oci_image::Digest> {
20 let tempdir = tempfile::tempdir_in("/var/tmp")?;
24 let tempsrc = tempdir.path().join("src");
25 let tempsrc_utf8 = Utf8Path::from_path(&tempsrc).ok_or_else(|| anyhow!("Invalid tempdir"))?;
26 let tempsrc_ref = ImageReference {
27 transport: Transport::OciDir,
28 name: tempsrc_utf8.to_string(),
29 };
30
31 let pulled_digest = skopeo::copy(src, &tempsrc_ref, None, None, false)
33 .await
34 .context("Creating temporary copy to OCI dir")?;
35
36 let detached_buf = detached_buf.map(Vec::from);
38 let tempsrc_ref_path = tempsrc_ref.name.clone();
39 crate::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| {
41 let tempsrc = Dir::open_ambient_dir(tempsrc_ref_path, cap_std::ambient_authority())
43 .context("Opening src")?;
44 let tempsrc = ocidir::OciDir::open(tempsrc)?;
45
46 let idx = tempsrc.read_index()?;
48 let manifest_descriptor = idx
49 .manifests()
50 .first()
51 .ok_or(anyhow!("No manifests in index"))?;
52 let mut manifest: oci_image::ImageManifest = tempsrc
53 .read_json_blob(manifest_descriptor)
54 .context("Reading manifest json blob")?;
55
56 anyhow::ensure!(manifest_descriptor.digest() == &pulled_digest);
57 let platform = manifest_descriptor
58 .platform()
59 .as_ref()
60 .cloned()
61 .unwrap_or_default();
62 let mut config: oci_image::ImageConfiguration =
63 tempsrc.read_json_blob(manifest.config())?;
64 let mut ctrcfg = config
65 .config()
66 .as_ref()
67 .cloned()
68 .ok_or_else(|| anyhow!("Image is missing container configuration"))?;
69
70 let (commit_layer, _, _) =
72 container_store::parse_ostree_manifest_layout(&manifest, &config)?;
73 let commit_layer_idx = manifest
74 .layers()
75 .iter()
76 .position(|x| x == commit_layer)
77 .unwrap();
78
79 let out_layer = {
81 let src_layer = BufReader::new(tempsrc.read_blob(commit_layer)?);
83 let mut src_layer = flate2::read::GzDecoder::new(src_layer);
84 let mut out_layer = BufWriter::new(tempsrc.create_gzip_layer(None)?);
85
86 crate::tar::update_detached_metadata(
88 &mut src_layer,
89 &mut out_layer,
90 detached_buf.as_deref(),
91 Some(cancellable),
92 )?;
93
94 out_layer
96 .into_inner()
97 .map_err(|_| anyhow!("Failed to flush buffer"))?
98 .complete()?
99 };
100 let out_layer_diffid = format!("sha256:{}", out_layer.uncompressed_sha256.digest());
102 let out_layer_descriptor = out_layer
103 .descriptor()
104 .media_type(oci_image::MediaType::ImageLayerGzip)
105 .build()
106 .unwrap(); manifest.layers_mut()[commit_layer_idx] = out_layer_descriptor;
110 config.rootfs_mut().diff_ids_mut()[commit_layer_idx].clone_from(&out_layer_diffid);
111
112 let labels = ctrcfg.labels_mut().get_or_insert_with(Default::default);
113 if manifest.layers().len() == 1 {
116 labels.insert(DIFFID_LABEL.into(), out_layer_diffid);
117 }
118 config.set_config(Some(ctrcfg));
119
120 let new_config_descriptor = tempsrc.write_config(config)?;
122 manifest.set_config(new_config_descriptor);
123 tempsrc
125 .replace_with_single_manifest(manifest, platform)
126 .context("Writing manifest")?;
127 Ok(())
128 })
129 .await
130 .context("Regenerating commit layer")?;
131
132 crate::container::skopeo::copy(&tempsrc_ref, dest, None, None, false)
135 .await
136 .context("Copying to destination")
137}