@@ -187,6 +187,8 @@ pub struct ImageImporter {
187187 offline : bool ,
188188 /// If true, we have ostree v2024.3 or newer.
189189 ostree_v2024_3 : bool ,
190+ /// Optional containers-storage root path for direct access to the layer content
191+ storage_root : Option < Utf8PathBuf > ,
190192
191193 layer_progress : Option < Sender < ImportProgress > > ,
192194 layer_byte_progress : Option < tokio:: sync:: watch:: Sender < Option < LayerProgress > > > ,
@@ -530,6 +532,7 @@ impl ImageImporter {
530532 disable_gc : false ,
531533 require_bootable : false ,
532534 offline : false ,
535+ storage_root : None ,
533536 imgref : imgref. clone ( ) ,
534537 layer_progress : None ,
535538 layer_byte_progress : None ,
@@ -568,6 +571,13 @@ impl ImageImporter {
568571 self . disable_gc = true ;
569572 }
570573
574+ /// Set the containers-storage root path for direct access to the layer content.
575+ /// When set, layers will be imported directly from the layer diff directory
576+ /// instead of being streamed through the proxy.
577+ pub fn set_storage_root ( & mut self , path : impl Into < Utf8PathBuf > ) {
578+ self . storage_root = Some ( path. into ( ) ) ;
579+ }
580+
571581 /// Determine if there is a new manifest, and if so return its digest.
572582 /// This will also serialize the new manifest and configuration into
573583 /// metadata associated with the image, so that invocations of `[query_cached]`
@@ -1120,16 +1130,6 @@ impl ImageImporter {
11201130 p. send ( ImportProgress :: DerivedLayerStarted ( layer. layer . clone ( ) ) )
11211131 . await ?;
11221132 }
1123- let ( blob, driver, media_type) = super :: unencapsulate:: fetch_layer (
1124- & proxy,
1125- & import. proxy_img ,
1126- & import. manifest ,
1127- & layer. layer ,
1128- self . layer_byte_progress . as_ref ( ) ,
1129- des_layers. as_ref ( ) ,
1130- self . imgref . imgref . transport ,
1131- )
1132- . await ?;
11331133 // An important aspect of this is that we SELinux label the derived layers using
11341134 // the base policy.
11351135 let opts = crate :: tar:: WriteTarOptions {
@@ -1138,16 +1138,61 @@ impl ImageImporter {
11381138 allow_nonusr : root_is_transient,
11391139 retain_var : self . ostree_v2024_3 ,
11401140 } ;
1141- let r = crate :: tar:: write_tar (
1142- & self . repo ,
1143- blob,
1144- media_type,
1145- layer. ostree_ref . as_str ( ) ,
1146- Some ( opts) ,
1141+
1142+ let layer_index = import
1143+ . manifest
1144+ . layers ( )
1145+ . iter ( )
1146+ . position ( |x| x == & layer. layer )
1147+ . ok_or_else ( || {
1148+ anyhow ! ( "Layer {} not found in manifest" , layer. layer. digest( ) )
1149+ } ) ?;
1150+ tracing:: debug!(
1151+ "Processing layer {}: digest={}, ostree_ref={}, transport={:?}" ,
1152+ layer_index,
1153+ layer. layer. digest( ) ,
1154+ layer. ostree_ref,
1155+ self . imgref. imgref. transport
11471156 ) ;
1148- let r = super :: unencapsulate:: join_fetch ( r, driver)
1149- . await
1150- . with_context ( || format ! ( "Parsing layer blob {}" , layer. layer. digest( ) ) ) ?;
1157+ let layer_diff_path = super :: unencapsulate:: try_get_layer_diff_path (
1158+ self . storage_root . as_deref ( ) ,
1159+ des_layers. as_ref ( ) ,
1160+ layer_index,
1161+ self . imgref . imgref . transport ,
1162+ ) ?;
1163+
1164+ let r = if let Some ( ref path) = layer_diff_path {
1165+ tracing:: info!( "Importing layer {} from filesystem: {}" , layer_index, path) ;
1166+ Self :: import_layer_from_filesystem ( & self . repo , path, & layer. ostree_ref , & opts)
1167+ . await ?
1168+ } else {
1169+ // Fall back to blob access
1170+ let ( blob, driver, media_type) = super :: unencapsulate:: fetch_layer (
1171+ & proxy,
1172+ & import. proxy_img ,
1173+ & import. manifest ,
1174+ & layer. layer ,
1175+ self . layer_byte_progress . as_ref ( ) ,
1176+ des_layers. as_ref ( ) ,
1177+ self . imgref . imgref . transport ,
1178+ )
1179+ . await ?;
1180+ tracing:: debug!(
1181+ "Importing layer {} from tar stream, media_type={:?}" ,
1182+ layer_index,
1183+ media_type
1184+ ) ;
1185+ let r = crate :: tar:: write_tar (
1186+ & self . repo ,
1187+ blob,
1188+ media_type,
1189+ layer. ostree_ref . as_str ( ) ,
1190+ Some ( opts) ,
1191+ ) ;
1192+ super :: unencapsulate:: join_fetch ( r, driver)
1193+ . await
1194+ . with_context ( || format ! ( "Parsing layer blob {}" , layer. layer. digest( ) ) ) ?
1195+ } ;
11511196 tracing:: debug!( "Imported layer: {}" , r. commit. as_str( ) ) ;
11521197 layer_commits. push ( r. commit ) ;
11531198 let filtered_owned = HashMap :: from_iter ( r. filtered . clone ( ) ) ;
@@ -1233,6 +1278,55 @@ impl ImageImporter {
12331278 state. filtered_files = layer_filtered_content;
12341279 Ok ( state)
12351280 }
1281+
1282+ /// Import a layer directly from filesystem path instead of tar stream.
1283+ /// This directly walks the filesystem and writes content objects to OSTree,
1284+ /// applying path transformations (e.g., /etc -> /usr/etc).
1285+ async fn import_layer_from_filesystem (
1286+ repo : & ostree:: Repo ,
1287+ layer_path : & Utf8Path ,
1288+ target_ref : & str ,
1289+ options : & crate :: tar:: WriteTarOptions ,
1290+ ) -> Result < crate :: tar:: WriteTarResult > {
1291+ tracing:: info!(
1292+ "import_layer_from_filesystem: layer_path={}, target_ref={}" ,
1293+ layer_path,
1294+ target_ref
1295+ ) ;
1296+
1297+ let config = crate :: filesystem:: FilesystemFilterConfig :: from_tar_options ( options) ;
1298+ let repo = repo. clone ( ) ;
1299+ let layer_path = layer_path. to_owned ( ) ;
1300+ let target_ref = target_ref. to_string ( ) ;
1301+
1302+ // Run the synchronous import in a blocking task
1303+ let result = {
1304+ let repo = repo. clone ( ) ;
1305+ crate :: tokio_util:: spawn_blocking_flatten ( move || {
1306+ crate :: filesystem:: import_filesystem_to_ostree ( & repo, & layer_path, & config)
1307+ } )
1308+ . await ?
1309+ } ;
1310+
1311+ // Cache the layer by setting the ref (matching write_tar behavior)
1312+ {
1313+ let target_ref = target_ref. clone ( ) ;
1314+ let commit = result. commit . clone ( ) ;
1315+ crate :: tokio_util:: spawn_blocking_flatten ( move || {
1316+ repo. set_ref_immediate ( None , & target_ref, Some ( & commit) , gio:: Cancellable :: NONE ) ?;
1317+ Ok :: < _ , anyhow:: Error > ( ( ) )
1318+ } )
1319+ . await ?;
1320+ }
1321+
1322+ tracing:: info!(
1323+ "Successfully imported layer from filesystem: commit={}, ref={}" ,
1324+ result. commit,
1325+ target_ref
1326+ ) ;
1327+
1328+ Ok ( result)
1329+ }
12361330}
12371331
12381332/// List all images stored
0 commit comments