1use core::fmt;
4
5use crate::{
6 digest::{Digest, Verifier, VerifyError},
7 tar::{
8 Archive, ArchiveError, ArchiveFinishError, EntryExtractor, ExtractError, FinishError,
9 TransactionalLayerSink,
10 },
11};
12
13pub const OCI_LAYER_TAR: &str = "application/vnd.oci.image.layer.v1.tar";
14pub const OCI_LAYER_GZIP: &str = "application/vnd.oci.image.layer.v1.tar+gzip";
15pub const OCI_LAYER_ZSTD: &str = "application/vnd.oci.image.layer.v1.tar+zstd";
16pub const OCI_NONDISTRIBUTABLE_TAR: &str =
17 "application/vnd.oci.image.layer.nondistributable.v1.tar";
18pub const OCI_NONDISTRIBUTABLE_GZIP: &str =
19 "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip";
20pub const OCI_NONDISTRIBUTABLE_ZSTD: &str =
21 "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd";
22pub const DOCKER_LAYER_GZIP: &str = "application/vnd.docker.image.rootfs.diff.tar.gzip";
23pub const DOCKER_FOREIGN_LAYER_GZIP: &str =
24 "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip";
25pub const DOCKER_LAYER_TAR: &str = "application/vnd.docker.image.rootfs.diff.tar";
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum Encoding {
29 Tar,
30 Gzip,
31 Zstd,
32}
33
34pub fn encoding(media_type: &str) -> Result<Encoding, LayerFormatError> {
35 match media_type {
36 OCI_LAYER_TAR | OCI_NONDISTRIBUTABLE_TAR | DOCKER_LAYER_TAR => Ok(Encoding::Tar),
37 OCI_LAYER_GZIP
38 | OCI_NONDISTRIBUTABLE_GZIP
39 | DOCKER_LAYER_GZIP
40 | DOCKER_FOREIGN_LAYER_GZIP => Ok(Encoding::Gzip),
41 OCI_LAYER_ZSTD | OCI_NONDISTRIBUTABLE_ZSTD => Ok(Encoding::Zstd),
42 _ => Err(LayerFormatError::UnsupportedMediaType),
43 }
44}
45
46#[allow(clippy::large_enum_variant)]
49pub enum Decoder<'a> {
50 Tar,
51 #[cfg(feature = "gzip")]
52 Gzip(gzip_zero::Decoder<'a>),
53 #[cfg(feature = "zstd")]
54 Zstd(zstd_zero::Decoder<'a>),
55 #[doc(hidden)]
56 _Lifetime(core::marker::PhantomData<&'a mut [u8]>),
57}
58
59impl<'a> Decoder<'a> {
60 pub const fn tar() -> Self {
61 Self::Tar
62 }
63
64 #[cfg(feature = "gzip")]
65 pub fn gzip(buffers: gzip_zero::DecoderBuffers<'a>) -> Result<Self, LayerFormatError> {
66 Ok(Self::Gzip(
67 gzip_zero::Decoder::new(buffers).map_err(LayerFormatError::Gzip)?,
68 ))
69 }
70
71 #[cfg(feature = "zstd")]
72 pub fn zstd(buffers: zstd_zero::DecoderBuffers<'a>) -> Self {
73 Self::Zstd(zstd_zero::Decoder::new(buffers))
74 }
75
76 pub fn decode<'decoder>(
77 &'decoder mut self,
78 input: &'decoder [u8],
79 ) -> Result<DecodeStep<'decoder>, LayerFormatError> {
80 match self {
81 Self::Tar => Ok(if input.is_empty() {
82 DecodeStep::NeedInput { consumed: 0 }
83 } else {
84 DecodeStep::Output {
85 consumed: input.len(),
86 bytes: input,
87 }
88 }),
89 #[cfg(feature = "gzip")]
90 Self::Gzip(decoder) => Ok(
91 match decoder.decode(input).map_err(LayerFormatError::Gzip)? {
92 gzip_zero::DecodeStep::NeedInput { consumed } => {
93 DecodeStep::NeedInput { consumed }
94 }
95 gzip_zero::DecodeStep::MemberStarted { consumed, .. }
96 | gzip_zero::DecodeStep::MemberFinished { consumed } => {
97 DecodeStep::Progress { consumed }
98 }
99 gzip_zero::DecodeStep::Output { consumed, bytes } => {
100 DecodeStep::Output { consumed, bytes }
101 }
102 },
103 ),
104 #[cfg(feature = "zstd")]
105 Self::Zstd(decoder) => Ok(
106 match decoder.decode(input).map_err(LayerFormatError::Zstd)? {
107 zstd_zero::DecodeStep::NeedInput { consumed } => {
108 DecodeStep::NeedInput { consumed }
109 }
110 zstd_zero::DecodeStep::FrameStarted { consumed, .. }
111 | zstd_zero::DecodeStep::FrameFinished { consumed, .. } => {
112 DecodeStep::Progress { consumed }
113 }
114 zstd_zero::DecodeStep::Output { consumed, bytes } => {
115 DecodeStep::Output { consumed, bytes }
116 }
117 },
118 ),
119 Self::_Lifetime(_) => unreachable!(),
120 }
121 }
122
123 pub fn finish(&self) -> Result<(), LayerFormatError> {
124 match self {
125 Self::Tar => Ok(()),
126 #[cfg(feature = "gzip")]
127 Self::Gzip(decoder) => decoder.finish().map_err(LayerFormatError::Gzip),
128 #[cfg(feature = "zstd")]
129 Self::Zstd(decoder) => decoder.finish().map_err(LayerFormatError::Zstd),
130 Self::_Lifetime(_) => unreachable!(),
131 }
132 }
133}
134
135#[derive(Debug, Eq, PartialEq)]
136pub enum DecodeStep<'a> {
137 NeedInput { consumed: usize },
138 Progress { consumed: usize },
139 Output { consumed: usize, bytes: &'a [u8] },
140}
141
142impl DecodeStep<'_> {
143 pub const fn consumed(&self) -> usize {
144 match self {
145 Self::NeedInput { consumed }
146 | Self::Progress { consumed }
147 | Self::Output { consumed, .. } => *consumed,
148 }
149 }
150}
151
152pub struct VerifiedDecoder<'a> {
153 decoder: Decoder<'a>,
154 compressed: Verifier,
155 uncompressed: Option<Verifier>,
156 decompressed_size: u64,
157}
158
159pub struct VerifiedEntryExtractor<'decoder, 'target> {
161 decoder: VerifiedDecoder<'decoder>,
162 extractor: EntryExtractor<'target>,
163 extracted_size: u64,
164}
165
166impl<'decoder, 'target> VerifiedEntryExtractor<'decoder, 'target> {
167 pub const fn new(decoder: VerifiedDecoder<'decoder>, target: &'target [u8]) -> Self {
168 Self {
169 decoder,
170 extractor: EntryExtractor::new(target),
171 extracted_size: 0,
172 }
173 }
174
175 pub fn push<E>(
176 &mut self,
177 input: &[u8],
178 mut output: impl FnMut(&[u8]) -> Result<(), E>,
179 ) -> Result<(), EntryLayerError<E>> {
180 let extractor = &mut self.extractor;
181 let extracted_size = &mut self.extracted_size;
182 self.decoder
183 .push(input, |decoded| {
184 extract_output(extractor, extracted_size, decoded, &mut output)
185 })
186 .map_err(EntryLayerError::Layer)
187 }
188
189 pub fn finish<E>(
190 &mut self,
191 mut output: impl FnMut(&[u8]) -> Result<(), E>,
192 ) -> Result<(), EntryLayerError<E>> {
193 let extractor = &mut self.extractor;
194 let extracted_size = &mut self.extracted_size;
195 self.decoder
196 .finish(|decoded| extract_output(extractor, extracted_size, decoded, &mut output))
197 .map_err(EntryLayerError::Layer)?;
198 self.extractor.finish().map_err(EntryLayerError::Finish)
199 }
200
201 pub const fn compressed_size(&self) -> u64 {
202 self.decoder.compressed_size()
203 }
204
205 pub const fn decompressed_size(&self) -> u64 {
206 self.decoder.decompressed_size()
207 }
208
209 pub const fn extracted_size(&self) -> u64 {
210 self.extracted_size
211 }
212
213 pub const fn found(&self) -> bool {
214 self.extractor.found()
215 }
216}
217
218fn extract_output<E>(
219 extractor: &mut EntryExtractor<'_>,
220 extracted_size: &mut u64,
221 decoded: &[u8],
222 output: &mut impl FnMut(&[u8]) -> Result<(), E>,
223) -> Result<(), ExtractError<E>> {
224 extractor.push(decoded, |contents| {
225 output(contents)?;
226 *extracted_size += contents.len() as u64;
227 Ok(())
228 })
229}
230
231#[derive(Debug)]
232pub enum EntryLayerError<E> {
233 Layer(LayerError<ExtractError<E>>),
234 Finish(FinishError),
235}
236
237impl<E: fmt::Display> fmt::Display for EntryLayerError<E> {
238 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 Self::Layer(error) => write!(formatter, "entry layer failed: {error}"),
241 Self::Finish(error) => write!(formatter, "entry extraction failed: {error}"),
242 }
243 }
244}
245
246pub struct LayerApplier<'decoder, 'archive> {
248 decoder: VerifiedDecoder<'decoder>,
249 archive: Archive<'archive>,
250 state: ApplyState,
251}
252
253#[derive(Clone, Copy, Debug, Eq, PartialEq)]
254enum ApplyState {
255 Ready,
256 Applying,
257 Failed,
258 Finished,
259}
260
261impl<'decoder, 'archive> LayerApplier<'decoder, 'archive> {
262 pub fn new(decoder: VerifiedDecoder<'decoder>, archive: Archive<'archive>) -> Self {
263 Self {
264 decoder,
265 archive,
266 state: ApplyState::Ready,
267 }
268 }
269
270 pub fn push<S: TransactionalLayerSink>(
271 &mut self,
272 input: &[u8],
273 sink: &mut S,
274 ) -> Result<(), ApplyError<S::Error>> {
275 match self.state {
276 ApplyState::Ready => {
277 sink.begin_layer().map_err(ApplyError::Sink)?;
278 self.state = ApplyState::Applying;
279 }
280 ApplyState::Applying => {}
281 ApplyState::Failed | ApplyState::Finished => return Err(ApplyError::InvalidState),
282 }
283 let (decoder, archive) = (&mut self.decoder, &mut self.archive);
284 let result = decoder.push(input, |bytes| archive.push(bytes, sink));
285 if let Err(error) = result {
286 sink.abort_layer();
287 self.state = ApplyState::Failed;
288 return Err(map_apply_error(error));
289 }
290 Ok(())
291 }
292
293 pub fn finish<S: TransactionalLayerSink>(
294 &mut self,
295 sink: &mut S,
296 ) -> Result<(), ApplyError<S::Error>> {
297 if self.state == ApplyState::Ready {
298 sink.begin_layer().map_err(ApplyError::Sink)?;
299 self.state = ApplyState::Applying;
300 }
301 if self.state != ApplyState::Applying {
302 return Err(ApplyError::InvalidState);
303 }
304 let (decoder, archive) = (&mut self.decoder, &mut self.archive);
305 if let Err(error) = decoder.finish(|bytes| archive.push(bytes, sink)) {
306 sink.abort_layer();
307 self.state = ApplyState::Failed;
308 return Err(map_apply_error(error));
309 }
310 if let Err(error) = archive.finish() {
311 sink.abort_layer();
312 self.state = ApplyState::Failed;
313 return Err(ApplyError::ArchiveFinish(error));
314 }
315 if let Err(error) = sink.commit_layer() {
316 sink.abort_layer();
317 self.state = ApplyState::Failed;
318 return Err(ApplyError::Sink(error));
319 }
320 self.state = ApplyState::Finished;
321 Ok(())
322 }
323}
324
325#[derive(Debug)]
326pub enum ApplyError<E> {
327 Format(LayerFormatError),
328 Integrity(VerifyError),
329 Archive(ArchiveError<E>),
330 ArchiveFinish(ArchiveFinishError),
331 Sink(E),
332 InvalidState,
333}
334
335impl<E: fmt::Display> fmt::Display for ApplyError<E> {
336 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
337 match self {
338 Self::Format(error) => write!(formatter, "layer format error: {error}"),
339 Self::Integrity(error) => write!(formatter, "layer integrity error: {error}"),
340 Self::Archive(error) => write!(formatter, "layer archive error: {error}"),
341 Self::ArchiveFinish(error) => write!(formatter, "layer archive error: {error}"),
342 Self::Sink(error) => write!(formatter, "layer sink failed: {error}"),
343 Self::InvalidState => formatter.write_str("invalid layer application state"),
344 }
345 }
346}
347
348fn map_apply_error<E>(error: LayerError<ArchiveError<E>>) -> ApplyError<E> {
349 match error {
350 LayerError::Format(error) => ApplyError::Format(error),
351 LayerError::Integrity(error) => ApplyError::Integrity(error),
352 LayerError::Output(error) => ApplyError::Archive(error),
353 }
354}
355
356impl<'a> VerifiedDecoder<'a> {
357 pub fn new(
358 decoder: Decoder<'a>,
359 compressed_digest: Digest,
360 compressed_size: u64,
361 diff_id: Digest,
362 ) -> Self {
363 Self {
364 decoder,
365 compressed: Verifier::new(compressed_digest, compressed_size),
366 uncompressed: Some(Verifier::digest_only(diff_id)),
367 decompressed_size: 0,
368 }
369 }
370
371 pub fn compressed_only(
377 decoder: Decoder<'a>,
378 compressed_digest: Digest,
379 compressed_size: u64,
380 ) -> Self {
381 Self {
382 decoder,
383 compressed: Verifier::new(compressed_digest, compressed_size),
384 uncompressed: None,
385 decompressed_size: 0,
386 }
387 }
388
389 pub fn push<E>(
390 &mut self,
391 input: &[u8],
392 mut output: impl FnMut(&[u8]) -> Result<(), E>,
393 ) -> Result<(), LayerError<E>> {
394 self.compressed
395 .update(input)
396 .map_err(LayerError::Integrity)?;
397 let mut remaining = input;
398 let mut turns_without_bytes = 0usize;
399 while !remaining.is_empty() {
400 let step = self.decoder.decode(remaining).map_err(LayerError::Format)?;
401 let consumed = step.consumed();
402 if let DecodeStep::Output { bytes, .. } = step {
403 self.decompressed_size += bytes.len() as u64;
404 if let Some(uncompressed) = &mut self.uncompressed {
405 uncompressed.update(bytes).map_err(LayerError::Integrity)?;
406 }
407 output(bytes).map_err(LayerError::Output)?;
408 }
409 if consumed == 0 {
410 turns_without_bytes += 1;
411 if turns_without_bytes > 16 {
412 return Err(LayerError::Format(LayerFormatError::DecoderStalled));
413 }
414 } else {
415 turns_without_bytes = 0;
416 remaining = &remaining[consumed..];
417 }
418 }
419 Ok(())
420 }
421
422 pub const fn compressed_size(&self) -> u64 {
423 self.compressed.actual_size()
424 }
425
426 pub const fn decompressed_size(&self) -> u64 {
427 self.decompressed_size
428 }
429
430 pub fn finish<E>(
431 &mut self,
432 mut output: impl FnMut(&[u8]) -> Result<(), E>,
433 ) -> Result<(), LayerError<E>> {
434 loop {
435 match self.decoder.decode(&[]).map_err(LayerError::Format)? {
436 DecodeStep::Output { bytes, .. } => {
437 self.decompressed_size += bytes.len() as u64;
438 if let Some(uncompressed) = &mut self.uncompressed {
439 uncompressed.update(bytes).map_err(LayerError::Integrity)?;
440 }
441 output(bytes).map_err(LayerError::Output)?;
442 }
443 DecodeStep::Progress { .. } => {}
444 DecodeStep::NeedInput { .. } => break,
445 }
446 }
447 self.decoder.finish().map_err(LayerError::Format)?;
448 self.compressed.finish().map_err(LayerError::Integrity)?;
449 if let Some(uncompressed) = &self.uncompressed {
450 uncompressed.finish().map_err(LayerError::Integrity)?;
451 }
452 Ok(())
453 }
454}
455
456#[derive(Debug)]
457pub enum LayerError<E> {
458 Format(LayerFormatError),
459 Integrity(VerifyError),
460 Output(E),
461}
462
463impl<E: fmt::Display> fmt::Display for LayerError<E> {
464 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
465 match self {
466 Self::Format(error) => write!(formatter, "layer format error: {error}"),
467 Self::Integrity(error) => write!(formatter, "layer integrity error: {error}"),
468 Self::Output(error) => write!(formatter, "layer output error: {error}"),
469 }
470 }
471}
472
473#[derive(Debug)]
474pub enum LayerFormatError {
475 UnsupportedMediaType,
476 EncodingDisabled(Encoding),
477 DecoderStalled,
478 #[cfg(feature = "gzip")]
479 Gzip(gzip_zero::DecodeError),
480 #[cfg(feature = "zstd")]
481 Zstd(zstd_zero::DecodeError),
482}
483
484impl fmt::Display for LayerFormatError {
485 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
486 match self {
487 Self::UnsupportedMediaType => formatter.write_str("unsupported OCI layer media type"),
488 Self::EncodingDisabled(encoding) => {
489 write!(formatter, "{encoding:?} layer support is disabled")
490 }
491 Self::DecoderStalled => formatter.write_str("layer decoder stopped making progress"),
492 #[cfg(feature = "gzip")]
493 Self::Gzip(error) => write!(formatter, "{error}"),
494 #[cfg(feature = "zstd")]
495 Self::Zstd(error) => write!(formatter, "{error}"),
496 }
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use sha2::{Digest as _, Sha256};
503
504 use super::{Decoder, LayerApplier, VerifiedDecoder, VerifiedEntryExtractor};
505 use crate::{
506 digest::Digest,
507 tar::{Archive, ArchiveBuffers, Entry, LayerEventSink, TransactionalLayerSink},
508 };
509
510 #[derive(Default)]
511 struct TransactionSink {
512 began: bool,
513 committed: bool,
514 aborted: bool,
515 }
516
517 impl LayerEventSink for TransactionSink {
518 type Error = ();
519
520 fn begin_entry(&mut self, _entry: Entry<'_>) -> Result<(), Self::Error> {
521 Ok(())
522 }
523
524 fn entry_data(&mut self, _bytes: &[u8]) -> Result<(), Self::Error> {
525 Ok(())
526 }
527
528 fn end_entry(&mut self) -> Result<(), Self::Error> {
529 Ok(())
530 }
531
532 fn whiteout(&mut self, _path: &[u8]) -> Result<(), Self::Error> {
533 Ok(())
534 }
535
536 fn opaque_directory(&mut self, _path: &[u8]) -> Result<(), Self::Error> {
537 Ok(())
538 }
539 }
540
541 impl TransactionalLayerSink for TransactionSink {
542 fn begin_layer(&mut self) -> Result<(), Self::Error> {
543 self.began = true;
544 Ok(())
545 }
546
547 fn commit_layer(&mut self) -> Result<(), Self::Error> {
548 self.committed = true;
549 Ok(())
550 }
551
552 fn abort_layer(&mut self) {
553 self.aborted = true;
554 }
555 }
556
557 #[test]
558 fn verifies_uncompressed_layers() {
559 let bytes = b"tar bytes";
560 let digest = Digest::from_bytes(Sha256::digest(bytes).into());
561 let mut decoder = VerifiedDecoder::new(Decoder::tar(), digest, bytes.len() as u64, digest);
562 let mut output = [0; 9];
563 let mut length = 0;
564 decoder
565 .push(bytes, |chunk| {
566 output[length..length + chunk.len()].copy_from_slice(chunk);
567 length += chunk.len();
568 Ok::<_, ()>(())
569 })
570 .unwrap();
571 decoder.finish(|_| Ok::<_, ()>(())).unwrap();
572 assert_eq!(&output, bytes);
573 }
574
575 #[test]
576 fn compressed_only_accepts_content_without_a_diff_id() {
577 let bytes = b"artifact contents";
578 let digest = Digest::from_bytes(Sha256::digest(bytes).into());
579 let mut decoder =
580 VerifiedDecoder::compressed_only(Decoder::tar(), digest, bytes.len() as u64);
581 decoder.push(bytes, |_| Ok::<_, ()>(())).unwrap();
582 decoder.finish(|_| Ok::<_, ()>(())).unwrap();
583 assert_eq!(decoder.decompressed_size(), bytes.len() as u64);
584 }
585
586 #[test]
587 fn compressed_only_still_verifies_descriptor_digest_and_size() {
588 let bytes = b"artifact contents";
589 let digest = Digest::from_bytes(Sha256::digest(bytes).into());
590 let mut wrong_digest = VerifiedDecoder::compressed_only(
591 Decoder::tar(),
592 Digest::from_bytes([9; 32]),
593 bytes.len() as u64,
594 );
595 wrong_digest.push(bytes, |_| Ok::<_, ()>(())).unwrap();
596 assert!(wrong_digest.finish(|_| Ok::<_, ()>(())).is_err());
597
598 let mut wrong_size = VerifiedDecoder::compressed_only(Decoder::tar(), digest, 1);
599 wrong_size.push(bytes, |_| Ok::<_, ()>(())).unwrap();
600 assert!(wrong_size.finish(|_| Ok::<_, ()>(())).is_err());
601 }
602
603 #[test]
604 fn verifies_and_extracts_one_entry() {
605 let mut tar = [0u8; 2048];
606 tar[..6].copy_from_slice(b"wanted");
607 write_octal(&mut tar[100..108], 0o644);
608 write_octal(&mut tar[124..136], 5);
609 tar[148..156].fill(b' ');
610 tar[156] = b'0';
611 let checksum = tar[..512].iter().map(|byte| u64::from(*byte)).sum();
612 write_octal(&mut tar[148..156], checksum);
613 tar[512..517].copy_from_slice(b"hello");
614
615 let digest = Digest::from_bytes(Sha256::digest(tar).into());
616 let decoder = VerifiedDecoder::new(Decoder::tar(), digest, tar.len() as u64, digest);
617 let mut extractor = VerifiedEntryExtractor::new(decoder, b"wanted");
618 let mut output = [0u8; 5];
619 let mut length = 0;
620 for fragment in tar.chunks(7) {
621 extractor
622 .push(fragment, |bytes| {
623 output[length..length + bytes.len()].copy_from_slice(bytes);
624 length += bytes.len();
625 Ok::<_, ()>(())
626 })
627 .unwrap();
628 }
629 extractor.finish(|_| Ok::<_, ()>(())).unwrap();
630 assert_eq!(&output, b"hello");
631 assert_eq!(extractor.decompressed_size(), tar.len() as u64);
632 assert_eq!(extractor.extracted_size(), 5);
633 }
634
635 fn write_octal(field: &mut [u8], value: u64) {
636 field.fill(b'0');
637 let digits = field.len() - 1;
638 field[digits] = 0;
639 let mut value = value;
640 for byte in field[..digits].iter_mut().rev() {
641 *byte = b'0' + (value & 7) as u8;
642 value >>= 3;
643 }
644 }
645
646 #[test]
647 fn commits_only_after_archive_and_integrity_finish() {
648 let tar = [0; 1024];
649 let digest = Digest::from_bytes(Sha256::digest(tar).into());
650 let mut path = [0; 64];
651 let mut link = [0; 64];
652 let mut pax = [0; 64];
653 let archive = Archive::new(ArchiveBuffers {
654 path: &mut path,
655 link: &mut link,
656 pax: &mut pax,
657 });
658 let decoder = VerifiedDecoder::new(Decoder::tar(), digest, tar.len() as u64, digest);
659 let mut applier = LayerApplier::new(decoder, archive);
660 let mut sink = TransactionSink::default();
661 applier.push(&tar, &mut sink).unwrap();
662 assert!(sink.began);
663 assert!(!sink.committed);
664 applier.finish(&mut sink).unwrap();
665 assert!(sink.committed);
666 assert!(!sink.aborted);
667 }
668
669 #[test]
670 fn aborts_when_integrity_fails() {
671 let tar = [0; 1024];
672 let diff_id = Digest::from_bytes(Sha256::digest(tar).into());
673 let mut path = [0; 64];
674 let mut link = [0; 64];
675 let mut pax = [0; 64];
676 let archive = Archive::new(ArchiveBuffers {
677 path: &mut path,
678 link: &mut link,
679 pax: &mut pax,
680 });
681 let decoder = VerifiedDecoder::new(
682 Decoder::tar(),
683 Digest::from_bytes([1; 32]),
684 tar.len() as u64,
685 diff_id,
686 );
687 let mut applier = LayerApplier::new(decoder, archive);
688 let mut sink = TransactionSink::default();
689 applier.push(&tar, &mut sink).unwrap();
690 assert!(applier.finish(&mut sink).is_err());
691 assert!(sink.aborted);
692 assert!(!sink.committed);
693 }
694
695 #[cfg(feature = "gzip")]
696 #[test]
697 fn verifies_gzip_layers() {
698 const ENCODED: &[u8] = &[
699 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcb, 0x48, 0xcd, 0xc9,
700 0xc9, 0x07, 0x00, 0x86, 0xa6, 0x10, 0x36, 0x05, 0x00, 0x00, 0x00,
701 ];
702 let compressed = Digest::from_bytes(Sha256::digest(ENCODED).into());
703 let diff_id = Digest::from_bytes(Sha256::digest(b"hello").into());
704 let mut history = [0; gzip_zero::HISTORY_SIZE];
705 let decoder = Decoder::gzip(gzip_zero::DecoderBuffers {
706 history: &mut history,
707 })
708 .unwrap();
709 let mut decoder = VerifiedDecoder::new(decoder, compressed, ENCODED.len() as u64, diff_id);
710 let mut output = [0; 5];
711 let mut length = 0;
712 for byte in ENCODED {
713 decoder
714 .push(core::slice::from_ref(byte), |chunk| {
715 output[length..length + chunk.len()].copy_from_slice(chunk);
716 length += chunk.len();
717 Ok::<_, ()>(())
718 })
719 .unwrap();
720 }
721 decoder.finish(|_| Ok::<_, ()>(())).unwrap();
722 assert_eq!(&output, b"hello");
723 }
724
725 #[cfg(feature = "zstd")]
726 #[test]
727 fn verifies_zstd_layers() {
728 const ENCODED: &[u8] = &[
729 0x28, 0xb5, 0x2f, 0xfd, 0x20, 0x05, 0x29, 0, 0, b'h', b'e', b'l', b'l', b'o',
730 ];
731 let compressed = Digest::from_bytes(Sha256::digest(ENCODED).into());
732 let diff_id = Digest::from_bytes(Sha256::digest(b"hello").into());
733 let mut history = [0; 5];
734 let mut block = [0; 5];
735 let mut literals = [0; 5];
736 let decoder = Decoder::zstd(zstd_zero::DecoderBuffers {
737 history: &mut history,
738 block: &mut block,
739 literals: &mut literals,
740 });
741 let mut decoder = VerifiedDecoder::new(decoder, compressed, ENCODED.len() as u64, diff_id);
742 let mut output = [0; 5];
743 let mut length = 0;
744 decoder
745 .push(ENCODED, |chunk| {
746 output[length..length + chunk.len()].copy_from_slice(chunk);
747 length += chunk.len();
748 Ok::<_, ()>(())
749 })
750 .unwrap();
751 decoder.finish(|_| Ok::<_, ()>(())).unwrap();
752 assert_eq!(&output, b"hello");
753 }
754}