@@ -50,9 +50,9 @@ use serde::Serialize;
5050
5151use composefs:: { fsverity:: FsVerityHashValue , repository:: Repository } ;
5252
53- use crate :: ContentAndVerity ;
5453use crate :: layer:: is_tar_media_type;
5554use crate :: skopeo:: { OCI_BLOB_CONTENT_TYPE , OCI_CONFIG_CONTENT_TYPE , OCI_MANIFEST_CONTENT_TYPE } ;
55+ use crate :: { ContentAndVerity , sha256_content_digest} ;
5656
5757/// Error marker: an OCI reference (tag) does not exist.
5858#[ derive( Debug , thiserror:: Error ) ]
@@ -682,6 +682,54 @@ pub(crate) fn rewrite_manifest<ObjectID: FsVerityHashValue, S: AsRef<str>>(
682682 Ok ( ( manifest_digest. clone ( ) , id) )
683683}
684684
685+ /// Writes a manifest to the repository from raw JSON bytes.
686+ ///
687+ /// Unlike [`write_manifest`], this preserves the exact JSON bytes from the
688+ /// original source, avoiding digest mismatches from re-serialization.
689+ pub fn write_manifest_raw < ObjectID : FsVerityHashValue > (
690+ repo : & Arc < Repository < ObjectID > > ,
691+ manifest_json : & [ u8 ] ,
692+ manifest_digest : & str ,
693+ config_verity : & ObjectID ,
694+ layer_verities : & HashMap < Box < str > , ObjectID > ,
695+ reference : Option < & str > ,
696+ ) -> Result < ( String , ObjectID ) > {
697+ let digest: OciDigest = manifest_digest. parse ( ) . context ( "parsing manifest digest" ) ?;
698+ let content_id = manifest_identifier ( & digest) ;
699+
700+ if let Some ( verity) = repo. has_stream ( & content_id) ? {
701+ if let Some ( name) = reference {
702+ tag_image ( repo, & digest, name) ?;
703+ }
704+ return Ok ( ( manifest_digest. to_string ( ) , verity) ) ;
705+ }
706+
707+ let computed = sha256_content_digest ( manifest_json) ;
708+ ensure ! (
709+ digest == computed,
710+ "Manifest digest mismatch: expected {digest}, got {computed}"
711+ ) ;
712+
713+ let manifest: ImageManifest =
714+ serde_json:: from_slice ( manifest_json) . context ( "parsing manifest JSON" ) ?;
715+
716+ let mut stream = repo. create_stream ( OCI_MANIFEST_CONTENT_TYPE ) ?;
717+
718+ let config_key = format ! ( "config:{}" , manifest. config( ) . digest( ) ) ;
719+ stream. add_named_stream_ref ( & config_key, config_verity) ;
720+
721+ for ( diff_id, verity) in layer_verities {
722+ stream. add_named_stream_ref ( diff_id, verity) ;
723+ }
724+
725+ stream. write_external ( manifest_json) ?;
726+
727+ let oci_ref = reference. map ( oci_ref_path) ;
728+ let id = repo. write_stream ( stream, & content_id, oci_ref. as_deref ( ) ) ?;
729+
730+ Ok ( ( manifest_digest. to_string ( ) , id) )
731+ }
732+
685733/// Checks if a manifest exists.
686734pub fn has_manifest < ObjectID : FsVerityHashValue > (
687735 repo : & Repository < ObjectID > ,
0 commit comments