Compose selected State Anchor paths over prepared roots

This commit is contained in:
ms2k 2026-07-17 03:58:27 +09:00
commit 4dd2b150d2

View file

@ -223,8 +223,10 @@ impl OciRegistryTransport {
}
verify_platform(&config_json, platform)?;
validate_manifest(&manifest)?;
let layout = LayoutWriter::create(destination)?;
layout.write_verified_blob(&manifest.config.digest, config_json.as_bytes())?;
let layout = LayoutWriter::create(destination).context("creating private OCI layout")?;
layout
.write_verified_blob(&manifest.config.digest, config_json.as_bytes())
.context("writing verified OCI config")?;
for descriptor in &manifest.layers {
let size = u64::try_from(descriptor.size).context("negative OCI layer size")?;
if size > MAX_LAYER_BYTES {
@ -235,23 +237,33 @@ impl OciRegistryTransport {
layout.require_existing_blob(&descriptor.digest, size)?;
continue;
}
let (file, temporary) = layout.create_blob(digest_component)?;
let (file, temporary) = layout
.create_blob(digest_component)
.with_context(|| format!("creating OCI layer {}", descriptor.digest))?;
let file = tokio::fs::File::from_std(file);
if let Err(error) = client.pull_blob(&reference, descriptor, file).await {
layout.remove_temporary_blob(&temporary);
return Err(error)
.with_context(|| format!("pulling OCI layer {}", descriptor.digest));
}
let observed = layout.temporary_blob_size(&temporary)?;
let observed = layout
.temporary_blob_size(&temporary)
.with_context(|| format!("measuring OCI layer {}", descriptor.digest))?;
if observed != size {
bail!("OCI layer {} size mismatch", descriptor.digest);
}
layout.publish_blob(&temporary, digest_component)?;
layout
.publish_blob(&temporary, digest_component)
.with_context(|| format!("publishing OCI layer {}", descriptor.digest))?;
}
let local_manifest = serde_json::to_vec(&manifest)?;
let local_digest = format!("sha256:{}", hex(&Sha256::digest(&local_manifest)));
layout.write_verified_blob(&local_digest, &local_manifest)?;
layout.write_layout_index(&local_digest, local_manifest.len())?;
layout
.write_verified_blob(&local_digest, &local_manifest)
.context("writing verified OCI manifest")?;
layout
.write_layout_index(&local_digest, local_manifest.len())
.context("writing OCI layout index")?;
let selected_manifest_digest = parse_sha256(&local_digest)?;
let source_identity = format!(
"oci-v1\0{}\0{}\0{manifest_digest}",
@ -406,13 +418,19 @@ impl LayoutWriter {
bail!("OCI descriptor digest mismatch for {digest}");
}
let component = digest_component(digest)?;
if self.blob_exists(component)? {
if self
.blob_exists(component)
.context("checking verified OCI blob destination")?
{
return self.require_existing_blob(digest, bytes.len() as u64);
}
let (mut file, temporary) = self.create_blob(component)?;
file.write_all(bytes)?;
file.sync_all()?;
let (mut file, temporary) = self
.create_blob(component)
.context("creating verified OCI blob")?;
file.write_all(bytes).context("writing verified OCI blob")?;
file.sync_all().context("syncing verified OCI blob")?;
self.publish_blob(&temporary, component)
.context("publishing verified OCI blob")
}
fn blob_exists(&self, component: &str) -> Result<bool> {
@ -552,11 +570,16 @@ fn open_directory(parent: &File, path: &Path) -> std::io::Result<File> {
}
fn open_relative_file(parent: &File, path: &Path, flags: OFlags) -> std::io::Result<File> {
let mode = if flags.contains(OFlags::CREATE) {
Mode::from_raw_mode(0o600)
} else {
Mode::empty()
};
openat2(
parent,
path,
flags | OFlags::NOFOLLOW | OFlags::CLOEXEC,
Mode::from_raw_mode(0o600),
mode,
confined_resolve(),
)
.map(File::from)
@ -566,7 +589,11 @@ fn open_relative_file(parent: &File, path: &Path, flags: OFlags) -> std::io::Res
fn write_relative_atomic(root: &File, path: &Path, bytes: &[u8]) -> Result<()> {
let name = path.file_name().context("layout file has no name")?;
let parent_path = path.parent().unwrap_or(Path::new("."));
let parent = open_directory(root, parent_path)?;
let parent = if parent_path.as_os_str().is_empty() {
File::from(rustix::io::dup(root)?)
} else {
open_directory(root, parent_path)?
};
let temporary = format!(".{}.partial", name.to_string_lossy());
let mut file = open_relative_file(
&parent,
@ -709,6 +736,25 @@ mod tests {
use super::*;
#[test]
fn atomic_layout_write_accepts_a_root_level_destination() {
let tree = tempfile::tempdir().unwrap();
let root = File::open(tree.path()).unwrap();
write_relative_atomic(&root, Path::new("oci-layout"), b"layout").unwrap();
assert_eq!(
std::fs::read(tree.path().join("oci-layout")).unwrap(),
b"layout"
);
}
#[test]
fn confined_read_open_uses_an_empty_creation_mode() {
let tree = tempfile::tempdir().unwrap();
let root = File::open(tree.path()).unwrap();
let error = open_relative_file(&root, Path::new("absent"), OFlags::RDONLY).unwrap_err();
assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
}
#[test]
fn preexisting_transport_parents_require_exact_private_mode() {
for name in ["image-transports", "blobs", "sha256"] {