1use std::{
75 future::poll_fn,
76 ops::Range,
77 pin::Pin,
78 sync::Arc,
79 task::{Context, Poll},
80};
81
82use tokio::io::{AsyncRead, ReadBuf};
83
84use crate::{
85 ArchiveFormat, BLOCK_SIZE, Block, DEFAULT_MAX_GLOBAL_PAX_EXTENSIONS_SIZE,
86 DEFAULT_MAX_GNU_EXTENSION_SIZE, DEFAULT_MAX_PAX_EXTENSION_SIZE, FrameError, FrameErrorInner,
87 GnuKind, HdrCharset, PaxError, PaxKind, PaxRecord, PaxState, PaxValue, UstarKind,
88 header::{
89 CHECKSUM_RANGE, GID_RANGE, GNAME_RANGE, GNU_IDENTITY, IDENTITY_RANGE, MODE_RANGE,
90 MTIME_RANGE, NAME_RANGE, PREFIX_RANGE, SIZE_RANGE, TYPEFLAG_OFFSET, UID_RANGE, UNAME_RANGE,
91 USTAR_IDENTITY, checksum, is_all_nul, parse_number, parse_octal,
92 },
93 pax::{GlobalPaxRecords, PaxRecords, SharedPaxRecords},
94};
95
96type PositionedBlock = (u64, Block);
97
98#[derive(Clone, Debug, Eq, PartialEq)]
100pub enum Frame {
101 Pax(PaxFrame),
103 Gnu(GnuFrame),
105 Header(HeaderFrame),
107 Data(DataFrame),
109}
110
111#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct PaxFrame {
114 pub position: u64,
116 pub block: Block,
118 pub kind: PaxKind,
120 pub payload_size: u64,
122}
123
124#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct GnuFrame {
127 pub position: u64,
129 pub block: Block,
131 pub kind: GnuKind,
133 pub payload_size: u64,
135}
136
137#[derive(Clone, Debug, Eq, PartialEq)]
142pub struct HeaderFrame {
143 pub position: u64,
145 pub block: Block,
147 pub format: ArchiveFormat,
149 pub kind: UstarKind,
151 pub declared_size: u64,
153 pub effective_size: u64,
159 pub(crate) mode: Option<u64>,
160 pub(crate) uid: Option<u64>,
161 pub(crate) gid: Option<u64>,
162 pub(crate) mtime: Option<u64>,
163}
164
165impl HeaderFrame {
166 fn ustar(
167 position: u64,
168 block: Block,
169 typeflag: u8,
170 declared_size: u64,
171 local_records: Option<&PaxRecords>,
172 global_records: Option<&GlobalPaxRecords>,
173 allow_all_nul_numeric_fields: bool,
174 ) -> Result<Self, FrameError> {
175 let kind = UstarKind::try_from_framed(position, typeflag)?;
176
177 let parse_numeric_field = |field, range: Range<usize>| {
181 Self::parse_numeric_field(
182 position,
183 ArchiveFormat::Pax,
184 field,
185 &block[range],
186 allow_all_nul_numeric_fields,
187 )
188 };
189 let mode = parse_numeric_field("mode", MODE_RANGE)?;
190 let uid = parse_numeric_field("uid", UID_RANGE)?;
191 let gid = parse_numeric_field("gid", GID_RANGE)?;
192 let mtime = parse_numeric_field("mtime", MTIME_RANGE)?;
193
194 let validate_string_field = |field: &'static str, bytes: &[u8]| {
195 if bytes.contains(&0) {
196 Ok(())
197 } else {
198 Err(FrameError::at(
199 position,
200 FrameErrorInner::UnterminatedUstarStringField { field },
201 ))
202 }
203 };
204 validate_string_field("uname", &block[UNAME_RANGE])?;
205 validate_string_field("gname", &block[GNAME_RANGE])?;
206
207 let effective_size = PaxState::effective_size(local_records, global_records).map_or(
213 Ok(declared_size),
214 |size| match size {
215 PaxValue::Value(size) => Ok(*size),
216 PaxValue::Deleted => Err(FrameError::deleted_pax_metadata(position, "size")),
217 },
218 )?;
219 validate_pax_member_size(position, kind, declared_size, effective_size)?;
220
221 Ok(Self {
222 position,
223 block,
224 format: ArchiveFormat::Pax,
225 kind,
226 declared_size,
227 effective_size,
228 mode,
229 uid,
230 gid,
231 mtime,
232 })
233 }
234
235 fn gnu(
236 position: u64,
237 block: Block,
238 typeflag: u8,
239 declared_size: u64,
240 require_link_kind: bool,
241 allow_all_nul_numeric_fields: bool,
242 ) -> Result<Self, FrameError> {
243 let kind = UstarKind::try_from_framed(position, typeflag)?;
244 if require_link_kind && !matches!(kind, UstarKind::HardLink | UstarKind::SymbolicLink) {
245 return Err(FrameError::unexpected_order(
246 position,
247 "hard-link or symbolic-link member after GNU long-link extension",
248 "non-link ordinary member",
249 ));
250 }
251 validate_gnu_member_size(position, kind, declared_size)?;
252 let parse_numeric_field = |field, range: Range<usize>| {
253 Self::parse_numeric_field(
254 position,
255 ArchiveFormat::Gnu,
256 field,
257 &block[range],
258 allow_all_nul_numeric_fields,
259 )
260 };
261 let mode = parse_numeric_field("mode", MODE_RANGE)?;
262 let uid = parse_numeric_field("uid", UID_RANGE)?;
263 let gid = parse_numeric_field("gid", GID_RANGE)?;
264 let mtime = parse_numeric_field("mtime", MTIME_RANGE)?;
265
266 Ok(Self {
267 position,
268 block,
269 format: ArchiveFormat::Gnu,
270 kind,
271 declared_size,
272 effective_size: declared_size,
273 mode,
274 uid,
275 gid,
276 mtime,
277 })
278 }
279
280 fn parse_numeric_field(
281 position: u64,
282 format: ArchiveFormat,
283 field: &'static str,
284 bytes: &[u8],
285 allow_all_nul_numeric_fields: bool,
286 ) -> Result<Option<u64>, FrameError> {
287 if allow_all_nul_numeric_fields && is_all_nul(bytes) {
288 return Ok(None);
289 }
290 parse_number(format, bytes).map(Some).ok_or_else(|| {
291 FrameError::at(
292 position,
293 FrameErrorInner::InvalidNumericField {
294 field,
295 found: bytes.to_vec(),
296 },
297 )
298 })
299 }
300
301 pub(crate) fn copy_header_path_into(&self, path: &mut Vec<u8>) {
302 path.clear();
303 let name = trim_nul(&self.block[NAME_RANGE]);
304 if self.format == ArchiveFormat::Gnu {
305 path.extend_from_slice(name);
306 return;
307 }
308 let prefix = trim_nul(&self.block[PREFIX_RANGE]);
309 if !prefix.is_empty() {
310 path.extend_from_slice(prefix);
311 path.push(b'/');
312 }
313 path.extend_from_slice(name);
314 }
315}
316
317#[derive(Clone, Copy, Debug, Eq, PartialEq)]
319pub enum DataOwner {
320 Pax(PaxKind),
322 Gnu(GnuKind),
324 Member,
326}
327
328#[derive(Clone, Debug, Eq, PartialEq)]
333pub struct DataFrame {
334 pub position: u64,
336 pub block: Block,
338 pub len: usize,
340 pub owner: DataOwner,
342 completed_pax_records: Option<SharedPaxRecords>,
347}
348
349impl DataFrame {
350 pub fn completed_pax_records(&self) -> Option<&[PaxRecord]> {
355 self.completed_pax_records
356 .as_deref()
357 .map(PaxRecords::as_slice)
358 }
359
360 pub(crate) fn into_completed_pax_records(self) -> Option<SharedPaxRecords> {
361 self.completed_pax_records
362 }
363}
364
365#[derive(Debug)]
367pub(super) enum State {
368 AwaitingHeader,
370 ReadingPax {
372 kind: PaxKind,
373 header_position: u64,
374 remaining: u64,
375 payload: Vec<u8>,
376 },
377 AwaitingUstarHeader { records: SharedPaxRecords },
379 ReadingGnu {
381 kind: GnuKind,
382 remaining: u64,
383 pending: PendingGnu,
384 },
385 AwaitingGnuMember { pending: PendingGnu },
387 ReadingMember { remaining: u64 },
389 AwaitingSecondZero,
391 Complete,
393 Failed,
395}
396
397#[derive(Clone, Copy, Debug, Default)]
398pub(super) struct PendingGnu {
399 pub(super) long_name: bool,
400 pub(super) long_link: bool,
401}
402
403#[derive(Default)]
405struct MemberChunk {
406 buffer: Vec<u8>,
407 start_position: u64,
408 physical_len: usize,
409 meaningful_len: usize,
410 state: Option<MemberChunkState>,
411}
412
413#[derive(Clone, Copy)]
414enum MemberChunkState {
415 Reading {
416 member_remaining: u64,
417 filled: usize,
418 },
419 Ready {
420 delivered: usize,
421 },
422}
423
424#[derive(Clone, Copy, Debug, Eq, PartialEq)]
426pub struct StreamPolicy {
427 max_pax_extension_size: u64,
428 max_global_pax_extensions_size: u64,
429 allow_all_nul_numeric_fields: bool,
430 max_gnu_extension_size: u64,
431}
432
433impl Default for StreamPolicy {
434 fn default() -> Self {
435 Self {
436 max_pax_extension_size: DEFAULT_MAX_PAX_EXTENSION_SIZE,
437 max_global_pax_extensions_size: DEFAULT_MAX_GLOBAL_PAX_EXTENSIONS_SIZE,
438 allow_all_nul_numeric_fields: true,
439 max_gnu_extension_size: DEFAULT_MAX_GNU_EXTENSION_SIZE,
440 }
441 }
442}
443
444impl StreamPolicy {
445 pub fn max_pax_extension_size(mut self, max_pax_extension_size: u64) -> Self {
453 self.max_pax_extension_size = max_pax_extension_size;
454 self
455 }
456
457 pub fn max_global_pax_extensions_size(mut self, max_global_pax_extensions_size: u64) -> Self {
465 self.max_global_pax_extensions_size = max_global_pax_extensions_size;
466 self
467 }
468
469 pub fn allow_all_nul_numeric_fields(mut self, allow: bool) -> Self {
476 self.allow_all_nul_numeric_fields = allow;
477 self
478 }
479
480 pub fn max_gnu_extension_size(mut self, max_gnu_extension_size: u64) -> Self {
486 self.max_gnu_extension_size = max_gnu_extension_size;
487 self
488 }
489}
490
491pub struct TarStream<R> {
493 pub(super) position: u64,
495 pub(super) inner: R,
497 pub(super) block: Block,
498 pub(super) block_len: usize,
499 pub(super) format: Option<ArchiveFormat>,
500 pub(super) global_pax_records: Option<GlobalPaxRecords>,
502 policy: StreamPolicy,
503 global_pax_extensions_size: u64,
504 member_chunk: MemberChunk,
505 pub(super) state: State,
506}
507
508impl<R> TarStream<R> {
509 pub fn new(reader: R) -> Self {
511 Self {
512 position: 0,
513 inner: reader,
514 block: [0; BLOCK_SIZE],
515 block_len: 0,
516 format: None,
517 global_pax_records: None,
518 policy: StreamPolicy::default(),
519 global_pax_extensions_size: 0,
520 member_chunk: MemberChunk::default(),
521 state: State::AwaitingHeader,
522 }
523 }
524
525 pub fn with_policy(mut self, policy: StreamPolicy) -> Self {
529 self.policy = policy;
530 self
531 }
532
533 pub fn format(&self) -> Option<ArchiveFormat> {
535 self.format
536 }
537}
538
539impl<R: AsyncRead + Unpin> TarStream<R> {
540 pub async fn next_frame(&mut self) -> Result<Option<Frame>, FrameError> {
546 let result = poll_fn(|context| self.poll_next_frame(context)).await;
547 self.fail_on_error(result)
548 }
549
550 pub(crate) async fn read_member_block(&mut self) -> Result<(u64, Block, usize), FrameError> {
554 let result = self.read_member_block_inner().await;
555 self.fail_on_error(result)
556 }
557
558 async fn read_member_block_inner(&mut self) -> Result<(u64, Block, usize), FrameError> {
559 if self.member_chunk.state.is_some() {
560 self.complete_member_chunk().await?;
561 return self.take_member_block_from_chunk();
562 }
563 let remaining = match &self.state {
564 State::ReadingMember { remaining } => *remaining,
565 _ => {
566 return Err(FrameError::unexpected_order(
567 self.position,
568 "ordinary member payload",
569 "parser state without member payload",
570 ));
571 }
572 };
573 let (position, block) = match poll_fn(|context| self.poll_read_block(context)).await {
574 Ok(Some(block)) => block,
575 Ok(None) => return Err(self.handle_eof()),
576 Err(error) => return Err(error),
577 };
578 let meaningful_len = remaining.min(BLOCK_SIZE as u64) as usize;
579 self.state = member_payload_state(remaining - meaningful_len as u64);
580 Ok((position, block, meaningful_len))
581 }
582
583 pub(crate) async fn read_member_chunk(
588 &mut self,
589 buffer: &mut Vec<u8>,
590 target_len: usize,
591 ) -> Result<usize, FrameError> {
592 let result = self.read_member_chunk_inner(buffer, target_len).await;
593 self.fail_on_error(result)
594 }
595
596 async fn read_member_chunk_inner(
597 &mut self,
598 buffer: &mut Vec<u8>,
599 target_len: usize,
600 ) -> Result<usize, FrameError> {
601 if self.member_chunk.state.is_none() && self.block_len != 0 {
604 let (_, block, meaningful_len) = self.read_member_block().await?;
605 buffer.clear();
606 buffer.extend_from_slice(&block[..meaningful_len]);
607 return Ok(meaningful_len);
608 }
609 if self.member_chunk.state.is_none() {
610 self.start_member_chunk(buffer, target_len)?;
611 }
612 self.complete_member_chunk().await?;
613 self.take_member_chunk(buffer)
614 }
615
616 fn fail_on_error<T>(&mut self, result: Result<T, FrameError>) -> Result<T, FrameError> {
617 if result.is_err() {
618 self.state = State::Failed;
619 }
620 result
621 }
622
623 fn start_member_chunk(
624 &mut self,
625 buffer: &mut Vec<u8>,
626 target_len: usize,
627 ) -> Result<(), FrameError> {
628 let member_remaining = match &self.state {
629 State::ReadingMember { remaining } => *remaining,
630 _ => {
631 return Err(FrameError::unexpected_order(
632 self.position,
633 "ordinary member payload",
634 "parser state without member payload",
635 ));
636 }
637 };
638 if self.block_len != 0 {
639 return Err(FrameError::unexpected_order(
640 self.position,
641 "aligned ordinary member payload",
642 "partially buffered physical block",
643 ));
644 }
645
646 let target_len = u64::try_from(target_len.max(BLOCK_SIZE)).map_err(|_| {
647 FrameError::arithmetic_overflow(self.position, "member payload chunk target length")
648 })?;
649 let physical_len = member_remaining
650 .min(target_len)
651 .div_ceil(BLOCK_SIZE as u64)
652 .checked_mul(BLOCK_SIZE as u64)
653 .ok_or_else(|| {
654 FrameError::arithmetic_overflow(
655 self.position,
656 "member payload chunk physical length",
657 )
658 })?;
659 let meaningful_len = member_remaining.min(physical_len);
660 let physical_len = usize::try_from(physical_len).map_err(|_| {
661 FrameError::arithmetic_overflow(self.position, "member payload chunk physical length")
662 })?;
663 let meaningful_len = usize::try_from(meaningful_len).map_err(|_| {
664 FrameError::arithmetic_overflow(self.position, "member payload chunk meaningful length")
665 })?;
666
667 self.member_chunk.buffer.clear();
670 std::mem::swap(buffer, &mut self.member_chunk.buffer);
671 if self.member_chunk.buffer.len() != physical_len {
672 self.member_chunk.buffer.resize(physical_len, 0);
673 }
674 self.member_chunk.start_position = self.position;
675 self.member_chunk.physical_len = physical_len;
676 self.member_chunk.meaningful_len = meaningful_len;
677 self.member_chunk.state = Some(MemberChunkState::Reading {
678 member_remaining,
679 filled: 0,
680 });
681 Ok(())
682 }
683
684 async fn complete_member_chunk(&mut self) -> Result<(), FrameError> {
685 loop {
686 let (member_remaining, filled) = match self.member_chunk.state {
687 Some(MemberChunkState::Reading {
688 member_remaining,
689 filled,
690 }) => (member_remaining, filled),
691 Some(MemberChunkState::Ready { .. }) => return Ok(()),
692 None => {
693 return Err(FrameError::unexpected_order(
694 self.position,
695 "pending member payload chunk",
696 "parser state without a pending chunk",
697 ));
698 }
699 };
700 let start_position = self.member_chunk.start_position;
701 let physical_len = self.member_chunk.physical_len;
702 let meaningful_len = self.member_chunk.meaningful_len;
703 if filled == physical_len {
704 self.position =
705 checked_position(start_position, physical_len).inspect_err(|_| {
706 self.member_chunk.state = None;
707 })?;
708 let remaining = member_remaining
709 .checked_sub(meaningful_len as u64)
710 .ok_or_else(|| {
711 self.member_chunk.state = None;
712 FrameError::arithmetic_overflow(
713 start_position,
714 "remaining member payload length",
715 )
716 })?;
717 self.state = member_payload_state(remaining);
718 self.member_chunk.state = Some(MemberChunkState::Ready { delivered: 0 });
719 return Ok(());
720 }
721
722 let read = match poll_fn(|context| {
723 let mut read_buffer =
724 ReadBuf::new(&mut self.member_chunk.buffer[filled..physical_len]);
725 match Pin::new(&mut self.inner).poll_read(context, &mut read_buffer) {
726 Poll::Pending => Poll::Pending,
727 Poll::Ready(Ok(())) => Poll::Ready(Ok(read_buffer.filled().len())),
728 Poll::Ready(Err(source)) => Poll::Ready(Err(source)),
729 }
730 })
731 .await
732 {
733 Ok(read) => read,
734 Err(source) => {
735 self.member_chunk.state = None;
736 let error_position = checked_position(start_position, filled)?;
737 self.position = checked_position(start_position, filled - filled % BLOCK_SIZE)?;
738 return Err(FrameError::at(
739 error_position,
740 FrameErrorInner::Io { source },
741 ));
742 }
743 };
744 if read == 0 {
745 self.member_chunk.state = None;
746 let partial_len = filled % BLOCK_SIZE;
747 let completed_len = filled - partial_len;
748 self.position = checked_position(start_position, completed_len)?;
749 if partial_len != 0 {
750 return Err(FrameError::at(
751 self.position,
752 FrameErrorInner::IncompleteBlock { read: partial_len },
753 ));
754 }
755 let completed_len = u64::try_from(completed_len).map_err(|_| {
756 FrameError::arithmetic_overflow(
757 self.position,
758 "completed member payload chunk length",
759 )
760 })?;
761 return Err(FrameError::truncated_payload(
762 self.position,
763 DataOwner::Member,
764 member_remaining - member_remaining.min(completed_len),
765 ));
766 }
767 if let Some(MemberChunkState::Reading { filled, .. }) = &mut self.member_chunk.state {
768 *filled += read;
769 }
770 }
771 }
772
773 fn take_member_chunk(&mut self, buffer: &mut Vec<u8>) -> Result<usize, FrameError> {
774 let Some(MemberChunkState::Ready { delivered }) = self.member_chunk.state.take() else {
775 return Err(FrameError::unexpected_order(
776 self.position,
777 "completed member payload chunk",
778 "incomplete member payload chunk",
779 ));
780 };
781 let meaningful_len = self.member_chunk.meaningful_len;
782 let remaining_len = meaningful_len.checked_sub(delivered).ok_or_else(|| {
783 FrameError::arithmetic_overflow(self.position, "undelivered member payload length")
784 })?;
785 if delivered != 0 {
786 self.member_chunk
787 .buffer
788 .copy_within(delivered..meaningful_len, 0);
789 }
790 self.member_chunk.buffer.truncate(remaining_len);
791 std::mem::swap(buffer, &mut self.member_chunk.buffer);
792 Ok(remaining_len)
793 }
794
795 fn take_member_block_from_chunk(&mut self) -> Result<(u64, Block, usize), FrameError> {
796 let Some(MemberChunkState::Ready { delivered }) = self.member_chunk.state else {
797 return Err(FrameError::unexpected_order(
798 self.position,
799 "completed member payload chunk",
800 "incomplete member payload chunk",
801 ));
802 };
803 let start_position = self.member_chunk.start_position;
804 let physical_len = self.member_chunk.physical_len;
805 let total_meaningful_len = self.member_chunk.meaningful_len;
806 let position = checked_position(start_position, delivered).inspect_err(|_| {
807 self.member_chunk.state = None;
808 })?;
809 let mut block = [0; BLOCK_SIZE];
810 block.copy_from_slice(&self.member_chunk.buffer[delivered..delivered + BLOCK_SIZE]);
811 let meaningful_len = total_meaningful_len
812 .checked_sub(delivered)
813 .ok_or_else(|| {
814 self.member_chunk.state = None;
815 FrameError::arithmetic_overflow(self.position, "undelivered member payload length")
816 })?
817 .min(BLOCK_SIZE);
818 let delivered = delivered + BLOCK_SIZE;
819 if delivered == physical_len {
820 self.member_chunk.state = None;
821 } else {
822 self.member_chunk.state = Some(MemberChunkState::Ready { delivered });
823 }
824 Ok((position, block, meaningful_len))
825 }
826
827 fn poll_read_block(
828 &mut self,
829 cx: &mut Context<'_>,
830 ) -> Poll<Result<Option<PositionedBlock>, FrameError>> {
831 while self.block_len < BLOCK_SIZE {
832 let mut read_buf = ReadBuf::new(&mut self.block[self.block_len..]);
833 match Pin::new(&mut self.inner).poll_read(cx, &mut read_buf) {
834 Poll::Pending => return Poll::Pending,
835 Poll::Ready(Err(source)) => {
836 return Poll::Ready(Err(FrameError::at(
837 self.position + self.block_len as u64,
838 FrameErrorInner::Io { source },
839 )));
840 }
841 Poll::Ready(Ok(())) => {
842 let read = read_buf.filled().len();
843 if read == 0 {
844 if self.block_len == 0 {
845 return Poll::Ready(Ok(None));
846 }
847 return Poll::Ready(Err(FrameError::at(
848 self.position,
849 FrameErrorInner::IncompleteBlock {
850 read: self.block_len,
851 },
852 )));
853 }
854 self.block_len += read;
855 }
856 }
857 }
858
859 let position = self.position;
860 self.position = self
861 .position
862 .checked_add(BLOCK_SIZE as u64)
863 .ok_or_else(|| FrameError::arithmetic_overflow(position, "stream position"))?;
864 self.block_len = 0;
865 let block = std::mem::replace(&mut self.block, [0; BLOCK_SIZE]);
866 Poll::Ready(Ok(Some((position, block))))
867 }
868
869 fn handle_eof(&mut self) -> FrameError {
870 let inner = match &self.state {
871 State::AwaitingHeader | State::AwaitingSecondZero => FrameErrorInner::MissingEndMarker,
872 State::ReadingPax {
873 kind, remaining, ..
874 } => FrameErrorInner::TruncatedPayload {
875 owner: DataOwner::Pax(*kind),
876 remaining: *remaining,
877 },
878 State::AwaitingUstarHeader { .. } => FrameErrorInner::UnexpectedEof {
879 expected: "ordinary ustar member header after a local pax header",
880 },
881 State::ReadingGnu {
882 kind, remaining, ..
883 } => FrameErrorInner::TruncatedPayload {
884 owner: DataOwner::Gnu(*kind),
885 remaining: *remaining,
886 },
887 State::AwaitingGnuMember { .. } => FrameErrorInner::UnexpectedEof {
888 expected: "ordinary GNU member header after a GNU metadata extension",
889 },
890 State::ReadingMember { remaining } => FrameErrorInner::TruncatedPayload {
891 owner: DataOwner::Member,
892 remaining: *remaining,
893 },
894 State::Complete | State::Failed => FrameErrorInner::UnexpectedEof {
895 expected: "no further input",
896 },
897 };
898 FrameError::at(self.position, inner)
899 }
900
901 fn process_block(&mut self, position: u64, block: Block) -> Result<Option<Frame>, FrameError> {
902 let state = std::mem::replace(&mut self.state, State::Failed);
903 match state {
904 State::AwaitingHeader => {
905 if is_zero_block(&block) {
906 self.state = State::AwaitingSecondZero;
907 Ok(None)
908 } else {
909 self.process_boundary_header(position, block).map(Some)
910 }
911 }
912 State::ReadingPax {
913 kind,
914 header_position,
915 mut remaining,
916 mut payload,
917 } => {
918 let len = remaining.min(BLOCK_SIZE as u64) as usize;
919 payload.extend_from_slice(&block[..len]);
920 remaining -= len as u64;
921 let completed_pax_records = if remaining == 0 {
922 let records = Arc::new(
923 PaxRecords::parse(
924 &payload,
925 self.global_pax_records
926 .as_ref()
927 .map_or(HdrCharset::Utf8, GlobalPaxRecords::hdrcharset),
928 )
929 .map_err(|source| {
930 FrameError::invalid_pax_record(header_position, source)
931 })?,
932 );
933 match kind {
934 PaxKind::Local => {
935 self.state = State::AwaitingUstarHeader {
936 records: records.clone(),
937 };
938 }
939 PaxKind::Global => {
940 records.apply_global(&mut self.global_pax_records);
941 self.state = State::AwaitingHeader;
942 }
943 }
944 Some(records)
945 } else {
946 self.state = State::ReadingPax {
947 kind,
948 header_position,
949 remaining,
950 payload,
951 };
952 None
953 };
954 Ok(Some(Frame::Data(DataFrame {
955 position,
956 block,
957 len,
958 owner: DataOwner::Pax(kind),
959 completed_pax_records,
960 })))
961 }
962 State::AwaitingUstarHeader { records } => {
963 if is_zero_block(&block) {
964 return Err(FrameError::unexpected_order(
965 position,
966 "ordinary ustar member header after a local pax header",
967 "end-of-archive marker",
968 ));
969 }
970 let parsed = self.parse_format_checked_header(position, &block)?;
971 if matches!(parsed.typeflag, b'x' | b'g') {
972 return Err(FrameError::unexpected_order(
973 position,
974 "ordinary ustar member header after a local pax header",
975 "another pax extended header",
976 ));
977 }
978 self.process_ustar_header(position, block, parsed, Some(records))
979 .map(Some)
980 }
981 State::ReadingGnu {
982 kind,
983 mut remaining,
984 pending,
985 } => {
986 let len = remaining.min(BLOCK_SIZE as u64) as usize;
987 remaining -= len as u64;
988 if remaining == 0 {
989 self.state = State::AwaitingGnuMember { pending };
990 } else {
991 self.state = State::ReadingGnu {
992 kind,
993 remaining,
994 pending,
995 };
996 }
997 Ok(Some(Frame::Data(DataFrame {
998 position,
999 block,
1000 len,
1001 owner: DataOwner::Gnu(kind),
1002 completed_pax_records: None,
1003 })))
1004 }
1005 State::AwaitingGnuMember { pending } => {
1006 if is_zero_block(&block) {
1007 return Err(FrameError::unexpected_order(
1008 position,
1009 "ordinary GNU member header after a GNU metadata extension",
1010 "end-of-archive marker",
1011 ));
1012 }
1013 let parsed = self.parse_format_checked_header(position, &block)?;
1014 self.process_gnu_header(position, block, parsed, pending)
1015 .map(Some)
1016 }
1017 State::ReadingMember { mut remaining } => {
1018 let len = remaining.min(BLOCK_SIZE as u64) as usize;
1019 remaining -= len as u64;
1020 self.state = member_payload_state(remaining);
1021 Ok(Some(Frame::Data(DataFrame {
1022 position,
1023 block,
1024 len,
1025 owner: DataOwner::Member,
1026 completed_pax_records: None,
1027 })))
1028 }
1029 State::AwaitingSecondZero => {
1030 if !is_zero_block(&block) {
1031 return Err(FrameError::at(position, FrameErrorInner::InvalidEndMarker));
1032 }
1033 self.state = State::Complete;
1034 Ok(None)
1035 }
1036 State::Complete => {
1037 self.state = State::Complete;
1038 Ok(None)
1039 }
1040 State::Failed => Ok(None),
1041 }
1042 }
1043
1044 fn process_boundary_header(
1045 &mut self,
1046 position: u64,
1047 block: Block,
1048 ) -> Result<Frame, FrameError> {
1049 let parsed = self.parse_format_checked_header(position, &block)?;
1050 match parsed.format {
1051 ArchiveFormat::Pax => self.process_pax_boundary_header(position, block, parsed),
1052 ArchiveFormat::Gnu => {
1053 self.process_gnu_header(position, block, parsed, PendingGnu::default())
1054 }
1055 }
1056 }
1057
1058 fn parse_format_checked_header(
1063 &mut self,
1064 position: u64,
1065 block: &Block,
1066 ) -> Result<ParsedHeader, FrameError> {
1067 let parsed = ParsedHeader::try_from_framed(position, block)?;
1068 if let Some(expected) = self.format
1069 && parsed.format != expected
1070 {
1071 return Err(FrameError::at(
1072 position,
1073 FrameErrorInner::FormatMismatch {
1074 expected,
1075 found: parsed.format,
1076 },
1077 ));
1078 }
1079 self.format.get_or_insert(parsed.format);
1080 Ok(parsed)
1081 }
1082
1083 fn process_pax_boundary_header(
1089 &mut self,
1090 position: u64,
1091 block: Block,
1092 parsed: ParsedHeader,
1093 ) -> Result<Frame, FrameError> {
1094 match parsed.typeflag {
1095 b'x' => self.process_pax_header(position, block, parsed.size, PaxKind::Local),
1096 b'g' => self.process_pax_header(position, block, parsed.size, PaxKind::Global),
1097 _ => self.process_ustar_header(position, block, parsed, None),
1098 }
1099 }
1100
1101 fn process_pax_header(
1106 &mut self,
1107 position: u64,
1108 block: Block,
1109 payload_size: u64,
1110 kind: PaxKind,
1111 ) -> Result<Frame, FrameError> {
1112 if payload_size > self.policy.max_pax_extension_size {
1113 return Err(FrameError::at(
1114 position,
1115 FrameErrorInner::ExtensionTooLarge {
1116 format: ArchiveFormat::Pax,
1117 size: payload_size,
1118 limit: self.policy.max_pax_extension_size,
1119 },
1120 ));
1121 }
1122 if kind == PaxKind::Global {
1123 let size = self
1124 .global_pax_extensions_size
1125 .checked_add(payload_size)
1126 .ok_or_else(|| {
1127 FrameError::arithmetic_overflow(position, "global pax extension payload total")
1128 })?;
1129 if size > self.policy.max_global_pax_extensions_size {
1130 return Err(FrameError::at(
1131 position,
1132 FrameErrorInner::GlobalPaxExtensionsTooLarge {
1133 size,
1134 limit: self.policy.max_global_pax_extensions_size,
1135 },
1136 ));
1137 }
1138 self.global_pax_extensions_size = size;
1139 }
1140 if payload_size == 0 {
1141 return Err(FrameError::invalid_pax_record(
1142 position,
1143 PaxError::InvalidRecords {
1144 reason: "extended header payload contains no records",
1145 },
1146 ));
1147 }
1148 self.state = State::ReadingPax {
1149 kind,
1150 header_position: position,
1151 remaining: payload_size,
1152 payload: Vec::new(),
1153 };
1154 Ok(Frame::Pax(PaxFrame {
1155 position,
1156 block,
1157 kind,
1158 payload_size,
1159 }))
1160 }
1161
1162 fn process_ustar_header(
1168 &mut self,
1169 position: u64,
1170 block: Block,
1171 parsed: ParsedHeader,
1172 local_pax_records: Option<SharedPaxRecords>,
1173 ) -> Result<Frame, FrameError> {
1174 let frame = HeaderFrame::ustar(
1175 position,
1176 block,
1177 parsed.typeflag,
1178 parsed.size,
1179 local_pax_records.as_deref(),
1180 self.global_pax_records.as_ref(),
1181 self.policy.allow_all_nul_numeric_fields,
1182 )?;
1183 self.global_pax_extensions_size = 0;
1184 self.state = member_payload_state(frame.effective_size);
1185 Ok(Frame::Header(frame))
1186 }
1187
1188 fn process_gnu_header(
1189 &mut self,
1190 position: u64,
1191 block: Block,
1192 parsed: ParsedHeader,
1193 mut pending: PendingGnu,
1194 ) -> Result<Frame, FrameError> {
1195 let extension = match parsed.typeflag {
1196 b'L' => Some(GnuKind::LongName),
1197 b'K' => Some(GnuKind::LongLink),
1198 _ => None,
1199 };
1200 if let Some(kind) = extension {
1201 let already_seen = match kind {
1202 GnuKind::LongName => &mut pending.long_name,
1203 GnuKind::LongLink => &mut pending.long_link,
1204 };
1205 if *already_seen {
1206 return Err(FrameError::unexpected_order(
1207 position,
1208 "ordinary GNU member header or the other GNU metadata extension",
1209 "duplicate GNU metadata extension",
1210 ));
1211 }
1212 if parsed.size > self.policy.max_gnu_extension_size {
1213 return Err(FrameError::at(
1214 position,
1215 FrameErrorInner::ExtensionTooLarge {
1216 format: ArchiveFormat::Gnu,
1217 size: parsed.size,
1218 limit: self.policy.max_gnu_extension_size,
1219 },
1220 ));
1221 }
1222 *already_seen = true;
1223 self.state = if parsed.size == 0 {
1224 State::AwaitingGnuMember { pending }
1225 } else {
1226 State::ReadingGnu {
1227 kind,
1228 remaining: parsed.size,
1229 pending,
1230 }
1231 };
1232 return Ok(Frame::Gnu(GnuFrame {
1233 position,
1234 block,
1235 kind,
1236 payload_size: parsed.size,
1237 }));
1238 }
1239
1240 let frame = HeaderFrame::gnu(
1241 position,
1242 block,
1243 parsed.typeflag,
1244 parsed.size,
1245 pending.long_link,
1246 self.policy.allow_all_nul_numeric_fields,
1247 )?;
1248 self.state = member_payload_state(frame.effective_size);
1249 Ok(Frame::Header(frame))
1250 }
1251
1252 fn poll_next_frame(
1253 &mut self,
1254 context: &mut Context<'_>,
1255 ) -> Poll<Result<Option<Frame>, FrameError>> {
1256 loop {
1257 if matches!(self.state, State::Complete | State::Failed) {
1258 return Poll::Ready(Ok(None));
1259 }
1260
1261 let (position, block) = match self.poll_read_block(context) {
1262 Poll::Pending => return Poll::Pending,
1263 Poll::Ready(Ok(Some(block))) => block,
1264 Poll::Ready(Ok(None)) => return Poll::Ready(Err(self.handle_eof())),
1265 Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
1266 };
1267
1268 match self.process_block(position, block) {
1269 Ok(Some(frame)) => return Poll::Ready(Ok(Some(frame))),
1270 Ok(None) => continue,
1271 Err(error) => return Poll::Ready(Err(error)),
1272 }
1273 }
1274 }
1275}
1276
1277struct ParsedHeader {
1278 format: ArchiveFormat,
1279 typeflag: u8,
1280 size: u64,
1281}
1282
1283trait TryFromFramed<T>: Sized {
1286 fn try_from_framed(position: u64, source: T) -> Result<Self, FrameError>;
1287}
1288
1289fn is_zero_block(block: &Block) -> bool {
1290 block.iter().all(|byte| *byte == 0)
1291}
1292
1293fn trim_nul(bytes: &[u8]) -> &[u8] {
1294 let end = bytes
1295 .iter()
1296 .position(|byte| *byte == 0)
1297 .unwrap_or(bytes.len());
1298 &bytes[..end]
1299}
1300
1301fn member_payload_state(remaining: u64) -> State {
1302 if remaining == 0 {
1303 State::AwaitingHeader
1304 } else {
1305 State::ReadingMember { remaining }
1306 }
1307}
1308
1309fn checked_position(position: u64, len: usize) -> Result<u64, FrameError> {
1310 let len = u64::try_from(len)
1311 .map_err(|_| FrameError::arithmetic_overflow(position, "stream position"))?;
1312 position
1313 .checked_add(len)
1314 .ok_or_else(|| FrameError::arithmetic_overflow(position, "stream position"))
1315}
1316
1317impl TryFromFramed<&Block> for ParsedHeader {
1318 fn try_from_framed(position: u64, block: &Block) -> Result<Self, FrameError> {
1319 let format = match &block[IDENTITY_RANGE] {
1320 identity if identity == USTAR_IDENTITY => ArchiveFormat::Pax,
1321 identity if identity == GNU_IDENTITY => ArchiveFormat::Gnu,
1322 identity => {
1323 return Err(FrameError::at(
1324 position,
1325 FrameErrorInner::InvalidIdentity {
1326 found: identity.try_into().expect("fixed header range"),
1327 },
1328 ));
1329 }
1330 };
1331
1332 let actual_checksum = checksum(block);
1333 let expected_checksum = parse_octal(&block[CHECKSUM_RANGE]);
1334 if expected_checksum != Some(actual_checksum) {
1335 return Err(FrameError::at(
1336 position,
1337 FrameErrorInner::InvalidChecksum {
1338 expected: expected_checksum,
1339 actual: actual_checksum,
1340 },
1341 ));
1342 }
1343
1344 let size_bytes: [u8; 12] = block[SIZE_RANGE].try_into().expect("fixed header range");
1345 let size = parse_number(format, &size_bytes).ok_or_else(|| {
1346 FrameError::at(position, FrameErrorInner::InvalidSize { found: size_bytes })
1347 })?;
1348
1349 Ok(Self {
1350 format,
1351 typeflag: block[TYPEFLAG_OFFSET],
1352 size,
1353 })
1354 }
1355}
1356
1357impl TryFromFramed<u8> for UstarKind {
1358 fn try_from_framed(position: u64, typeflag: u8) -> Result<Self, FrameError> {
1359 match typeflag {
1360 0 | b'0' => Ok(Self::Regular),
1361 b'1' => Ok(Self::HardLink),
1362 b'2' => Ok(Self::SymbolicLink),
1363 b'3' => Ok(Self::CharacterDevice),
1364 b'4' => Ok(Self::BlockDevice),
1365 b'5' => Ok(Self::Directory),
1366 b'6' => Ok(Self::Fifo),
1367 b'7' => Ok(Self::Contiguous),
1368 _ => Err(FrameError::at(
1369 position,
1370 FrameErrorInner::UnsupportedTypeflag { typeflag },
1371 )),
1372 }
1373 }
1374}
1375
1376fn validate_pax_member_size(
1377 position: u64,
1378 kind: UstarKind,
1379 declared_size: u64,
1380 effective_size: u64,
1381) -> Result<(), FrameError> {
1382 match kind {
1383 UstarKind::Regular | UstarKind::HardLink | UstarKind::Contiguous => Ok(()),
1388 UstarKind::SymbolicLink
1389 | UstarKind::CharacterDevice
1390 | UstarKind::BlockDevice
1391 | UstarKind::Directory
1392 | UstarKind::Fifo => {
1393 validate_payload_free_size(position, kind, declared_size)?;
1400 validate_payload_free_size(position, kind, effective_size)
1401 }
1402 }
1403}
1404
1405fn validate_gnu_member_size(position: u64, kind: UstarKind, size: u64) -> Result<(), FrameError> {
1406 match kind {
1407 UstarKind::Regular | UstarKind::Contiguous => Ok(()),
1408 UstarKind::HardLink
1409 | UstarKind::SymbolicLink
1410 | UstarKind::CharacterDevice
1411 | UstarKind::BlockDevice
1412 | UstarKind::Directory
1413 | UstarKind::Fifo => validate_payload_free_size(position, kind, size),
1414 }
1415}
1416
1417fn validate_payload_free_size(position: u64, kind: UstarKind, size: u64) -> Result<(), FrameError> {
1418 if size == 0 {
1419 Ok(())
1420 } else {
1421 Err(FrameError::at(
1422 position,
1423 FrameErrorInner::InvalidMemberSize { kind, size },
1424 ))
1425 }
1426}
1427
1428#[cfg(test)]
1429mod tests {
1430 use std::{
1431 cell::Cell,
1432 pin::Pin,
1433 rc::Rc,
1434 task::{Context, Poll},
1435 };
1436
1437 use tokio::io::ReadBuf;
1438
1439 use super::*;
1440 use crate::{
1441 ArchiveFormat, FrameError, FrameErrorInner, HdrCharset, PaxString, PaxValue,
1442 header::{DEVMAJOR_RANGE, DEVMINOR_RANGE},
1443 test_support::{
1444 ChunkedReader, append_block, append_gnu, append_pax, append_payload, append_terminator,
1445 collect_frames, gnu_base256_header, gnu_header, header, ready, record, set_checksum,
1446 },
1447 };
1448
1449 fn collect(bytes: Vec<u8>, max_chunk: usize) -> Vec<Result<Frame, FrameError>> {
1450 ready(collect_frames(TarStream::new(ChunkedReader::new(
1451 bytes, max_chunk,
1452 ))))
1453 }
1454
1455 fn collect_with_max_pax_extension_size(
1456 bytes: Vec<u8>,
1457 max_chunk: usize,
1458 max_pax_extension_size: u64,
1459 ) -> Vec<Result<Frame, FrameError>> {
1460 ready(collect_frames(
1461 TarStream::new(ChunkedReader::new(bytes, max_chunk)).with_policy(
1462 StreamPolicy::default().max_pax_extension_size(max_pax_extension_size),
1463 ),
1464 ))
1465 }
1466
1467 fn header_frame(frames: &[Result<Frame, FrameError>], index: usize) -> &HeaderFrame {
1468 let Ok(Frame::Header(frame)) = &frames[index] else {
1469 panic!("expected header frame");
1470 };
1471 frame
1472 }
1473
1474 fn data_frame(frames: &[Result<Frame, FrameError>], index: usize) -> &DataFrame {
1475 let Ok(Frame::Data(frame)) = &frames[index] else {
1476 panic!("expected data frame");
1477 };
1478 frame
1479 }
1480
1481 fn last_error(frames: &[Result<Frame, FrameError>]) -> &FrameError {
1482 frames
1483 .last()
1484 .expect("stream should emit an item")
1485 .as_ref()
1486 .expect_err("last item should be an error")
1487 }
1488
1489 fn last_error_inner(frames: &[Result<Frame, FrameError>]) -> &FrameErrorInner {
1490 &last_error(frames).inner
1491 }
1492
1493 struct CountingReader {
1494 bytes: Vec<u8>,
1495 position: usize,
1496 consumed: Rc<Cell<usize>>,
1497 }
1498
1499 impl AsyncRead for CountingReader {
1500 fn poll_read(
1501 mut self: Pin<&mut Self>,
1502 _context: &mut Context<'_>,
1503 buffer: &mut ReadBuf<'_>,
1504 ) -> Poll<std::io::Result<()>> {
1505 let len = buffer
1506 .remaining()
1507 .min(self.bytes.len().saturating_sub(self.position));
1508 let end = self.position + len;
1509 buffer.put_slice(&self.bytes[self.position..end]);
1510 self.position = end;
1511 self.consumed.set(self.consumed.get() + len);
1512 Poll::Ready(Ok(()))
1513 }
1514 }
1515
1516 #[derive(Clone, Copy)]
1517 enum ExpectedHeaderError {
1518 InvalidIdentity,
1519 InvalidChecksum,
1520 InvalidSize,
1521 InvalidNumericField(&'static str),
1522 UnterminatedUstarStringField(&'static str),
1523 UnsupportedTypeflag(u8),
1524 }
1525
1526 impl ExpectedHeaderError {
1527 fn matches(self, error: &FrameErrorInner) -> bool {
1528 match (self, error) {
1529 (Self::InvalidIdentity, FrameErrorInner::InvalidIdentity { .. })
1530 | (Self::InvalidChecksum, FrameErrorInner::InvalidChecksum { .. })
1531 | (Self::InvalidSize, FrameErrorInner::InvalidSize { .. }) => true,
1532 (
1533 Self::InvalidNumericField(field),
1534 FrameErrorInner::InvalidNumericField { field: found, .. },
1535 )
1536 | (
1537 Self::UnterminatedUstarStringField(field),
1538 FrameErrorInner::UnterminatedUstarStringField { field: found },
1539 ) => field == *found,
1540 (
1541 Self::UnsupportedTypeflag(typeflag),
1542 FrameErrorInner::UnsupportedTypeflag { typeflag: found },
1543 ) => typeflag == *found,
1544 _ => false,
1545 }
1546 }
1547 }
1548
1549 fn checksummed_header(mutate: impl FnOnce(&mut Block)) -> Block {
1550 let mut block = header(b'0', 0);
1551 mutate(&mut block);
1552 set_checksum(&mut block);
1553 block
1554 }
1555
1556 fn invalid_header_cases() -> Vec<(&'static str, Block, ExpectedHeaderError)> {
1557 let mut bad_magic = header(b'0', 0);
1558 bad_magic[IDENTITY_RANGE.start] = b'g';
1559 let mut bad_version = header(b'0', 0);
1560 bad_version[IDENTITY_RANGE.end - 2..IDENTITY_RANGE.end].copy_from_slice(b" ");
1561 let mut bad_checksum = header(b'0', 0);
1562 bad_checksum[0] = b'X';
1563
1564 vec![
1565 ("magic", bad_magic, ExpectedHeaderError::InvalidIdentity),
1566 ("version", bad_version, ExpectedHeaderError::InvalidIdentity),
1567 (
1568 "checksum",
1569 bad_checksum,
1570 ExpectedHeaderError::InvalidChecksum,
1571 ),
1572 (
1573 "octal size",
1574 checksummed_header(|block| {
1575 block[SIZE_RANGE].copy_from_slice(b"00000000008\0");
1576 }),
1577 ExpectedHeaderError::InvalidSize,
1578 ),
1579 (
1580 "base256 size",
1581 checksummed_header(|block| block[SIZE_RANGE.start] = 0x80),
1582 ExpectedHeaderError::InvalidSize,
1583 ),
1584 (
1585 "octal mode",
1586 checksummed_header(|block| {
1587 block[MODE_RANGE].copy_from_slice(b"0000080\0");
1588 }),
1589 ExpectedHeaderError::InvalidNumericField("mode"),
1590 ),
1591 (
1592 "uid",
1593 checksummed_header(|block| {
1594 block[UID_RANGE].copy_from_slice(b"invalid\0");
1595 }),
1596 ExpectedHeaderError::InvalidNumericField("uid"),
1597 ),
1598 (
1599 "gid",
1600 checksummed_header(|block| block[GID_RANGE.start] = b'8'),
1601 ExpectedHeaderError::InvalidNumericField("gid"),
1602 ),
1603 (
1604 "mtime",
1605 checksummed_header(|block| {
1606 block[MTIME_RANGE].copy_from_slice(b"00000000008\0");
1607 }),
1608 ExpectedHeaderError::InvalidNumericField("mtime"),
1609 ),
1610 (
1611 "uname",
1612 checksummed_header(|block| block[UNAME_RANGE].fill(b'u')),
1613 ExpectedHeaderError::UnterminatedUstarStringField("uname"),
1614 ),
1615 (
1616 "gname",
1617 checksummed_header(|block| block[GNAME_RANGE].fill(b'g')),
1618 ExpectedHeaderError::UnterminatedUstarStringField("gname"),
1619 ),
1620 (
1621 "POSIX typeflag",
1622 header(b'X', 0),
1623 ExpectedHeaderError::UnsupportedTypeflag(b'X'),
1624 ),
1625 (
1626 "GNU typeflag",
1627 header(b'L', 0),
1628 ExpectedHeaderError::UnsupportedTypeflag(b'L'),
1629 ),
1630 ]
1631 }
1632
1633 #[test]
1634 fn frames_bare_member_across_fragmented_reads() {
1635 let mut bytes = Vec::new();
1636 append_block(&mut bytes, &header(b'0', 513));
1637 append_payload(&mut bytes, &[b'a'; BLOCK_SIZE]);
1638 append_payload(&mut bytes, b"b");
1639 append_terminator(&mut bytes);
1640
1641 let frames = collect(bytes, 7);
1642 assert_eq!(frames.len(), 3);
1643 let header = header_frame(&frames, 0);
1644 assert_eq!(header.kind, UstarKind::Regular);
1645 assert_eq!(header.declared_size, 513);
1646 assert_eq!(header.effective_size, 513);
1647 let first = data_frame(&frames, 1);
1648 let last = data_frame(&frames, 2);
1649 assert_eq!(first.len, BLOCK_SIZE);
1650 assert_eq!(last.len, 1);
1651 assert_eq!(last.owner, DataOwner::Member);
1652 assert!(first.completed_pax_records().is_none());
1653 assert!(last.completed_pax_records().is_none());
1654 }
1655
1656 #[test]
1657 fn frames_multiblock_pax_records_and_applies_size_override() {
1658 let mut payload = record("comment", &"x".repeat(BLOCK_SIZE));
1659 payload.extend_from_slice(&record("size", "513"));
1660 assert!(payload.len() > BLOCK_SIZE);
1661
1662 let mut bytes = Vec::new();
1663 append_pax(&mut bytes, b'x', &payload);
1664 append_block(&mut bytes, &header(b'0', 1));
1665 append_payload(&mut bytes, &[b'a'; BLOCK_SIZE]);
1666 append_payload(&mut bytes, b"b");
1667 append_terminator(&mut bytes);
1668
1669 let frames = collect(bytes, 19);
1670 assert_eq!(frames.len(), 6);
1671 let Frame::Pax(pax) = frames[0].as_ref().unwrap() else {
1672 panic!("expected pax header");
1673 };
1674 assert_eq!(pax.kind, PaxKind::Local);
1675 assert_eq!(pax.payload_size, payload.len() as u64);
1676 let first_pax_data = data_frame(&frames, 1);
1677 assert_eq!(first_pax_data.owner, DataOwner::Pax(PaxKind::Local));
1678 assert!(first_pax_data.completed_pax_records().is_none());
1679 let final_pax_data = data_frame(&frames, 2);
1680 assert_eq!(final_pax_data.owner, DataOwner::Pax(PaxKind::Local));
1681 assert_eq!(
1682 final_pax_data
1683 .completed_pax_records()
1684 .and_then(|records| records.last()),
1685 Some(&PaxRecord::Size(PaxValue::Value(513)))
1686 );
1687 let header = header_frame(&frames, 3);
1688 assert_eq!(header.declared_size, 1);
1689 assert_eq!(header.effective_size, 513);
1690 let last = data_frame(&frames, 5);
1691 assert_eq!(last.len, 1);
1692 }
1693
1694 #[test]
1695 fn rejects_oversized_pax_extensions_before_consuming_payload() {
1696 let mut payload = record("comment", "metadata");
1697 payload.extend_from_slice(&record("mtime", "1"));
1698 let declared_size = u64::try_from(payload.len()).expect("payload size should fit u64");
1699 for (case, typeflag) in [("local", b'x'), ("global", b'g')] {
1700 let mut bytes = Vec::new();
1701 append_pax(&mut bytes, typeflag, &payload);
1702 let frames = collect_with_max_pax_extension_size(bytes, BLOCK_SIZE, declared_size - 1);
1703 assert_eq!(frames.len(), 1, "{case}");
1704 assert!(matches!(
1705 last_error(&frames),
1706 FrameError {
1707 position: 0,
1708 inner: FrameErrorInner::ExtensionTooLarge {
1709 format: ArchiveFormat::Pax,
1710 size,
1711 limit,
1712 },
1713 } if *size == declared_size && *limit == declared_size - 1
1714 ));
1715 }
1716
1717 let frames = collect(
1718 header(b'x', DEFAULT_MAX_PAX_EXTENSION_SIZE + 1).to_vec(),
1719 BLOCK_SIZE,
1720 );
1721 assert_eq!(frames.len(), 1);
1722 assert!(matches!(
1723 last_error(&frames),
1724 FrameError {
1725 position: 0,
1726 inner: FrameErrorInner::ExtensionTooLarge {
1727 format: ArchiveFormat::Pax,
1728 size,
1729 limit: DEFAULT_MAX_PAX_EXTENSION_SIZE,
1730 },
1731 } if *size == DEFAULT_MAX_PAX_EXTENSION_SIZE + 1
1732 ));
1733 }
1734
1735 #[test]
1736 fn oversized_pax_extension_does_not_read_its_payload_block() {
1737 let mut bytes = header(b'x', 1).to_vec();
1738 bytes.resize(BLOCK_SIZE * 2, 0);
1739 let consumed = Rc::new(Cell::new(0));
1740 let reader = CountingReader {
1741 bytes,
1742 position: 0,
1743 consumed: Rc::clone(&consumed),
1744 };
1745 let mut stream =
1746 TarStream::new(reader).with_policy(StreamPolicy::default().max_pax_extension_size(0));
1747
1748 assert!(matches!(
1749 ready(stream.next_frame()),
1750 Err(FrameError {
1751 position: 0,
1752 inner: FrameErrorInner::ExtensionTooLarge {
1753 format: ArchiveFormat::Pax,
1754 size: 1,
1755 limit: 0,
1756 },
1757 })
1758 ));
1759 assert_eq!(consumed.get(), BLOCK_SIZE);
1760 }
1761
1762 #[test]
1763 fn accepts_pax_extensions_at_the_configured_limit() {
1764 let mut payload = record("comment", "metadata");
1765 payload.extend_from_slice(&record("ACME.attribute", "value"));
1766 for (case, typeflag) in [("local", b'x'), ("global", b'g')] {
1767 let mut bytes = Vec::new();
1768 append_pax(&mut bytes, typeflag, &payload);
1769 if typeflag == b'x' {
1770 append_block(&mut bytes, &header(b'0', 0));
1771 }
1772 append_terminator(&mut bytes);
1773
1774 let frames = collect_with_max_pax_extension_size(
1775 bytes,
1776 7,
1777 payload
1778 .len()
1779 .try_into()
1780 .expect("payload size should fit u64"),
1781 );
1782 assert!(frames.iter().all(Result::is_ok), "{case}");
1783 }
1784 }
1785
1786 #[test]
1787 fn applies_global_pax_records_overrides_and_rejects_size_deletions() {
1788 let mut initial_global = record("comment", "old");
1789 initial_global.extend_from_slice(&record("size", "2"));
1790 let replacement_global = record("comment", "new");
1791 let mut local = record("comment", "local");
1792 local.extend_from_slice(&record("size", "3"));
1793 let mut deletion = record("comment", "");
1794 deletion.extend_from_slice(&record("size", ""));
1795
1796 let mut bytes = Vec::new();
1797 append_pax(&mut bytes, b'g', &initial_global);
1798 append_pax(&mut bytes, b'g', &replacement_global);
1799 append_block(&mut bytes, &header(b'0', 1));
1800 append_payload(&mut bytes, b"ab");
1801 append_pax(&mut bytes, b'x', &local);
1802 append_block(&mut bytes, &header(b'0', 1));
1803 append_payload(&mut bytes, b"abc");
1804 append_pax(&mut bytes, b'g', &deletion);
1805 append_block(&mut bytes, &header(b'5', 1));
1806 append_terminator(&mut bytes);
1807
1808 let frames = collect(bytes, 31);
1809 assert!(frames.iter().any(|frame| matches!(
1810 frame,
1811 Ok(Frame::Pax(PaxFrame {
1812 kind: PaxKind::Global,
1813 ..
1814 }))
1815 )));
1816 assert!(frames.iter().any(|frame| matches!(
1817 frame,
1818 Ok(Frame::Data(DataFrame {
1819 owner: DataOwner::Pax(PaxKind::Global),
1820 ..
1821 }))
1822 )));
1823 let completed_global_payloads: Vec<&[PaxRecord]> = frames
1824 .iter()
1825 .filter_map(|frame| match frame {
1826 Ok(Frame::Data(frame)) if frame.owner == DataOwner::Pax(PaxKind::Global) => {
1827 frame.completed_pax_records()
1828 }
1829 _ => None,
1830 })
1831 .collect();
1832 assert_eq!(completed_global_payloads.len(), 3);
1833 assert_eq!(
1834 completed_global_payloads[2],
1835 [
1836 PaxRecord::Comment(PaxValue::Deleted),
1837 PaxRecord::Size(PaxValue::Deleted),
1838 ]
1839 );
1840 let headers: Vec<&HeaderFrame> = frames
1841 .iter()
1842 .filter_map(|frame| match frame {
1843 Ok(Frame::Header(header)) => Some(header),
1844 _ => None,
1845 })
1846 .collect();
1847 assert_eq!(headers.len(), 2);
1848 assert_eq!(headers[0].effective_size, 2);
1849 assert_eq!(headers[1].effective_size, 3);
1850 assert!(frames.iter().any(|frame| {
1851 matches!(
1852 frame,
1853 Ok(Frame::Data(frame))
1854 if frame.owner == DataOwner::Pax(PaxKind::Local)
1855 && frame.completed_pax_records() == Some(local_records("local", 3).as_slice())
1856 )
1857 }));
1858 assert!(matches!(
1859 last_error_inner(&frames),
1860 FrameErrorInner::DeletedPaxMetadata { keyword: "size" }
1861 ));
1862 }
1863
1864 fn local_records(comment: &str, size: u64) -> Vec<PaxRecord> {
1865 vec![
1866 PaxRecord::Comment(PaxValue::Value(comment.into())),
1867 PaxRecord::Size(PaxValue::Value(size)),
1868 ]
1869 }
1870
1871 #[test]
1872 fn allows_local_size_deletion_when_a_later_record_restores_size() {
1873 let mut local = record("size", "");
1874 local.extend_from_slice(&record("size", "2"));
1875 let mut bytes = Vec::new();
1876 append_pax(&mut bytes, b'x', &local);
1877 append_block(&mut bytes, &header(b'0', 1));
1878 append_payload(&mut bytes, b"ab");
1879 append_terminator(&mut bytes);
1880
1881 let frames = collect(bytes, BLOCK_SIZE);
1882 let header = header_frame(&frames, 2);
1883 assert_eq!(header.effective_size, 2);
1884 assert_eq!(
1885 data_frame(&frames, 1).completed_pax_records(),
1886 Some(
1887 [
1888 PaxRecord::Size(PaxValue::Deleted),
1889 PaxRecord::Size(PaxValue::Value(2)),
1890 ]
1891 .as_slice()
1892 )
1893 );
1894 }
1895
1896 #[test]
1897 fn pax_records_do_not_make_malformed_ordinary_header_fields_valid() {
1898 let cases = [
1899 (
1900 "local uid",
1901 b'x',
1902 record("uid", "1"),
1903 checksummed_header(|block| block[UID_RANGE].fill(b'u')),
1904 ExpectedHeaderError::InvalidNumericField("uid"),
1905 ),
1906 (
1907 "global gid",
1908 b'g',
1909 record("gid", "2"),
1910 checksummed_header(|block| block[GID_RANGE].fill(b'g')),
1911 ExpectedHeaderError::InvalidNumericField("gid"),
1912 ),
1913 (
1914 "local mtime",
1915 b'x',
1916 record("mtime", "3"),
1917 checksummed_header(|block| block[MTIME_RANGE].fill(b'm')),
1918 ExpectedHeaderError::InvalidNumericField("mtime"),
1919 ),
1920 (
1921 "global uname",
1922 b'g',
1923 record("uname", "user"),
1924 checksummed_header(|block| block[UNAME_RANGE].fill(b'u')),
1925 ExpectedHeaderError::UnterminatedUstarStringField("uname"),
1926 ),
1927 (
1928 "local gname",
1929 b'x',
1930 record("gname", "group"),
1931 checksummed_header(|block| block[GNAME_RANGE].fill(b'g')),
1932 ExpectedHeaderError::UnterminatedUstarStringField("gname"),
1933 ),
1934 ];
1935
1936 for (case, typeflag, records, malformed, expected) in cases {
1937 let mut bytes = Vec::new();
1938 append_pax(&mut bytes, typeflag, &records);
1939 append_block(&mut bytes, &malformed);
1940 append_terminator(&mut bytes);
1941
1942 let frames = collect(bytes, BLOCK_SIZE);
1943 assert!(
1944 expected.matches(last_error_inner(&frames)),
1945 "{case}: {frames:?}"
1946 );
1947 }
1948 }
1949
1950 #[test]
1951 fn accepts_all_nul_unused_device_fields() {
1952 let block = header(b'0', 0);
1953 assert_eq!(parse_octal(&block[DEVMAJOR_RANGE]), None);
1954 assert_eq!(parse_octal(&block[DEVMINOR_RANGE]), None);
1955
1956 let mut bytes = Vec::new();
1957 append_block(&mut bytes, &block);
1958 append_terminator(&mut bytes);
1959 assert!(collect(bytes, BLOCK_SIZE).iter().all(Result::is_ok));
1960 }
1961
1962 #[test]
1963 fn rejects_local_size_deletion_for_payload_free_members() {
1964 let global = record("size", "7");
1965 let local = record("size", "");
1966 let mut bytes = Vec::new();
1967 append_pax(&mut bytes, b'g', &global);
1968 append_pax(&mut bytes, b'x', &local);
1969 append_block(&mut bytes, &header(b'5', 3));
1970 append_terminator(&mut bytes);
1971
1972 assert!(matches!(
1973 last_error_inner(&collect(bytes, BLOCK_SIZE)),
1974 FrameErrorInner::DeletedPaxMetadata { keyword: "size" }
1975 ));
1976 }
1977
1978 #[test]
1979 fn rejects_deleted_size_when_member_payload_cannot_be_framed() {
1980 let records = record("size", "");
1981 for typeflag in *b"xg" {
1982 let mut bytes = Vec::new();
1983 append_pax(&mut bytes, typeflag, &records);
1984 append_block(&mut bytes, &header(b'0', 0));
1985
1986 assert!(
1987 matches!(
1988 last_error_inner(&collect(bytes, BLOCK_SIZE)),
1989 FrameErrorInner::DeletedPaxMetadata { keyword: "size" }
1990 ),
1991 "{typeflag:?}"
1992 );
1993 }
1994 }
1995
1996 #[test]
1997 fn allows_local_size_to_restore_an_active_global_deletion() {
1998 let global = record("size", "");
1999 let local = record("size", "2");
2000 let mut bytes = Vec::new();
2001 append_pax(&mut bytes, b'g', &global);
2002 append_pax(&mut bytes, b'x', &local);
2003 append_block(&mut bytes, &header(b'0', 1));
2004 append_payload(&mut bytes, b"ab");
2005 append_terminator(&mut bytes);
2006
2007 let frames = collect(bytes, BLOCK_SIZE);
2008 let header = header_frame(&frames, 4);
2009 assert_eq!(header.effective_size, 2);
2010 assert_eq!(
2011 data_frame(&frames, 1).completed_pax_records(),
2012 Some([PaxRecord::Size(PaxValue::Deleted)].as_slice())
2013 );
2014 assert_eq!(
2015 data_frame(&frames, 3).completed_pax_records(),
2016 Some([PaxRecord::Size(PaxValue::Value(2))].as_slice())
2017 );
2018 }
2019
2020 #[test]
2021 fn frames_pax_hard_link_bodies_from_header_or_size_override() {
2022 for (case, declared_size, override_size, header_index, data_index) in [
2023 ("physical size", 3, None, 0, 1),
2024 ("pax size", 0, Some("3"), 2, 3),
2025 ("pax size overrides physical size", 1, Some("3"), 2, 3),
2026 ] {
2027 let mut bytes = Vec::new();
2028 if let Some(override_size) = override_size {
2029 append_pax(&mut bytes, b'x', &record("size", override_size));
2030 }
2031 append_block(&mut bytes, &header(b'1', declared_size));
2032 append_payload(&mut bytes, b"abc");
2033 append_terminator(&mut bytes);
2034
2035 let frames = collect(bytes, BLOCK_SIZE);
2036 let header = header_frame(&frames, header_index);
2037 assert_eq!(header.format, ArchiveFormat::Pax, "{case}");
2038 assert_eq!(header.kind, UstarKind::HardLink, "{case}");
2039 assert_eq!(header.declared_size, declared_size, "{case}");
2040 assert_eq!(header.effective_size, 3, "{case}");
2041 assert_eq!(data_frame(&frames, data_index).len, 3, "{case}");
2042 }
2043 }
2044
2045 #[test]
2046 fn zero_data_block_is_not_a_terminator() {
2047 let mut bytes = Vec::new();
2048 append_block(&mut bytes, &header(b'0', BLOCK_SIZE as u64));
2049 append_block(&mut bytes, &[0; BLOCK_SIZE]);
2050 append_terminator(&mut bytes);
2051
2052 let frames = collect(bytes, BLOCK_SIZE);
2053 assert_eq!(frames.len(), 2);
2054 assert!(matches!(frames[1], Ok(Frame::Data(_))));
2055 }
2056
2057 #[test]
2058 fn zero_filled_block_inside_pax_payload_is_data() {
2059 let payload = record("comment", &"\0".repeat(BLOCK_SIZE * 3));
2060 let mut bytes = Vec::new();
2061 append_pax(&mut bytes, b'x', &payload);
2062 append_block(&mut bytes, &header(b'0', 0));
2063 append_terminator(&mut bytes);
2064
2065 let frames = collect(bytes, BLOCK_SIZE);
2066 assert!(frames.iter().any(|frame| matches!(
2067 frame,
2068 Ok(Frame::Data(DataFrame {
2069 block,
2070 owner: DataOwner::Pax(PaxKind::Local),
2071 ..
2072 })) if is_zero_block(block)
2073 )));
2074 }
2075
2076 #[test]
2077 fn frames_gnu_long_metadata_and_base256_payloads() {
2078 let mut bytes = Vec::new();
2079 append_block(&mut bytes, &gnu_base256_header(b'L', 513));
2080 append_payload(&mut bytes, &[b'n'; BLOCK_SIZE]);
2081 append_payload(&mut bytes, b"\0");
2082 append_gnu(&mut bytes, b'K', b"link\0");
2083 append_block(&mut bytes, &gnu_header(b'2', 0));
2084 append_terminator(&mut bytes);
2085
2086 let frames = collect(bytes, 13);
2087 assert_eq!(frames.len(), 6);
2088 assert!(matches!(
2089 frames[0].as_ref().unwrap(),
2090 Frame::Gnu(GnuFrame {
2091 kind: GnuKind::LongName,
2092 payload_size: 513,
2093 ..
2094 })
2095 ));
2096 let final_name = data_frame(&frames, 2);
2097 assert_eq!(final_name.owner, DataOwner::Gnu(GnuKind::LongName));
2098 assert_eq!(final_name.len, 1);
2099 assert!(final_name.completed_pax_records().is_none());
2100 assert!(matches!(
2101 frames[3].as_ref().unwrap(),
2102 Frame::Gnu(GnuFrame {
2103 kind: GnuKind::LongLink,
2104 ..
2105 })
2106 ));
2107 let header = header_frame(&frames, 5);
2108 assert_eq!(header.kind, UstarKind::SymbolicLink);
2109 }
2110
2111 #[test]
2112 fn rejects_header_format_type_and_field_errors() {
2113 for (case, block, expected) in invalid_header_cases() {
2114 let frames = collect(block.to_vec(), BLOCK_SIZE);
2115 let error = last_error_inner(&frames);
2116 assert!(expected.matches(error), "{case}: {error:?}");
2117 }
2118 }
2119
2120 #[test]
2121 fn rejects_malformed_gnu_numeric_fields() {
2122 for (field, range) in [
2123 ("mode", MODE_RANGE),
2124 ("uid", UID_RANGE),
2125 ("gid", GID_RANGE),
2126 ("mtime", MTIME_RANGE),
2127 ] {
2128 let mut block = gnu_header(b'0', 0);
2129 block[range].fill(b'x');
2130 set_checksum(&mut block);
2131
2132 assert!(matches!(
2133 last_error_inner(&collect(block.to_vec(), BLOCK_SIZE)),
2134 FrameErrorInner::InvalidNumericField { field: found, .. } if *found == field
2135 ));
2136 }
2137 }
2138
2139 #[test]
2140 fn rejects_nonzero_physical_sizes_for_payload_free_members() {
2141 for (format, block, kind) in [
2142 (ArchiveFormat::Pax, header(b'2', 1), UstarKind::SymbolicLink),
2143 (ArchiveFormat::Gnu, gnu_header(b'1', 1), UstarKind::HardLink),
2144 (
2145 ArchiveFormat::Gnu,
2146 gnu_header(b'2', 1),
2147 UstarKind::SymbolicLink,
2148 ),
2149 (
2150 ArchiveFormat::Pax,
2151 header(b'3', 1),
2152 UstarKind::CharacterDevice,
2153 ),
2154 (
2155 ArchiveFormat::Gnu,
2156 gnu_header(b'3', 1),
2157 UstarKind::CharacterDevice,
2158 ),
2159 (ArchiveFormat::Pax, header(b'4', 1), UstarKind::BlockDevice),
2160 (
2161 ArchiveFormat::Gnu,
2162 gnu_header(b'4', 1),
2163 UstarKind::BlockDevice,
2164 ),
2165 (ArchiveFormat::Pax, header(b'5', 1), UstarKind::Directory),
2166 (
2167 ArchiveFormat::Gnu,
2168 gnu_header(b'5', 1),
2169 UstarKind::Directory,
2170 ),
2171 (ArchiveFormat::Pax, header(b'6', 1), UstarKind::Fifo),
2172 (ArchiveFormat::Gnu, gnu_header(b'6', 1), UstarKind::Fifo),
2173 ] {
2174 let frames = collect(block.to_vec(), BLOCK_SIZE);
2175 assert!(
2176 matches!(
2177 last_error_inner(&frames),
2178 FrameErrorInner::InvalidMemberSize {
2179 kind: found,
2180 size: 1,
2181 } if *found == kind
2182 ),
2183 "{format:?} {kind:?}"
2184 );
2185 }
2186 }
2187
2188 #[test]
2189 fn rejects_nonzero_declared_or_effective_pax_sizes_for_payload_free_members() {
2190 for (case, declared_size, override_size) in [("effective", 0, "1"), ("declared", 1, "0")] {
2191 for (typeflag, kind) in [
2192 (b'2', UstarKind::SymbolicLink),
2193 (b'3', UstarKind::CharacterDevice),
2194 (b'4', UstarKind::BlockDevice),
2195 (b'5', UstarKind::Directory),
2196 (b'6', UstarKind::Fifo),
2197 ] {
2198 let mut bytes = Vec::new();
2199 append_pax(&mut bytes, b'x', &record("size", override_size));
2200 append_block(&mut bytes, &header(typeflag, declared_size));
2201
2202 assert!(
2203 matches!(
2204 last_error_inner(&collect(bytes, BLOCK_SIZE)),
2205 FrameErrorInner::InvalidMemberSize {
2206 kind: found,
2207 size: 1,
2208 } if *found == kind
2209 ),
2210 "{case} {kind:?}"
2211 );
2212 }
2213 }
2214 }
2215
2216 #[test]
2217 fn header_errors_preserve_later_header_positions() {
2218 let position = BLOCK_SIZE as u64;
2219
2220 for (case, block, expected) in invalid_header_cases() {
2221 let mut bytes = Vec::new();
2222 append_block(&mut bytes, &header(b'0', 0));
2223 append_block(&mut bytes, &block);
2224 let frames = collect(bytes, BLOCK_SIZE);
2225 let error = last_error(&frames);
2226 assert_eq!(error.position, position, "{case}");
2227 assert!(expected.matches(&error.inner), "{case}: {error:?}");
2228 }
2229 }
2230
2231 #[test]
2232 fn rejects_invalid_pax_sequences() {
2233 assert!(matches!(
2234 last_error_inner(&collect(header(b'x', 0).to_vec(), BLOCK_SIZE)),
2235 FrameErrorInner::InvalidPaxRecord {
2236 source: PaxError::InvalidRecords { .. },
2237 }
2238 ));
2239
2240 let valid = record("path", "name");
2241 let mut consecutive = Vec::new();
2242 append_pax(&mut consecutive, b'x', &valid);
2243 append_block(&mut consecutive, &header(b'x', valid.len() as u64));
2244 assert!(matches!(
2245 last_error_inner(&collect(consecutive, BLOCK_SIZE)),
2246 FrameErrorInner::UnexpectedOrder { .. }
2247 ));
2248
2249 let mut missing_member = Vec::new();
2250 append_pax(&mut missing_member, b'x', &valid);
2251 assert!(matches!(
2252 last_error_inner(&collect(missing_member, BLOCK_SIZE)),
2253 FrameErrorInner::UnexpectedEof { .. }
2254 ));
2255 }
2256
2257 #[test]
2258 fn preserves_pax_parse_error_positions_in_stream() {
2259 let invalid = record("size", "bad");
2260 let mut bytes = Vec::new();
2261 append_block(&mut bytes, &header(b'0', 0));
2262 append_pax(&mut bytes, b'x', &invalid);
2263
2264 let frames = collect(bytes, BLOCK_SIZE);
2265 assert!(matches!(
2266 frames.last(),
2267 Some(Err(FrameError {
2268 position,
2269 inner: FrameErrorInner::InvalidPaxRecord {
2270 source: PaxError::InvalidInteger { .. },
2271 },
2272 })) if *position == BLOCK_SIZE as u64
2273 ));
2274 }
2275
2276 #[test]
2277 fn accepts_binary_and_rejects_unknown_pax_charsets() {
2278 let mut global = record("hdrcharset", "BINARY");
2279 global.extend_from_slice(&record("path", "global"));
2280 let local = record("path", "local");
2281 let mut bytes = Vec::new();
2282 append_pax(&mut bytes, b'g', &global);
2283 append_pax(&mut bytes, b'x', &local);
2284 append_block(&mut bytes, &header(b'0', 0));
2285 append_terminator(&mut bytes);
2286 let frames = collect(bytes, BLOCK_SIZE);
2287 let member_header = header_frame(&frames, 4);
2288 assert_eq!(member_header.kind, UstarKind::Regular);
2289 assert_eq!(
2290 data_frame(&frames, 1).completed_pax_records(),
2291 Some(
2292 [
2293 PaxRecord::HdrCharset(PaxValue::Value(HdrCharset::Binary)),
2294 PaxRecord::Path(PaxValue::Value(PaxString::Binary(
2295 b"global".to_vec().into(),
2296 ))),
2297 ]
2298 .as_slice()
2299 )
2300 );
2301 assert_eq!(
2302 data_frame(&frames, 3).completed_pax_records(),
2303 Some(
2304 [PaxRecord::Path(PaxValue::Value(PaxString::Binary(
2305 b"local".to_vec().into()
2306 )))]
2307 .as_slice()
2308 )
2309 );
2310
2311 let records = record("hdrcharset", "ISO-IR 8859 1 1998");
2312 let mut bytes = Vec::new();
2313 append_pax(&mut bytes, b'x', &records);
2314 assert!(matches!(
2315 last_error_inner(&collect(bytes, BLOCK_SIZE)),
2316 FrameErrorInner::InvalidPaxRecord {
2317 source: PaxError::UnsupportedCharset { value },
2318 } if value == "ISO-IR 8859 1 1998"
2319 ));
2320 }
2321
2322 #[test]
2323 fn rejects_invalid_gnu_sequences_and_sizes() {
2324 let mut duplicate = Vec::new();
2325 append_block(&mut duplicate, &gnu_header(b'L', 0));
2326 append_block(&mut duplicate, &gnu_header(b'L', 0));
2327 let mut long_link_for_regular = Vec::new();
2328 append_block(&mut long_link_for_regular, &gnu_header(b'K', 0));
2329 append_block(&mut long_link_for_regular, &gnu_header(b'0', 0));
2330 let mut dangling = Vec::new();
2331 append_block(&mut dangling, &gnu_header(b'L', 0));
2332 append_terminator(&mut dangling);
2333 for (case, bytes) in [
2334 ("duplicate", duplicate),
2335 ("long-link-for-regular", long_link_for_regular),
2336 ("dangling", dangling),
2337 ] {
2338 assert!(
2339 matches!(
2340 last_error_inner(&collect(bytes, BLOCK_SIZE)),
2341 FrameErrorInner::UnexpectedOrder { .. }
2342 ),
2343 "{case}"
2344 );
2345 }
2346
2347 assert!(matches!(
2348 last_error_inner(&collect(gnu_header(b'S', 0).to_vec(), BLOCK_SIZE)),
2349 FrameErrorInner::UnsupportedTypeflag { typeflag: b'S' }
2350 ));
2351
2352 let mut negative_size = gnu_header(b'0', 0);
2353 negative_size[SIZE_RANGE].fill(0xff);
2354 set_checksum(&mut negative_size);
2355 assert!(matches!(
2356 last_error_inner(&collect(negative_size.to_vec(), BLOCK_SIZE)),
2357 FrameErrorInner::InvalidSize { .. }
2358 ));
2359 }
2360
2361 #[test]
2362 fn detects_one_archive_family_and_rejects_mixing() {
2363 let mut posix_then_gnu = Vec::new();
2364 append_block(&mut posix_then_gnu, &header(b'0', 0));
2365 append_block(&mut posix_then_gnu, &gnu_header(b'0', 0));
2366 assert!(matches!(
2367 last_error_inner(&collect(posix_then_gnu, BLOCK_SIZE)),
2368 FrameErrorInner::FormatMismatch {
2369 expected: ArchiveFormat::Pax,
2370 found: ArchiveFormat::Gnu,
2371 }
2372 ));
2373
2374 let mut malformed_gnu = gnu_header(b'0', 0);
2376 malformed_gnu[0] = b'X';
2377 let mut posix_then_malformed_gnu = Vec::new();
2378 append_block(&mut posix_then_malformed_gnu, &header(b'0', 0));
2379 append_block(&mut posix_then_malformed_gnu, &malformed_gnu);
2380 assert!(matches!(
2381 last_error_inner(&collect(posix_then_malformed_gnu, BLOCK_SIZE)),
2382 FrameErrorInner::InvalidChecksum { .. }
2383 ));
2384
2385 let mut gnu_then_posix = Vec::new();
2386 append_block(&mut gnu_then_posix, &gnu_header(b'0', 0));
2387 append_block(&mut gnu_then_posix, &header(b'0', 0));
2388 assert!(matches!(
2389 last_error_inner(&collect(gnu_then_posix, BLOCK_SIZE)),
2390 FrameErrorInner::FormatMismatch {
2391 expected: ArchiveFormat::Gnu,
2392 found: ArchiveFormat::Pax,
2393 }
2394 ));
2395
2396 for typeflag in *b"xg" {
2397 assert!(
2398 matches!(
2399 last_error_inner(&collect(gnu_header(typeflag, 0).to_vec(), BLOCK_SIZE)),
2400 FrameErrorInner::UnsupportedTypeflag { typeflag: found } if *found == typeflag
2401 ),
2402 "{typeflag:?}"
2403 );
2404 }
2405
2406 let mut empty = Vec::new();
2407 append_terminator(&mut empty);
2408 let mut stream = TarStream::new(ChunkedReader::new(empty, BLOCK_SIZE));
2409 assert!(matches!(ready(stream.next_frame()), Ok(None)));
2410 assert_eq!(stream.format(), None);
2411 }
2412
2413 #[test]
2414 fn rejects_truncation_and_invalid_termination() {
2415 assert!(matches!(
2416 last_error_inner(&collect(vec![0; 3], 1)),
2417 FrameErrorInner::IncompleteBlock { read: 3 }
2418 ));
2419
2420 let mut payload_truncated = Vec::new();
2421 append_block(&mut payload_truncated, &header(b'0', 1));
2422 assert!(matches!(
2423 last_error_inner(&collect(payload_truncated, BLOCK_SIZE)),
2424 FrameErrorInner::TruncatedPayload {
2425 owner: DataOwner::Member,
2426 ..
2427 }
2428 ));
2429
2430 let mut pax_payload_truncated = Vec::new();
2431 append_block(&mut pax_payload_truncated, &header(b'x', 513));
2432 append_payload(&mut pax_payload_truncated, b"11 path=x\n");
2433 assert!(matches!(
2434 last_error_inner(&collect(pax_payload_truncated, BLOCK_SIZE)),
2435 FrameErrorInner::TruncatedPayload {
2436 owner: DataOwner::Pax(PaxKind::Local),
2437 ..
2438 }
2439 ));
2440
2441 let mut missing_second_zero = Vec::new();
2442 append_block(&mut missing_second_zero, &header(b'0', 0));
2443 append_block(&mut missing_second_zero, &[0; BLOCK_SIZE]);
2444 assert!(matches!(
2445 last_error_inner(&collect(missing_second_zero, BLOCK_SIZE)),
2446 FrameErrorInner::MissingEndMarker
2447 ));
2448
2449 let mut bad_second_zero = Vec::new();
2450 append_block(&mut bad_second_zero, &header(b'0', 0));
2451 append_block(&mut bad_second_zero, &[0; BLOCK_SIZE]);
2452 append_block(&mut bad_second_zero, &header(b'0', 0));
2453 assert!(matches!(
2454 last_error_inner(&collect(bad_second_zero, BLOCK_SIZE)),
2455 FrameErrorInner::InvalidEndMarker
2456 ));
2457 }
2458
2459 #[test]
2460 fn stream_is_fused_after_first_error() {
2461 let mut stream = TarStream::new(ChunkedReader::new(header(b'L', 0).to_vec(), BLOCK_SIZE));
2462 assert!(matches!(
2463 ready(stream.next_frame()),
2464 Err(FrameError {
2465 position: 0,
2466 inner: FrameErrorInner::UnsupportedTypeflag { typeflag: b'L' },
2467 })
2468 ));
2469 assert!(matches!(ready(stream.next_frame()), Ok(None)));
2470 }
2471}