@@ -227,15 +227,16 @@ pub struct PreparedImport {
227227 /// The layers containing split objects
228228 pub ostree_layers : Vec < ManifestLayerState > ,
229229 /// The layer for the ostree commit.
230- pub ostree_commit_layer : ManifestLayerState ,
230+ pub ostree_commit_layer : Option < ManifestLayerState > ,
231231 /// Any further non-ostree (derived) layers.
232232 pub layers : Vec < ManifestLayerState > ,
233233}
234234
235235impl PreparedImport {
236236 /// Iterate over all layers; the commit layer, the ostree split object layers, and any non-ostree layers.
237237 pub fn all_layers ( & self ) -> impl Iterator < Item = & ManifestLayerState > {
238- std:: iter:: once ( & self . ostree_commit_layer )
238+ self . ostree_commit_layer
239+ . iter ( )
239240 . chain ( self . ostree_layers . iter ( ) )
240241 . chain ( self . layers . iter ( ) )
241242 }
@@ -376,21 +377,20 @@ fn layer_from_diffid<'a>(
376377pub ( crate ) fn parse_manifest_layout < ' a > (
377378 manifest : & ' a ImageManifest ,
378379 config : & ImageConfiguration ,
379- ) -> Result < ( & ' a Descriptor , Vec < & ' a Descriptor > , Vec < & ' a Descriptor > ) > {
380+ ) -> Result < (
381+ Option < & ' a Descriptor > ,
382+ Vec < & ' a Descriptor > ,
383+ Vec < & ' a Descriptor > ,
384+ ) > {
380385 let config_labels = super :: labels_of ( config) ;
381386
382387 let first_layer = manifest
383388 . layers ( )
384389 . first ( )
385390 . ok_or_else ( || anyhow ! ( "No layers in manifest" ) ) ?;
386- let target_diffid = config_labels
387- . and_then ( |labels| labels. get ( DIFFID_LABEL ) )
388- . ok_or_else ( || {
389- anyhow ! (
390- "No {} label found, not an ostree encapsulated container" ,
391- DIFFID_LABEL
392- )
393- } ) ?;
391+ let Some ( target_diffid) = config_labels. and_then ( |labels| labels. get ( DIFFID_LABEL ) ) else {
392+ return Ok ( ( None , Vec :: new ( ) , manifest. layers ( ) . iter ( ) . collect ( ) ) ) ;
393+ } ;
394394
395395 let target_layer = layer_from_diffid ( manifest, config, target_diffid. as_str ( ) ) ?;
396396 let mut chunk_layers = Vec :: new ( ) ;
@@ -416,7 +416,20 @@ pub(crate) fn parse_manifest_layout<'a>(
416416 }
417417 }
418418
419- Ok ( ( ostree_layer, chunk_layers, derived_layers) )
419+ Ok ( ( Some ( ostree_layer) , chunk_layers, derived_layers) )
420+ }
421+
422+ /// Like [`parse_manifest_layout`] but requires the image has an ostree base.
423+ #[ context( "Parsing manifest layout" ) ]
424+ pub ( crate ) fn parse_ostree_manifest_layout < ' a > (
425+ manifest : & ' a ImageManifest ,
426+ config : & ImageConfiguration ,
427+ ) -> Result < ( & ' a Descriptor , Vec < & ' a Descriptor > , Vec < & ' a Descriptor > ) > {
428+ let ( ostree_layer, component_layers, derived_layers) = parse_manifest_layout ( manifest, config) ?;
429+ let ostree_layer = ostree_layer. ok_or_else ( || {
430+ anyhow ! ( "No {DIFFID_LABEL} label found, not an ostree encapsulated container" )
431+ } ) ?;
432+ Ok ( ( ostree_layer, component_layers, derived_layers) )
420433}
421434
422435/// Find the timestamp of the manifest (or config), ignoring errors.
@@ -601,10 +614,10 @@ impl ImageImporter {
601614 parse_manifest_layout ( & manifest, & config) ?;
602615
603616 let query = |l : & Descriptor | query_layer ( & self . repo , l. clone ( ) ) ;
604- let commit_layer = query ( commit_layer ) ?;
617+ let commit_layer = commit_layer . map ( query) . transpose ( ) ?;
605618 let component_layers = component_layers
606619 . into_iter ( )
607- . map ( query)
620+ . map ( |l| query ( l ) )
608621 . collect :: < Result < Vec < _ > > > ( ) ?;
609622 let remaining_layers = remaining_layers
610623 . into_iter ( )
@@ -693,6 +706,7 @@ impl ImageImporter {
693706 pub ( crate ) async fn unencapsulate_base (
694707 & mut self ,
695708 import : & mut store:: PreparedImport ,
709+ require_ostree : bool ,
696710 write_refs : bool ,
697711 ) -> Result < ( ) > {
698712 tracing:: debug!( "Fetching base" ) ;
@@ -707,6 +721,14 @@ impl ImageImporter {
707721 None
708722 }
709723 } ;
724+ let Some ( commit_layer) = import. ostree_commit_layer . as_mut ( ) else {
725+ if require_ostree {
726+ anyhow:: bail!(
727+ "No {DIFFID_LABEL} label found, not an ostree encapsulated container"
728+ ) ;
729+ }
730+ return Ok ( ( ) ) ;
731+ } ;
710732 let des_layers = self . proxy . get_layer_info ( & self . proxy_img ) . await ?;
711733 for layer in import. ostree_layers . iter_mut ( ) {
712734 if layer. commit . is_some ( ) {
@@ -754,31 +776,31 @@ impl ImageImporter {
754776 . await ?;
755777 }
756778 }
757- if import . ostree_commit_layer . commit . is_none ( ) {
779+ if commit_layer . commit . is_none ( ) {
758780 if let Some ( p) = self . layer_progress . as_ref ( ) {
759781 p. send ( ImportProgress :: OstreeChunkStarted (
760- import . ostree_commit_layer . layer . clone ( ) ,
782+ commit_layer . layer . clone ( ) ,
761783 ) )
762784 . await ?;
763785 }
764786 let ( blob, driver) = fetch_layer_decompress (
765787 & self . proxy ,
766788 & self . proxy_img ,
767789 & import. manifest ,
768- & import . ostree_commit_layer . layer ,
790+ & commit_layer . layer ,
769791 self . layer_byte_progress . as_ref ( ) ,
770792 des_layers. as_ref ( ) ,
771793 self . imgref . imgref . transport ,
772794 )
773795 . await ?;
774796 let repo = self . repo . clone ( ) ;
775- let target_ref = import . ostree_commit_layer . ostree_ref . clone ( ) ;
797+ let target_ref = commit_layer . ostree_ref . clone ( ) ;
776798 let import_task =
777799 crate :: tokio_util:: spawn_blocking_cancellable_flatten ( move |cancellable| {
778800 let txn = repo. auto_transaction ( Some ( cancellable) ) ?;
779801 let mut importer = crate :: tar:: Importer :: new_for_commit ( & repo, remote) ;
780- let blob = tokio_util:: io:: SyncIoBridge :: new ( blob) ;
781- let mut archive = tar:: Archive :: new ( blob) ;
802+ let mut blob = tokio_util:: io:: SyncIoBridge :: new ( blob) ;
803+ let mut archive = tar:: Archive :: new ( & mut blob) ;
782804 importer. import_commit ( & mut archive, Some ( cancellable) ) ?;
783805 let commit = importer. finish_import_commit ( ) ;
784806 if write_refs {
@@ -790,10 +812,10 @@ impl ImageImporter {
790812 Ok :: < _ , anyhow:: Error > ( commit)
791813 } ) ;
792814 let commit = super :: unencapsulate:: join_fetch ( import_task, driver) . await ?;
793- import . ostree_commit_layer . commit = Some ( commit) ;
815+ commit_layer . commit = Some ( commit) ;
794816 if let Some ( p) = self . layer_progress . as_ref ( ) {
795817 p. send ( ImportProgress :: OstreeChunkCompleted (
796- import . ostree_commit_layer . layer . clone ( ) ,
818+ commit_layer . layer . clone ( ) ,
797819 ) )
798820 . await ?;
799821 }
@@ -816,11 +838,12 @@ impl ImageImporter {
816838 anyhow:: bail!( "Image has {} non-ostree layers" , prep. layers. len( ) ) ;
817839 }
818840 let deprecated_warning = prep. deprecated_warning ( ) . map ( ToOwned :: to_owned) ;
819- self . unencapsulate_base ( & mut prep, false ) . await ?;
841+ self . unencapsulate_base ( & mut prep, true , false ) . await ?;
820842 // TODO change the imageproxy API to ensure this happens automatically when
821843 // the image reference is dropped
822844 self . proxy . close_image ( & self . proxy_img ) . await ?;
823- let ostree_commit = prep. ostree_commit_layer . commit . unwrap ( ) ;
845+ // SAFETY: We know we have a commit
846+ let ostree_commit = prep. ostree_commit_layer . unwrap ( ) . commit . unwrap ( ) ;
824847 let image_digest = prep. manifest_digest ;
825848 Ok ( Import {
826849 ostree_commit,
@@ -842,20 +865,23 @@ impl ImageImporter {
842865 }
843866 // First download all layers for the base image (if necessary) - we need the SELinux policy
844867 // there to label all following layers.
845- self . unencapsulate_base ( & mut import, true ) . await ?;
868+ self . unencapsulate_base ( & mut import, false , true ) . await ?;
846869 let des_layers = self . proxy . get_layer_info ( & self . proxy_img ) . await ?;
847870 let proxy = self . proxy ;
848871 let proxy_img = self . proxy_img ;
849872 let target_imgref = self . target_imgref . as_ref ( ) . unwrap_or ( & self . imgref ) ;
850- let base_commit = import. ostree_commit_layer . commit . clone ( ) . unwrap ( ) ;
873+ let base_commit = import
874+ . ostree_commit_layer
875+ . as_ref ( )
876+ . map ( |c| c. commit . clone ( ) . unwrap ( ) ) ;
851877
852- let root_is_transient = {
853- let rootf = self
854- . repo
855- . read_commit ( & base_commit, gio:: Cancellable :: NONE ) ?
856- . 0 ;
878+ let root_is_transient = if let Some ( base) = base_commit. as_ref ( ) {
879+ let rootf = self . repo . read_commit ( & base, gio:: Cancellable :: NONE ) ?. 0 ;
857880 let rootf = rootf. downcast_ref :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
858881 crate :: ostree_prepareroot:: overlayfs_root_enabled ( rootf) ?
882+ } else {
883+ // For generic images we assume they're using composefs
884+ true
859885 } ;
860886 tracing:: debug!( "Base rootfs is transient: {root_is_transient}" ) ;
861887
@@ -886,7 +912,7 @@ impl ImageImporter {
886912 // An important aspect of this is that we SELinux label the derived layers using
887913 // the base policy.
888914 let opts = crate :: tar:: WriteTarOptions {
889- base : Some ( base_commit. clone ( ) ) ,
915+ base : base_commit. clone ( ) ,
890916 selinux : true ,
891917 allow_nonusr : root_is_transient,
892918 retain_var : self . ostree_v2024_3 ,
@@ -968,14 +994,16 @@ impl ImageImporter {
968994 process_whiteouts : false ,
969995 ..Default :: default ( )
970996 } ;
971- repo. checkout_at (
972- Some ( & checkout_opts) ,
973- ( * td) . as_raw_fd ( ) ,
974- rootpath,
975- & base_commit,
976- cancellable,
977- )
978- . context ( "Checking out base commit" ) ?;
997+ if let Some ( base) = base_commit. as_ref ( ) {
998+ repo. checkout_at (
999+ Some ( & checkout_opts) ,
1000+ ( * td) . as_raw_fd ( ) ,
1001+ rootpath,
1002+ & base,
1003+ cancellable,
1004+ )
1005+ . context ( "Checking out base commit" ) ?;
1006+ }
9791007
9801008 // Layer all subsequent commits
9811009 checkout_opts. process_whiteouts = true ;
@@ -1001,10 +1029,12 @@ impl ImageImporter {
10011029 let sepolicy = ostree:: SePolicy :: new_at ( rootpath. as_raw_fd ( ) , cancellable) ?;
10021030 tracing:: debug!( "labeling from merged tree" ) ;
10031031 modifier. set_sepolicy ( Some ( & sepolicy) ) ;
1004- } else {
1032+ } else if let Some ( base ) = base_commit . as_ref ( ) {
10051033 tracing:: debug!( "labeling from base tree" ) ;
10061034 // TODO: We can likely drop this; we know all labels should be pre-computed.
1007- modifier. set_sepolicy_from_commit ( repo, & base_commit, cancellable) ?;
1035+ modifier. set_sepolicy_from_commit ( repo, & base, cancellable) ?;
1036+ } else {
1037+ unreachable ! ( )
10081038 }
10091039
10101040 let mt = ostree:: MutableTree :: new ( ) ;
@@ -1296,6 +1326,7 @@ pub(crate) fn export_to_oci(
12961326 let srcinfo = query_image ( repo, imgref) ?. ok_or_else ( || anyhow ! ( "No such image" ) ) ?;
12971327 let ( commit_layer, component_layers, remaining_layers) =
12981328 parse_manifest_layout ( & srcinfo. manifest , & srcinfo. configuration ) ?;
1329+ let commit_layer = commit_layer. ok_or_else ( || anyhow ! ( "Missing {DIFFID_LABEL}" ) ) ?;
12991330 let commit_chunk_ref = ref_for_layer ( commit_layer) ?;
13001331 let commit_chunk_rev = repo. require_rev ( & commit_chunk_ref) ?;
13011332 let mut chunking = chunking:: Chunking :: new ( repo, & commit_chunk_rev) ?;
@@ -1761,10 +1792,12 @@ pub(crate) fn verify_container_image(
17611792 . 0
17621793 . downcast :: < ostree:: RepoFile > ( )
17631794 . expect ( "downcast" ) ;
1764- println ! (
1765- "Verifying with base ostree layer {}" ,
1766- ref_for_layer( commit_layer) ?
1767- ) ;
1795+ if let Some ( commit_layer) = commit_layer {
1796+ println ! (
1797+ "Verifying with base ostree layer {}" ,
1798+ ref_for_layer( commit_layer) ?
1799+ ) ;
1800+ }
17681801 compare_commit_trees (
17691802 repo,
17701803 "/" . into ( ) ,
0 commit comments