@@ -940,6 +940,132 @@ impl ImageImporter {
940940 } )
941941 }
942942
943+ /// Generate a single ostree commit that combines all layers, and also
944+ /// includes container image metadata such as the manifest and config.
945+ fn write_merge_commit_impl (
946+ repo : & ostree:: Repo ,
947+ base_commit : Option < & str > ,
948+ layer_commits : & [ String ] ,
949+ have_derived_layers : bool ,
950+ metadata : glib:: Variant ,
951+ timestamp : u64 ,
952+ ostree_ref : & str ,
953+ no_imgref : bool ,
954+ disable_gc : bool ,
955+ cancellable : Option < & gio:: Cancellable > ,
956+ ) -> Result < Box < LayeredImageState > > {
957+ use rustix:: fd:: AsRawFd ;
958+
959+ let txn = repo. auto_transaction ( cancellable) ?;
960+
961+ let devino = ostree:: RepoDevInoCache :: new ( ) ;
962+ let repodir = Dir :: reopen_dir ( & repo. dfd_borrow ( ) ) ?;
963+ let repo_tmp = repodir. open_dir ( "tmp" ) ?;
964+ let td = cap_std_ext:: cap_tempfile:: TempDir :: new_in ( & repo_tmp) ?;
965+
966+ let rootpath = "root" ;
967+ let checkout_mode = if repo. mode ( ) == ostree:: RepoMode :: Bare {
968+ ostree:: RepoCheckoutMode :: None
969+ } else {
970+ ostree:: RepoCheckoutMode :: User
971+ } ;
972+ let mut checkout_opts = ostree:: RepoCheckoutAtOptions {
973+ mode : checkout_mode,
974+ overwrite_mode : ostree:: RepoCheckoutOverwriteMode :: UnionFiles ,
975+ devino_to_csum_cache : Some ( devino. clone ( ) ) ,
976+ no_copy_fallback : true ,
977+ force_copy_zerosized : true ,
978+ process_whiteouts : false ,
979+ ..Default :: default ( )
980+ } ;
981+ if let Some ( base) = base_commit. as_ref ( ) {
982+ repo. checkout_at (
983+ Some ( & checkout_opts) ,
984+ ( * td) . as_raw_fd ( ) ,
985+ rootpath,
986+ & base,
987+ cancellable,
988+ )
989+ . context ( "Checking out base commit" ) ?;
990+ }
991+
992+ // Layer all subsequent commits
993+ checkout_opts. process_whiteouts = true ;
994+ for commit in layer_commits {
995+ tracing:: debug!( "Unpacking {commit}" ) ;
996+ repo. checkout_at (
997+ Some ( & checkout_opts) ,
998+ ( * td) . as_raw_fd ( ) ,
999+ rootpath,
1000+ & commit,
1001+ cancellable,
1002+ )
1003+ . with_context ( || format ! ( "Checking out layer {commit}" ) ) ?;
1004+ }
1005+
1006+ let root_dir = td. open_dir ( rootpath) ?;
1007+
1008+ let modifier =
1009+ ostree:: RepoCommitModifier :: new ( ostree:: RepoCommitModifierFlags :: empty ( ) , None ) ;
1010+ modifier. set_devino_cache ( & devino) ;
1011+ // If we have derived layers, then we need to handle the case where
1012+ // the derived layers include custom policy. Just relabel everything
1013+ // in this case.
1014+ if have_derived_layers {
1015+ let sepolicy = ostree:: SePolicy :: new_at ( root_dir. as_raw_fd ( ) , cancellable) ?;
1016+ tracing:: debug!( "labeling from merged tree" ) ;
1017+ modifier. set_sepolicy ( Some ( & sepolicy) ) ;
1018+ } else if let Some ( base) = base_commit. as_ref ( ) {
1019+ tracing:: debug!( "labeling from base tree" ) ;
1020+ // TODO: We can likely drop this; we know all labels should be pre-computed.
1021+ modifier. set_sepolicy_from_commit ( repo, & base, cancellable) ?;
1022+ } else {
1023+ panic ! ( "Unexpected state: no derived layers and no base" )
1024+ }
1025+
1026+ cleanup_root ( & root_dir) ?;
1027+
1028+ let mt = ostree:: MutableTree :: new ( ) ;
1029+ repo. write_dfd_to_mtree (
1030+ ( * td) . as_raw_fd ( ) ,
1031+ rootpath,
1032+ & mt,
1033+ Some ( & modifier) ,
1034+ cancellable,
1035+ )
1036+ . context ( "Writing merged filesystem to mtree" ) ?;
1037+
1038+ let merged_root = repo
1039+ . write_mtree ( & mt, cancellable)
1040+ . context ( "Writing mtree" ) ?;
1041+ let merged_root = merged_root. downcast :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
1042+ let merged_commit = repo
1043+ . write_commit_with_time (
1044+ None ,
1045+ None ,
1046+ None ,
1047+ Some ( & metadata) ,
1048+ & merged_root,
1049+ timestamp,
1050+ cancellable,
1051+ )
1052+ . context ( "Writing commit" ) ?;
1053+ if !no_imgref {
1054+ repo. transaction_set_ref ( None , ostree_ref, Some ( merged_commit. as_str ( ) ) ) ;
1055+ }
1056+ txn. commit ( cancellable) ?;
1057+
1058+ if !disable_gc {
1059+ let n: u32 = gc_image_layers_impl ( repo, cancellable) ?;
1060+ tracing:: debug!( "pruned {n} layers" ) ;
1061+ }
1062+
1063+ // Here we re-query state just to run through the same code path,
1064+ // though it'd be cheaper to synthesize it from the data we already have.
1065+ let state = query_image_commit ( repo, & merged_commit) ?;
1066+ Ok ( state)
1067+ }
1068+
9431069 /// Import a layered container image.
9441070 ///
9451071 /// If enabled, this will also prune unused container image layers.
@@ -1080,118 +1206,18 @@ impl ImageImporter {
10801206 let repo = self . repo ;
10811207 let mut state = crate :: tokio_util:: spawn_blocking_cancellable_flatten (
10821208 move |cancellable| -> Result < Box < LayeredImageState > > {
1083- use rustix:: fd:: AsRawFd ;
1084-
1085- let cancellable = Some ( cancellable) ;
1086- let repo = & repo;
1087- let txn = repo. auto_transaction ( cancellable) ?;
1088-
1089- let devino = ostree:: RepoDevInoCache :: new ( ) ;
1090- let repodir = Dir :: reopen_dir ( & repo. dfd_borrow ( ) ) ?;
1091- let repo_tmp = repodir. open_dir ( "tmp" ) ?;
1092- let td = cap_std_ext:: cap_tempfile:: TempDir :: new_in ( & repo_tmp) ?;
1093-
1094- let rootpath = "root" ;
1095- let checkout_mode = if repo. mode ( ) == ostree:: RepoMode :: Bare {
1096- ostree:: RepoCheckoutMode :: None
1097- } else {
1098- ostree:: RepoCheckoutMode :: User
1099- } ;
1100- let mut checkout_opts = ostree:: RepoCheckoutAtOptions {
1101- mode : checkout_mode,
1102- overwrite_mode : ostree:: RepoCheckoutOverwriteMode :: UnionFiles ,
1103- devino_to_csum_cache : Some ( devino. clone ( ) ) ,
1104- no_copy_fallback : true ,
1105- force_copy_zerosized : true ,
1106- process_whiteouts : false ,
1107- ..Default :: default ( )
1108- } ;
1109- if let Some ( base) = base_commit. as_ref ( ) {
1110- repo. checkout_at (
1111- Some ( & checkout_opts) ,
1112- ( * td) . as_raw_fd ( ) ,
1113- rootpath,
1114- & base,
1115- cancellable,
1116- )
1117- . context ( "Checking out base commit" ) ?;
1118- }
1119-
1120- // Layer all subsequent commits
1121- checkout_opts. process_whiteouts = true ;
1122- for commit in layer_commits {
1123- tracing:: debug!( "Unpacking {commit}" ) ;
1124- repo. checkout_at (
1125- Some ( & checkout_opts) ,
1126- ( * td) . as_raw_fd ( ) ,
1127- rootpath,
1128- & commit,
1129- cancellable,
1130- )
1131- . with_context ( || format ! ( "Checking out layer {commit}" ) ) ?;
1132- }
1133-
1134- let root_dir = td. open_dir ( rootpath) ?;
1135-
1136- let modifier =
1137- ostree:: RepoCommitModifier :: new ( ostree:: RepoCommitModifierFlags :: empty ( ) , None ) ;
1138- modifier. set_devino_cache ( & devino) ;
1139- // If we have derived layers, then we need to handle the case where
1140- // the derived layers include custom policy. Just relabel everything
1141- // in this case.
1142- if have_derived_layers {
1143- let sepolicy = ostree:: SePolicy :: new_at ( root_dir. as_raw_fd ( ) , cancellable) ?;
1144- tracing:: debug!( "labeling from merged tree" ) ;
1145- modifier. set_sepolicy ( Some ( & sepolicy) ) ;
1146- } else if let Some ( base) = base_commit. as_ref ( ) {
1147- tracing:: debug!( "labeling from base tree" ) ;
1148- // TODO: We can likely drop this; we know all labels should be pre-computed.
1149- modifier. set_sepolicy_from_commit ( repo, & base, cancellable) ?;
1150- } else {
1151- unreachable ! ( )
1152- }
1153-
1154- cleanup_root ( & root_dir) ?;
1155-
1156- let mt = ostree:: MutableTree :: new ( ) ;
1157- repo. write_dfd_to_mtree (
1158- ( * td) . as_raw_fd ( ) ,
1159- rootpath,
1160- & mt,
1161- Some ( & modifier) ,
1162- cancellable,
1209+ Self :: write_merge_commit_impl (
1210+ & repo,
1211+ base_commit. as_deref ( ) ,
1212+ & layer_commits,
1213+ have_derived_layers,
1214+ metadata,
1215+ timestamp,
1216+ & ostree_ref,
1217+ self . no_imgref ,
1218+ self . disable_gc ,
1219+ Some ( cancellable) ,
11631220 )
1164- . context ( "Writing merged filesystem to mtree" ) ?;
1165-
1166- let merged_root = repo
1167- . write_mtree ( & mt, cancellable)
1168- . context ( "Writing mtree" ) ?;
1169- let merged_root = merged_root. downcast :: < ostree:: RepoFile > ( ) . unwrap ( ) ;
1170- let merged_commit = repo
1171- . write_commit_with_time (
1172- None ,
1173- None ,
1174- None ,
1175- Some ( & metadata) ,
1176- & merged_root,
1177- timestamp,
1178- cancellable,
1179- )
1180- . context ( "Writing commit" ) ?;
1181- if !self . no_imgref {
1182- repo. transaction_set_ref ( None , & ostree_ref, Some ( merged_commit. as_str ( ) ) ) ;
1183- }
1184- txn. commit ( cancellable) ?;
1185-
1186- if !self . disable_gc {
1187- let n: u32 = gc_image_layers_impl ( repo, cancellable) ?;
1188- tracing:: debug!( "pruned {n} layers" ) ;
1189- }
1190-
1191- // Here we re-query state just to run through the same code path,
1192- // though it'd be cheaper to synthesize it from the data we already have.
1193- let state = query_image_commit ( repo, & merged_commit) ?;
1194- Ok ( state)
11951221 } ,
11961222 )
11971223 . await ?;
0 commit comments