@@ -191,11 +191,47 @@ pub async fn unencapsulate(repo: &ostree::Repo, imgref: &OstreeImageReference) -
191
191
importer. unencapsulate ( ) . await
192
192
}
193
193
194
+ pub ( crate ) struct Decompressor {
195
+ inner : Box < dyn Read + Send + ' static > ,
196
+ }
197
+
198
+ impl Read for Decompressor {
199
+ fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
200
+ self . inner . read ( buf)
201
+ }
202
+ }
203
+
204
+ impl Drop for Decompressor {
205
+ fn drop ( & mut self ) {
206
+ // We need to make sure to flush out the decompressor and/or
207
+ // tar stream here. For tar, we might not read through the
208
+ // entire stream, because the archive has zero-block-markers
209
+ // at the end; or possibly because the final entry is filtered
210
+ // in filter_tar so we don't advance to read the data. For
211
+ // decompressor, zstd:chunked layers will have
212
+ // metadata/skippable frames at the end of the stream. That
213
+ // data isn't relevant to the tar stream, but if we don't read
214
+ // it here then on the skopeo proxy we'll block trying to
215
+ // write the end of the stream. That in turn will block our
216
+ // client end trying to call FinishPipe, and we end up
217
+ // deadlocking ourselves through skopeo.
218
+ //
219
+ // https://github.com/bootc-dev/bootc/issues/1204
220
+
221
+ let mut sink = std:: io:: sink ( ) ;
222
+ match std:: io:: copy ( & mut self . inner , & mut sink) {
223
+ Err ( e) => tracing:: debug!( "Ignoring error while dropping decompressor: {e}" ) ,
224
+ Ok ( 0 ) => { /* We already read everything and are happy */ }
225
+ Ok ( n) => tracing:: debug!( "Read extra {n} bytes at end of decompressor stream" ) ,
226
+ }
227
+ }
228
+ }
229
+
194
230
/// Create a decompressor for this MIME type, given a stream of input.
195
231
pub ( crate ) fn decompressor (
196
232
media_type : & oci_image:: MediaType ,
197
233
src : impl Read + Send + ' static ,
198
- ) -> Result < Box < dyn Read + Send + ' static > > {
234
+ ) -> Result < Decompressor > {
199
235
let r: Box < dyn std:: io:: Read + Send + ' static > = match media_type {
200
236
oci_image:: MediaType :: ImageLayerZstd => Box :: new ( zstd:: stream:: read:: Decoder :: new ( src) ?) ,
201
237
oci_image:: MediaType :: ImageLayerGzip => Box :: new ( flate2:: bufread:: GzDecoder :: new (
@@ -205,7 +241,7 @@ pub(crate) fn decompressor(
205
241
oci_image:: MediaType :: Other ( t) if t. as_str ( ) == DOCKER_TYPE_LAYER_TAR => Box :: new ( src) ,
206
242
o => anyhow:: bail!( "Unhandled layer type: {}" , o) ,
207
243
} ;
208
- Ok ( r )
244
+ Ok ( Decompressor { inner : r } )
209
245
}
210
246
211
247
/// A wrapper for [`get_blob`] which fetches a layer and decompresses it.
0 commit comments