@@ -940,6 +940,135 @@ impl ImageImporter {
940
940
} )
941
941
}
942
942
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
+ // The merge has the base commit as a parent, if it exists. See
1043
+ // https://github.com/ostreedev/ostree/pull/3523
1044
+ let parent = base_commit. as_deref ( ) ;
1045
+ let merged_commit = repo
1046
+ . write_commit_with_time (
1047
+ parent,
1048
+ None ,
1049
+ None ,
1050
+ Some ( & metadata) ,
1051
+ & merged_root,
1052
+ timestamp,
1053
+ cancellable,
1054
+ )
1055
+ . context ( "Writing commit" ) ?;
1056
+ if !no_imgref {
1057
+ repo. transaction_set_ref ( None , ostree_ref, Some ( merged_commit. as_str ( ) ) ) ;
1058
+ }
1059
+ txn. commit ( cancellable) ?;
1060
+
1061
+ if !disable_gc {
1062
+ let n: u32 = gc_image_layers_impl ( repo, cancellable) ?;
1063
+ tracing:: debug!( "pruned {n} layers" ) ;
1064
+ }
1065
+
1066
+ // Here we re-query state just to run through the same code path,
1067
+ // though it'd be cheaper to synthesize it from the data we already have.
1068
+ let state = query_image_commit ( repo, & merged_commit) ?;
1069
+ Ok ( state)
1070
+ }
1071
+
943
1072
/// Import a layered container image.
944
1073
///
945
1074
/// If enabled, this will also prune unused container image layers.
@@ -1080,121 +1209,18 @@ impl ImageImporter {
1080
1209
let repo = self . repo ;
1081
1210
let mut state = crate :: tokio_util:: spawn_blocking_cancellable_flatten (
1082
1211
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,
1212
+ Self :: write_merge_commit_impl (
1213
+ & repo,
1214
+ base_commit. as_deref ( ) ,
1215
+ & layer_commits,
1216
+ have_derived_layers,
1217
+ metadata,
1218
+ timestamp,
1219
+ & ostree_ref,
1220
+ self . no_imgref ,
1221
+ self . disable_gc ,
1222
+ Some ( cancellable) ,
1163
1223
)
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
- // The merge has the base commit as a parent, if it exists. See
1171
- // https://github.com/ostreedev/ostree/pull/3523
1172
- let parent = base_commit. as_deref ( ) ;
1173
- let merged_commit = repo
1174
- . write_commit_with_time (
1175
- parent,
1176
- None ,
1177
- None ,
1178
- Some ( & metadata) ,
1179
- & merged_root,
1180
- timestamp,
1181
- cancellable,
1182
- )
1183
- . context ( "Writing commit" ) ?;
1184
- if !self . no_imgref {
1185
- repo. transaction_set_ref ( None , & ostree_ref, Some ( merged_commit. as_str ( ) ) ) ;
1186
- }
1187
- txn. commit ( cancellable) ?;
1188
-
1189
- if !self . disable_gc {
1190
- let n: u32 = gc_image_layers_impl ( repo, cancellable) ?;
1191
- tracing:: debug!( "pruned {n} layers" ) ;
1192
- }
1193
-
1194
- // Here we re-query state just to run through the same code path,
1195
- // though it'd be cheaper to synthesize it from the data we already have.
1196
- let state = query_image_commit ( repo, & merged_commit) ?;
1197
- Ok ( state)
1198
1224
} ,
1199
1225
)
1200
1226
. await ?;
0 commit comments