Skip to content

Commit cc52892

Browse files
committed
unencapsulate: use "inner" stream when finishing Decompressor
For things like GzDecoder, we don't want to read via the actual decompression reader because we don't care about decompressing at this point. Plus, the inner reader may have encountered an error partway through, and trying to decode via decompression will error with UnexpectedEof. Instead, wrap a reader for each content type which knows how to get a mutable reference to the underlying data stream. When we finish decompressing, read directly from this "inner" stream to flush any data. Resolves: #1407 Signed-off-by: John Eckersberg <[email protected]>
1 parent f9cb9e4 commit cc52892

File tree

1 file changed

+75
-8
lines changed

1 file changed

+75
-8
lines changed

ostree-ext/src/container/unencapsulate.rs

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,59 @@ pub async fn unencapsulate(repo: &ostree::Repo, imgref: &OstreeImageReference) -
191191
importer.unencapsulate().await
192192
}
193193

194+
trait Decompressable: Read + Send + 'static {
195+
fn get_inner_mut(&mut self) -> &mut (dyn Read);
196+
}
197+
198+
// TransparentDecompressor
199+
200+
struct TransparentDecompressor<R: Read + Send + 'static>(R);
201+
202+
impl<R: Read + Send + 'static> Read for TransparentDecompressor<R> {
203+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
204+
self.0.read(buf)
205+
}
206+
}
207+
208+
impl<R: Read + Send + 'static> Decompressable for TransparentDecompressor<R> {
209+
fn get_inner_mut(&mut self) -> &mut (dyn Read) {
210+
&mut self.0
211+
}
212+
}
213+
214+
// GzipDecompressor
215+
216+
struct GzipDecompressor<R: std::io::BufRead>(flate2::bufread::GzDecoder<R>);
217+
218+
impl<R: std::io::BufRead + Send + 'static> Read for GzipDecompressor<R> {
219+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
220+
self.0.read(buf)
221+
}
222+
}
223+
224+
impl<R: std::io::BufRead + Send + 'static> Decompressable for GzipDecompressor<R> {
225+
fn get_inner_mut(&mut self) -> &mut (dyn Read) {
226+
self.0.get_mut()
227+
}
228+
}
229+
230+
// ZstdDecompressor
231+
struct ZstdDecompressor<'a, R: std::io::BufRead>(zstd::stream::read::Decoder<'a, R>);
232+
233+
impl<'a: 'static, R: std::io::BufRead + Send + 'static> Read for ZstdDecompressor<'a, R> {
234+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
235+
self.0.read(buf)
236+
}
237+
}
238+
239+
impl<'a: 'static, R: std::io::BufRead + Send + 'static> Decompressable for ZstdDecompressor<'a, R> {
240+
fn get_inner_mut(&mut self) -> &mut (dyn Read) {
241+
self.0.get_mut()
242+
}
243+
}
244+
194245
pub(crate) struct Decompressor {
195-
inner: Box<dyn Read + Send + 'static>,
246+
inner: Box<dyn Decompressable>,
196247
finished: bool,
197248
}
198249

@@ -224,15 +275,17 @@ impl Decompressor {
224275
media_type: &oci_image::MediaType,
225276
src: impl Read + Send + 'static,
226277
) -> Result<Self> {
227-
let r: Box<dyn std::io::Read + Send + 'static> = match media_type {
278+
let r: Box<dyn Decompressable> = match media_type {
228279
oci_image::MediaType::ImageLayerZstd => {
229-
Box::new(zstd::stream::read::Decoder::new(src)?)
280+
Box::new(ZstdDecompressor(zstd::stream::read::Decoder::new(src)?))
230281
}
231-
oci_image::MediaType::ImageLayerGzip => Box::new(flate2::bufread::GzDecoder::new(
232-
std::io::BufReader::new(src),
282+
oci_image::MediaType::ImageLayerGzip => Box::new(GzipDecompressor(
283+
flate2::bufread::GzDecoder::new(std::io::BufReader::new(src)),
233284
)),
234-
oci_image::MediaType::ImageLayer => Box::new(src),
235-
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR => Box::new(src),
285+
oci_image::MediaType::ImageLayer => Box::new(TransparentDecompressor(src)),
286+
oci_image::MediaType::Other(t) if t.as_str() == DOCKER_TYPE_LAYER_TAR => {
287+
Box::new(TransparentDecompressor(src))
288+
}
236289
o => anyhow::bail!("Unhandled layer type: {}", o),
237290
};
238291
Ok(Self {
@@ -264,7 +317,7 @@ impl Decompressor {
264317
// https://github.com/bootc-dev/bootc/issues/1204
265318

266319
let mut sink = std::io::sink();
267-
let n = std::io::copy(&mut self.inner, &mut sink)?;
320+
let n = std::io::copy(self.inner.get_inner_mut(), &mut sink)?;
268321

269322
if n > 0 {
270323
tracing::debug!("Read extra {n} bytes at end of decompressor stream");
@@ -362,4 +415,18 @@ mod tests {
362415
let d = Decompressor::new(&oci_image::MediaType::ImageLayer, empty).unwrap();
363416
drop(d)
364417
}
418+
419+
#[test]
420+
fn test_drop_decompressor_with_incomplete_gzip_data() {
421+
let empty = std::io::empty();
422+
let d = Decompressor::new(&oci_image::MediaType::ImageLayerGzip, empty).unwrap();
423+
drop(d)
424+
}
425+
426+
#[test]
427+
fn test_drop_decompressor_with_incomplete_zstd_data() {
428+
let empty = std::io::empty();
429+
let d = Decompressor::new(&oci_image::MediaType::ImageLayerZstd, empty).unwrap();
430+
drop(d)
431+
}
365432
}

0 commit comments

Comments
 (0)