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