@@ -50,8 +50,8 @@ use serde::Serialize;
5050
5151use composefs:: { fsverity:: FsVerityHashValue , repository:: Repository } ;
5252
53- use crate :: ContentAndVerity ;
5453use crate :: skopeo:: { OCI_BLOB_CONTENT_TYPE , OCI_CONFIG_CONTENT_TYPE , OCI_MANIFEST_CONTENT_TYPE } ;
54+ use crate :: { ContentAndVerity , sha256_content_digest} ;
5555
5656/// Data and named refs from a splitstream with external object storage.
5757type ExternalData < ObjectID > = ( Vec < u8 > , HashMap < Box < str > , ObjectID > ) ;
@@ -684,6 +684,54 @@ pub(crate) fn rewrite_manifest<ObjectID: FsVerityHashValue>(
684684 Ok ( ( manifest_digest. clone ( ) , id) )
685685}
686686
687+ /// Writes a manifest to the repository from raw JSON bytes.
688+ ///
689+ /// Unlike [`write_manifest`], this preserves the exact JSON bytes from the
690+ /// original source, avoiding digest mismatches from re-serialization.
691+ pub fn write_manifest_raw < ObjectID : FsVerityHashValue > (
692+ repo : & Arc < Repository < ObjectID > > ,
693+ manifest_json : & [ u8 ] ,
694+ manifest_digest : & str ,
695+ config_verity : & ObjectID ,
696+ layer_verities : & HashMap < Box < str > , ObjectID > ,
697+ reference : Option < & str > ,
698+ ) -> Result < ( String , ObjectID ) > {
699+ let digest: OciDigest = manifest_digest. parse ( ) . context ( "parsing manifest digest" ) ?;
700+ let content_id = manifest_identifier ( & digest) ;
701+
702+ if let Some ( verity) = repo. has_stream ( & content_id) ? {
703+ if let Some ( name) = reference {
704+ tag_image ( repo, & digest, name) ?;
705+ }
706+ return Ok ( ( manifest_digest. to_string ( ) , verity) ) ;
707+ }
708+
709+ let computed = sha256_content_digest ( manifest_json) ;
710+ ensure ! (
711+ digest == computed,
712+ "Manifest digest mismatch: expected {digest}, got {computed}"
713+ ) ;
714+
715+ let manifest: ImageManifest =
716+ serde_json:: from_slice ( manifest_json) . context ( "parsing manifest JSON" ) ?;
717+
718+ let mut stream = repo. create_stream ( OCI_MANIFEST_CONTENT_TYPE ) ?;
719+
720+ let config_key = format ! ( "config:{}" , manifest. config( ) . digest( ) ) ;
721+ stream. add_named_stream_ref ( & config_key, config_verity) ;
722+
723+ for ( diff_id, verity) in layer_verities {
724+ stream. add_named_stream_ref ( diff_id, verity) ;
725+ }
726+
727+ stream. write_external ( manifest_json) ?;
728+
729+ let oci_ref = reference. map ( oci_ref_path) ;
730+ let id = repo. write_stream ( stream, & content_id, oci_ref. as_deref ( ) ) ?;
731+
732+ Ok ( ( manifest_digest. to_string ( ) , id) )
733+ }
734+
687735/// Checks if a manifest exists.
688736pub fn has_manifest < ObjectID : FsVerityHashValue > (
689737 repo : & Repository < ObjectID > ,
0 commit comments