1#![warn(missing_docs)]
68
69use std::cmp;
70use std::collections::{HashMap, HashSet};
71use std::ffi::OsStr;
72use std::fs::{File, Metadata};
73use std::io::{
74 self, BufRead, BufReader, Error, ErrorKind, Read, Result, Seek, SeekFrom,
75 Write,
76};
77use std::path::Path;
78use std::str;
79
80#[cfg(unix)]
81use std::os::unix::fs::MetadataExt;
82
83#[cfg(unix)]
84use std::os::unix::ffi::OsStrExt;
85
86fn read_le_u32(r: &mut impl io::Read) -> io::Result<u32> {
89 let mut buf = [0; 4];
90 r.read_exact(&mut buf).map(|()| u32::from_le_bytes(buf))
91}
92
93fn read_be_u32(r: &mut impl io::Read) -> io::Result<u32> {
94 let mut buf = [0; 4];
95 r.read_exact(&mut buf).map(|()| u32::from_be_bytes(buf))
96}
97
98const GLOBAL_HEADER_LEN: usize = 8;
101const GLOBAL_HEADER: &'static [u8; GLOBAL_HEADER_LEN] = b"!<arch>\n";
102
103const ENTRY_HEADER_LEN: usize = 60;
104
105const BSD_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"__.SYMDEF";
106const BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"__.SYMDEF SORTED";
107
108const GNU_NAME_TABLE_ID: &str = "//";
109const GNU_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"/";
110
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
115pub enum Variant {
116 Common,
118 BSD,
120 GNU,
122}
123
124#[derive(Clone, Debug, Eq, PartialEq)]
128pub struct Header {
129 identifier: Vec<u8>,
130 mtime: u64,
131 uid: u32,
132 gid: u32,
133 mode: u32,
134 size: u64,
135}
136
137impl Header {
138 pub fn new(identifier: Vec<u8>, size: u64) -> Header {
141 Header { identifier, mtime: 0, uid: 0, gid: 0, mode: 0, size }
142 }
143
144 #[cfg(unix)]
147 pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header {
148 Header {
149 identifier,
150 mtime: meta.mtime() as u64,
151 uid: meta.uid(),
152 gid: meta.gid(),
153 mode: meta.mode(),
154 size: meta.len(),
155 }
156 }
157
158 #[cfg(not(unix))]
159 pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header {
160 Header::new(identifier, meta.len())
161 }
162
163 pub fn identifier(&self) -> &[u8] {
165 &self.identifier
166 }
167
168 pub fn set_identifier(&mut self, identifier: Vec<u8>) {
170 self.identifier = identifier;
171 }
172
173 pub fn mtime(&self) -> u64 {
175 self.mtime
176 }
177
178 pub fn set_mtime(&mut self, mtime: u64) {
180 self.mtime = mtime;
181 }
182
183 pub fn uid(&self) -> u32 {
185 self.uid
186 }
187
188 pub fn set_uid(&mut self, uid: u32) {
190 self.uid = uid;
191 }
192
193 pub fn gid(&self) -> u32 {
195 self.gid
196 }
197
198 pub fn set_gid(&mut self, gid: u32) {
200 self.gid = gid;
201 }
202
203 pub fn mode(&self) -> u32 {
205 self.mode
206 }
207
208 pub fn set_mode(&mut self, mode: u32) {
210 self.mode = mode;
211 }
212
213 pub fn size(&self) -> u64 {
215 self.size
216 }
217
218 pub fn set_size(&mut self, size: u64) {
220 self.size = size;
221 }
222
223 fn read<R>(
226 reader: &mut R,
227 variant: &mut Variant,
228 name_table: &mut Vec<u8>,
229 ) -> Result<Option<(Header, u64)>>
230 where
231 R: Read,
232 {
233 let mut buffer = [0; 60];
234 let bytes_read = reader.read(&mut buffer)?;
235 if bytes_read == 0 {
236 return Ok(None);
237 } else if bytes_read < buffer.len() {
238 if let Err(error) = reader.read_exact(&mut buffer[bytes_read..]) {
239 if error.kind() == ErrorKind::UnexpectedEof {
240 let msg = "unexpected EOF in the middle of archive entry \
241 header";
242 return Err(Error::new(ErrorKind::UnexpectedEof, msg));
243 } else {
244 let msg = "failed to read archive entry header";
245 return Err(annotate(error, msg));
246 }
247 }
248 }
249 let mut identifier = buffer[0..16].to_vec();
250 while identifier.last() == Some(&b' ') {
251 identifier.pop();
252 }
253 let mut size = parse_number("file size", &buffer[48..58], 10)?;
254 let mut header_len = ENTRY_HEADER_LEN as u64;
255 if *variant != Variant::BSD && identifier.starts_with(b"/") {
256 *variant = Variant::GNU;
257 if identifier == GNU_SYMBOL_LOOKUP_TABLE_ID {
258 io::copy(&mut reader.by_ref().take(size), &mut io::sink())?;
259 return Ok(Some((Header::new(identifier, size), header_len)));
260 } else if identifier == GNU_NAME_TABLE_ID.as_bytes() {
261 *name_table = vec![0; size as usize];
262 reader.read_exact(name_table as &mut [u8]).map_err(|err| {
263 annotate(err, "failed to read name table")
264 })?;
265 return Ok(Some((Header::new(identifier, size), header_len)));
266 }
267 let start = parse_number("GNU filename index", &buffer[1..16], 10)?
268 as usize;
269 let end = match name_table[start..]
270 .iter()
271 .position(|&ch| ch == b'/' || ch == b'\x00')
272 {
273 Some(len) => start + len,
274 None => name_table.len(),
275 };
276 identifier = name_table[start..end].to_vec();
277 } else if *variant != Variant::BSD && identifier.ends_with(b"/") {
278 *variant = Variant::GNU;
279 identifier.pop();
280 }
281 let mtime = parse_number("timestamp", &buffer[16..28], 10)?;
282 let uid = if *variant == Variant::GNU {
283 parse_number_permitting_empty("owner ID", &buffer[28..34], 10)?
284 } else {
285 parse_number("owner ID", &buffer[28..34], 10)?
286 } as u32;
287 let gid = if *variant == Variant::GNU {
288 parse_number_permitting_empty("group ID", &buffer[34..40], 10)?
289 } else {
290 parse_number("group ID", &buffer[34..40], 10)?
291 } as u32;
292 let mode = parse_number("file mode", &buffer[40..48], 8)? as u32;
293 if *variant != Variant::GNU && identifier.starts_with(b"#1/") {
294 *variant = Variant::BSD;
295 let padded_length =
296 parse_number("BSD filename length", &buffer[3..16], 10)?;
297 if size < padded_length {
298 let msg = format!(
299 "Entry size ({}) smaller than extended \
300 entry identifier length ({})",
301 size, padded_length
302 );
303 return Err(Error::new(ErrorKind::InvalidData, msg));
304 }
305 size -= padded_length;
306 header_len += padded_length;
307 let mut id_buffer = vec![0; padded_length as usize];
308 let bytes_read = reader.read(&mut id_buffer)?;
309 if bytes_read < id_buffer.len() {
310 if let Err(error) =
311 reader.read_exact(&mut id_buffer[bytes_read..])
312 {
313 if error.kind() == ErrorKind::UnexpectedEof {
314 let msg = "unexpected EOF in the middle of extended \
315 entry identifier";
316 return Err(Error::new(ErrorKind::UnexpectedEof, msg));
317 } else {
318 let msg = "failed to read extended entry identifier";
319 return Err(annotate(error, msg));
320 }
321 }
322 }
323 while id_buffer.last() == Some(&0) {
324 id_buffer.pop();
325 }
326 identifier = id_buffer;
327 if identifier == BSD_SYMBOL_LOOKUP_TABLE_ID
328 || identifier == BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID
329 {
330 io::copy(&mut reader.by_ref().take(size), &mut io::sink())?;
331 return Ok(Some((Header::new(identifier, size), header_len)));
332 }
333 }
334 Ok(Some((
335 Header { identifier, mtime, uid, gid, mode, size },
336 header_len,
337 )))
338 }
339
340 fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
341 if self.identifier.len() > 16 || self.identifier.contains(&b' ') {
342 let padding_length = (4 - self.identifier.len() % 4) % 4;
343 let padded_length = self.identifier.len() + padding_length;
344 write!(
345 writer,
346 "#1/{:<13}{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
347 padded_length,
348 self.mtime,
349 self.uid,
350 self.gid,
351 self.mode,
352 self.size + padded_length as u64
353 )?;
354 writer.write_all(&self.identifier)?;
355 writer.write_all(&vec![0; padding_length])?;
356 } else {
357 writer.write_all(&self.identifier)?;
358 writer.write_all(&vec![b' '; 16 - self.identifier.len()])?;
359 write!(
360 writer,
361 "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
362 self.mtime, self.uid, self.gid, self.mode, self.size
363 )?;
364 }
365 Ok(())
366 }
367
368 fn write_gnu<W>(
369 &self,
370 writer: &mut W,
371 names: &HashMap<Vec<u8>, usize>,
372 ) -> Result<()>
373 where
374 W: Write,
375 {
376 if self.identifier.len() > 15 {
377 let offset = names[&self.identifier];
378 write!(writer, "/{:<15}", offset)?;
379 } else {
380 writer.write_all(&self.identifier)?;
381 writer.write_all(b"/")?;
382 writer.write_all(&vec![b' '; 15 - self.identifier.len()])?;
383 }
384 write!(
385 writer,
386 "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
387 self.mtime, self.uid, self.gid, self.mode, self.size
388 )?;
389 Ok(())
390 }
391}
392
393fn parse_number(field_name: &str, bytes: &[u8], radix: u32) -> Result<u64> {
394 if let Ok(string) = str::from_utf8(bytes) {
395 if let Ok(value) = u64::from_str_radix(string.trim_end(), radix) {
396 return Ok(value);
397 }
398 }
399 let msg = format!(
400 "Invalid {} field in entry header ({:?})",
401 field_name,
402 String::from_utf8_lossy(bytes)
403 );
404 Err(Error::new(ErrorKind::InvalidData, msg))
405}
406
407fn parse_number_permitting_empty(
412 field_name: &str,
413 bytes: &[u8],
414 radix: u32,
415) -> Result<u64> {
416 if let Ok(string) = str::from_utf8(bytes) {
417 let trimmed = string.trim_end();
418 if trimmed.len() == 0 {
419 return Ok(0);
420 } else if let Ok(value) = u64::from_str_radix(trimmed, radix) {
421 return Ok(value);
422 }
423 }
424 let msg = format!(
425 "Invalid {} field in entry header ({:?})",
426 field_name,
427 String::from_utf8_lossy(bytes)
428 );
429 Err(Error::new(ErrorKind::InvalidData, msg))
430}
431
432struct HeaderAndLocation {
435 header: Header,
436 header_start: u64,
437 data_start: u64,
438}
439
440pub struct Archive<R: Read> {
444 reader: R,
445 variant: Variant,
446 name_table: Vec<u8>,
447 entry_headers: Vec<HeaderAndLocation>,
448 new_entry_start: u64,
449 next_entry_index: usize,
450 symbol_table_header: Option<HeaderAndLocation>,
451 symbol_table: Option<Vec<(Vec<u8>, u64)>>,
452 started: bool, padding: bool, scanned: bool, error: bool, }
457
458impl<R: Read> Archive<R> {
459 pub fn new(reader: R) -> Archive<R> {
462 Archive {
463 reader,
464 variant: Variant::Common,
465 name_table: Vec::new(),
466 entry_headers: Vec::new(),
467 new_entry_start: GLOBAL_HEADER_LEN as u64,
468 next_entry_index: 0,
469 symbol_table_header: None,
470 symbol_table: None,
471 started: false,
472 padding: false,
473 scanned: false,
474 error: false,
475 }
476 }
477
478 pub fn variant(&self) -> Variant {
485 self.variant
486 }
487
488 pub fn into_inner(self) -> Result<R> {
490 Ok(self.reader)
491 }
492
493 fn is_name_table_id(&self, identifier: &[u8]) -> bool {
494 self.variant == Variant::GNU
495 && identifier == GNU_NAME_TABLE_ID.as_bytes()
496 }
497
498 fn is_symbol_lookup_table_id(&self, identifier: &[u8]) -> bool {
499 match self.variant {
500 Variant::Common => false,
501 Variant::BSD => {
502 identifier == BSD_SYMBOL_LOOKUP_TABLE_ID
503 || identifier == BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID
504 }
505 Variant::GNU => identifier == GNU_SYMBOL_LOOKUP_TABLE_ID,
506 }
507 }
508
509 fn read_global_header_if_necessary(&mut self) -> Result<()> {
510 if self.started {
511 return Ok(());
512 }
513 let mut buffer = [0; GLOBAL_HEADER_LEN];
514 match self.reader.read_exact(&mut buffer) {
515 Ok(()) => {}
516 Err(error) => {
517 self.error = true;
518 return Err(annotate(error, "failed to read global header"));
519 }
520 }
521 if &buffer != GLOBAL_HEADER {
522 self.error = true;
523 let msg = "Not an archive file (invalid global header)";
524 return Err(Error::new(ErrorKind::InvalidData, msg));
525 }
526 self.started = true;
527 Ok(())
528 }
529
530 pub fn next_entry(&mut self) -> Option<Result<Entry<R>>> {
533 loop {
534 if self.error {
535 return None;
536 }
537 if self.scanned
538 && self.next_entry_index == self.entry_headers.len()
539 {
540 return None;
541 }
542 match self.read_global_header_if_necessary() {
543 Ok(()) => {}
544 Err(error) => return Some(Err(error)),
545 }
546 if self.padding {
547 let mut buffer = [0u8; 1];
548 match self.reader.read_exact(&mut buffer) {
549 Ok(()) => {
550 if buffer[0] != b'\n' {
551 self.error = true;
552 let msg = format!(
553 "invalid padding byte ({})",
554 buffer[0]
555 );
556 let error =
557 Error::new(ErrorKind::InvalidData, msg);
558 return Some(Err(error));
559 }
560 }
561 Err(error) => {
562 if error.kind() != ErrorKind::UnexpectedEof {
563 self.error = true;
564 let msg = "failed to read padding byte";
565 return Some(Err(annotate(error, msg)));
566 }
567 }
568 }
569 self.padding = false;
570 }
571 let header_start = self.new_entry_start;
572 match Header::read(
573 &mut self.reader,
574 &mut self.variant,
575 &mut self.name_table,
576 ) {
577 Ok(Some((header, header_len))) => {
578 let size = header.size();
579 if size % 2 != 0 {
580 self.padding = true;
581 }
582 if self.next_entry_index == self.entry_headers.len() {
583 self.new_entry_start += header_len + size + (size % 2);
584 }
585 if self.is_name_table_id(header.identifier()) {
586 continue;
587 }
588 if self.is_symbol_lookup_table_id(header.identifier()) {
589 self.symbol_table_header = Some(HeaderAndLocation {
590 header,
591 header_start,
592 data_start: header_start + header_len,
593 });
594 continue;
595 }
596 if self.next_entry_index == self.entry_headers.len() {
597 self.entry_headers.push(HeaderAndLocation {
598 header,
599 header_start,
600 data_start: header_start + header_len,
601 });
602 }
603 let header =
604 &self.entry_headers[self.next_entry_index].header;
605 self.next_entry_index += 1;
606 return Some(Ok(Entry {
607 header,
608 reader: self.reader.by_ref(),
609 length: size,
610 position: 0,
611 }));
612 }
613 Ok(None) => {
614 self.scanned = true;
615 return None;
616 }
617 Err(error) => {
618 self.error = true;
619 return Some(Err(error));
620 }
621 }
622 }
623 }
624}
625
626impl<R: Read + Seek> Archive<R> {
627 fn scan_if_necessary(&mut self) -> io::Result<()> {
628 if self.scanned {
629 return Ok(());
630 }
631 self.read_global_header_if_necessary()?;
632 loop {
633 let header_start = self.new_entry_start;
634 self.reader.seek(SeekFrom::Start(header_start))?;
635 if let Some((header, header_len)) = Header::read(
636 &mut self.reader,
637 &mut self.variant,
638 &mut self.name_table,
639 )? {
640 let size = header.size();
641 self.new_entry_start += header_len + size + (size % 2);
642 if self.is_name_table_id(header.identifier()) {
643 continue;
644 }
645 if self.is_symbol_lookup_table_id(header.identifier()) {
646 self.symbol_table_header = Some(HeaderAndLocation {
647 header,
648 header_start,
649 data_start: header_start + header_len,
650 });
651 continue;
652 }
653 self.entry_headers.push(HeaderAndLocation {
654 header,
655 header_start,
656 data_start: header_start + header_len,
657 });
658 } else {
659 break;
660 }
661 }
662 if self.next_entry_index < self.entry_headers.len() {
664 let offset =
665 self.entry_headers[self.next_entry_index].header_start;
666 self.reader.seek(SeekFrom::Start(offset))?;
667 }
668 self.scanned = true;
669 Ok(())
670 }
671
672 pub fn count_entries(&mut self) -> io::Result<usize> {
676 self.scan_if_necessary()?;
677 Ok(self.entry_headers.len())
678 }
679
680 pub fn jump_to_entry(&mut self, index: usize) -> io::Result<Entry<R>> {
683 self.scan_if_necessary()?;
684 if index >= self.entry_headers.len() {
685 let msg = "Entry index out of bounds";
686 return Err(Error::new(ErrorKind::InvalidInput, msg));
687 }
688 let offset = self.entry_headers[index].data_start;
689 self.reader.seek(SeekFrom::Start(offset))?;
690 let header = &self.entry_headers[index].header;
691 let size = header.size();
692 if size % 2 != 0 {
693 self.padding = true;
694 } else {
695 self.padding = false;
696 }
697 self.next_entry_index = index + 1;
698 Ok(Entry {
699 header,
700 reader: self.reader.by_ref(),
701 length: size,
702 position: 0,
703 })
704 }
705
706 fn parse_symbol_table_if_necessary(&mut self) -> io::Result<()> {
707 self.scan_if_necessary()?;
708 if self.symbol_table.is_some() {
709 return Ok(());
710 }
711 if let Some(ref header_and_loc) = self.symbol_table_header {
712 let offset = header_and_loc.data_start;
713 self.reader.seek(SeekFrom::Start(offset))?;
714 let mut reader = BufReader::new(
715 self.reader.by_ref().take(header_and_loc.header.size()),
716 );
717 if self.variant == Variant::GNU {
718 let num_symbols = read_be_u32(&mut reader)? as usize;
719 let mut symbol_offsets =
720 Vec::<u32>::with_capacity(num_symbols);
721 for _ in 0..num_symbols {
722 let offset = read_be_u32(&mut reader)?;
723 symbol_offsets.push(offset);
724 }
725 let mut symbol_table = Vec::with_capacity(num_symbols);
726 for offset in symbol_offsets.into_iter() {
727 let mut buffer = Vec::<u8>::new();
728 reader.read_until(0, &mut buffer)?;
729 if buffer.last() == Some(&0) {
730 buffer.pop();
731 }
732 buffer.shrink_to_fit();
733 symbol_table.push((buffer, offset as u64));
734 }
735 self.symbol_table = Some(symbol_table);
736 } else {
737 let num_symbols = (read_le_u32(&mut reader)? / 8) as usize;
738 let mut symbol_offsets =
739 Vec::<(u32, u32)>::with_capacity(num_symbols);
740 for _ in 0..num_symbols {
741 let str_offset = read_le_u32(&mut reader)?;
742 let file_offset = read_le_u32(&mut reader)?;
743 symbol_offsets.push((str_offset, file_offset));
744 }
745 let str_table_len = read_le_u32(&mut reader)?;
746 let mut str_table_data = vec![0u8; str_table_len as usize];
747 reader.read_exact(&mut str_table_data).map_err(|err| {
748 annotate(err, "failed to read string table")
749 })?;
750 let mut symbol_table = Vec::with_capacity(num_symbols);
751 for (str_start, file_offset) in symbol_offsets.into_iter() {
752 let str_start = str_start as usize;
753 let mut str_end = str_start;
754 while str_end < str_table_data.len()
755 && str_table_data[str_end] != 0u8
756 {
757 str_end += 1;
758 }
759 let string = &str_table_data[str_start..str_end];
760 symbol_table.push((string.to_vec(), file_offset as u64));
761 }
762 self.symbol_table = Some(symbol_table);
763 }
764 }
765 if self.entry_headers.len() > 0 {
767 let offset =
768 self.entry_headers[self.next_entry_index].header_start;
769 self.reader.seek(SeekFrom::Start(offset))?;
770 }
771 Ok(())
772 }
773
774 pub fn symbols(&mut self) -> io::Result<Symbols<R>> {
779 self.parse_symbol_table_if_necessary()?;
780 Ok(Symbols { archive: self, index: 0 })
781 }
782}
783
784pub struct Entry<'a, R: 'a + Read> {
792 header: &'a Header,
793 reader: &'a mut R,
794 length: u64,
795 position: u64,
796}
797
798impl<'a, R: 'a + Read> Entry<'a, R> {
799 pub fn header(&self) -> &Header {
801 self.header
802 }
803}
804
805impl<'a, R: 'a + Read> Read for Entry<'a, R> {
806 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
807 debug_assert!(self.position <= self.length);
808 if self.position == self.length {
809 return Ok(0);
810 }
811 let max_len =
812 cmp::min(self.length - self.position, buf.len() as u64) as usize;
813 let bytes_read = self.reader.read(&mut buf[0..max_len])?;
814 self.position += bytes_read as u64;
815 debug_assert!(self.position <= self.length);
816 Ok(bytes_read)
817 }
818}
819
820impl<'a, R: 'a + Read + Seek> Seek for Entry<'a, R> {
821 fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
822 let delta = match pos {
823 SeekFrom::Start(offset) => offset as i64 - self.position as i64,
824 SeekFrom::End(offset) => {
825 self.length as i64 + offset - self.position as i64
826 }
827 SeekFrom::Current(delta) => delta,
828 };
829 let new_position = self.position as i64 + delta;
830 if new_position < 0 {
831 let msg = format!(
832 "Invalid seek to negative position ({})",
833 new_position
834 );
835 return Err(Error::new(ErrorKind::InvalidInput, msg));
836 }
837 let new_position = new_position as u64;
838 if new_position > self.length {
839 let msg = format!(
840 "Invalid seek to position past end of entry ({} vs. {})",
841 new_position, self.length
842 );
843 return Err(Error::new(ErrorKind::InvalidInput, msg));
844 }
845 self.reader.seek(SeekFrom::Current(delta))?;
846 self.position = new_position;
847 Ok(self.position)
848 }
849}
850
851impl<'a, R: 'a + Read> Drop for Entry<'a, R> {
852 fn drop(&mut self) {
853 if self.position < self.length {
854 let mut remaining = self.reader.take(self.length - self.position);
856 let _ = io::copy(&mut remaining, &mut io::sink());
857 }
858 }
859}
860
861pub struct Symbols<'a, R: 'a + Read> {
865 archive: &'a Archive<R>,
866 index: usize,
867}
868
869impl<'a, R: Read> Iterator for Symbols<'a, R> {
870 type Item = &'a [u8];
871
872 fn next(&mut self) -> Option<&'a [u8]> {
873 if let Some(ref table) = self.archive.symbol_table {
874 if self.index < table.len() {
875 let next = table[self.index].0.as_slice();
876 self.index += 1;
877 return Some(next);
878 }
879 }
880 None
881 }
882
883 fn size_hint(&self) -> (usize, Option<usize>) {
884 let remaining = if let Some(ref table) = self.archive.symbol_table {
885 table.len() - self.index
886 } else {
887 0
888 };
889 (remaining, Some(remaining))
890 }
891}
892
893impl<'a, R: Read> ExactSizeIterator for Symbols<'a, R> {}
894
895pub struct Builder<W: Write> {
903 writer: W,
904 started: bool,
905}
906
907impl<W: Write> Builder<W> {
908 pub fn new(writer: W) -> Builder<W> {
911 Builder { writer, started: false }
912 }
913
914 pub fn into_inner(self) -> Result<W> {
916 Ok(self.writer)
917 }
918
919 pub fn append<R: Read>(
921 &mut self,
922 header: &Header,
923 mut data: R,
924 ) -> Result<()> {
925 if !self.started {
926 self.writer.write_all(GLOBAL_HEADER)?;
927 self.started = true;
928 }
929 header.write(&mut self.writer)?;
930 let actual_size = io::copy(&mut data, &mut self.writer)?;
931 if actual_size != header.size() {
932 let msg = format!(
933 "Wrong file size (header.size() = {}, actual \
934 size was {})",
935 header.size(),
936 actual_size
937 );
938 return Err(Error::new(ErrorKind::InvalidData, msg));
939 }
940 if actual_size % 2 != 0 {
941 self.writer.write_all(&['\n' as u8])?;
942 }
943 Ok(())
944 }
945
946 pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
949 let name: &OsStr = path.as_ref().file_name().ok_or_else(|| {
950 let msg = "Given path doesn't have a file name";
951 Error::new(ErrorKind::InvalidInput, msg)
952 })?;
953 let identifier = osstr_to_bytes(name)?;
954 let mut file = File::open(&path)?;
955 self.append_file_id(identifier, &mut file)
956 }
957
958 pub fn append_file(&mut self, name: &[u8], file: &mut File) -> Result<()> {
960 self.append_file_id(name.to_vec(), file)
961 }
962
963 fn append_file_id(&mut self, id: Vec<u8>, file: &mut File) -> Result<()> {
964 let metadata = file.metadata()?;
965 let header = Header::from_metadata(id, &metadata);
966 self.append(&header, file)
967 }
968}
969
970pub struct GnuBuilder<W: Write> {
978 writer: W,
979 short_names: HashSet<Vec<u8>>,
980 long_names: HashMap<Vec<u8>, usize>,
981 name_table_size: usize,
982 name_table_needs_padding: bool,
983 started: bool,
984}
985
986impl<W: Write> GnuBuilder<W> {
987 pub fn new(writer: W, identifiers: Vec<Vec<u8>>) -> GnuBuilder<W> {
992 let mut short_names = HashSet::<Vec<u8>>::new();
993 let mut long_names = HashMap::<Vec<u8>, usize>::new();
994 let mut name_table_size: usize = 0;
995 for identifier in identifiers.into_iter() {
996 let length = identifier.len();
997 if length > 15 {
998 long_names.insert(identifier, name_table_size);
999 name_table_size += length + 2;
1000 } else {
1001 short_names.insert(identifier);
1002 }
1003 }
1004 let name_table_needs_padding = name_table_size % 2 != 0;
1005 if name_table_needs_padding {
1006 name_table_size += 3; }
1008
1009 GnuBuilder {
1010 writer,
1011 short_names,
1012 long_names,
1013 name_table_size,
1014 name_table_needs_padding,
1015 started: false,
1016 }
1017 }
1018
1019 pub fn into_inner(self) -> Result<W> {
1021 Ok(self.writer)
1022 }
1023
1024 pub fn append<R: Read>(
1026 &mut self,
1027 header: &Header,
1028 mut data: R,
1029 ) -> Result<()> {
1030 let is_long_name = header.identifier().len() > 15;
1031 let has_name = if is_long_name {
1032 self.long_names.contains_key(header.identifier())
1033 } else {
1034 self.short_names.contains(header.identifier())
1035 };
1036 if !has_name {
1037 let msg = format!(
1038 "Identifier {:?} was not in the list of \
1039 identifiers passed to GnuBuilder::new()",
1040 String::from_utf8_lossy(header.identifier())
1041 );
1042 return Err(Error::new(ErrorKind::InvalidInput, msg));
1043 }
1044
1045 if !self.started {
1046 self.writer.write_all(GLOBAL_HEADER)?;
1047 if !self.long_names.is_empty() {
1048 write!(
1049 self.writer,
1050 "{:<48}{:<10}`\n",
1051 GNU_NAME_TABLE_ID, self.name_table_size
1052 )?;
1053 let mut entries: Vec<(usize, &[u8])> = self
1054 .long_names
1055 .iter()
1056 .map(|(id, &start)| (start, id.as_slice()))
1057 .collect();
1058 entries.sort();
1059 for (_, id) in entries {
1060 self.writer.write_all(id)?;
1061 self.writer.write_all(b"/\n")?;
1062 }
1063 if self.name_table_needs_padding {
1064 self.writer.write_all(b" /\n")?;
1065 }
1066 }
1067 self.started = true;
1068 }
1069
1070 header.write_gnu(&mut self.writer, &self.long_names)?;
1071 let actual_size = io::copy(&mut data, &mut self.writer)?;
1072 if actual_size != header.size() {
1073 let msg = format!(
1074 "Wrong file size (header.size() = {}, actual \
1075 size was {})",
1076 header.size(),
1077 actual_size
1078 );
1079 return Err(Error::new(ErrorKind::InvalidData, msg));
1080 }
1081 if actual_size % 2 != 0 {
1082 self.writer.write_all(&['\n' as u8])?;
1083 }
1084
1085 Ok(())
1086 }
1087
1088 pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1091 let name: &OsStr = path.as_ref().file_name().ok_or_else(|| {
1092 let msg = "Given path doesn't have a file name";
1093 Error::new(ErrorKind::InvalidInput, msg)
1094 })?;
1095 let identifier = osstr_to_bytes(name)?;
1096 let mut file = File::open(&path)?;
1097 self.append_file_id(identifier, &mut file)
1098 }
1099
1100 pub fn append_file(&mut self, name: &[u8], file: &mut File) -> Result<()> {
1102 self.append_file_id(name.to_vec(), file)
1103 }
1104
1105 fn append_file_id(&mut self, id: Vec<u8>, file: &mut File) -> Result<()> {
1106 let metadata = file.metadata()?;
1107 let header = Header::from_metadata(id, &metadata);
1108 self.append(&header, file)
1109 }
1110}
1111
1112#[cfg(unix)]
1115fn osstr_to_bytes(string: &OsStr) -> Result<Vec<u8>> {
1116 Ok(string.as_bytes().to_vec())
1117}
1118
1119#[cfg(not(unix))]
1120fn osstr_to_bytes(string: &OsStr) -> Result<Vec<u8>> {
1121 let utf8: &str = string.to_str().ok_or_else(|| {
1122 Error::new(ErrorKind::InvalidData, "Non-UTF8 file name")
1123 })?;
1124 Ok(utf8.as_bytes().to_vec())
1125}
1126
1127fn annotate(error: io::Error, msg: &str) -> io::Error {
1130 let kind = error.kind();
1131 if let Some(inner) = error.into_inner() {
1132 io::Error::new(kind, format!("{}: {}", msg, inner))
1133 } else {
1134 io::Error::new(kind, msg)
1135 }
1136}
1137
1138#[cfg(test)]
1141mod tests {
1142 use super::{Archive, Builder, GnuBuilder, Header, Variant};
1143 use std::io::{Cursor, Read, Result, Seek, SeekFrom};
1144 use std::str;
1145
1146 struct SlowReader<'a> {
1147 current_position: usize,
1148 buffer: &'a [u8],
1149 }
1150
1151 impl<'a> Read for SlowReader<'a> {
1152 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1153 if self.current_position >= self.buffer.len() {
1154 return Ok(0);
1155 }
1156 buf[0] = self.buffer[self.current_position];
1157 self.current_position += 1;
1158 return Ok(1);
1159 }
1160 }
1161
1162 #[test]
1163 fn build_common_archive() {
1164 let mut builder = Builder::new(Vec::new());
1165 let mut header1 = Header::new(b"foo.txt".to_vec(), 7);
1166 header1.set_mtime(1487552916);
1167 header1.set_uid(501);
1168 header1.set_gid(20);
1169 header1.set_mode(0o100644);
1170 builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1171 let header2 = Header::new(b"baz.txt".to_vec(), 4);
1172 builder.append(&header2, "baz\n".as_bytes()).unwrap();
1173 let actual = builder.into_inner().unwrap();
1174 let expected = "\
1175 !<arch>\n\
1176 foo.txt 1487552916 501 20 100644 7 `\n\
1177 foobar\n\n\
1178 baz.txt 0 0 0 0 4 `\n\
1179 baz\n";
1180 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1181 }
1182
1183 #[test]
1184 fn build_bsd_archive_with_long_filenames() {
1185 let mut builder = Builder::new(Vec::new());
1186 let mut header1 = Header::new(b"short".to_vec(), 1);
1187 header1.set_identifier(b"this_is_a_very_long_filename.txt".to_vec());
1188 header1.set_mtime(1487552916);
1189 header1.set_uid(501);
1190 header1.set_gid(20);
1191 header1.set_mode(0o100644);
1192 header1.set_size(7);
1193 builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1194 let header2 = Header::new(
1195 b"and_this_is_another_very_long_filename.txt".to_vec(),
1196 4,
1197 );
1198 builder.append(&header2, "baz\n".as_bytes()).unwrap();
1199 let actual = builder.into_inner().unwrap();
1200 let expected = "\
1201 !<arch>\n\
1202 #1/32 1487552916 501 20 100644 39 `\n\
1203 this_is_a_very_long_filename.txtfoobar\n\n\
1204 #1/44 0 0 0 0 48 `\n\
1205 and_this_is_another_very_long_filename.txt\x00\x00baz\n";
1206 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1207 }
1208
1209 #[test]
1210 fn build_bsd_archive_with_space_in_filename() {
1211 let mut builder = Builder::new(Vec::new());
1212 let header = Header::new(b"foo bar".to_vec(), 4);
1213 builder.append(&header, "baz\n".as_bytes()).unwrap();
1214 let actual = builder.into_inner().unwrap();
1215 let expected = "\
1216 !<arch>\n\
1217 #1/8 0 0 0 0 12 `\n\
1218 foo bar\x00baz\n";
1219 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1220 }
1221
1222 #[test]
1223 fn build_gnu_archive() {
1224 let names = vec![b"baz.txt".to_vec(), b"foo.txt".to_vec()];
1225 let mut builder = GnuBuilder::new(Vec::new(), names);
1226 let mut header1 = Header::new(b"foo.txt".to_vec(), 7);
1227 header1.set_mtime(1487552916);
1228 header1.set_uid(501);
1229 header1.set_gid(20);
1230 header1.set_mode(0o100644);
1231 builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1232 let header2 = Header::new(b"baz.txt".to_vec(), 4);
1233 builder.append(&header2, "baz\n".as_bytes()).unwrap();
1234 let actual = builder.into_inner().unwrap();
1235 let expected = "\
1236 !<arch>\n\
1237 foo.txt/ 1487552916 501 20 100644 7 `\n\
1238 foobar\n\n\
1239 baz.txt/ 0 0 0 0 4 `\n\
1240 baz\n";
1241 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1242 }
1243
1244 #[test]
1245 fn build_gnu_archive_with_long_filenames() {
1246 let names = vec![
1247 b"this_is_a_very_long_filename.txt".to_vec(),
1248 b"and_this_is_another_very_long_filename.txt".to_vec(),
1249 ];
1250 let mut builder = GnuBuilder::new(Vec::new(), names);
1251 let mut header1 = Header::new(b"short".to_vec(), 1);
1252 header1.set_identifier(b"this_is_a_very_long_filename.txt".to_vec());
1253 header1.set_mtime(1487552916);
1254 header1.set_uid(501);
1255 header1.set_gid(20);
1256 header1.set_mode(0o100644);
1257 header1.set_size(7);
1258 builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1259 let header2 = Header::new(
1260 b"and_this_is_another_very_long_filename.txt".to_vec(),
1261 4,
1262 );
1263 builder.append(&header2, "baz\n".as_bytes()).unwrap();
1264 let actual = builder.into_inner().unwrap();
1265 let expected = "\
1266 !<arch>\n\
1267 // 78 `\n\
1268 this_is_a_very_long_filename.txt/\n\
1269 and_this_is_another_very_long_filename.txt/\n\
1270 /0 1487552916 501 20 100644 7 `\n\
1271 foobar\n\n\
1272 /34 0 0 0 0 4 `\n\
1273 baz\n";
1274 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1275 }
1276
1277 #[test]
1278 fn build_gnu_archive_with_space_in_filename() {
1279 let names = vec![b"foo bar".to_vec()];
1280 let mut builder = GnuBuilder::new(Vec::new(), names);
1281 let header = Header::new(b"foo bar".to_vec(), 4);
1282 builder.append(&header, "baz\n".as_bytes()).unwrap();
1283 let actual = builder.into_inner().unwrap();
1284 let expected = "\
1285 !<arch>\n\
1286 foo bar/ 0 0 0 0 4 `\n\
1287 baz\n";
1288 assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1289 }
1290
1291 #[test]
1292 #[should_panic(
1293 expected = "Identifier \\\"bar\\\" was not in the list of \
1294 identifiers passed to GnuBuilder::new()"
1295 )]
1296 fn build_gnu_archive_with_unexpected_identifier() {
1297 let names = vec![b"foo".to_vec()];
1298 let mut builder = GnuBuilder::new(Vec::new(), names);
1299 let header = Header::new(b"bar".to_vec(), 4);
1300 builder.append(&header, "baz\n".as_bytes()).unwrap();
1301 }
1302
1303 #[test]
1304 fn read_common_archive() {
1305 let input = "\
1306 !<arch>\n\
1307 foo.txt 1487552916 501 20 100644 7 `\n\
1308 foobar\n\n\
1309 bar.awesome.txt 1487552919 501 20 100644 22 `\n\
1310 This file is awesome!\n\
1311 baz.txt 1487552349 42 12345 100664 4 `\n\
1312 baz\n";
1313 let reader =
1314 SlowReader { current_position: 0, buffer: input.as_bytes() };
1315 let mut archive = Archive::new(reader);
1316 {
1317 let mut entry = archive.next_entry().unwrap().unwrap();
1319 assert_eq!(entry.header().identifier(), b"foo.txt");
1320 assert_eq!(entry.header().mtime(), 1487552916);
1321 assert_eq!(entry.header().uid(), 501);
1322 assert_eq!(entry.header().gid(), 20);
1323 assert_eq!(entry.header().mode(), 0o100644);
1324 assert_eq!(entry.header().size(), 7);
1325 let mut buffer = [0; 4];
1328 entry.read_exact(&mut buffer).unwrap();
1329 assert_eq!(&buffer, "foob".as_bytes());
1330 }
1334 {
1335 let mut entry = archive.next_entry().unwrap().unwrap();
1337 assert_eq!(entry.header().identifier(), b"bar.awesome.txt");
1338 assert_eq!(entry.header().size(), 22);
1339 let mut buffer = Vec::new();
1341 entry.read_to_end(&mut buffer).unwrap();
1342 assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
1343 }
1344 {
1345 let entry = archive.next_entry().unwrap().unwrap();
1347 assert_eq!(entry.header().identifier(), b"baz.txt");
1348 assert_eq!(entry.header().size(), 4);
1349 }
1350 assert!(archive.next_entry().is_none());
1351 assert_eq!(archive.variant(), Variant::Common);
1352 }
1353
1354 #[test]
1355 fn read_bsd_archive_with_long_filenames() {
1356 let input = "\
1357 !<arch>\n\
1358 #1/32 1487552916 501 20 100644 39 `\n\
1359 this_is_a_very_long_filename.txtfoobar\n\n\
1360 #1/44 0 0 0 0 48 `\n\
1361 and_this_is_another_very_long_filename.txt\x00\x00baz\n";
1362 let mut archive = Archive::new(input.as_bytes());
1363 {
1364 let mut entry = archive.next_entry().unwrap().unwrap();
1366 assert_eq!(
1367 entry.header().identifier(),
1368 "this_is_a_very_long_filename.txt".as_bytes()
1369 );
1370 assert_eq!(entry.header().mtime(), 1487552916);
1371 assert_eq!(entry.header().uid(), 501);
1372 assert_eq!(entry.header().gid(), 20);
1373 assert_eq!(entry.header().mode(), 0o100644);
1374 assert_eq!(entry.header().size(), 7);
1378 let mut buffer = Vec::new();
1381 entry.read_to_end(&mut buffer).unwrap();
1382 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1383 }
1384 {
1385 let mut entry = archive.next_entry().unwrap().unwrap();
1387 assert_eq!(
1388 entry.header().identifier(),
1389 "and_this_is_another_very_long_filename.txt".as_bytes()
1390 );
1391 assert_eq!(entry.header().size(), 4);
1392 let mut buffer = Vec::new();
1395 entry.read_to_end(&mut buffer).unwrap();
1396 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1397 }
1398 assert!(archive.next_entry().is_none());
1399 assert_eq!(archive.variant(), Variant::BSD);
1400 }
1401
1402 #[test]
1403 fn read_bsd_archive_with_space_in_filename() {
1404 let input = "\
1405 !<arch>\n\
1406 #1/8 0 0 0 0 12 `\n\
1407 foo bar\x00baz\n";
1408 let mut archive = Archive::new(input.as_bytes());
1409 {
1410 let mut entry = archive.next_entry().unwrap().unwrap();
1411 assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
1412 assert_eq!(entry.header().size(), 4);
1413 let mut buffer = Vec::new();
1414 entry.read_to_end(&mut buffer).unwrap();
1415 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1416 }
1417 assert!(archive.next_entry().is_none());
1418 assert_eq!(archive.variant(), Variant::BSD);
1419 }
1420
1421 #[test]
1422 fn read_gnu_archive() {
1423 let input = "\
1424 !<arch>\n\
1425 foo.txt/ 1487552916 501 20 100644 7 `\n\
1426 foobar\n\n\
1427 bar.awesome.txt/1487552919 501 20 100644 22 `\n\
1428 This file is awesome!\n\
1429 baz.txt/ 1487552349 42 12345 100664 4 `\n\
1430 baz\n";
1431 let mut archive = Archive::new(input.as_bytes());
1432 {
1433 let entry = archive.next_entry().unwrap().unwrap();
1434 assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
1435 assert_eq!(entry.header().size(), 7);
1436 }
1437 {
1438 let entry = archive.next_entry().unwrap().unwrap();
1439 assert_eq!(
1440 entry.header().identifier(),
1441 "bar.awesome.txt".as_bytes()
1442 );
1443 assert_eq!(entry.header().size(), 22);
1444 }
1445 {
1446 let entry = archive.next_entry().unwrap().unwrap();
1447 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1448 assert_eq!(entry.header().size(), 4);
1449 }
1450 assert!(archive.next_entry().is_none());
1451 assert_eq!(archive.variant(), Variant::GNU);
1452 }
1453
1454 #[test]
1455 fn read_gnu_archive_with_long_filenames() {
1456 let input = "\
1457 !<arch>\n\
1458 // 78 `\n\
1459 this_is_a_very_long_filename.txt/\n\
1460 and_this_is_another_very_long_filename.txt/\n\
1461 /0 1487552916 501 20 100644 7 `\n\
1462 foobar\n\n\
1463 /34 0 0 0 0 4 `\n\
1464 baz\n";
1465 let mut archive = Archive::new(input.as_bytes());
1466 {
1467 let mut entry = archive.next_entry().unwrap().unwrap();
1468 assert_eq!(
1469 entry.header().identifier(),
1470 "this_is_a_very_long_filename.txt".as_bytes()
1471 );
1472 assert_eq!(entry.header().mtime(), 1487552916);
1473 assert_eq!(entry.header().uid(), 501);
1474 assert_eq!(entry.header().gid(), 20);
1475 assert_eq!(entry.header().mode(), 0o100644);
1476 assert_eq!(entry.header().size(), 7);
1477 let mut buffer = Vec::new();
1478 entry.read_to_end(&mut buffer).unwrap();
1479 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1480 }
1481 {
1482 let mut entry = archive.next_entry().unwrap().unwrap();
1483 assert_eq!(
1484 entry.header().identifier(),
1485 "and_this_is_another_very_long_filename.txt".as_bytes()
1486 );
1487 assert_eq!(entry.header().size(), 4);
1488 let mut buffer = Vec::new();
1489 entry.read_to_end(&mut buffer).unwrap();
1490 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1491 }
1492 assert!(archive.next_entry().is_none());
1493 assert_eq!(archive.variant(), Variant::GNU);
1494 }
1495
1496 #[test]
1501 fn read_ms_archive_with_long_filenames() {
1502 let input = "\
1503 !<arch>\n\
1504 // 76 `\n\
1505 this_is_a_very_long_filename.txt\x00\
1506 and_this_is_another_very_long_filename.txt\x00\
1507 /0 1487552916 100644 7 `\n\
1508 foobar\n\n\
1509 /33 1446790218 100666 4 `\n\
1510 baz\n";
1511 let mut archive = Archive::new(input.as_bytes());
1512 {
1513 let mut entry = archive.next_entry().unwrap().unwrap();
1514 assert_eq!(
1515 entry.header().identifier(),
1516 "this_is_a_very_long_filename.txt".as_bytes()
1517 );
1518 assert_eq!(entry.header().mtime(), 1487552916);
1519 assert_eq!(entry.header().uid(), 0);
1520 assert_eq!(entry.header().gid(), 0);
1521 assert_eq!(entry.header().mode(), 0o100644);
1522 assert_eq!(entry.header().size(), 7);
1523 let mut buffer = Vec::new();
1524 entry.read_to_end(&mut buffer).unwrap();
1525 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1526 }
1527 {
1528 let mut entry = archive.next_entry().unwrap().unwrap();
1529 assert_eq!(
1530 entry.header().identifier(),
1531 "and_this_is_another_very_long_filename.txt".as_bytes()
1532 );
1533 assert_eq!(entry.header().size(), 4);
1534 let mut buffer = Vec::new();
1535 entry.read_to_end(&mut buffer).unwrap();
1536 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1537 }
1538 assert!(archive.next_entry().is_none());
1539 assert_eq!(archive.variant(), Variant::GNU);
1540 }
1541
1542 #[test]
1543 fn read_gnu_archive_with_space_in_filename() {
1544 let input = "\
1545 !<arch>\n\
1546 foo bar/ 0 0 0 0 4 `\n\
1547 baz\n";
1548 let mut archive = Archive::new(input.as_bytes());
1549 {
1550 let mut entry = archive.next_entry().unwrap().unwrap();
1551 assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
1552 assert_eq!(entry.header().size(), 4);
1553 let mut buffer = Vec::new();
1554 entry.read_to_end(&mut buffer).unwrap();
1555 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1556 }
1557 assert!(archive.next_entry().is_none());
1558 assert_eq!(archive.variant(), Variant::GNU);
1559 }
1560
1561 #[test]
1562 fn read_gnu_archive_with_symbol_lookup_table() {
1563 let input = b"\
1564 !<arch>\n\
1565 / 0 0 0 0 15 `\n\
1566 \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
1567 // 34 `\n\
1568 this_is_a_very_long_filename.txt/\n\
1569 /0 1487552916 501 20 100644 7 `\n\
1570 foobar\n";
1571 let mut archive = Archive::new(input as &[u8]);
1572 {
1573 let mut entry = archive.next_entry().unwrap().unwrap();
1574 assert_eq!(
1575 entry.header().identifier(),
1576 "this_is_a_very_long_filename.txt".as_bytes()
1577 );
1578 let mut buffer = Vec::new();
1579 entry.read_to_end(&mut buffer).unwrap();
1580 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1581 }
1582 assert!(archive.next_entry().is_none());
1583 }
1584
1585 #[test]
1586 fn read_archive_with_no_padding_byte_in_final_entry() {
1587 let input = "\
1588 !<arch>\n\
1589 foo.txt 1487552916 501 20 100644 7 `\n\
1590 foobar\n\n\
1591 bar.txt 1487552919 501 20 100644 3 `\n\
1592 foo";
1593 let mut archive = Archive::new(input.as_bytes());
1594 {
1595 let entry = archive.next_entry().unwrap().unwrap();
1596 assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
1597 assert_eq!(entry.header().size(), 7);
1598 }
1599 {
1600 let entry = archive.next_entry().unwrap().unwrap();
1601 assert_eq!(entry.header().identifier(), "bar.txt".as_bytes());
1602 assert_eq!(entry.header().size(), 3);
1603 }
1604 assert!(archive.next_entry().is_none());
1605 }
1606
1607 #[test]
1608 #[should_panic(expected = "Invalid timestamp field in entry header \
1609 (\\\"helloworld \\\")")]
1610 fn read_archive_with_invalid_mtime() {
1611 let input = "\
1612 !<arch>\n\
1613 foo.txt helloworld 501 20 100644 7 `\n\
1614 foobar\n\n";
1615 let mut archive = Archive::new(input.as_bytes());
1616 archive.next_entry().unwrap().unwrap();
1617 }
1618
1619 #[test]
1620 #[should_panic(expected = "Invalid owner ID field in entry header \
1621 (\\\"foo \\\")")]
1622 fn read_archive_with_invalid_uid() {
1623 let input = "\
1624 !<arch>\n\
1625 foo.txt 1487552916 foo 20 100644 7 `\n\
1626 foobar\n\n";
1627 let mut archive = Archive::new(input.as_bytes());
1628 archive.next_entry().unwrap().unwrap();
1629 }
1630
1631 #[test]
1632 #[should_panic(expected = "Invalid group ID field in entry header \
1633 (\\\"bar \\\")")]
1634 fn read_archive_with_invalid_gid() {
1635 let input = "\
1636 !<arch>\n\
1637 foo.txt 1487552916 501 bar 100644 7 `\n\
1638 foobar\n\n";
1639 let mut archive = Archive::new(input.as_bytes());
1640 archive.next_entry().unwrap().unwrap();
1641 }
1642
1643 #[test]
1644 #[should_panic(expected = "Invalid file mode field in entry header \
1645 (\\\"foobar \\\")")]
1646 fn read_archive_with_invalid_mode() {
1647 let input = "\
1648 !<arch>\n\
1649 foo.txt 1487552916 501 20 foobar 7 `\n\
1650 foobar\n\n";
1651 let mut archive = Archive::new(input.as_bytes());
1652 archive.next_entry().unwrap().unwrap();
1653 }
1654
1655 #[test]
1656 #[should_panic(expected = "Invalid file size field in entry header \
1657 (\\\"whatever \\\")")]
1658 fn read_archive_with_invalid_size() {
1659 let input = "\
1660 !<arch>\n\
1661 foo.txt 1487552916 501 20 100644 whatever `\n\
1662 foobar\n\n";
1663 let mut archive = Archive::new(input.as_bytes());
1664 archive.next_entry().unwrap().unwrap();
1665 }
1666
1667 #[test]
1668 #[should_panic(expected = "Invalid BSD filename length field in entry \
1669 header (\\\"foobar \\\")")]
1670 fn read_bsd_archive_with_invalid_filename_length() {
1671 let input = "\
1672 !<arch>\n\
1673 #1/foobar 1487552916 501 20 100644 39 `\n\
1674 this_is_a_very_long_filename.txtfoobar\n\n";
1675 let mut archive = Archive::new(input.as_bytes());
1676 archive.next_entry().unwrap().unwrap();
1677 }
1678
1679 #[test]
1680 #[should_panic(expected = "Invalid GNU filename index field in entry \
1681 header (\\\"foobar \\\")")]
1682 fn read_gnu_archive_with_invalid_filename_index() {
1683 let input = "\
1684 !<arch>\n\
1685 // 34 `\n\
1686 this_is_a_very_long_filename.txt/\n\
1687 /foobar 1487552916 501 20 100644 7 `\n\
1688 foobar\n\n";
1689 let mut archive = Archive::new(input.as_bytes());
1690 archive.next_entry().unwrap().unwrap();
1691 }
1692
1693 #[test]
1694 fn seek_within_entry() {
1695 let input = "\
1696 !<arch>\n\
1697 foo.txt 1487552916 501 20 100644 31 `\n\
1698 abcdefghij0123456789ABCDEFGHIJ\n\n\
1699 bar.awesome.txt 1487552919 501 20 100644 22 `\n\
1700 This file is awesome!\n";
1701 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1702 {
1703 let mut entry = archive.next_entry().unwrap().unwrap();
1706 let mut buffer = [0; 5];
1707 entry.seek(SeekFrom::Start(10)).unwrap();
1708 entry.read_exact(&mut buffer).unwrap();
1709 assert_eq!(&buffer, "01234".as_bytes());
1710 entry.seek(SeekFrom::Start(5)).unwrap();
1711 entry.read_exact(&mut buffer).unwrap();
1712 assert_eq!(&buffer, "fghij".as_bytes());
1713 entry.seek(SeekFrom::End(-10)).unwrap();
1714 entry.read_exact(&mut buffer).unwrap();
1715 assert_eq!(&buffer, "BCDEF".as_bytes());
1716 entry.seek(SeekFrom::End(-30)).unwrap();
1717 entry.read_exact(&mut buffer).unwrap();
1718 assert_eq!(&buffer, "bcdef".as_bytes());
1719 entry.seek(SeekFrom::Current(10)).unwrap();
1720 entry.read_exact(&mut buffer).unwrap();
1721 assert_eq!(&buffer, "6789A".as_bytes());
1722 entry.seek(SeekFrom::Current(-8)).unwrap();
1723 entry.read_exact(&mut buffer).unwrap();
1724 assert_eq!(&buffer, "34567".as_bytes());
1725 }
1729 {
1730 let mut entry = archive.next_entry().unwrap().unwrap();
1732 let mut buffer = Vec::new();
1733 entry.read_to_end(&mut buffer).unwrap();
1734 assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
1735 }
1736 }
1737
1738 #[test]
1739 #[should_panic(expected = "Invalid seek to negative position (-17)")]
1740 fn seek_entry_to_negative_position() {
1741 let input = "\
1742 !<arch>\n\
1743 foo.txt 1487552916 501 20 100644 30 `\n\
1744 abcdefghij0123456789ABCDEFGHIJ";
1745 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1746 let mut entry = archive.next_entry().unwrap().unwrap();
1747 entry.seek(SeekFrom::End(-47)).unwrap();
1748 }
1749
1750 #[test]
1751 #[should_panic(expected = "Invalid seek to position past end of entry \
1752 (47 vs. 30)")]
1753 fn seek_entry_beyond_end() {
1754 let input = "\
1755 !<arch>\n\
1756 foo.txt 1487552916 501 20 100644 30 `\n\
1757 abcdefghij0123456789ABCDEFGHIJ";
1758 let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1759 let mut entry = archive.next_entry().unwrap().unwrap();
1760 entry.seek(SeekFrom::Start(47)).unwrap();
1761 }
1762
1763 #[test]
1764 fn count_entries_in_bsd_archive() {
1765 let input = b"\
1766 !<arch>\n\
1767 #1/32 1487552916 501 20 100644 39 `\n\
1768 this_is_a_very_long_filename.txtfoobar\n\n\
1769 baz.txt 0 0 0 0 4 `\n\
1770 baz\n";
1771 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1772 assert_eq!(archive.count_entries().unwrap(), 2);
1773 {
1774 let mut entry = archive.next_entry().unwrap().unwrap();
1775 assert_eq!(
1776 entry.header().identifier(),
1777 "this_is_a_very_long_filename.txt".as_bytes()
1778 );
1779 let mut buffer = Vec::new();
1780 entry.read_to_end(&mut buffer).unwrap();
1781 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1782 }
1783 assert_eq!(archive.count_entries().unwrap(), 2);
1784 {
1785 let mut entry = archive.next_entry().unwrap().unwrap();
1786 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1787 let mut buffer = Vec::new();
1788 entry.read_to_end(&mut buffer).unwrap();
1789 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1790 }
1791 assert_eq!(archive.count_entries().unwrap(), 2);
1792 }
1793
1794 #[test]
1795 fn count_entries_in_gnu_archive() {
1796 let input = b"\
1797 !<arch>\n\
1798 / 0 0 0 0 15 `\n\
1799 \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
1800 // 34 `\n\
1801 this_is_a_very_long_filename.txt/\n\
1802 /0 1487552916 501 20 100644 7 `\n\
1803 foobar\n\n\
1804 baz.txt/ 1487552349 42 12345 100664 4 `\n\
1805 baz\n";
1806 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1807 assert_eq!(archive.count_entries().unwrap(), 2);
1808 {
1809 let mut entry = archive.next_entry().unwrap().unwrap();
1810 assert_eq!(
1811 entry.header().identifier(),
1812 "this_is_a_very_long_filename.txt".as_bytes()
1813 );
1814 let mut buffer = Vec::new();
1815 entry.read_to_end(&mut buffer).unwrap();
1816 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1817 }
1818 assert_eq!(archive.count_entries().unwrap(), 2);
1819 {
1820 let mut entry = archive.next_entry().unwrap().unwrap();
1821 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1822 let mut buffer = Vec::new();
1823 entry.read_to_end(&mut buffer).unwrap();
1824 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1825 }
1826 assert_eq!(archive.count_entries().unwrap(), 2);
1827 }
1828
1829 #[test]
1830 fn jump_to_entry_in_bsd_archive() {
1831 let input = b"\
1832 !<arch>\n\
1833 hello.txt 1487552316 42 12345 100644 14 `\n\
1834 Hello, world!\n\
1835 #1/32 1487552916 501 20 100644 39 `\n\
1836 this_is_a_very_long_filename.txtfoobar\n\n\
1837 baz.txt 1487552349 42 12345 100664 4 `\n\
1838 baz\n";
1839 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1840 {
1841 let mut entry = archive.jump_to_entry(1).unwrap();
1843 assert_eq!(
1844 entry.header().identifier(),
1845 "this_is_a_very_long_filename.txt".as_bytes()
1846 );
1847 let mut buffer = Vec::new();
1848 entry.read_to_end(&mut buffer).unwrap();
1849 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1850 }
1851 {
1852 let mut entry = archive.next_entry().unwrap().unwrap();
1854 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1855 let mut buffer = Vec::new();
1856 entry.read_to_end(&mut buffer).unwrap();
1857 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1858 }
1859 assert!(archive.next_entry().is_none());
1861 {
1862 let mut entry = archive.jump_to_entry(0).unwrap();
1864 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1865 let mut buffer = Vec::new();
1866 entry.read_to_end(&mut buffer).unwrap();
1867 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1868 }
1869 {
1870 let mut entry = archive.jump_to_entry(1).unwrap();
1872 assert_eq!(
1873 entry.header().identifier(),
1874 "this_is_a_very_long_filename.txt".as_bytes()
1875 );
1876 let mut buffer = Vec::new();
1877 entry.read_to_end(&mut buffer).unwrap();
1878 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1879 }
1880 {
1881 let mut entry = archive.jump_to_entry(0).unwrap();
1883 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1884 let mut buffer = Vec::new();
1885 entry.read_to_end(&mut buffer).unwrap();
1886 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1887 }
1888 {
1889 let mut entry = archive.next_entry().unwrap().unwrap();
1891 assert_eq!(
1892 entry.header().identifier(),
1893 "this_is_a_very_long_filename.txt".as_bytes()
1894 );
1895 let mut buffer = Vec::new();
1896 entry.read_to_end(&mut buffer).unwrap();
1897 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1898 }
1899 }
1900
1901 #[test]
1902 fn jump_to_entry_in_gnu_archive() {
1903 let input = b"\
1904 !<arch>\n\
1905 // 34 `\n\
1906 this_is_a_very_long_filename.txt/\n\
1907 hello.txt/ 1487552316 42 12345 100644 14 `\n\
1908 Hello, world!\n\
1909 /0 1487552916 501 20 100644 7 `\n\
1910 foobar\n\n\
1911 baz.txt/ 1487552349 42 12345 100664 4 `\n\
1912 baz\n";
1913 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1914 {
1915 let mut entry = archive.jump_to_entry(1).unwrap();
1917 assert_eq!(
1918 entry.header().identifier(),
1919 "this_is_a_very_long_filename.txt".as_bytes()
1920 );
1921 let mut buffer = Vec::new();
1922 entry.read_to_end(&mut buffer).unwrap();
1923 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1924 }
1925 {
1926 let mut entry = archive.next_entry().unwrap().unwrap();
1928 assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1929 let mut buffer = Vec::new();
1930 entry.read_to_end(&mut buffer).unwrap();
1931 assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1932 }
1933 assert!(archive.next_entry().is_none());
1935 {
1936 let mut entry = archive.jump_to_entry(0).unwrap();
1938 assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1939 let mut buffer = Vec::new();
1940 entry.read_to_end(&mut buffer).unwrap();
1941 assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1942 }
1943 {
1944 let mut entry = archive.next_entry().unwrap().unwrap();
1946 assert_eq!(
1947 entry.header().identifier(),
1948 "this_is_a_very_long_filename.txt".as_bytes()
1949 );
1950 let mut buffer = Vec::new();
1951 entry.read_to_end(&mut buffer).unwrap();
1952 assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1953 }
1954 }
1955
1956 #[test]
1957 fn list_symbols_in_bsd_archive() {
1958 let input = b"\
1959 !<arch>\n\
1960 #1/12 0 0 0 0 60 `\n\
1961 __.SYMDEF\x00\x00\x00\x18\x00\x00\x00\
1962 \x00\x00\x00\x00\x80\x00\x00\x00\
1963 \x07\x00\x00\x00\x80\x00\x00\x00\
1964 \x0b\x00\x00\x00\x80\x00\x00\x00\
1965 \x10\x00\x00\x00foobar\x00baz\x00quux\x00\
1966 foo.o/ 1487552916 501 20 100644 16 `\n\
1967 foobar,baz,quux\n";
1968 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1969 assert_eq!(archive.symbols().unwrap().len(), 3);
1970 assert_eq!(archive.variant(), Variant::BSD);
1971 let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
1972 let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
1973 assert_eq!(symbols, expected);
1974 }
1975
1976 #[test]
1977 fn list_sorted_symbols_in_bsd_archive() {
1978 let input = b"\
1979 !<arch>\n\
1980 #1/16 0 0 0 0 64 `\n\
1981 __.SYMDEF SORTED\x18\x00\x00\x00\
1982 \x00\x00\x00\x00\x80\x00\x00\x00\
1983 \x04\x00\x00\x00\x80\x00\x00\x00\
1984 \x0b\x00\x00\x00\x80\x00\x00\x00\
1985 \x10\x00\x00\x00baz\x00foobar\x00quux\x00\
1986 foo.o/ 1487552916 501 20 100644 16 `\n\
1987 foobar,baz,quux\n";
1988 let mut archive = Archive::new(Cursor::new(input as &[u8]));
1989 assert_eq!(archive.symbols().unwrap().len(), 3);
1990 assert_eq!(archive.variant(), Variant::BSD);
1991 let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
1992 let expected: Vec<&[u8]> = vec![b"baz", b"foobar", b"quux"];
1993 assert_eq!(symbols, expected);
1994 }
1995
1996 #[test]
1997 fn list_symbols_in_gnu_archive() {
1998 let input = b"\
1999 !<arch>\n\
2000 / 0 0 0 0 32 `\n\
2001 \x00\x00\x00\x03\x00\x00\x00\x5c\x00\x00\x00\x5c\x00\x00\x00\x5c\
2002 foobar\x00baz\x00quux\x00\
2003 foo.o/ 1487552916 501 20 100644 16 `\n\
2004 foobar,baz,quux\n";
2005 let mut archive = Archive::new(Cursor::new(input as &[u8]));
2006 assert_eq!(archive.symbols().unwrap().len(), 3);
2007 assert_eq!(archive.variant(), Variant::GNU);
2008 let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
2009 let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
2010 assert_eq!(symbols, expected);
2011 }
2012
2013 #[test]
2014 fn non_multiple_of_two_long_ident_in_gnu_archive() {
2015 let mut buffer = std::io::Cursor::new(Vec::new());
2016
2017 {
2018 let filenames = vec![
2019 b"rust.metadata.bin".to_vec(),
2020 b"compiler_builtins-78891cf83a7d3547.dummy_name.rcgu.o"
2021 .to_vec(),
2022 ];
2023 let mut builder = GnuBuilder::new(&mut buffer, filenames.clone());
2024
2025 for filename in filenames {
2026 builder
2027 .append(&Header::new(filename, 1), &mut (&[b'?'] as &[u8]))
2028 .expect("add file");
2029 }
2030 }
2031
2032 buffer.set_position(0);
2033
2034 let mut archive = Archive::new(buffer);
2035 while let Some(entry) = archive.next_entry() {
2036 entry.unwrap();
2037 }
2038 }
2039}
2040
2041