1use crate::entry::Entry;
2use crate::error::annotate;
3use crate::header::Header;
4use crate::symbols::Symbols;
5use std::io::{Error, ErrorKind, Result, SeekFrom};
6use tokio::io::{
7 AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt,
8 BufReader,
9};
10
11pub(crate) const GLOBAL_HEADER_LEN: usize = 8;
12pub(crate) const GLOBAL_HEADER: &[u8; GLOBAL_HEADER_LEN] = b"!<arch>\n";
13
14pub(crate) const BSD_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"__.SYMDEF";
15pub(crate) const BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID: &[u8] =
16 b"__.SYMDEF SORTED";
17
18pub(crate) const GNU_NAME_TABLE_ID: &str = "//";
19pub(crate) const GNU_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"/";
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum Variant {
24 Common,
26 BSD,
28 GNU,
30}
31
32pub(crate) struct HeaderAndLocation {
33 header: Header,
34 header_start: u64,
35 data_start: u64,
36}
37
38pub struct Archive<R: AsyncRead + Unpin> {
40 reader: R,
41 variant: Variant,
42 name_table: Vec<u8>,
43 entry_headers: Vec<HeaderAndLocation>,
44 new_entry_start: u64,
45 next_entry_index: usize,
46 pub(crate) symbol_table_header: Option<HeaderAndLocation>,
47 pub(crate) symbol_table: Option<Vec<(Vec<u8>, u64)>>,
48 started: bool, padding: bool, scanned: bool, error: bool, unread_entry_data: u64, }
54
55impl<R: AsyncRead + Unpin> Archive<R> {
56 pub fn new(reader: R) -> Archive<R> {
59 Archive {
60 reader,
61 variant: Variant::Common,
62 name_table: Vec::new(),
63 entry_headers: Vec::new(),
64 new_entry_start: GLOBAL_HEADER_LEN as u64,
65 next_entry_index: 0,
66 symbol_table_header: None,
67 symbol_table: None,
68 started: false,
69 padding: false,
70 scanned: false,
71 error: false,
72 unread_entry_data: 0,
73 }
74 }
75
76 pub fn variant(&self) -> Variant {
83 self.variant
84 }
85
86 pub fn into_inner(self) -> Result<R> {
88 Ok(self.reader)
89 }
90
91 fn is_name_table_id(&self, identifier: &[u8]) -> bool {
92 self.variant == Variant::GNU
93 && identifier == GNU_NAME_TABLE_ID.as_bytes()
94 }
95
96 fn is_symbol_lookup_table_id(&self, identifier: &[u8]) -> bool {
97 match self.variant {
98 Variant::Common => false,
99 Variant::BSD => {
100 identifier == BSD_SYMBOL_LOOKUP_TABLE_ID
101 || identifier == BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID
102 }
103 Variant::GNU => identifier == GNU_SYMBOL_LOOKUP_TABLE_ID,
104 }
105 }
106
107 async fn read_global_header_if_necessary(&mut self) -> Result<()> {
108 if self.started {
109 return Ok(());
110 }
111 let mut buffer = [0; GLOBAL_HEADER_LEN];
112 match self.reader.read_exact(&mut buffer).await {
113 Ok(_) => {}
114 Err(error) => {
115 self.error = true;
116 return Err(annotate(error, "failed to read global header"));
117 }
118 }
119 if &buffer != GLOBAL_HEADER {
120 self.error = true;
121 let msg = "Not an archive file (invalid global header)";
122 return Err(Error::new(ErrorKind::InvalidData, msg));
123 }
124 self.started = true;
125 Ok(())
126 }
127
128 pub async fn next_entry(&mut self) -> Option<Result<Entry<'_, R>>> {
131 loop {
132 if self.error {
133 return None;
134 }
135 if self.scanned
136 && self.next_entry_index == self.entry_headers.len()
137 {
138 return None;
139 }
140 match self.read_global_header_if_necessary().await {
141 Ok(()) => {}
142 Err(error) => return Some(Err(error)),
143 }
144 if self.unread_entry_data > 0 {
146 match tokio::io::copy(
147 &mut (&mut self.reader).take(self.unread_entry_data),
148 &mut tokio::io::sink(),
149 )
150 .await
151 {
152 Ok(_) => self.unread_entry_data = 0,
153 Err(error) => {
154 self.error = true;
155 return Some(Err(annotate(
156 error,
157 "failed to skip unread entry data",
158 )));
159 }
160 }
161 }
162 if self.padding {
163 let mut buffer = [0u8; 1];
164 match self.reader.read_exact(&mut buffer).await {
165 Ok(_) => {
166 if buffer[0] != b'\n' {
167 self.error = true;
168 let msg = format!(
169 "invalid padding byte ({})",
170 buffer[0]
171 );
172 let error =
173 Error::new(ErrorKind::InvalidData, msg);
174 return Some(Err(error));
175 }
176 }
177 Err(error) => {
178 if error.kind() != ErrorKind::UnexpectedEof {
179 self.error = true;
180 let msg = "failed to read padding byte";
181 return Some(Err(annotate(error, msg)));
182 }
183 }
184 }
185 self.padding = false;
186 }
187 let header_start = self.new_entry_start;
188 match Header::read(
189 &mut self.reader,
190 &mut self.variant,
191 &mut self.name_table,
192 )
193 .await
194 {
195 Ok(Some((header, header_len))) => {
196 let size = header.size();
197 if size % 2 != 0 {
198 self.padding = true;
199 }
200 if self.next_entry_index == self.entry_headers.len() {
201 self.new_entry_start += header_len + size + (size % 2);
202 }
203 if self.is_name_table_id(header.identifier()) {
204 continue;
205 }
206 if self.is_symbol_lookup_table_id(header.identifier()) {
207 self.symbol_table_header = Some(HeaderAndLocation {
208 header,
209 header_start,
210 data_start: header_start + header_len,
211 });
212 continue;
213 }
214 if self.next_entry_index == self.entry_headers.len() {
215 self.entry_headers.push(HeaderAndLocation {
216 header,
217 header_start,
218 data_start: header_start + header_len,
219 });
220 }
221 let header =
222 &self.entry_headers[self.next_entry_index].header;
223 self.next_entry_index += 1;
224 self.unread_entry_data = size;
225 return Some(Ok(Entry {
226 header,
227 reader: &mut self.reader,
228 length: size,
229 position: 0,
230 unread_counter: &mut self.unread_entry_data,
231 }));
232 }
233 Ok(None) => {
234 self.scanned = true;
235 return None;
236 }
237 Err(error) => {
238 self.error = true;
239 return Some(Err(error));
240 }
241 }
242 }
243 }
244}
245
246impl<R: AsyncRead + AsyncSeek + Unpin> Archive<R> {
247 async fn scan_if_necessary(&mut self) -> Result<()> {
248 if self.scanned {
249 return Ok(());
250 }
251 self.read_global_header_if_necessary().await?;
252 loop {
253 let header_start = self.new_entry_start;
254 self.reader.seek(SeekFrom::Start(header_start)).await?;
255 if let Some((header, header_len)) = Header::read(
256 &mut self.reader,
257 &mut self.variant,
258 &mut self.name_table,
259 )
260 .await?
261 {
262 let size = header.size();
263 self.new_entry_start += header_len + size + (size % 2);
264 if self.is_name_table_id(header.identifier()) {
265 continue;
266 }
267 if self.is_symbol_lookup_table_id(header.identifier()) {
268 self.symbol_table_header = Some(HeaderAndLocation {
269 header,
270 header_start,
271 data_start: header_start + header_len,
272 });
273 continue;
274 }
275 self.entry_headers.push(HeaderAndLocation {
276 header,
277 header_start,
278 data_start: header_start + header_len,
279 });
280 } else {
281 break;
282 }
283 }
284 if self.next_entry_index < self.entry_headers.len() {
286 let offset =
287 self.entry_headers[self.next_entry_index].header_start;
288 self.reader.seek(SeekFrom::Start(offset)).await?;
289 }
290 self.scanned = true;
291 Ok(())
292 }
293
294 pub async fn count_entries(&mut self) -> Result<usize> {
298 self.scan_if_necessary().await?;
299 Ok(self.entry_headers.len())
300 }
301
302 pub async fn jump_to_entry(
305 &mut self,
306 index: usize,
307 ) -> Result<Entry<'_, R>> {
308 self.scan_if_necessary().await?;
309 if index >= self.entry_headers.len() {
310 let msg = "Entry index out of bounds";
311 return Err(Error::new(ErrorKind::InvalidInput, msg));
312 }
313 let offset = self.entry_headers[index].data_start;
314 self.reader.seek(SeekFrom::Start(offset)).await?;
315 let header = &self.entry_headers[index].header;
316 let size = header.size();
317 self.padding = !size.is_multiple_of(2);
318 self.next_entry_index = index + 1;
319 self.unread_entry_data = size;
320 Ok(Entry {
321 header,
322 reader: &mut self.reader,
323 length: size,
324 position: 0,
325 unread_counter: &mut self.unread_entry_data,
326 })
327 }
328
329 async fn parse_symbol_table_if_necessary(&mut self) -> Result<()> {
330 self.scan_if_necessary().await?;
331 if self.symbol_table.is_some() {
332 return Ok(());
333 }
334 if let Some(ref header_and_loc) = self.symbol_table_header {
335 let offset = header_and_loc.data_start;
336 self.reader.seek(SeekFrom::Start(offset)).await?;
337 let mut reader = BufReader::new(
338 (&mut self.reader).take(header_and_loc.header.size()),
339 );
340 if self.variant == Variant::GNU {
341 let num_symbols = read_be_u32(&mut reader).await? as usize;
342 let mut symbol_offsets =
343 Vec::<u32>::with_capacity(num_symbols);
344 for _ in 0..num_symbols {
345 let offset = read_be_u32(&mut reader).await?;
346 symbol_offsets.push(offset);
347 }
348 let mut symbol_table = Vec::with_capacity(num_symbols);
349 for offset in symbol_offsets.into_iter() {
350 let mut buffer = Vec::<u8>::new();
351 reader.read_until(0, &mut buffer).await?;
352 if buffer.last() == Some(&0) {
353 buffer.pop();
354 }
355 buffer.shrink_to_fit();
356 symbol_table.push((buffer, offset as u64));
357 }
358 self.symbol_table = Some(symbol_table);
359 } else {
360 let num_symbols =
361 (read_le_u32(&mut reader).await? / 8) as usize;
362 let mut symbol_offsets =
363 Vec::<(u32, u32)>::with_capacity(num_symbols);
364 for _ in 0..num_symbols {
365 let str_offset = read_le_u32(&mut reader).await?;
366 let file_offset = read_le_u32(&mut reader).await?;
367 symbol_offsets.push((str_offset, file_offset));
368 }
369 let str_table_len = read_le_u32(&mut reader).await?;
370 let mut str_table_data = vec![0u8; str_table_len as usize];
371 reader.read_exact(&mut str_table_data).await.map_err(
372 |err| annotate(err, "failed to read string table"),
373 )?;
374 let mut symbol_table = Vec::with_capacity(num_symbols);
375 for (str_start, file_offset) in symbol_offsets.into_iter() {
376 let str_start = str_start as usize;
377 let mut str_end = str_start;
378 while str_end < str_table_data.len()
379 && str_table_data[str_end] != 0u8
380 {
381 str_end += 1;
382 }
383 let string = &str_table_data[str_start..str_end];
384 symbol_table.push((string.to_vec(), file_offset as u64));
385 }
386 self.symbol_table = Some(symbol_table);
387 }
388 }
389 if !self.entry_headers.is_empty() {
391 let offset =
392 self.entry_headers[self.next_entry_index].header_start;
393 self.reader.seek(SeekFrom::Start(offset)).await?;
394 }
395 Ok(())
396 }
397
398 pub async fn symbols(&mut self) -> Result<Symbols<'_, R>> {
403 self.parse_symbol_table_if_necessary().await?;
404 Ok(Symbols { archive: self, index: 0 })
405 }
406}
407
408async fn read_le_u32(mut r: impl AsyncRead + Unpin) -> Result<u32> {
409 let mut buf = [0; 4];
410 r.read_exact(&mut buf).await?;
411 Ok(u32::from_le_bytes(buf))
412}
413
414async fn read_be_u32(mut r: impl AsyncRead + Unpin) -> Result<u32> {
415 let mut buf = [0; 4];
416 r.read_exact(&mut buf).await?;
417 Ok(u32::from_be_bytes(buf))
418}
419
420#[cfg(test)]
421mod tests {
422 use crate::{Archive, GnuBuilder, Header, Variant};
423 use std::io::{Cursor, SeekFrom};
424 use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt};
425
426 struct SlowReader<'a> {
427 current_position: usize,
428 buffer: &'a [u8],
429 }
430
431 impl AsyncRead for SlowReader<'_> {
432 fn poll_read(
433 self: std::pin::Pin<&mut Self>,
434 _cx: &mut std::task::Context<'_>,
435 buf: &mut tokio::io::ReadBuf<'_>,
436 ) -> std::task::Poll<std::io::Result<()>> {
437 let this = self.get_mut();
438 if this.current_position >= this.buffer.len() {
439 std::task::Poll::Ready(Ok(()))
440 } else {
441 buf.put_slice(&[this.buffer[this.current_position]]);
442 this.current_position += 1;
443 std::task::Poll::Ready(Ok(()))
444 }
445 }
446 }
447
448 #[tokio::test]
449 async fn read_common_archive() {
450 let input = "\
451 !<arch>\n\
452 foo.txt 1487552916 501 20 100644 7 `\n\
453 foobar\n\n\
454 bar.awesome.txt 1487552919 501 20 100644 22 `\n\
455 This file is awesome!\n\
456 baz.txt 1487552349 42 12345 100664 4 `\n\
457 baz\n";
458 let reader =
459 SlowReader { current_position: 0, buffer: input.as_bytes() };
460 let mut archive = Archive::new(reader);
461 {
462 let mut entry = archive.next_entry().await.unwrap().unwrap();
464 assert_eq!(entry.header().identifier(), b"foo.txt");
465 assert_eq!(entry.header().mtime(), 1487552916);
466 assert_eq!(entry.header().uid(), 501);
467 assert_eq!(entry.header().gid(), 20);
468 assert_eq!(entry.header().mode(), 0o100644);
469 assert_eq!(entry.header().size(), 7);
470 let mut buffer = [0; 4];
473 entry.read_exact(&mut buffer).await.unwrap();
474 assert_eq!(&buffer, "foob".as_bytes());
475 }
479 {
480 let mut entry = archive.next_entry().await.unwrap().unwrap();
482 assert_eq!(entry.header().identifier(), b"bar.awesome.txt");
483 assert_eq!(entry.header().size(), 22);
484 let mut buffer = Vec::new();
486 entry.read_to_end(&mut buffer).await.unwrap();
487 assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
488 }
489 {
490 let entry = archive.next_entry().await.unwrap().unwrap();
492 assert_eq!(entry.header().identifier(), b"baz.txt");
493 assert_eq!(entry.header().size(), 4);
494 }
495 assert!(archive.next_entry().await.is_none());
496 assert_eq!(archive.variant(), Variant::Common);
497 }
498
499 #[tokio::test]
500 async fn read_bsd_archive_with_long_filenames() {
501 let input = "\
502 !<arch>\n\
503 #1/32 1487552916 501 20 100644 39 `\n\
504 this_is_a_very_long_filename.txtfoobar\n\n\
505 #1/44 0 0 0 0 48 `\n\
506 and_this_is_another_very_long_filename.txt\x00\x00baz\n";
507 let mut archive = Archive::new(input.as_bytes());
508 {
509 let mut entry = archive.next_entry().await.unwrap().unwrap();
511 assert_eq!(
512 entry.header().identifier(),
513 "this_is_a_very_long_filename.txt".as_bytes()
514 );
515 assert_eq!(entry.header().mtime(), 1487552916);
516 assert_eq!(entry.header().uid(), 501);
517 assert_eq!(entry.header().gid(), 20);
518 assert_eq!(entry.header().mode(), 0o100644);
519 assert_eq!(entry.header().size(), 7);
523 let mut buffer = Vec::new();
526 entry.read_to_end(&mut buffer).await.unwrap();
527 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
528 }
529 {
530 let mut entry = archive.next_entry().await.unwrap().unwrap();
532 assert_eq!(
533 entry.header().identifier(),
534 "and_this_is_another_very_long_filename.txt".as_bytes()
535 );
536 assert_eq!(entry.header().size(), 4);
537 let mut buffer = Vec::new();
540 entry.read_to_end(&mut buffer).await.unwrap();
541 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
542 }
543 assert!(archive.next_entry().await.is_none());
544 assert_eq!(archive.variant(), Variant::BSD);
545 }
546
547 #[tokio::test]
548 async fn read_bsd_archive_with_space_in_filename() {
549 let input = "\
550 !<arch>\n\
551 #1/8 0 0 0 0 12 `\n\
552 foo bar\x00baz\n";
553 let mut archive = Archive::new(input.as_bytes());
554 {
555 let mut entry = archive.next_entry().await.unwrap().unwrap();
556 assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
557 assert_eq!(entry.header().size(), 4);
558 let mut buffer = Vec::new();
559 entry.read_to_end(&mut buffer).await.unwrap();
560 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
561 }
562 assert!(archive.next_entry().await.is_none());
563 assert_eq!(archive.variant(), Variant::BSD);
564 }
565
566 #[tokio::test]
567 async fn read_gnu_archive() {
568 let input = "\
569 !<arch>\n\
570 foo.txt/ 1487552916 501 20 100644 7 `\n\
571 foobar\n\n\
572 bar.awesome.txt/1487552919 501 20 100644 22 `\n\
573 This file is awesome!\n\
574 baz.txt/ 1487552349 42 12345 100664 4 `\n\
575 baz\n";
576 let mut archive = Archive::new(input.as_bytes());
577 {
578 let entry = archive.next_entry().await.unwrap().unwrap();
579 assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
580 assert_eq!(entry.header().size(), 7);
581 }
582 {
583 let entry = archive.next_entry().await.unwrap().unwrap();
584 assert_eq!(
585 entry.header().identifier(),
586 "bar.awesome.txt".as_bytes()
587 );
588 assert_eq!(entry.header().size(), 22);
589 }
590 {
591 let entry = archive.next_entry().await.unwrap().unwrap();
592 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
593 assert_eq!(entry.header().size(), 4);
594 }
595 assert!(archive.next_entry().await.is_none());
596 assert_eq!(archive.variant(), Variant::GNU);
597 }
598
599 #[tokio::test]
600 async fn read_gnu_archive_with_long_filenames() {
601 let input = "\
602 !<arch>\n\
603 // 78 `\n\
604 this_is_a_very_long_filename.txt/\n\
605 and_this_is_another_very_long_filename.txt/\n\
606 /0 1487552916 501 20 100644 7 `\n\
607 foobar\n\n\
608 /34 0 0 0 0 4 `\n\
609 baz\n";
610 let mut archive = Archive::new(input.as_bytes());
611 {
612 let mut entry = archive.next_entry().await.unwrap().unwrap();
613 assert_eq!(
614 entry.header().identifier(),
615 "this_is_a_very_long_filename.txt".as_bytes()
616 );
617 assert_eq!(entry.header().mtime(), 1487552916);
618 assert_eq!(entry.header().uid(), 501);
619 assert_eq!(entry.header().gid(), 20);
620 assert_eq!(entry.header().mode(), 0o100644);
621 assert_eq!(entry.header().size(), 7);
622 let mut buffer = Vec::new();
623 entry.read_to_end(&mut buffer).await.unwrap();
624 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
625 }
626 {
627 let mut entry = archive.next_entry().await.unwrap().unwrap();
628 assert_eq!(
629 entry.header().identifier(),
630 "and_this_is_another_very_long_filename.txt".as_bytes()
631 );
632 assert_eq!(entry.header().size(), 4);
633 let mut buffer = Vec::new();
634 entry.read_to_end(&mut buffer).await.unwrap();
635 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
636 }
637 assert!(archive.next_entry().await.is_none());
638 assert_eq!(archive.variant(), Variant::GNU);
639 }
640
641 #[tokio::test]
646 async fn read_ms_archive_with_long_filenames() {
647 let input = "\
648 !<arch>\n\
649 // 76 `\n\
650 this_is_a_very_long_filename.txt\x00\
651 and_this_is_another_very_long_filename.txt\x00\
652 /0 1487552916 100644 7 `\n\
653 foobar\n\n\
654 /33 1446790218 100666 4 `\n\
655 baz\n";
656 let mut archive = Archive::new(input.as_bytes());
657 {
658 let mut entry = archive.next_entry().await.unwrap().unwrap();
659 assert_eq!(
660 entry.header().identifier(),
661 "this_is_a_very_long_filename.txt".as_bytes()
662 );
663 assert_eq!(entry.header().mtime(), 1487552916);
664 assert_eq!(entry.header().uid(), 0);
665 assert_eq!(entry.header().gid(), 0);
666 assert_eq!(entry.header().mode(), 0o100644);
667 assert_eq!(entry.header().size(), 7);
668 let mut buffer = Vec::new();
669 entry.read_to_end(&mut buffer).await.unwrap();
670 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
671 }
672 {
673 let mut entry = archive.next_entry().await.unwrap().unwrap();
674 assert_eq!(
675 entry.header().identifier(),
676 "and_this_is_another_very_long_filename.txt".as_bytes()
677 );
678 assert_eq!(entry.header().size(), 4);
679 let mut buffer = Vec::new();
680 entry.read_to_end(&mut buffer).await.unwrap();
681 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
682 }
683 assert!(archive.next_entry().await.is_none());
684 assert_eq!(archive.variant(), Variant::GNU);
685 }
686
687 #[tokio::test]
688 async fn read_gnu_archive_with_space_in_filename() {
689 let input = "\
690 !<arch>\n\
691 foo bar/ 0 0 0 0 4 `\n\
692 baz\n";
693 let mut archive = Archive::new(input.as_bytes());
694 {
695 let mut entry = archive.next_entry().await.unwrap().unwrap();
696 assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
697 assert_eq!(entry.header().size(), 4);
698 let mut buffer = Vec::new();
699 entry.read_to_end(&mut buffer).await.unwrap();
700 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
701 }
702 assert!(archive.next_entry().await.is_none());
703 assert_eq!(archive.variant(), Variant::GNU);
704 }
705
706 #[tokio::test]
707 async fn read_gnu_archive_with_symbol_lookup_table() {
708 let input = b"\
709 !<arch>\n\
710 / 0 0 0 0 15 `\n\
711 \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
712 // 34 `\n\
713 this_is_a_very_long_filename.txt/\n\
714 /0 1487552916 501 20 100644 7 `\n\
715 foobar\n";
716 let mut archive = Archive::new(input as &[u8]);
717 {
718 let mut entry = archive.next_entry().await.unwrap().unwrap();
719 assert_eq!(
720 entry.header().identifier(),
721 "this_is_a_very_long_filename.txt".as_bytes()
722 );
723 let mut buffer = Vec::new();
724 entry.read_to_end(&mut buffer).await.unwrap();
725 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
726 }
727 assert!(archive.next_entry().await.is_none());
728 }
729
730 #[tokio::test]
731 async fn read_archive_with_no_padding_byte_in_final_entry() {
732 let input = "\
733 !<arch>\n\
734 foo.txt 1487552916 501 20 100644 7 `\n\
735 foobar\n\n\
736 bar.txt 1487552919 501 20 100644 3 `\n\
737 foo";
738 let mut archive = Archive::new(input.as_bytes());
739 {
740 let entry = archive.next_entry().await.unwrap().unwrap();
741 assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
742 assert_eq!(entry.header().size(), 7);
743 }
744 {
745 let entry = archive.next_entry().await.unwrap().unwrap();
746 assert_eq!(entry.header().identifier(), "bar.txt".as_bytes());
747 assert_eq!(entry.header().size(), 3);
748 }
749 assert!(archive.next_entry().await.is_none());
750 }
751
752 #[tokio::test]
753 #[should_panic(expected = "Invalid timestamp field in entry header \
754 (\\\"helloworld \\\")")]
755 async fn read_archive_with_invalid_mtime() {
756 let input = "\
757 !<arch>\n\
758 foo.txt helloworld 501 20 100644 7 `\n\
759 foobar\n\n";
760 let mut archive = Archive::new(input.as_bytes());
761 archive.next_entry().await.unwrap().unwrap();
762 }
763
764 #[tokio::test]
765 async fn read_archive_with_mtime_minus_one() {
766 let input = "\
767 !<arch>\n\
768 foo.txt -1 501 20 100644 7 `\n\
769 foobar\n\n";
770 let mut archive = Archive::new(input.as_bytes());
771 archive.next_entry().await.unwrap().unwrap();
772 }
773
774 #[tokio::test]
775 #[should_panic(expected = "Invalid owner ID field in entry header \
776 (\\\"foo \\\")")]
777 async fn read_archive_with_invalid_uid() {
778 let input = "\
779 !<arch>\n\
780 foo.txt 1487552916 foo 20 100644 7 `\n\
781 foobar\n\n";
782 let mut archive = Archive::new(input.as_bytes());
783 archive.next_entry().await.unwrap().unwrap();
784 }
785
786 #[tokio::test]
787 #[should_panic(expected = "Invalid group ID field in entry header \
788 (\\\"bar \\\")")]
789 async fn read_archive_with_invalid_gid() {
790 let input = "\
791 !<arch>\n\
792 foo.txt 1487552916 501 bar 100644 7 `\n\
793 foobar\n\n";
794 let mut archive = Archive::new(input.as_bytes());
795 archive.next_entry().await.unwrap().unwrap();
796 }
797
798 #[tokio::test]
799 #[should_panic(expected = "Invalid file mode field in entry header \
800 (\\\"foobar \\\")")]
801 async fn read_archive_with_invalid_mode() {
802 let input = "\
803 !<arch>\n\
804 foo.txt 1487552916 501 20 foobar 7 `\n\
805 foobar\n\n";
806 let mut archive = Archive::new(input.as_bytes());
807 archive.next_entry().await.unwrap().unwrap();
808 }
809
810 #[tokio::test]
811 #[should_panic(expected = "Invalid file size field in entry header \
812 (\\\"whatever \\\")")]
813 async fn read_archive_with_invalid_size() {
814 let input = "\
815 !<arch>\n\
816 foo.txt 1487552916 501 20 100644 whatever `\n\
817 foobar\n\n";
818 let mut archive = Archive::new(input.as_bytes());
819 archive.next_entry().await.unwrap().unwrap();
820 }
821
822 #[tokio::test]
823 #[should_panic(expected = "Invalid BSD filename length field in entry \
824 header (\\\"foobar \\\")")]
825 async fn read_bsd_archive_with_invalid_filename_length() {
826 let input = "\
827 !<arch>\n\
828 #1/foobar 1487552916 501 20 100644 39 `\n\
829 this_is_a_very_long_filename.txtfoobar\n\n";
830 let mut archive = Archive::new(input.as_bytes());
831 archive.next_entry().await.unwrap().unwrap();
832 }
833
834 #[tokio::test]
835 #[should_panic(expected = "Invalid GNU filename index field in entry \
836 header (\\\"foobar \\\")")]
837 async fn read_gnu_archive_with_invalid_filename_index() {
838 let input = "\
839 !<arch>\n\
840 // 34 `\n\
841 this_is_a_very_long_filename.txt/\n\
842 /foobar 1487552916 501 20 100644 7 `\n\
843 foobar\n\n";
844 let mut archive = Archive::new(input.as_bytes());
845 archive.next_entry().await.unwrap().unwrap();
846 }
847
848 #[tokio::test]
849 async fn seek_within_entry() {
850 let input = "\
851 !<arch>\n\
852 foo.txt 1487552916 501 20 100644 31 `\n\
853 abcdefghij0123456789ABCDEFGHIJ\n\n\
854 bar.awesome.txt 1487552919 501 20 100644 22 `\n\
855 This file is awesome!\n";
856 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
857 {
858 let mut entry = archive.next_entry().await.unwrap().unwrap();
861 let mut buffer = [0; 5];
862 entry.seek(SeekFrom::Start(10)).await.unwrap();
863 entry.read_exact(&mut buffer).await.unwrap();
864 assert_eq!(&buffer, "01234".as_bytes());
865 entry.seek(SeekFrom::Start(5)).await.unwrap();
866 entry.read_exact(&mut buffer).await.unwrap();
867 assert_eq!(&buffer, "fghij".as_bytes());
868 entry.seek(SeekFrom::End(-10)).await.unwrap();
869 entry.read_exact(&mut buffer).await.unwrap();
870 assert_eq!(&buffer, "BCDEF".as_bytes());
871 entry.seek(SeekFrom::End(-30)).await.unwrap();
872 entry.read_exact(&mut buffer).await.unwrap();
873 assert_eq!(&buffer, "bcdef".as_bytes());
874 entry.seek(SeekFrom::Current(10)).await.unwrap();
875 entry.read_exact(&mut buffer).await.unwrap();
876 assert_eq!(&buffer, "6789A".as_bytes());
877 entry.seek(SeekFrom::Current(-8)).await.unwrap();
878 entry.read_exact(&mut buffer).await.unwrap();
879 assert_eq!(&buffer, "34567".as_bytes());
880 }
884 {
885 let mut entry = archive.next_entry().await.unwrap().unwrap();
887 let mut buffer = Vec::new();
888 entry.read_to_end(&mut buffer).await.unwrap();
889 assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
890 }
891 }
892
893 #[tokio::test]
894 #[should_panic(expected = "Invalid seek to negative position (-17)")]
895 async fn seek_entry_to_negative_position() {
896 let input = "\
897 !<arch>\n\
898 foo.txt 1487552916 501 20 100644 30 `\n\
899 abcdefghij0123456789ABCDEFGHIJ";
900 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
901 let mut entry = archive.next_entry().await.unwrap().unwrap();
902 entry.seek(SeekFrom::End(-47)).await.unwrap();
903 }
904
905 #[tokio::test]
906 #[should_panic(expected = "Invalid seek to position past end of entry \
907 (47 vs. 30)")]
908 async fn seek_entry_beyond_end() {
909 let input = "\
910 !<arch>\n\
911 foo.txt 1487552916 501 20 100644 30 `\n\
912 abcdefghij0123456789ABCDEFGHIJ";
913 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
914 let mut entry = archive.next_entry().await.unwrap().unwrap();
915 entry.seek(SeekFrom::Start(47)).await.unwrap();
916 }
917
918 #[tokio::test]
919 async fn count_entries_in_bsd_archive() {
920 let input = b"\
921 !<arch>\n\
922 #1/32 1487552916 501 20 100644 39 `\n\
923 this_is_a_very_long_filename.txtfoobar\n\n\
924 baz.txt 0 0 0 0 4 `\n\
925 baz\n";
926 let mut archive = Archive::new(Cursor::new(input as &[u8]));
927 assert_eq!(archive.count_entries().await.unwrap(), 2);
928 {
929 let mut entry = archive.next_entry().await.unwrap().unwrap();
930 assert_eq!(
931 entry.header().identifier(),
932 "this_is_a_very_long_filename.txt".as_bytes()
933 );
934 let mut buffer = Vec::new();
935 entry.read_to_end(&mut buffer).await.unwrap();
936 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
937 }
938 assert_eq!(archive.count_entries().await.unwrap(), 2);
939 {
940 let mut entry = archive.next_entry().await.unwrap().unwrap();
941 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
942 let mut buffer = Vec::new();
943 entry.read_to_end(&mut buffer).await.unwrap();
944 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
945 }
946 assert_eq!(archive.count_entries().await.unwrap(), 2);
947 }
948
949 #[tokio::test]
950 async fn count_entries_in_gnu_archive() {
951 let input = b"\
952 !<arch>\n\
953 / 0 0 0 0 15 `\n\
954 \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
955 // 34 `\n\
956 this_is_a_very_long_filename.txt/\n\
957 /0 1487552916 501 20 100644 7 `\n\
958 foobar\n\n\
959 baz.txt/ 1487552349 42 12345 100664 4 `\n\
960 baz\n";
961 let mut archive = Archive::new(Cursor::new(input as &[u8]));
962 assert_eq!(archive.count_entries().await.unwrap(), 2);
963 {
964 let mut entry = archive.next_entry().await.unwrap().unwrap();
965 assert_eq!(
966 entry.header().identifier(),
967 "this_is_a_very_long_filename.txt".as_bytes()
968 );
969 let mut buffer = Vec::new();
970 entry.read_to_end(&mut buffer).await.unwrap();
971 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
972 }
973 assert_eq!(archive.count_entries().await.unwrap(), 2);
974 {
975 let mut entry = archive.next_entry().await.unwrap().unwrap();
976 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
977 let mut buffer = Vec::new();
978 entry.read_to_end(&mut buffer).await.unwrap();
979 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
980 }
981 assert_eq!(archive.count_entries().await.unwrap(), 2);
982 }
983
984 #[tokio::test]
985 async fn jump_to_entry_in_bsd_archive() {
986 let input = b"\
987 !<arch>\n\
988 hello.txt 1487552316 42 12345 100644 14 `\n\
989 Hello, world!\n\
990 #1/32 1487552916 501 20 100644 39 `\n\
991 this_is_a_very_long_filename.txtfoobar\n\n\
992 baz.txt 1487552349 42 12345 100664 4 `\n\
993 baz\n";
994 let mut archive = Archive::new(Cursor::new(input as &[u8]));
995 {
996 let mut entry = archive.jump_to_entry(1).await.unwrap();
998 assert_eq!(
999 entry.header().identifier(),
1000 "this_is_a_very_long_filename.txt".as_bytes()
1001 );
1002 let mut buffer = Vec::new();
1003 entry.read_to_end(&mut buffer).await.unwrap();
1004 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1005 }
1006 {
1007 let mut entry = archive.next_entry().await.unwrap().unwrap();
1009 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1010 let mut buffer = Vec::new();
1011 entry.read_to_end(&mut buffer).await.unwrap();
1012 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1013 }
1014 assert!(archive.next_entry().await.is_none());
1016 {
1017 let mut entry = archive.jump_to_entry(0).await.unwrap();
1019 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1020 let mut buffer = Vec::new();
1021 entry.read_to_end(&mut buffer).await.unwrap();
1022 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1023 }
1024 {
1025 let mut entry = archive.jump_to_entry(1).await.unwrap();
1027 assert_eq!(
1028 entry.header().identifier(),
1029 "this_is_a_very_long_filename.txt".as_bytes()
1030 );
1031 let mut buffer = Vec::new();
1032 entry.read_to_end(&mut buffer).await.unwrap();
1033 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1034 }
1035 {
1036 let mut entry = archive.jump_to_entry(0).await.unwrap();
1038 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1039 let mut buffer = Vec::new();
1040 entry.read_to_end(&mut buffer).await.unwrap();
1041 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1042 }
1043 {
1044 let mut entry = archive.next_entry().await.unwrap().unwrap();
1046 assert_eq!(
1047 entry.header().identifier(),
1048 "this_is_a_very_long_filename.txt".as_bytes()
1049 );
1050 let mut buffer = Vec::new();
1051 entry.read_to_end(&mut buffer).await.unwrap();
1052 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1053 }
1054 }
1055
1056 #[tokio::test]
1057 async fn jump_to_entry_in_gnu_archive() {
1058 let input = b"\
1059 !<arch>\n\
1060 // 34 `\n\
1061 this_is_a_very_long_filename.txt/\n\
1062 hello.txt/ 1487552316 42 12345 100644 14 `\n\
1063 Hello, world!\n\
1064 /0 1487552916 501 20 100644 7 `\n\
1065 foobar\n\n\
1066 baz.txt/ 1487552349 42 12345 100664 4 `\n\
1067 baz\n";
1068 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1069 {
1070 let mut entry = archive.jump_to_entry(1).await.unwrap();
1072 assert_eq!(
1073 entry.header().identifier(),
1074 "this_is_a_very_long_filename.txt".as_bytes()
1075 );
1076 let mut buffer = Vec::new();
1077 entry.read_to_end(&mut buffer).await.unwrap();
1078 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1079 }
1080 {
1081 let mut entry = archive.next_entry().await.unwrap().unwrap();
1083 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1084 let mut buffer = Vec::new();
1085 entry.read_to_end(&mut buffer).await.unwrap();
1086 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1087 }
1088 assert!(archive.next_entry().await.is_none());
1090 {
1091 let mut entry = archive.jump_to_entry(0).await.unwrap();
1093 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1094 let mut buffer = Vec::new();
1095 entry.read_to_end(&mut buffer).await.unwrap();
1096 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1097 }
1098 {
1099 let mut entry = archive.next_entry().await.unwrap().unwrap();
1101 assert_eq!(
1102 entry.header().identifier(),
1103 "this_is_a_very_long_filename.txt".as_bytes()
1104 );
1105 let mut buffer = Vec::new();
1106 entry.read_to_end(&mut buffer).await.unwrap();
1107 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1108 }
1109 }
1110
1111 #[tokio::test]
1112 async fn list_symbols_in_bsd_archive() {
1113 let input = b"\
1114 !<arch>\n\
1115 #1/12 0 0 0 0 60 `\n\
1116 __.SYMDEF\x00\x00\x00\x18\x00\x00\x00\
1117 \x00\x00\x00\x00\x80\x00\x00\x00\
1118 \x07\x00\x00\x00\x80\x00\x00\x00\
1119 \x0b\x00\x00\x00\x80\x00\x00\x00\
1120 \x10\x00\x00\x00foobar\x00baz\x00quux\x00\
1121 foo.o/ 1487552916 501 20 100644 16 `\n\
1122 foobar,baz,quux\n";
1123 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1124 assert_eq!(archive.symbols().await.unwrap().len(), 3);
1125 assert_eq!(archive.variant(), Variant::BSD);
1126 let symbols = archive.symbols().await.unwrap().collect::<Vec<&[u8]>>();
1127 let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
1128 assert_eq!(symbols, expected);
1129 }
1130
1131 #[tokio::test]
1132 async fn list_sorted_symbols_in_bsd_archive() {
1133 let input = b"\
1134 !<arch>\n\
1135 #1/16 0 0 0 0 64 `\n\
1136 __.SYMDEF SORTED\x18\x00\x00\x00\
1137 \x00\x00\x00\x00\x80\x00\x00\x00\
1138 \x04\x00\x00\x00\x80\x00\x00\x00\
1139 \x0b\x00\x00\x00\x80\x00\x00\x00\
1140 \x10\x00\x00\x00baz\x00foobar\x00quux\x00\
1141 foo.o/ 1487552916 501 20 100644 16 `\n\
1142 foobar,baz,quux\n";
1143 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1144 assert_eq!(archive.symbols().await.unwrap().len(), 3);
1145 assert_eq!(archive.variant(), Variant::BSD);
1146 let symbols = archive.symbols().await.unwrap().collect::<Vec<&[u8]>>();
1147 let expected: Vec<&[u8]> = vec![b"baz", b"foobar", b"quux"];
1148 assert_eq!(symbols, expected);
1149 }
1150
1151 #[tokio::test]
1152 async fn list_symbols_in_gnu_archive() {
1153 let input = b"\
1154 !<arch>\n\
1155 / 0 0 0 0 32 `\n\
1156 \x00\x00\x00\x03\x00\x00\x00\x5c\x00\x00\x00\x5c\x00\x00\x00\x5c\
1157 foobar\x00baz\x00quux\x00\
1158 foo.o/ 1487552916 501 20 100644 16 `\n\
1159 foobar,baz,quux\n";
1160 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1161 assert_eq!(archive.symbols().await.unwrap().len(), 3);
1162 assert_eq!(archive.variant(), Variant::GNU);
1163 let symbols = archive.symbols().await.unwrap().collect::<Vec<&[u8]>>();
1164 let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
1165 assert_eq!(symbols, expected);
1166 }
1167
1168 #[tokio::test]
1169 async fn non_multiple_of_two_long_ident_in_gnu_archive() {
1170 let mut buffer = std::io::Cursor::new(Vec::new());
1171
1172 {
1173 let filenames = vec![
1174 b"rust.metadata.bin".to_vec(),
1175 b"compiler_builtins-78891cf83a7d3547.dummy_name.rcgu.o"
1176 .to_vec(),
1177 ];
1178 let mut builder = GnuBuilder::new(&mut buffer, filenames.clone());
1179
1180 for filename in filenames {
1181 builder
1182 .append(&Header::new(filename, 1), &b"?"[..])
1183 .await
1184 .expect("add file");
1185 }
1186 }
1187
1188 buffer.set_position(0);
1189
1190 let mut archive = Archive::new(buffer);
1191 while let Some(entry) = archive.next_entry().await {
1192 entry.unwrap();
1193 }
1194 }
1195
1196 #[tokio::test]
1198 #[should_panic(expected = "GNU filename index out of range")]
1199 async fn issue_22() {
1200 let data = &[
1201 33, 60, 97, 114, 99, 104, 62, 10, 99, 104, 60, 159, 149, 33, 62,
1202 10, 219, 87, 219, 219, 219, 96, 48, 48, 48, 48, 48, 48, 48, 48,
1203 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
1204 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
1205 48, 48, 48, 48, 48, 48, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48,
1206 48, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 39, 48, 48, 48,
1207 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
1208 48, 48, 48, 48, 51, 49, 50, 56, 48, 48, 54, 54, 54, 51, 52, 56,
1209 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
1210 48, 48, 48, 48, 48, 48, 48, 48, 47, 48, 0, 40,
1211 ];
1212 let mut archive = Archive::new(std::io::Cursor::new(data));
1213 let _num_entries = archive.count_entries().await.unwrap();
1214 }
1215
1216 #[tokio::test]
1218 async fn read_archive_with_radix_prefixed_mode() {
1219 let input = "\
1220 !<arch>\n\
1221 foo.txt/ 1487552916 501 20 0o1006447 `\n\
1222 foobar\n";
1223 let mut archive = Archive::new(input.as_bytes());
1224 {
1225 let entry = archive.next_entry().await.unwrap().unwrap();
1226 assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
1227 assert_eq!(entry.header().mode(), 0o100644);
1228 assert_eq!(entry.header().size(), 7);
1229 }
1230 assert!(archive.next_entry().await.is_none());
1231 }
1232}