1 | //! Support for archive files. |
2 | //! |
3 | //! ## Example |
4 | //! ```no_run |
5 | //! use object::{Object, ObjectSection}; |
6 | //! use std::error::Error; |
7 | //! use std::fs; |
8 | //! |
9 | //! /// Reads an archive and displays the name of each member. |
10 | //! fn main() -> Result<(), Box<dyn Error>> { |
11 | //! # #[cfg (feature = "std" )] { |
12 | //! let data = fs::read("path/to/binary" )?; |
13 | //! let file = object::read::archive::ArchiveFile::parse(&*data)?; |
14 | //! for member in file.members() { |
15 | //! let member = member?; |
16 | //! println!("{}" , String::from_utf8_lossy(member.name())); |
17 | //! } |
18 | //! # } |
19 | //! Ok(()) |
20 | //! } |
21 | //! ``` |
22 | |
23 | use core::convert::TryInto; |
24 | |
25 | use crate::archive; |
26 | use crate::read::{self, Bytes, Error, ReadError, ReadRef}; |
27 | |
28 | /// The kind of archive format. |
29 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Hash)] |
30 | #[non_exhaustive ] |
31 | pub enum ArchiveKind { |
32 | /// There are no special files that indicate the archive format. |
33 | Unknown, |
34 | /// The GNU (or System V) archive format. |
35 | Gnu, |
36 | /// The GNU (or System V) archive format with 64-bit symbol table. |
37 | Gnu64, |
38 | /// The BSD archive format. |
39 | Bsd, |
40 | /// The BSD archive format with 64-bit symbol table. |
41 | /// |
42 | /// This is used for Darwin. |
43 | Bsd64, |
44 | /// The Windows COFF archive format. |
45 | Coff, |
46 | /// The AIX big archive format. |
47 | AixBig, |
48 | } |
49 | |
50 | /// The list of members in the archive. |
51 | #[derive (Debug, Clone, Copy)] |
52 | enum Members<'data> { |
53 | Common { |
54 | offset: u64, |
55 | end_offset: u64, |
56 | }, |
57 | AixBig { |
58 | index: &'data [archive::AixMemberOffset], |
59 | }, |
60 | } |
61 | |
62 | /// A partially parsed archive file. |
63 | #[derive (Debug, Clone, Copy)] |
64 | pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> { |
65 | data: R, |
66 | kind: ArchiveKind, |
67 | members: Members<'data>, |
68 | symbols: (u64, u64), |
69 | names: &'data [u8], |
70 | } |
71 | |
72 | impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { |
73 | /// Parse the archive header and special members. |
74 | pub fn parse(data: R) -> read::Result<Self> { |
75 | let len = data.len().read_error("Unknown archive length" )?; |
76 | let mut tail = 0; |
77 | let magic = data |
78 | .read_bytes(&mut tail, archive::MAGIC.len() as u64) |
79 | .read_error("Invalid archive size" )?; |
80 | |
81 | if magic == archive::AIX_BIG_MAGIC { |
82 | return Self::parse_aixbig(data); |
83 | } else if magic != archive::MAGIC { |
84 | return Err(Error("Unsupported archive identifier" )); |
85 | } |
86 | |
87 | let mut members_offset = tail; |
88 | let members_end_offset = len; |
89 | |
90 | let mut file = ArchiveFile { |
91 | data, |
92 | kind: ArchiveKind::Unknown, |
93 | members: Members::Common { |
94 | offset: 0, |
95 | end_offset: 0, |
96 | }, |
97 | symbols: (0, 0), |
98 | names: &[], |
99 | }; |
100 | |
101 | // The first few members may be special, so parse them. |
102 | // GNU has: |
103 | // - "/" or "/SYM64/": symbol table (optional) |
104 | // - "//": names table (optional) |
105 | // COFF has: |
106 | // - "/": first linker member |
107 | // - "/": second linker member |
108 | // - "//": names table |
109 | // BSD has: |
110 | // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional) |
111 | // BSD 64-bit has: |
112 | // - "__.SYMDEF_64" or "__.SYMDEF_64 SORTED": symbol table (optional) |
113 | // BSD may use the extended name for the symbol table. This is handled |
114 | // by `ArchiveMember::parse`. |
115 | if tail < len { |
116 | let member = ArchiveMember::parse(data, &mut tail, &[])?; |
117 | if member.name == b"/" { |
118 | // GNU symbol table (unless we later determine this is COFF). |
119 | file.kind = ArchiveKind::Gnu; |
120 | file.symbols = member.file_range(); |
121 | members_offset = tail; |
122 | |
123 | if tail < len { |
124 | let member = ArchiveMember::parse(data, &mut tail, &[])?; |
125 | if member.name == b"/" { |
126 | // COFF linker member. |
127 | file.kind = ArchiveKind::Coff; |
128 | file.symbols = member.file_range(); |
129 | members_offset = tail; |
130 | |
131 | if tail < len { |
132 | let member = ArchiveMember::parse(data, &mut tail, &[])?; |
133 | if member.name == b"//" { |
134 | // COFF names table. |
135 | file.names = member.data(data)?; |
136 | members_offset = tail; |
137 | } |
138 | } |
139 | } else if member.name == b"//" { |
140 | // GNU names table. |
141 | file.names = member.data(data)?; |
142 | members_offset = tail; |
143 | } |
144 | } |
145 | } else if member.name == b"/SYM64/" { |
146 | // GNU 64-bit symbol table. |
147 | file.kind = ArchiveKind::Gnu64; |
148 | file.symbols = member.file_range(); |
149 | members_offset = tail; |
150 | |
151 | if tail < len { |
152 | let member = ArchiveMember::parse(data, &mut tail, &[])?; |
153 | if member.name == b"//" { |
154 | // GNU names table. |
155 | file.names = member.data(data)?; |
156 | members_offset = tail; |
157 | } |
158 | } |
159 | } else if member.name == b"//" { |
160 | // GNU names table. |
161 | file.kind = ArchiveKind::Gnu; |
162 | file.names = member.data(data)?; |
163 | members_offset = tail; |
164 | } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" { |
165 | // BSD symbol table. |
166 | file.kind = ArchiveKind::Bsd; |
167 | file.symbols = member.file_range(); |
168 | members_offset = tail; |
169 | } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" { |
170 | // BSD 64-bit symbol table. |
171 | file.kind = ArchiveKind::Bsd64; |
172 | file.symbols = member.file_range(); |
173 | members_offset = tail; |
174 | } else { |
175 | // TODO: This could still be a BSD file. We leave this as unknown for now. |
176 | } |
177 | } |
178 | file.members = Members::Common { |
179 | offset: members_offset, |
180 | end_offset: members_end_offset, |
181 | }; |
182 | Ok(file) |
183 | } |
184 | |
185 | fn parse_aixbig(data: R) -> read::Result<Self> { |
186 | let mut tail = 0; |
187 | |
188 | let file_header = data |
189 | .read::<archive::AixFileHeader>(&mut tail) |
190 | .read_error("Invalid AIX big archive file header" )?; |
191 | // Caller already validated this. |
192 | debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC); |
193 | |
194 | let mut file = ArchiveFile { |
195 | data, |
196 | kind: ArchiveKind::AixBig, |
197 | members: Members::AixBig { index: &[] }, |
198 | symbols: (0, 0), |
199 | names: &[], |
200 | }; |
201 | |
202 | // Read the span of symbol table. |
203 | let symtbl64 = parse_u64_digits(&file_header.gst64off, 10) |
204 | .read_error("Invalid offset to 64-bit symbol table in AIX big archive" )?; |
205 | if symtbl64 > 0 { |
206 | // The symbol table is also a file with header. |
207 | let member = ArchiveMember::parse_aixbig(data, symtbl64)?; |
208 | file.symbols = member.file_range(); |
209 | } else { |
210 | let symtbl = parse_u64_digits(&file_header.gstoff, 10) |
211 | .read_error("Invalid offset to symbol table in AIX big archive" )?; |
212 | if symtbl > 0 { |
213 | // The symbol table is also a file with header. |
214 | let member = ArchiveMember::parse_aixbig(data, symtbl)?; |
215 | file.symbols = member.file_range(); |
216 | } |
217 | } |
218 | |
219 | // Big archive member index table lists file entries with offsets and names. |
220 | // To avoid potential infinite loop (members are double-linked list), the |
221 | // iterator goes through the index instead of real members. |
222 | let member_table_offset = parse_u64_digits(&file_header.memoff, 10) |
223 | .read_error("Invalid offset for member table of AIX big archive" )?; |
224 | if member_table_offset == 0 { |
225 | // The offset would be zero if archive contains no file. |
226 | return Ok(file); |
227 | } |
228 | |
229 | // The member index table is also a file with header. |
230 | let member = ArchiveMember::parse_aixbig(data, member_table_offset)?; |
231 | let mut member_data = Bytes(member.data(data)?); |
232 | |
233 | // Structure of member index table: |
234 | // Number of entries (20 bytes) |
235 | // Offsets of each entry (20*N bytes) |
236 | // Names string table (the rest of bytes to fill size defined in header) |
237 | let members_count_bytes = member_data |
238 | .read_slice::<u8>(20) |
239 | .read_error("Missing member count in AIX big archive" )?; |
240 | let members_count = parse_u64_digits(members_count_bytes, 10) |
241 | .and_then(|size| size.try_into().ok()) |
242 | .read_error("Invalid member count in AIX big archive" )?; |
243 | let index = member_data |
244 | .read_slice::<archive::AixMemberOffset>(members_count) |
245 | .read_error("Member count overflow in AIX big archive" )?; |
246 | file.members = Members::AixBig { index }; |
247 | |
248 | Ok(file) |
249 | } |
250 | |
251 | /// Return the archive format. |
252 | #[inline ] |
253 | pub fn kind(&self) -> ArchiveKind { |
254 | self.kind |
255 | } |
256 | |
257 | /// Iterate over the members of the archive. |
258 | /// |
259 | /// This does not return special members. |
260 | #[inline ] |
261 | pub fn members(&self) -> ArchiveMemberIterator<'data, R> { |
262 | ArchiveMemberIterator { |
263 | data: self.data, |
264 | members: self.members, |
265 | names: self.names, |
266 | } |
267 | } |
268 | } |
269 | |
270 | /// An iterator over the members of an archive. |
271 | #[derive (Debug)] |
272 | pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> { |
273 | data: R, |
274 | members: Members<'data>, |
275 | names: &'data [u8], |
276 | } |
277 | |
278 | impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { |
279 | type Item = read::Result<ArchiveMember<'data>>; |
280 | |
281 | fn next(&mut self) -> Option<Self::Item> { |
282 | match &mut self.members { |
283 | Members::Common { |
284 | ref mut offset, |
285 | ref mut end_offset, |
286 | } => { |
287 | if *offset >= *end_offset { |
288 | return None; |
289 | } |
290 | let member = ArchiveMember::parse(self.data, offset, self.names); |
291 | if member.is_err() { |
292 | *offset = *end_offset; |
293 | } |
294 | Some(member) |
295 | } |
296 | Members::AixBig { ref mut index } => match **index { |
297 | [] => None, |
298 | [ref first, ref rest @ ..] => { |
299 | *index = rest; |
300 | let member = ArchiveMember::parse_aixbig_index(self.data, first); |
301 | if member.is_err() { |
302 | *index = &[]; |
303 | } |
304 | Some(member) |
305 | } |
306 | }, |
307 | } |
308 | } |
309 | } |
310 | |
311 | /// An archive member header. |
312 | #[derive (Debug, Clone, Copy)] |
313 | enum MemberHeader<'data> { |
314 | /// Common header used by many formats. |
315 | Common(&'data archive::Header), |
316 | /// AIX big archive header |
317 | AixBig(&'data archive::AixHeader), |
318 | } |
319 | |
320 | /// A partially parsed archive member. |
321 | #[derive (Debug)] |
322 | pub struct ArchiveMember<'data> { |
323 | header: MemberHeader<'data>, |
324 | name: &'data [u8], |
325 | offset: u64, |
326 | size: u64, |
327 | } |
328 | |
329 | impl<'data> ArchiveMember<'data> { |
330 | /// Parse the member header, name, and file data in an archive with the common format. |
331 | /// |
332 | /// This reads the extended name (if any) and adjusts the file size. |
333 | fn parse<R: ReadRef<'data>>( |
334 | data: R, |
335 | offset: &mut u64, |
336 | names: &'data [u8], |
337 | ) -> read::Result<Self> { |
338 | let header = data |
339 | .read::<archive::Header>(offset) |
340 | .read_error("Invalid archive member header" )?; |
341 | if header.terminator != archive::TERMINATOR { |
342 | return Err(Error("Invalid archive terminator" )); |
343 | } |
344 | |
345 | let mut file_offset = *offset; |
346 | let mut file_size = |
347 | parse_u64_digits(&header.size, 10).read_error("Invalid archive member size" )?; |
348 | *offset = offset |
349 | .checked_add(file_size) |
350 | .read_error("Archive member size is too large" )?; |
351 | // Entries are padded to an even number of bytes. |
352 | if (file_size & 1) != 0 { |
353 | *offset = offset.saturating_add(1); |
354 | } |
355 | |
356 | let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() { |
357 | // Read file name from the names table. |
358 | parse_sysv_extended_name(&header.name[1..], names) |
359 | .read_error("Invalid archive extended name offset" )? |
360 | } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() { |
361 | // Read file name from the start of the file data. |
362 | parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size) |
363 | .read_error("Invalid archive extended name length" )? |
364 | } else if header.name[0] == b'/' { |
365 | let name_len = memchr::memchr(b' ' , &header.name).unwrap_or(header.name.len()); |
366 | &header.name[..name_len] |
367 | } else { |
368 | let name_len = memchr::memchr(b'/' , &header.name) |
369 | .or_else(|| memchr::memchr(b' ' , &header.name)) |
370 | .unwrap_or(header.name.len()); |
371 | &header.name[..name_len] |
372 | }; |
373 | |
374 | Ok(ArchiveMember { |
375 | header: MemberHeader::Common(header), |
376 | name, |
377 | offset: file_offset, |
378 | size: file_size, |
379 | }) |
380 | } |
381 | |
382 | /// Parse a member index entry in an AIX big archive, |
383 | /// and then parse the member header, name, and file data. |
384 | fn parse_aixbig_index<R: ReadRef<'data>>( |
385 | data: R, |
386 | index: &archive::AixMemberOffset, |
387 | ) -> read::Result<Self> { |
388 | let offset = parse_u64_digits(&index.0, 10) |
389 | .read_error("Invalid AIX big archive file member offset" )?; |
390 | Self::parse_aixbig(data, offset) |
391 | } |
392 | |
393 | /// Parse the member header, name, and file data in an AIX big archive. |
394 | fn parse_aixbig<R: ReadRef<'data>>(data: R, mut offset: u64) -> read::Result<Self> { |
395 | // The format was described at |
396 | // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big |
397 | let header = data |
398 | .read::<archive::AixHeader>(&mut offset) |
399 | .read_error("Invalid AIX big archive member header" )?; |
400 | let name_length = parse_u64_digits(&header.namlen, 10) |
401 | .read_error("Invalid AIX big archive member name length" )?; |
402 | let name = data |
403 | .read_bytes(&mut offset, name_length) |
404 | .read_error("Invalid AIX big archive member name" )?; |
405 | |
406 | // The actual data for a file member begins at the first even-byte boundary beyond the |
407 | // member header and continues for the number of bytes specified by the ar_size field. The |
408 | // ar command inserts null bytes for padding where necessary. |
409 | if offset & 1 != 0 { |
410 | offset = offset.saturating_add(1); |
411 | } |
412 | // Because of the even-byte boundary, we have to read and check terminator after header. |
413 | let terminator = data |
414 | .read_bytes(&mut offset, 2) |
415 | .read_error("Invalid AIX big archive terminator" )?; |
416 | if terminator != archive::TERMINATOR { |
417 | return Err(Error("Invalid AIX big archive terminator" )); |
418 | } |
419 | |
420 | let size = parse_u64_digits(&header.size, 10) |
421 | .read_error("Invalid archive member size in AIX big archive" )?; |
422 | Ok(ArchiveMember { |
423 | header: MemberHeader::AixBig(header), |
424 | name, |
425 | offset, |
426 | size, |
427 | }) |
428 | } |
429 | |
430 | /// Return the raw header that is common to many archive formats. |
431 | /// |
432 | /// Returns `None` if this archive does not use the common header format. |
433 | #[inline ] |
434 | pub fn header(&self) -> Option<&'data archive::Header> { |
435 | match self.header { |
436 | MemberHeader::Common(header) => Some(header), |
437 | _ => None, |
438 | } |
439 | } |
440 | |
441 | /// Return the raw header for AIX big archives. |
442 | /// |
443 | /// Returns `None` if this is not an AIX big archive. |
444 | #[inline ] |
445 | pub fn aix_header(&self) -> Option<&'data archive::AixHeader> { |
446 | match self.header { |
447 | MemberHeader::AixBig(header) => Some(header), |
448 | _ => None, |
449 | } |
450 | } |
451 | |
452 | /// Return the parsed file name. |
453 | /// |
454 | /// This may be an extended file name. |
455 | #[inline ] |
456 | pub fn name(&self) -> &'data [u8] { |
457 | self.name |
458 | } |
459 | |
460 | /// Parse the file modification timestamp from the header. |
461 | #[inline ] |
462 | pub fn date(&self) -> Option<u64> { |
463 | match &self.header { |
464 | MemberHeader::Common(header) => parse_u64_digits(&header.date, 10), |
465 | MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10), |
466 | } |
467 | } |
468 | |
469 | /// Parse the user ID from the header. |
470 | #[inline ] |
471 | pub fn uid(&self) -> Option<u64> { |
472 | match &self.header { |
473 | MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10), |
474 | MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10), |
475 | } |
476 | } |
477 | |
478 | /// Parse the group ID from the header. |
479 | #[inline ] |
480 | pub fn gid(&self) -> Option<u64> { |
481 | match &self.header { |
482 | MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10), |
483 | MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10), |
484 | } |
485 | } |
486 | |
487 | /// Parse the file mode from the header. |
488 | #[inline ] |
489 | pub fn mode(&self) -> Option<u64> { |
490 | match &self.header { |
491 | MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), |
492 | MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), |
493 | } |
494 | } |
495 | |
496 | /// Return the offset and size of the file data. |
497 | pub fn file_range(&self) -> (u64, u64) { |
498 | (self.offset, self.size) |
499 | } |
500 | |
501 | /// Return the file data. |
502 | #[inline ] |
503 | pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> { |
504 | data.read_bytes_at(self.offset, self.size) |
505 | .read_error("Archive member size is too large" ) |
506 | } |
507 | } |
508 | |
509 | // Ignores bytes starting from the first space. |
510 | fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> { |
511 | if let [b' ' , ..] = digits { |
512 | return None; |
513 | } |
514 | let mut result: u64 = 0; |
515 | for &c: u8 in digits { |
516 | if c == b' ' { |
517 | return Some(result); |
518 | } else { |
519 | let x: u32 = (c as char).to_digit(radix)?; |
520 | result = resultu64 |
521 | .checked_mul(u64::from(radix))? |
522 | .checked_add(u64::from(x))?; |
523 | } |
524 | } |
525 | Some(result) |
526 | } |
527 | |
528 | fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> { |
529 | let offset: u64 = parse_u64_digits(digits, 10).ok_or(())?; |
530 | let offset: usize = offset.try_into().map_err(|_| ())?; |
531 | let name_data: &[u8] = names.get(offset..).ok_or(())?; |
532 | let name: &[u8] = match memchr::memchr2(needle1:b'/' , needle2:b' \0' , haystack:name_data) { |
533 | Some(len: usize) => &name_data[..len], |
534 | None => name_data, |
535 | }; |
536 | Ok(name) |
537 | } |
538 | |
539 | /// Modifies `data` to start after the extended name. |
540 | fn parse_bsd_extended_name<'data, R: ReadRef<'data>>( |
541 | digits: &[u8], |
542 | data: R, |
543 | offset: &mut u64, |
544 | size: &mut u64, |
545 | ) -> Result<&'data [u8], ()> { |
546 | let len: u64 = parse_u64_digits(digits, 10).ok_or(())?; |
547 | *size = size.checked_sub(len).ok_or(())?; |
548 | let name_data: &[u8] = data.read_bytes(offset, size:len)?; |
549 | let name: &[u8] = match memchr::memchr(needle:b' \0' , haystack:name_data) { |
550 | Some(len: usize) => &name_data[..len], |
551 | None => name_data, |
552 | }; |
553 | Ok(name) |
554 | } |
555 | |
556 | #[cfg (test)] |
557 | mod tests { |
558 | use super::*; |
559 | |
560 | #[test ] |
561 | fn kind() { |
562 | let data = b"!<arch> \n" ; |
563 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
564 | assert_eq!(archive.kind(), ArchiveKind::Unknown); |
565 | |
566 | let data = b"\ |
567 | !<arch> \n\ |
568 | / 4 ` \n\ |
569 | 0000" ; |
570 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
571 | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
572 | |
573 | let data = b"\ |
574 | !<arch> \n\ |
575 | // 4 ` \n\ |
576 | 0000" ; |
577 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
578 | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
579 | |
580 | let data = b"\ |
581 | !<arch> \n\ |
582 | / 4 ` \n\ |
583 | 0000\ |
584 | // 4 ` \n\ |
585 | 0000" ; |
586 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
587 | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
588 | |
589 | let data = b"\ |
590 | !<arch> \n\ |
591 | /SYM64/ 4 ` \n\ |
592 | 0000" ; |
593 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
594 | assert_eq!(archive.kind(), ArchiveKind::Gnu64); |
595 | |
596 | let data = b"\ |
597 | !<arch> \n\ |
598 | /SYM64/ 4 ` \n\ |
599 | 0000\ |
600 | // 4 ` \n\ |
601 | 0000" ; |
602 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
603 | assert_eq!(archive.kind(), ArchiveKind::Gnu64); |
604 | |
605 | let data = b"\ |
606 | !<arch> \n\ |
607 | __.SYMDEF 4 ` \n\ |
608 | 0000" ; |
609 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
610 | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
611 | |
612 | let data = b"\ |
613 | !<arch> \n\ |
614 | #1/9 13 ` \n\ |
615 | __.SYMDEF0000" ; |
616 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
617 | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
618 | |
619 | let data = b"\ |
620 | !<arch> \n\ |
621 | #1/16 20 ` \n\ |
622 | __.SYMDEF SORTED0000" ; |
623 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
624 | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
625 | |
626 | let data = b"\ |
627 | !<arch> \n\ |
628 | __.SYMDEF_64 4 ` \n\ |
629 | 0000" ; |
630 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
631 | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
632 | |
633 | let data = b"\ |
634 | !<arch> \n\ |
635 | #1/12 16 ` \n\ |
636 | __.SYMDEF_640000" ; |
637 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
638 | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
639 | |
640 | let data = b"\ |
641 | !<arch> \n\ |
642 | #1/19 23 ` \n\ |
643 | __.SYMDEF_64 SORTED0000" ; |
644 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
645 | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
646 | |
647 | let data = b"\ |
648 | !<arch> \n\ |
649 | / 4 ` \n\ |
650 | 0000\ |
651 | / 4 ` \n\ |
652 | 0000\ |
653 | // 4 ` \n\ |
654 | 0000" ; |
655 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
656 | assert_eq!(archive.kind(), ArchiveKind::Coff); |
657 | |
658 | let data = b"\ |
659 | <bigaf> \n\ |
660 | 0 0 \ |
661 | 0 0 \ |
662 | 0 128 \ |
663 | 6 0 \ |
664 | 0 \0\0\0\0\0\0\0\0\0\0\0\0\ |
665 | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ |
666 | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ |
667 | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ; |
668 | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
669 | assert_eq!(archive.kind(), ArchiveKind::AixBig); |
670 | } |
671 | |
672 | #[test ] |
673 | fn gnu_names() { |
674 | let data = b"\ |
675 | !<arch> \n\ |
676 | // 18 ` \n\ |
677 | 0123456789abcdef/ \n\ |
678 | s p a c e/ 0 0 0 644 4 ` \n\ |
679 | 0000\ |
680 | 0123456789abcde/0 0 0 644 3 ` \n\ |
681 | odd \n\ |
682 | /0 0 0 0 644 4 ` \n\ |
683 | even" ; |
684 | let data = &data[..]; |
685 | let archive = ArchiveFile::parse(data).unwrap(); |
686 | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
687 | let mut members = archive.members(); |
688 | |
689 | let member = members.next().unwrap().unwrap(); |
690 | assert_eq!(member.name(), b"s p a c e" ); |
691 | assert_eq!(member.data(data).unwrap(), &b"0000" [..]); |
692 | |
693 | let member = members.next().unwrap().unwrap(); |
694 | assert_eq!(member.name(), b"0123456789abcde" ); |
695 | assert_eq!(member.data(data).unwrap(), &b"odd" [..]); |
696 | |
697 | let member = members.next().unwrap().unwrap(); |
698 | assert_eq!(member.name(), b"0123456789abcdef" ); |
699 | assert_eq!(member.data(data).unwrap(), &b"even" [..]); |
700 | |
701 | assert!(members.next().is_none()); |
702 | } |
703 | |
704 | #[test ] |
705 | fn bsd_names() { |
706 | let data = b"\ |
707 | !<arch> \n\ |
708 | 0123456789abcde 0 0 0 644 3 ` \n\ |
709 | odd \n\ |
710 | #1/16 0 0 0 644 20 ` \n\ |
711 | 0123456789abcdefeven" ; |
712 | let data = &data[..]; |
713 | let archive = ArchiveFile::parse(data).unwrap(); |
714 | assert_eq!(archive.kind(), ArchiveKind::Unknown); |
715 | let mut members = archive.members(); |
716 | |
717 | let member = members.next().unwrap().unwrap(); |
718 | assert_eq!(member.name(), b"0123456789abcde" ); |
719 | assert_eq!(member.data(data).unwrap(), &b"odd" [..]); |
720 | |
721 | let member = members.next().unwrap().unwrap(); |
722 | assert_eq!(member.name(), b"0123456789abcdef" ); |
723 | assert_eq!(member.data(data).unwrap(), &b"even" [..]); |
724 | |
725 | assert!(members.next().is_none()); |
726 | } |
727 | |
728 | #[test ] |
729 | fn aix_names() { |
730 | let data = b"\ |
731 | <bigaf> \n\ |
732 | 396 0 0 \ |
733 | 128 262 0 \ |
734 | 4 262 0 \ |
735 | 1662610370 223 1 644 16 \ |
736 | 0123456789abcdef` \nord \n\ |
737 | 4 396 128 \ |
738 | 1662610374 223 1 644 16 \ |
739 | fedcba9876543210` \nrev \n\ |
740 | 94 0 262 \ |
741 | 0 0 0 0 0 \ |
742 | ` \n2 128 \ |
743 | 262 0123456789abcdef \0fedcba9876543210 \0" ; |
744 | let data = &data[..]; |
745 | let archive = ArchiveFile::parse(data).unwrap(); |
746 | assert_eq!(archive.kind(), ArchiveKind::AixBig); |
747 | let mut members = archive.members(); |
748 | |
749 | let member = members.next().unwrap().unwrap(); |
750 | assert_eq!(member.name(), b"0123456789abcdef" ); |
751 | assert_eq!(member.data(data).unwrap(), &b"ord \n" [..]); |
752 | |
753 | let member = members.next().unwrap().unwrap(); |
754 | assert_eq!(member.name(), b"fedcba9876543210" ); |
755 | assert_eq!(member.data(data).unwrap(), &b"rev \n" [..]); |
756 | |
757 | assert!(members.next().is_none()); |
758 | } |
759 | } |
760 | |