@@ -191,11 +191,47 @@ pub async fn unencapsulate(repo: &ostree::Repo, imgref: &OstreeImageReference) -
191191 importer. unencapsulate ( ) . await
192192}
193193
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+
194230/// Create a decompressor for this MIME type, given a stream of input.
195231pub ( crate ) fn decompressor (
196232 media_type : & oci_image:: MediaType ,
197233 src : impl Read + Send + ' static ,
198- ) -> Result < Box < dyn Read + Send + ' static > > {
234+ ) -> Result < Decompressor > {
199235 let r: Box < dyn std:: io:: Read + Send + ' static > = match media_type {
200236 oci_image:: MediaType :: ImageLayerZstd => Box :: new ( zstd:: stream:: read:: Decoder :: new ( src) ?) ,
201237 oci_image:: MediaType :: ImageLayerGzip => Box :: new ( flate2:: bufread:: GzDecoder :: new (
@@ -205,7 +241,7 @@ pub(crate) fn decompressor(
205241 oci_image:: MediaType :: Other ( t) if t. as_str ( ) == DOCKER_TYPE_LAYER_TAR => Box :: new ( src) ,
206242 o => anyhow:: bail!( "Unhandled layer type: {}" , o) ,
207243 } ;
208- Ok ( r )
244+ Ok ( Decompressor { inner : r } )
209245}
210246
211247/// A wrapper for [`get_blob`] which fetches a layer and decompresses it.
0 commit comments