@@ -227,15 +227,16 @@ pub struct PreparedImport {
227
227
/// The layers containing split objects
228
228
pub ostree_layers : Vec < ManifestLayerState > ,
229
229
/// The layer for the ostree commit.
230
- pub ostree_commit_layer : ManifestLayerState ,
230
+ pub ostree_commit_layer : Option < ManifestLayerState > ,
231
231
/// Any further non-ostree (derived) layers.
232
232
pub layers : Vec < ManifestLayerState > ,
233
233
}
234
234
235
235
impl PreparedImport {
236
236
/// Iterate over all layers; the commit layer, the ostree split object layers, and any non-ostree layers.
237
237
pub fn all_layers ( & self ) -> impl Iterator < Item = & ManifestLayerState > {
238
- std:: iter:: once ( & self . ostree_commit_layer )
238
+ self . ostree_commit_layer
239
+ . iter ( )
239
240
. chain ( self . ostree_layers . iter ( ) )
240
241
. chain ( self . layers . iter ( ) )
241
242
}
@@ -376,21 +377,20 @@ fn layer_from_diffid<'a>(
376
377
pub ( crate ) fn parse_manifest_layout < ' a > (
377
378
manifest : & ' a ImageManifest ,
378
379
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
+ ) > {
380
385
let config_labels = super :: labels_of ( config) ;
381
386
382
387
let first_layer = manifest
383
388
. layers ( )
384
389
. first ( )
385
390
. 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
+ } ;
394
394
395
395
let target_layer = layer_from_diffid ( manifest, config, target_diffid. as_str ( ) ) ?;
396
396
let mut chunk_layers = Vec :: new ( ) ;
@@ -416,7 +416,20 @@ pub(crate) fn parse_manifest_layout<'a>(
416
416
}
417
417
}
418
418
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) )
420
433
}
421
434
422
435
/// Find the timestamp of the manifest (or config), ignoring errors.
@@ -601,10 +614,10 @@ impl ImageImporter {
601
614
parse_manifest_layout ( & manifest, & config) ?;
602
615
603
616
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 ( ) ?;
605
618
let component_layers = component_layers
606
619
. into_iter ( )
607
- . map ( query)
620
+ . map ( |l| query ( l ) )
608
621
. collect :: < Result < Vec < _ > > > ( ) ?;
609
622
let remaining_layers = remaining_layers
610
623
. into_iter ( )
@@ -693,6 +706,7 @@ impl ImageImporter {
693
706
pub ( crate ) async fn unencapsulate_base (
694
707
& mut self ,
695
708
import : & mut store:: PreparedImport ,
709
+ require_ostree : bool ,
696
710
write_refs : bool ,
697
711
) -> Result < ( ) > {
698
712
tracing:: debug!( "Fetching base" ) ;
@@ -707,6 +721,14 @@ impl ImageImporter {
707
721
None
708
722
}
709
723
} ;
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
+ } ;
710
732
let des_layers = self . proxy . get_layer_info ( & self . proxy_img ) . await ?;
711
733
for layer in import. ostree_layers . iter_mut ( ) {
712
734
if layer. commit . is_some ( ) {
@@ -755,25 +777,25 @@ impl ImageImporter {
755
777
. await ?;
756
778
}
757
779
}
758
- if import . ostree_commit_layer . commit . is_none ( ) {
780
+ if commit_layer . commit . is_none ( ) {
759
781
if let Some ( p) = self . layer_progress . as_ref ( ) {
760
782
p. send ( ImportProgress :: OstreeChunkStarted (
761
- import . ostree_commit_layer . layer . clone ( ) ,
783
+ commit_layer . layer . clone ( ) ,
762
784
) )
763
785
. await ?;
764
786
}
765
787
let ( blob, driver, media_type) = fetch_layer (
766
788
& self . proxy ,
767
789
& self . proxy_img ,
768
790
& import. manifest ,
769
- & import . ostree_commit_layer . layer ,
791
+ & commit_layer . layer ,
770
792
self . layer_byte_progress . as_ref ( ) ,
771
793
des_layers. as_ref ( ) ,
772
794
self . imgref . imgref . transport ,
773
795
)
774
796
. await ?;
775
797
let repo = self . repo . clone ( ) ;
776
- let target_ref = import . ostree_commit_layer . ostree_ref . clone ( ) ;
798
+ let target_ref = commit_layer . ostree_ref . clone ( ) ;
777
799
let import_task =
778
800
crate :: tokio_util:: spawn_blocking_cancellable_flatten ( move |cancellable| {
779
801
let txn = repo. auto_transaction ( Some ( cancellable) ) ?;
@@ -792,10 +814,10 @@ impl ImageImporter {
792
814
Ok :: < _ , anyhow:: Error > ( commit)
793
815
} ) ;
794
816
let commit = super :: unencapsulate:: join_fetch ( import_task, driver) . await ?;
795
- import . ostree_commit_layer . commit = Some ( commit) ;
817
+ commit_layer . commit = Some ( commit) ;
796
818
if let Some ( p) = self . layer_progress . as_ref ( ) {
797
819
p. send ( ImportProgress :: OstreeChunkCompleted (
798
- import . ostree_commit_layer . layer . clone ( ) ,
820
+ commit_layer . layer . clone ( ) ,
799
821
) )
800
822
. await ?;
801
823
}
@@ -818,11 +840,12 @@ impl ImageImporter {
818
840
anyhow:: bail!( "Image has {} non-ostree layers" , prep. layers. len( ) ) ;
819
841
}
820
842
let deprecated_warning = prep. deprecated_warning ( ) . map ( ToOwned :: to_owned) ;
821
- self . unencapsulate_base ( & mut prep, false ) . await ?;
843
+ self . unencapsulate_base ( & mut prep, true , false ) . await ?;
822
844
// TODO change the imageproxy API to ensure this happens automatically when
823
845
// the image reference is dropped
824
846
self . proxy . close_image ( & self . proxy_img ) . await ?;
825
- let ostree_commit = prep. ostree_commit_layer . commit . unwrap ( ) ;
847
+ // SAFETY: We know we have a commit
848
+ let ostree_commit = prep. ostree_commit_layer . unwrap ( ) . commit . unwrap ( ) ;
826
849
let image_digest = prep. manifest_digest ;
827
850
Ok ( Import {
828
851
ostree_commit,
@@ -844,20 +867,23 @@ impl ImageImporter {
844
867
}
845
868
// First download all layers for the base image (if necessary) - we need the SELinux policy
846
869
// there to label all following layers.
847
- self . unencapsulate_base ( & mut import, true ) . await ?;
870
+ self . unencapsulate_base ( & mut import, false , true ) . await ?;
848
871
let des_layers = self . proxy . get_layer_info ( & self . proxy_img ) . await ?;
849
872
let proxy = self . proxy ;
850
873
let proxy_img = self . proxy_img ;
851
874
let target_imgref = self . target_imgref . as_ref ( ) . unwrap_or ( & self . imgref ) ;
852
- let base_commit = import. ostree_commit_layer . commit . clone ( ) . unwrap ( ) ;
875
+ let base_commit = import
876
+ . ostree_commit_layer
877
+ . as_ref ( )
878
+ . map ( |c| c. commit . clone ( ) . unwrap ( ) ) ;
853
879
854
- let root_is_transient = {
855
- let rootf = self
856
- . repo
857
- . read_commit ( & base_commit, gio:: Cancellable :: NONE ) ?
858
- . 0 ;
880
+ let root_is_transient = if let Some ( base) = base_commit. as_ref ( ) {
881
+ let rootf = self . repo . read_commit ( & base, gio:: Cancellable :: NONE ) ?. 0 ;
859
882
let rootf = rootf. downcast_ref :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
860
883
crate :: ostree_prepareroot:: overlayfs_root_enabled ( rootf) ?
884
+ } else {
885
+ // For generic images we assume they're using composefs
886
+ true
861
887
} ;
862
888
tracing:: debug!( "Base rootfs is transient: {root_is_transient}" ) ;
863
889
@@ -888,7 +914,7 @@ impl ImageImporter {
888
914
// An important aspect of this is that we SELinux label the derived layers using
889
915
// the base policy.
890
916
let opts = crate :: tar:: WriteTarOptions {
891
- base : Some ( base_commit. clone ( ) ) ,
917
+ base : base_commit. clone ( ) ,
892
918
selinux : true ,
893
919
allow_nonusr : root_is_transient,
894
920
retain_var : self . ostree_v2024_3 ,
@@ -975,14 +1001,16 @@ impl ImageImporter {
975
1001
process_whiteouts : false ,
976
1002
..Default :: default ( )
977
1003
} ;
978
- repo. checkout_at (
979
- Some ( & checkout_opts) ,
980
- ( * td) . as_raw_fd ( ) ,
981
- rootpath,
982
- & base_commit,
983
- cancellable,
984
- )
985
- . context ( "Checking out base commit" ) ?;
1004
+ if let Some ( base) = base_commit. as_ref ( ) {
1005
+ repo. checkout_at (
1006
+ Some ( & checkout_opts) ,
1007
+ ( * td) . as_raw_fd ( ) ,
1008
+ rootpath,
1009
+ & base,
1010
+ cancellable,
1011
+ )
1012
+ . context ( "Checking out base commit" ) ?;
1013
+ }
986
1014
987
1015
// Layer all subsequent commits
988
1016
checkout_opts. process_whiteouts = true ;
@@ -1008,10 +1036,12 @@ impl ImageImporter {
1008
1036
let sepolicy = ostree:: SePolicy :: new_at ( rootpath. as_raw_fd ( ) , cancellable) ?;
1009
1037
tracing:: debug!( "labeling from merged tree" ) ;
1010
1038
modifier. set_sepolicy ( Some ( & sepolicy) ) ;
1011
- } else {
1039
+ } else if let Some ( base ) = base_commit . as_ref ( ) {
1012
1040
tracing:: debug!( "labeling from base tree" ) ;
1013
1041
// TODO: We can likely drop this; we know all labels should be pre-computed.
1014
- modifier. set_sepolicy_from_commit ( repo, & base_commit, cancellable) ?;
1042
+ modifier. set_sepolicy_from_commit ( repo, & base, cancellable) ?;
1043
+ } else {
1044
+ unreachable ! ( )
1015
1045
}
1016
1046
1017
1047
let mt = ostree:: MutableTree :: new ( ) ;
@@ -1303,6 +1333,7 @@ pub(crate) fn export_to_oci(
1303
1333
let srcinfo = query_image ( repo, imgref) ?. ok_or_else ( || anyhow ! ( "No such image" ) ) ?;
1304
1334
let ( commit_layer, component_layers, remaining_layers) =
1305
1335
parse_manifest_layout ( & srcinfo. manifest , & srcinfo. configuration ) ?;
1336
+ let commit_layer = commit_layer. ok_or_else ( || anyhow ! ( "Missing {DIFFID_LABEL}" ) ) ?;
1306
1337
let commit_chunk_ref = ref_for_layer ( commit_layer) ?;
1307
1338
let commit_chunk_rev = repo. require_rev ( & commit_chunk_ref) ?;
1308
1339
let mut chunking = chunking:: Chunking :: new ( repo, & commit_chunk_rev) ?;
@@ -1768,10 +1799,12 @@ pub(crate) fn verify_container_image(
1768
1799
. 0
1769
1800
. downcast :: < ostree:: RepoFile > ( )
1770
1801
. expect ( "downcast" ) ;
1771
- println ! (
1772
- "Verifying with base ostree layer {}" ,
1773
- ref_for_layer( commit_layer) ?
1774
- ) ;
1802
+ if let Some ( commit_layer) = commit_layer {
1803
+ println ! (
1804
+ "Verifying with base ostree layer {}" ,
1805
+ ref_for_layer( commit_layer) ?
1806
+ ) ;
1807
+ }
1775
1808
compare_commit_trees (
1776
1809
repo,
1777
1810
"/" . into ( ) ,
0 commit comments