/rust/registry/src/index.crates.io-1949cf8c6b5b557f/object-0.37.3/src/read/archive.rs
Line | Count | Source |
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 | | use core::slice; |
25 | | |
26 | | use crate::archive; |
27 | | use crate::endian::{BigEndian as BE, LittleEndian as LE, U16Bytes, U32Bytes, U64Bytes}; |
28 | | use crate::read::{self, Bytes, Error, ReadError, ReadRef}; |
29 | | |
30 | | /// The kind of archive format. |
31 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
32 | | #[non_exhaustive] |
33 | | pub enum ArchiveKind { |
34 | | /// There are no special files that indicate the archive format. |
35 | | Unknown, |
36 | | /// The GNU (or System V) archive format. |
37 | | Gnu, |
38 | | /// The GNU (or System V) archive format with 64-bit symbol table. |
39 | | Gnu64, |
40 | | /// The BSD archive format. |
41 | | Bsd, |
42 | | /// The BSD archive format with 64-bit symbol table. |
43 | | /// |
44 | | /// This is used for Darwin. |
45 | | Bsd64, |
46 | | /// The Windows COFF archive format. |
47 | | Coff, |
48 | | /// The AIX big archive format. |
49 | | AixBig, |
50 | | } |
51 | | |
52 | | /// The list of members in the archive. |
53 | | #[derive(Debug, Clone, Copy)] |
54 | | enum Members<'data> { |
55 | | Common { |
56 | | offset: u64, |
57 | | end_offset: u64, |
58 | | }, |
59 | | AixBig { |
60 | | index: &'data [archive::AixMemberOffset], |
61 | | }, |
62 | | } |
63 | | |
64 | | /// A partially parsed archive file. |
65 | | #[derive(Debug, Clone, Copy)] |
66 | | pub struct ArchiveFile<'data, R: ReadRef<'data> = &'data [u8]> { |
67 | | data: R, |
68 | | kind: ArchiveKind, |
69 | | members: Members<'data>, |
70 | | symbols: (u64, u64), |
71 | | names: &'data [u8], |
72 | | thin: bool, |
73 | | } |
74 | | |
75 | | impl<'data, R: ReadRef<'data>> ArchiveFile<'data, R> { |
76 | | /// Parse the archive header and special members. |
77 | 0 | pub fn parse(data: R) -> read::Result<Self> { |
78 | 0 | let len = data.len().read_error("Unknown archive length")?; |
79 | 0 | let mut tail = 0; |
80 | 0 | let magic = data |
81 | 0 | .read_bytes(&mut tail, archive::MAGIC.len() as u64) |
82 | 0 | .read_error("Invalid archive size")?; |
83 | | |
84 | 0 | let thin = if magic == archive::AIX_BIG_MAGIC { |
85 | 0 | return Self::parse_aixbig(data); |
86 | 0 | } else if magic == archive::THIN_MAGIC { |
87 | 0 | true |
88 | 0 | } else if magic == archive::MAGIC { |
89 | 0 | false |
90 | | } else { |
91 | 0 | return Err(Error("Unsupported archive identifier")); |
92 | | }; |
93 | | |
94 | 0 | let mut members_offset = tail; |
95 | 0 | let members_end_offset = len; |
96 | | |
97 | 0 | let mut file = ArchiveFile { |
98 | 0 | data, |
99 | 0 | kind: ArchiveKind::Unknown, |
100 | 0 | members: Members::Common { |
101 | 0 | offset: 0, |
102 | 0 | end_offset: 0, |
103 | 0 | }, |
104 | 0 | symbols: (0, 0), |
105 | 0 | names: &[], |
106 | 0 | thin, |
107 | 0 | }; |
108 | | |
109 | | // The first few members may be special, so parse them. |
110 | | // GNU has: |
111 | | // - "/" or "/SYM64/": symbol table (optional) |
112 | | // - "//": names table (optional) |
113 | | // COFF has: |
114 | | // - "/": first linker member |
115 | | // - "/": second linker member |
116 | | // - "//": names table |
117 | | // BSD has: |
118 | | // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional) |
119 | | // BSD 64-bit has: |
120 | | // - "__.SYMDEF_64" or "__.SYMDEF_64 SORTED": symbol table (optional) |
121 | | // BSD may use the extended name for the symbol table. This is handled |
122 | | // by `ArchiveMember::parse`. |
123 | 0 | if tail < len { |
124 | 0 | let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; |
125 | 0 | if member.name == b"/" { |
126 | | // GNU symbol table (unless we later determine this is COFF). |
127 | 0 | file.kind = ArchiveKind::Gnu; |
128 | 0 | file.symbols = member.file_range(); |
129 | 0 | members_offset = tail; |
130 | | |
131 | 0 | if tail < len { |
132 | 0 | let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; |
133 | 0 | if member.name == b"/" { |
134 | | // COFF linker member. |
135 | 0 | file.kind = ArchiveKind::Coff; |
136 | 0 | file.symbols = member.file_range(); |
137 | 0 | members_offset = tail; |
138 | | |
139 | 0 | if tail < len { |
140 | 0 | let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; |
141 | 0 | if member.name == b"//" { |
142 | | // COFF names table. |
143 | 0 | file.names = member.data(data)?; |
144 | 0 | members_offset = tail; |
145 | 0 | } |
146 | 0 | } |
147 | 0 | if tail < len { |
148 | 0 | let member = ArchiveMember::parse(data, &mut tail, file.names, thin)?; |
149 | 0 | if member.name == b"/<ECSYMBOLS>/" { |
150 | 0 | // COFF EC Symbol Table. |
151 | 0 | members_offset = tail; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | } else if member.name == b"//" { |
155 | | // GNU names table. |
156 | 0 | file.names = member.data(data)?; |
157 | 0 | members_offset = tail; |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } else if member.name == b"/SYM64/" { |
161 | | // GNU 64-bit symbol table. |
162 | 0 | file.kind = ArchiveKind::Gnu64; |
163 | 0 | file.symbols = member.file_range(); |
164 | 0 | members_offset = tail; |
165 | | |
166 | 0 | if tail < len { |
167 | 0 | let member = ArchiveMember::parse(data, &mut tail, &[], thin)?; |
168 | 0 | if member.name == b"//" { |
169 | | // GNU names table. |
170 | 0 | file.names = member.data(data)?; |
171 | 0 | members_offset = tail; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } else if member.name == b"//" { |
175 | | // GNU names table. |
176 | 0 | file.kind = ArchiveKind::Gnu; |
177 | 0 | file.names = member.data(data)?; |
178 | 0 | members_offset = tail; |
179 | 0 | } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" { |
180 | 0 | // BSD symbol table. |
181 | 0 | file.kind = ArchiveKind::Bsd; |
182 | 0 | file.symbols = member.file_range(); |
183 | 0 | members_offset = tail; |
184 | 0 | } else if member.name == b"__.SYMDEF_64" || member.name == b"__.SYMDEF_64 SORTED" { |
185 | 0 | // BSD 64-bit symbol table. |
186 | 0 | file.kind = ArchiveKind::Bsd64; |
187 | 0 | file.symbols = member.file_range(); |
188 | 0 | members_offset = tail; |
189 | 0 | } else { |
190 | 0 | // TODO: This could still be a BSD file. We leave this as unknown for now. |
191 | 0 | } |
192 | 0 | } |
193 | 0 | file.members = Members::Common { |
194 | 0 | offset: members_offset, |
195 | 0 | end_offset: members_end_offset, |
196 | 0 | }; |
197 | 0 | Ok(file) |
198 | 0 | } |
199 | | |
200 | 0 | fn parse_aixbig(data: R) -> read::Result<Self> { |
201 | 0 | let mut tail = 0; |
202 | | |
203 | 0 | let file_header = data |
204 | 0 | .read::<archive::AixFileHeader>(&mut tail) |
205 | 0 | .read_error("Invalid AIX big archive file header")?; |
206 | | // Caller already validated this. |
207 | 0 | debug_assert_eq!(file_header.magic, archive::AIX_BIG_MAGIC); |
208 | | |
209 | 0 | let mut file = ArchiveFile { |
210 | 0 | data, |
211 | 0 | kind: ArchiveKind::AixBig, |
212 | 0 | members: Members::AixBig { index: &[] }, |
213 | 0 | symbols: (0, 0), |
214 | 0 | names: &[], |
215 | 0 | thin: false, |
216 | 0 | }; |
217 | | |
218 | | // Read the span of symbol table. |
219 | | // TODO: an archive may have both 32-bit and 64-bit symbol tables. |
220 | 0 | let symtbl64 = parse_u64_digits(&file_header.gst64off, 10) |
221 | 0 | .read_error("Invalid offset to 64-bit symbol table in AIX big archive")?; |
222 | 0 | if symtbl64 > 0 { |
223 | | // The symbol table is also a file with header. |
224 | 0 | let member = ArchiveMember::parse_aixbig(data, symtbl64)?; |
225 | 0 | file.symbols = member.file_range(); |
226 | | } else { |
227 | 0 | let symtbl = parse_u64_digits(&file_header.gstoff, 10) |
228 | 0 | .read_error("Invalid offset to symbol table in AIX big archive")?; |
229 | 0 | if symtbl > 0 { |
230 | | // The symbol table is also a file with header. |
231 | 0 | let member = ArchiveMember::parse_aixbig(data, symtbl)?; |
232 | 0 | file.symbols = member.file_range(); |
233 | 0 | } |
234 | | } |
235 | | |
236 | | // Big archive member index table lists file entries with offsets and names. |
237 | | // To avoid potential infinite loop (members are double-linked list), the |
238 | | // iterator goes through the index instead of real members. |
239 | 0 | let member_table_offset = parse_u64_digits(&file_header.memoff, 10) |
240 | 0 | .read_error("Invalid offset for member table of AIX big archive")?; |
241 | 0 | if member_table_offset == 0 { |
242 | | // The offset would be zero if archive contains no file. |
243 | 0 | return Ok(file); |
244 | 0 | } |
245 | | |
246 | | // The member index table is also a file with header. |
247 | 0 | let member = ArchiveMember::parse_aixbig(data, member_table_offset)?; |
248 | 0 | let mut member_data = Bytes(member.data(data)?); |
249 | | |
250 | | // Structure of member index table: |
251 | | // Number of entries (20 bytes) |
252 | | // Offsets of each entry (20*N bytes) |
253 | | // Names string table (the rest of bytes to fill size defined in header) |
254 | 0 | let members_count_bytes = member_data |
255 | 0 | .read_slice::<u8>(20) |
256 | 0 | .read_error("Missing member count in AIX big archive")?; |
257 | 0 | let members_count = parse_u64_digits(members_count_bytes, 10) |
258 | 0 | .and_then(|size| size.try_into().ok()) |
259 | 0 | .read_error("Invalid member count in AIX big archive")?; |
260 | 0 | let index = member_data |
261 | 0 | .read_slice::<archive::AixMemberOffset>(members_count) |
262 | 0 | .read_error("Member count overflow in AIX big archive")?; |
263 | 0 | file.members = Members::AixBig { index }; |
264 | | |
265 | 0 | Ok(file) |
266 | 0 | } |
267 | | |
268 | | /// Return the archive format. |
269 | | #[inline] |
270 | 0 | pub fn kind(&self) -> ArchiveKind { |
271 | 0 | self.kind |
272 | 0 | } |
273 | | |
274 | | /// Return true if the archive is a thin archive. |
275 | 0 | pub fn is_thin(&self) -> bool { |
276 | 0 | self.thin |
277 | 0 | } |
278 | | |
279 | | /// Iterate over the members of the archive. |
280 | | /// |
281 | | /// This does not return special members. |
282 | | #[inline] |
283 | 0 | pub fn members(&self) -> ArchiveMemberIterator<'data, R> { |
284 | 0 | ArchiveMemberIterator { |
285 | 0 | data: self.data, |
286 | 0 | members: self.members, |
287 | 0 | names: self.names, |
288 | 0 | thin: self.thin, |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | | /// Return the member at the given offset. |
293 | 0 | pub fn member(&self, member: ArchiveOffset) -> read::Result<ArchiveMember<'data>> { |
294 | 0 | match self.members { |
295 | 0 | Members::Common { offset, end_offset } => { |
296 | 0 | if member.0 < offset || member.0 >= end_offset { |
297 | 0 | return Err(Error("Invalid archive member offset")); |
298 | 0 | } |
299 | 0 | let mut offset = member.0; |
300 | 0 | ArchiveMember::parse(self.data, &mut offset, self.names, self.thin) |
301 | | } |
302 | | Members::AixBig { .. } => { |
303 | 0 | let offset = member.0; |
304 | 0 | ArchiveMember::parse_aixbig(self.data, offset) |
305 | | } |
306 | | } |
307 | 0 | } |
308 | | |
309 | | /// Iterate over the symbols in the archive. |
310 | 0 | pub fn symbols(&self) -> read::Result<Option<ArchiveSymbolIterator<'data>>> { |
311 | 0 | if self.symbols == (0, 0) { |
312 | 0 | return Ok(None); |
313 | 0 | } |
314 | 0 | let (offset, size) = self.symbols; |
315 | 0 | ArchiveSymbolIterator::new(self.kind, self.data, offset, size) |
316 | 0 | .read_error("Invalid archive symbol table") |
317 | 0 | .map(Some) |
318 | 0 | } |
319 | | } |
320 | | |
321 | | /// An iterator over the members of an archive. |
322 | | #[derive(Debug)] |
323 | | pub struct ArchiveMemberIterator<'data, R: ReadRef<'data> = &'data [u8]> { |
324 | | data: R, |
325 | | members: Members<'data>, |
326 | | names: &'data [u8], |
327 | | thin: bool, |
328 | | } |
329 | | |
330 | | impl<'data, R: ReadRef<'data>> Iterator for ArchiveMemberIterator<'data, R> { |
331 | | type Item = read::Result<ArchiveMember<'data>>; |
332 | | |
333 | 0 | fn next(&mut self) -> Option<Self::Item> { |
334 | 0 | match &mut self.members { |
335 | | Members::Common { |
336 | 0 | ref mut offset, |
337 | 0 | ref mut end_offset, |
338 | | } => { |
339 | 0 | if *offset >= *end_offset { |
340 | 0 | return None; |
341 | 0 | } |
342 | 0 | let member = ArchiveMember::parse(self.data, offset, self.names, self.thin); |
343 | 0 | if member.is_err() { |
344 | 0 | *offset = *end_offset; |
345 | 0 | } |
346 | 0 | Some(member) |
347 | | } |
348 | 0 | Members::AixBig { ref mut index } => match **index { |
349 | 0 | [] => None, |
350 | 0 | [ref first, ref rest @ ..] => { |
351 | 0 | *index = rest; |
352 | 0 | let member = ArchiveMember::parse_aixbig_index(self.data, first); |
353 | 0 | if member.is_err() { |
354 | 0 | *index = &[]; |
355 | 0 | } |
356 | 0 | Some(member) |
357 | | } |
358 | | }, |
359 | | } |
360 | 0 | } |
361 | | } |
362 | | |
363 | | /// An archive member header. |
364 | | #[derive(Debug, Clone, Copy)] |
365 | | enum MemberHeader<'data> { |
366 | | /// Common header used by many formats. |
367 | | Common(&'data archive::Header), |
368 | | /// AIX big archive header |
369 | | AixBig(&'data archive::AixHeader), |
370 | | } |
371 | | |
372 | | /// A partially parsed archive member. |
373 | | #[derive(Debug)] |
374 | | pub struct ArchiveMember<'data> { |
375 | | header: MemberHeader<'data>, |
376 | | name: &'data [u8], |
377 | | // May be zero for thin members. |
378 | | offset: u64, |
379 | | size: u64, |
380 | | } |
381 | | |
382 | | impl<'data> ArchiveMember<'data> { |
383 | | /// Parse the member header, name, and file data in an archive with the common format. |
384 | | /// |
385 | | /// This reads the extended name (if any) and adjusts the file size. |
386 | 0 | fn parse<R: ReadRef<'data>>( |
387 | 0 | data: R, |
388 | 0 | offset: &mut u64, |
389 | 0 | names: &'data [u8], |
390 | 0 | thin: bool, |
391 | 0 | ) -> read::Result<Self> { |
392 | 0 | let header = data |
393 | 0 | .read::<archive::Header>(offset) |
394 | 0 | .read_error("Invalid archive member header")?; |
395 | 0 | if header.terminator != archive::TERMINATOR { |
396 | 0 | return Err(Error("Invalid archive terminator")); |
397 | 0 | } |
398 | | |
399 | 0 | let header_file_size = |
400 | 0 | parse_u64_digits(&header.size, 10).read_error("Invalid archive member size")?; |
401 | 0 | let mut file_offset = *offset; |
402 | 0 | let mut file_size = header_file_size; |
403 | | |
404 | 0 | let name = if header.name[0] == b'/' && (header.name[1] as char).is_ascii_digit() { |
405 | | // Read file name from the names table. |
406 | 0 | parse_sysv_extended_name(&header.name[1..], names) |
407 | 0 | .read_error("Invalid archive extended name offset")? |
408 | 0 | } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_ascii_digit() { |
409 | | // Read file name from the start of the file data. |
410 | 0 | parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size) |
411 | 0 | .read_error("Invalid archive extended name length")? |
412 | 0 | } else if header.name[0] == b'/' { |
413 | 0 | let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len()); |
414 | 0 | &header.name[..name_len] |
415 | | } else { |
416 | | // Name is terminated by slash or space. |
417 | | // Slash allows embedding spaces in the name, so only look |
418 | | // for space if there is no slash. |
419 | 0 | let name_len = memchr::memchr(b'/', &header.name) |
420 | 0 | .or_else(|| memchr::memchr(b' ', &header.name)) |
421 | 0 | .unwrap_or(header.name.len()); |
422 | 0 | &header.name[..name_len] |
423 | | }; |
424 | | |
425 | | // Members in thin archives don't have data unless they are special members. |
426 | 0 | if thin && name != b"/" && name != b"//" && name != b"/SYM64/" { |
427 | 0 | return Ok(ArchiveMember { |
428 | 0 | header: MemberHeader::Common(header), |
429 | 0 | name, |
430 | 0 | offset: 0, |
431 | 0 | size: file_size, |
432 | 0 | }); |
433 | 0 | } |
434 | | |
435 | | // Skip the file data. |
436 | 0 | *offset = offset |
437 | 0 | .checked_add(header_file_size) |
438 | 0 | .read_error("Archive member size is too large")?; |
439 | | // Entries are padded to an even number of bytes. |
440 | 0 | if (header_file_size & 1) != 0 { |
441 | 0 | *offset = offset.saturating_add(1); |
442 | 0 | } |
443 | | |
444 | 0 | Ok(ArchiveMember { |
445 | 0 | header: MemberHeader::Common(header), |
446 | 0 | name, |
447 | 0 | offset: file_offset, |
448 | 0 | size: file_size, |
449 | 0 | }) |
450 | 0 | } |
451 | | |
452 | | /// Parse a member index entry in an AIX big archive, |
453 | | /// and then parse the member header, name, and file data. |
454 | 0 | fn parse_aixbig_index<R: ReadRef<'data>>( |
455 | 0 | data: R, |
456 | 0 | index: &archive::AixMemberOffset, |
457 | 0 | ) -> read::Result<Self> { |
458 | 0 | let offset = parse_u64_digits(&index.0, 10) |
459 | 0 | .read_error("Invalid AIX big archive file member offset")?; |
460 | 0 | Self::parse_aixbig(data, offset) |
461 | 0 | } |
462 | | |
463 | | /// Parse the member header, name, and file data in an AIX big archive. |
464 | 0 | fn parse_aixbig<R: ReadRef<'data>>(data: R, mut offset: u64) -> read::Result<Self> { |
465 | | // The format was described at |
466 | | // https://www.ibm.com/docs/en/aix/7.3?topic=formats-ar-file-format-big |
467 | 0 | let header = data |
468 | 0 | .read::<archive::AixHeader>(&mut offset) |
469 | 0 | .read_error("Invalid AIX big archive member header")?; |
470 | 0 | let name_length = parse_u64_digits(&header.namlen, 10) |
471 | 0 | .read_error("Invalid AIX big archive member name length")?; |
472 | 0 | let name = data |
473 | 0 | .read_bytes(&mut offset, name_length) |
474 | 0 | .read_error("Invalid AIX big archive member name")?; |
475 | | |
476 | | // The actual data for a file member begins at the first even-byte boundary beyond the |
477 | | // member header and continues for the number of bytes specified by the ar_size field. The |
478 | | // ar command inserts null bytes for padding where necessary. |
479 | 0 | if offset & 1 != 0 { |
480 | 0 | offset = offset.saturating_add(1); |
481 | 0 | } |
482 | | // Because of the even-byte boundary, we have to read and check terminator after header. |
483 | 0 | let terminator = data |
484 | 0 | .read_bytes(&mut offset, 2) |
485 | 0 | .read_error("Invalid AIX big archive terminator")?; |
486 | 0 | if terminator != archive::TERMINATOR { |
487 | 0 | return Err(Error("Invalid AIX big archive terminator")); |
488 | 0 | } |
489 | | |
490 | 0 | let size = parse_u64_digits(&header.size, 10) |
491 | 0 | .read_error("Invalid archive member size in AIX big archive")?; |
492 | 0 | Ok(ArchiveMember { |
493 | 0 | header: MemberHeader::AixBig(header), |
494 | 0 | name, |
495 | 0 | offset, |
496 | 0 | size, |
497 | 0 | }) |
498 | 0 | } |
499 | | |
500 | | /// Return the raw header that is common to many archive formats. |
501 | | /// |
502 | | /// Returns `None` if this archive does not use the common header format. |
503 | | #[inline] |
504 | 0 | pub fn header(&self) -> Option<&'data archive::Header> { |
505 | 0 | match self.header { |
506 | 0 | MemberHeader::Common(header) => Some(header), |
507 | 0 | _ => None, |
508 | | } |
509 | 0 | } |
510 | | |
511 | | /// Return the raw header for AIX big archives. |
512 | | /// |
513 | | /// Returns `None` if this is not an AIX big archive. |
514 | | #[inline] |
515 | 0 | pub fn aix_header(&self) -> Option<&'data archive::AixHeader> { |
516 | 0 | match self.header { |
517 | 0 | MemberHeader::AixBig(header) => Some(header), |
518 | 0 | _ => None, |
519 | | } |
520 | 0 | } |
521 | | |
522 | | /// Return the parsed file name. |
523 | | /// |
524 | | /// This may be an extended file name. |
525 | | #[inline] |
526 | 0 | pub fn name(&self) -> &'data [u8] { |
527 | 0 | self.name |
528 | 0 | } |
529 | | |
530 | | /// Parse the file modification timestamp from the header. |
531 | | #[inline] |
532 | 0 | pub fn date(&self) -> Option<u64> { |
533 | 0 | match &self.header { |
534 | 0 | MemberHeader::Common(header) => parse_u64_digits(&header.date, 10), |
535 | 0 | MemberHeader::AixBig(header) => parse_u64_digits(&header.date, 10), |
536 | | } |
537 | 0 | } |
538 | | |
539 | | /// Parse the user ID from the header. |
540 | | #[inline] |
541 | 0 | pub fn uid(&self) -> Option<u64> { |
542 | 0 | match &self.header { |
543 | 0 | MemberHeader::Common(header) => parse_u64_digits(&header.uid, 10), |
544 | 0 | MemberHeader::AixBig(header) => parse_u64_digits(&header.uid, 10), |
545 | | } |
546 | 0 | } |
547 | | |
548 | | /// Parse the group ID from the header. |
549 | | #[inline] |
550 | 0 | pub fn gid(&self) -> Option<u64> { |
551 | 0 | match &self.header { |
552 | 0 | MemberHeader::Common(header) => parse_u64_digits(&header.gid, 10), |
553 | 0 | MemberHeader::AixBig(header) => parse_u64_digits(&header.gid, 10), |
554 | | } |
555 | 0 | } |
556 | | |
557 | | /// Parse the file mode from the header. |
558 | | #[inline] |
559 | 0 | pub fn mode(&self) -> Option<u64> { |
560 | 0 | match &self.header { |
561 | 0 | MemberHeader::Common(header) => parse_u64_digits(&header.mode, 8), |
562 | 0 | MemberHeader::AixBig(header) => parse_u64_digits(&header.mode, 8), |
563 | | } |
564 | 0 | } |
565 | | |
566 | | /// Return the size of the file data. |
567 | 0 | pub fn size(&self) -> u64 { |
568 | 0 | self.size |
569 | 0 | } |
570 | | |
571 | | /// Return the offset and size of the file data. |
572 | 0 | pub fn file_range(&self) -> (u64, u64) { |
573 | 0 | (self.offset, self.size) |
574 | 0 | } |
575 | | |
576 | | /// Return true if the member is a thin member. |
577 | | /// |
578 | | /// Thin members have no file data. |
579 | 0 | pub fn is_thin(&self) -> bool { |
580 | 0 | self.offset == 0 |
581 | 0 | } |
582 | | |
583 | | /// Return the file data. |
584 | | /// |
585 | | /// This is an empty slice for thin members. |
586 | | #[inline] |
587 | 0 | pub fn data<R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [u8]> { |
588 | 0 | if self.is_thin() { |
589 | 0 | return Ok(&[]); |
590 | 0 | } |
591 | 0 | data.read_bytes_at(self.offset, self.size) |
592 | 0 | .read_error("Archive member size is too large") |
593 | 0 | } |
594 | | } |
595 | | |
596 | | /// An offset of a member in an archive. |
597 | | #[derive(Debug, Clone, Copy)] |
598 | | pub struct ArchiveOffset(pub u64); |
599 | | |
600 | | /// An iterator over the symbols in the archive symbol table. |
601 | | #[derive(Debug, Clone)] |
602 | | pub struct ArchiveSymbolIterator<'data>(SymbolIteratorInternal<'data>); |
603 | | |
604 | | #[derive(Debug, Clone)] |
605 | | enum SymbolIteratorInternal<'data> { |
606 | | /// There is no symbol table. |
607 | | None, |
608 | | /// A GNU symbol table. |
609 | | /// |
610 | | /// Contains: |
611 | | /// - the number of symbols as a 32-bit big-endian integer |
612 | | /// - the offsets of the member headers as 32-bit big-endian integers |
613 | | /// - the symbol names as null-terminated strings |
614 | | Gnu { |
615 | | offsets: slice::Iter<'data, U32Bytes<BE>>, |
616 | | names: Bytes<'data>, |
617 | | }, |
618 | | /// A GNU 64-bit symbol table |
619 | | /// |
620 | | /// Contains: |
621 | | /// - the number of symbols as a 64-bit big-endian integer |
622 | | /// - the offsets of the member headers as 64-bit big-endian integers |
623 | | /// - the symbol names as null-terminated strings |
624 | | Gnu64 { |
625 | | offsets: slice::Iter<'data, U64Bytes<BE>>, |
626 | | names: Bytes<'data>, |
627 | | }, |
628 | | /// A BSD symbol table. |
629 | | /// |
630 | | /// Contains: |
631 | | /// - the size in bytes of the offsets array as a 32-bit little-endian integer |
632 | | /// - the offsets array, for which each entry is a pair of 32-bit little-endian integers |
633 | | /// for the offset of the member header and the offset of the symbol name |
634 | | /// - the size in bytes of the symbol names as a 32-bit little-endian integer |
635 | | /// - the symbol names as null-terminated strings |
636 | | Bsd { |
637 | | offsets: slice::Iter<'data, [U32Bytes<LE>; 2]>, |
638 | | names: Bytes<'data>, |
639 | | }, |
640 | | /// A BSD 64-bit symbol table. |
641 | | /// |
642 | | /// Contains: |
643 | | /// - the size in bytes of the offsets array as a 64-bit little-endian integer |
644 | | /// - the offsets array, for which each entry is a pair of 64-bit little-endian integers |
645 | | /// for the offset of the member header and the offset of the symbol name |
646 | | /// - the size in bytes of the symbol names as a 64-bit little-endian integer |
647 | | /// - the symbol names as null-terminated strings |
648 | | Bsd64 { |
649 | | offsets: slice::Iter<'data, [U64Bytes<LE>; 2]>, |
650 | | names: Bytes<'data>, |
651 | | }, |
652 | | /// A Windows COFF symbol table. |
653 | | /// |
654 | | /// Contains: |
655 | | /// - the number of members as a 32-bit little-endian integer |
656 | | /// - the offsets of the member headers as 32-bit little-endian integers |
657 | | /// - the number of symbols as a 32-bit little-endian integer |
658 | | /// - the member index for each symbol as a 16-bit little-endian integer |
659 | | /// - the symbol names as null-terminated strings in lexical order |
660 | | Coff { |
661 | | members: &'data [U32Bytes<LE>], |
662 | | indices: slice::Iter<'data, U16Bytes<LE>>, |
663 | | names: Bytes<'data>, |
664 | | }, |
665 | | } |
666 | | |
667 | | impl<'data> ArchiveSymbolIterator<'data> { |
668 | 0 | fn new<R: ReadRef<'data>>( |
669 | 0 | kind: ArchiveKind, |
670 | 0 | data: R, |
671 | 0 | offset: u64, |
672 | 0 | size: u64, |
673 | 0 | ) -> Result<Self, ()> { |
674 | 0 | let mut data = data.read_bytes_at(offset, size).map(Bytes)?; |
675 | 0 | match kind { |
676 | 0 | ArchiveKind::Unknown => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), |
677 | | ArchiveKind::Gnu => { |
678 | 0 | let offsets_count = data.read::<U32Bytes<BE>>()?.get(BE); |
679 | 0 | let offsets = data.read_slice::<U32Bytes<BE>>(offsets_count as usize)?; |
680 | 0 | Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu { |
681 | 0 | offsets: offsets.iter(), |
682 | 0 | names: data, |
683 | 0 | })) |
684 | | } |
685 | | ArchiveKind::Gnu64 => { |
686 | 0 | let offsets_count = data.read::<U64Bytes<BE>>()?.get(BE); |
687 | 0 | let offsets = data.read_slice::<U64Bytes<BE>>(offsets_count as usize)?; |
688 | 0 | Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Gnu64 { |
689 | 0 | offsets: offsets.iter(), |
690 | 0 | names: data, |
691 | 0 | })) |
692 | | } |
693 | | ArchiveKind::Bsd => { |
694 | 0 | let offsets_size = data.read::<U32Bytes<LE>>()?.get(LE); |
695 | 0 | let offsets = data.read_slice::<[U32Bytes<LE>; 2]>(offsets_size as usize / 8)?; |
696 | 0 | let names_size = data.read::<U32Bytes<LE>>()?.get(LE); |
697 | 0 | let names = data.read_bytes(names_size as usize)?; |
698 | 0 | Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd { |
699 | 0 | offsets: offsets.iter(), |
700 | 0 | names, |
701 | 0 | })) |
702 | | } |
703 | | ArchiveKind::Bsd64 => { |
704 | 0 | let offsets_size = data.read::<U64Bytes<LE>>()?.get(LE); |
705 | 0 | let offsets = data.read_slice::<[U64Bytes<LE>; 2]>(offsets_size as usize / 16)?; |
706 | 0 | let names_size = data.read::<U64Bytes<LE>>()?.get(LE); |
707 | 0 | let names = data.read_bytes(names_size as usize)?; |
708 | 0 | Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Bsd64 { |
709 | 0 | offsets: offsets.iter(), |
710 | 0 | names, |
711 | 0 | })) |
712 | | } |
713 | | ArchiveKind::Coff => { |
714 | 0 | let members_count = data.read::<U32Bytes<LE>>()?.get(LE); |
715 | 0 | let members = data.read_slice::<U32Bytes<LE>>(members_count as usize)?; |
716 | 0 | let indices_count = data.read::<U32Bytes<LE>>()?.get(LE); |
717 | 0 | let indices = data.read_slice::<U16Bytes<LE>>(indices_count as usize)?; |
718 | 0 | Ok(ArchiveSymbolIterator(SymbolIteratorInternal::Coff { |
719 | 0 | members, |
720 | 0 | indices: indices.iter(), |
721 | 0 | names: data, |
722 | 0 | })) |
723 | | } |
724 | | // TODO: Implement AIX big archive symbol table. |
725 | 0 | ArchiveKind::AixBig => Ok(ArchiveSymbolIterator(SymbolIteratorInternal::None)), |
726 | | } |
727 | 0 | } |
728 | | } |
729 | | |
730 | | impl<'data> Iterator for ArchiveSymbolIterator<'data> { |
731 | | type Item = read::Result<ArchiveSymbol<'data>>; |
732 | | |
733 | 0 | fn next(&mut self) -> Option<Self::Item> { |
734 | 0 | match &mut self.0 { |
735 | 0 | SymbolIteratorInternal::None => None, |
736 | 0 | SymbolIteratorInternal::Gnu { offsets, names } => { |
737 | 0 | let offset = offsets.next()?.get(BE); |
738 | | Some( |
739 | 0 | names |
740 | 0 | .read_string() |
741 | 0 | .read_error("Missing archive symbol name") |
742 | 0 | .map(|name| ArchiveSymbol { |
743 | 0 | name, |
744 | 0 | offset: ArchiveOffset(offset.into()), |
745 | 0 | }), |
746 | | ) |
747 | | } |
748 | 0 | SymbolIteratorInternal::Gnu64 { offsets, names } => { |
749 | 0 | let offset = offsets.next()?.get(BE); |
750 | | Some( |
751 | 0 | names |
752 | 0 | .read_string() |
753 | 0 | .read_error("Missing archive symbol name") |
754 | 0 | .map(|name| ArchiveSymbol { |
755 | 0 | name, |
756 | 0 | offset: ArchiveOffset(offset), |
757 | 0 | }), |
758 | | ) |
759 | | } |
760 | 0 | SymbolIteratorInternal::Bsd { offsets, names } => { |
761 | 0 | let entry = offsets.next()?; |
762 | | Some( |
763 | 0 | names |
764 | 0 | .read_string_at(entry[0].get(LE) as usize) |
765 | 0 | .read_error("Invalid archive symbol name offset") |
766 | 0 | .map(|name| ArchiveSymbol { |
767 | 0 | name, |
768 | 0 | offset: ArchiveOffset(entry[1].get(LE).into()), |
769 | 0 | }), |
770 | | ) |
771 | | } |
772 | 0 | SymbolIteratorInternal::Bsd64 { offsets, names } => { |
773 | 0 | let entry = offsets.next()?; |
774 | | Some( |
775 | 0 | names |
776 | 0 | .read_string_at(entry[0].get(LE) as usize) |
777 | 0 | .read_error("Invalid archive symbol name offset") |
778 | 0 | .map(|name| ArchiveSymbol { |
779 | 0 | name, |
780 | 0 | offset: ArchiveOffset(entry[1].get(LE)), |
781 | 0 | }), |
782 | | ) |
783 | | } |
784 | | SymbolIteratorInternal::Coff { |
785 | 0 | members, |
786 | 0 | indices, |
787 | 0 | names, |
788 | | } => { |
789 | 0 | let index = indices.next()?.get(LE).wrapping_sub(1); |
790 | 0 | let member = members |
791 | 0 | .get(index as usize) |
792 | 0 | .read_error("Invalid archive symbol member index"); |
793 | 0 | let name = names |
794 | 0 | .read_string() |
795 | 0 | .read_error("Missing archive symbol name"); |
796 | 0 | Some(member.and_then(|member| { |
797 | 0 | name.map(|name| ArchiveSymbol { |
798 | 0 | name, |
799 | 0 | offset: ArchiveOffset(member.get(LE).into()), |
800 | 0 | }) |
801 | 0 | })) |
802 | | } |
803 | | } |
804 | 0 | } |
805 | | |
806 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
807 | 0 | match &self.0 { |
808 | 0 | SymbolIteratorInternal::None => (0, None), |
809 | 0 | SymbolIteratorInternal::Gnu { offsets, .. } => offsets.size_hint(), |
810 | 0 | SymbolIteratorInternal::Gnu64 { offsets, .. } => offsets.size_hint(), |
811 | 0 | SymbolIteratorInternal::Bsd { offsets, .. } => offsets.size_hint(), |
812 | 0 | SymbolIteratorInternal::Bsd64 { offsets, .. } => offsets.size_hint(), |
813 | 0 | SymbolIteratorInternal::Coff { indices, .. } => { |
814 | | // The `slice::Iter` is in the indices field for this variant |
815 | 0 | indices.size_hint() |
816 | | } |
817 | | } |
818 | 0 | } |
819 | | } |
820 | | |
821 | | /// A symbol in the archive symbol table. |
822 | | /// |
823 | | /// This is used to find the member containing the symbol. |
824 | | #[derive(Debug, Clone, Copy)] |
825 | | pub struct ArchiveSymbol<'data> { |
826 | | name: &'data [u8], |
827 | | offset: ArchiveOffset, |
828 | | } |
829 | | |
830 | | impl<'data> ArchiveSymbol<'data> { |
831 | | /// Return the symbol name. |
832 | | #[inline] |
833 | 0 | pub fn name(&self) -> &'data [u8] { |
834 | 0 | self.name |
835 | 0 | } |
836 | | |
837 | | /// Return the offset of the header for the member containing the symbol. |
838 | | #[inline] |
839 | 0 | pub fn offset(&self) -> ArchiveOffset { |
840 | 0 | self.offset |
841 | 0 | } |
842 | | } |
843 | | |
844 | | // Ignores bytes starting from the first space. |
845 | 0 | fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> { |
846 | 0 | if let [b' ', ..] = digits { |
847 | 0 | return None; |
848 | 0 | } |
849 | 0 | let mut result: u64 = 0; |
850 | 0 | for &c in digits { |
851 | 0 | if c == b' ' { |
852 | 0 | return Some(result); |
853 | | } else { |
854 | 0 | let x = (c as char).to_digit(radix)?; |
855 | 0 | result = result |
856 | 0 | .checked_mul(u64::from(radix))? |
857 | 0 | .checked_add(u64::from(x))?; |
858 | | } |
859 | | } |
860 | 0 | Some(result) |
861 | 0 | } |
862 | | |
863 | | /// Digits are a decimal offset into the extended name table. |
864 | | /// Name is terminated by "/\n" (for GNU) or a null byte (for COFF). |
865 | 0 | fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<&'data [u8], ()> { |
866 | 0 | let offset = parse_u64_digits(digits, 10).ok_or(())?; |
867 | 0 | let offset = offset.try_into().map_err(|_| ())?; |
868 | 0 | let name_data = names.get(offset..).ok_or(())?; |
869 | 0 | let len = memchr::memchr2(b'\n', b'\0', name_data).ok_or(())?; |
870 | 0 | if name_data[len] == b'\n' { |
871 | 0 | if len < 1 || name_data[len - 1] != b'/' { |
872 | 0 | Err(()) |
873 | | } else { |
874 | 0 | Ok(&name_data[..len - 1]) |
875 | | } |
876 | | } else { |
877 | 0 | Ok(&name_data[..len]) |
878 | | } |
879 | 0 | } |
880 | | |
881 | | /// Digits are a decimal length of the extended name, which is contained |
882 | | /// in `data` at `offset`. |
883 | | /// Modifies `offset` and `size` to start after the extended name. |
884 | 0 | fn parse_bsd_extended_name<'data, R: ReadRef<'data>>( |
885 | 0 | digits: &[u8], |
886 | 0 | data: R, |
887 | 0 | offset: &mut u64, |
888 | 0 | size: &mut u64, |
889 | 0 | ) -> Result<&'data [u8], ()> { |
890 | 0 | let len = parse_u64_digits(digits, 10).ok_or(())?; |
891 | 0 | *size = size.checked_sub(len).ok_or(())?; |
892 | 0 | let name_data = data.read_bytes(offset, len)?; |
893 | 0 | let name = match memchr::memchr(b'\0', name_data) { |
894 | 0 | Some(len) => &name_data[..len], |
895 | 0 | None => name_data, |
896 | | }; |
897 | 0 | Ok(name) |
898 | 0 | } |
899 | | |
900 | | #[cfg(test)] |
901 | | mod tests { |
902 | | use super::*; |
903 | | |
904 | | #[test] |
905 | | fn kind() { |
906 | | let data = b"!<arch>\n"; |
907 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
908 | | assert_eq!(archive.kind(), ArchiveKind::Unknown); |
909 | | |
910 | | let data = b"\ |
911 | | !<arch>\n\ |
912 | | / 4 `\n\ |
913 | | 0000"; |
914 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
915 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
916 | | |
917 | | let data = b"\ |
918 | | !<arch>\n\ |
919 | | // 4 `\n\ |
920 | | 0000"; |
921 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
922 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
923 | | |
924 | | let data = b"\ |
925 | | !<arch>\n\ |
926 | | / 4 `\n\ |
927 | | 0000\ |
928 | | // 4 `\n\ |
929 | | 0000"; |
930 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
931 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
932 | | |
933 | | let data = b"\ |
934 | | !<arch>\n\ |
935 | | /SYM64/ 4 `\n\ |
936 | | 0000"; |
937 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
938 | | assert_eq!(archive.kind(), ArchiveKind::Gnu64); |
939 | | |
940 | | let data = b"\ |
941 | | !<arch>\n\ |
942 | | /SYM64/ 4 `\n\ |
943 | | 0000\ |
944 | | // 4 `\n\ |
945 | | 0000"; |
946 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
947 | | assert_eq!(archive.kind(), ArchiveKind::Gnu64); |
948 | | |
949 | | let data = b"\ |
950 | | !<arch>\n\ |
951 | | __.SYMDEF 4 `\n\ |
952 | | 0000"; |
953 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
954 | | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
955 | | |
956 | | let data = b"\ |
957 | | !<arch>\n\ |
958 | | #1/9 13 `\n\ |
959 | | __.SYMDEF0000"; |
960 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
961 | | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
962 | | |
963 | | let data = b"\ |
964 | | !<arch>\n\ |
965 | | #1/16 20 `\n\ |
966 | | __.SYMDEF SORTED0000"; |
967 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
968 | | assert_eq!(archive.kind(), ArchiveKind::Bsd); |
969 | | |
970 | | let data = b"\ |
971 | | !<arch>\n\ |
972 | | __.SYMDEF_64 4 `\n\ |
973 | | 0000"; |
974 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
975 | | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
976 | | |
977 | | let data = b"\ |
978 | | !<arch>\n\ |
979 | | #1/12 16 `\n\ |
980 | | __.SYMDEF_640000"; |
981 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
982 | | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
983 | | |
984 | | let data = b"\ |
985 | | !<arch>\n\ |
986 | | #1/19 23 `\n\ |
987 | | __.SYMDEF_64 SORTED0000"; |
988 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
989 | | assert_eq!(archive.kind(), ArchiveKind::Bsd64); |
990 | | |
991 | | let data = b"\ |
992 | | !<arch>\n\ |
993 | | / 4 `\n\ |
994 | | 0000\ |
995 | | / 4 `\n\ |
996 | | 0000\ |
997 | | // 4 `\n\ |
998 | | 0000"; |
999 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
1000 | | assert_eq!(archive.kind(), ArchiveKind::Coff); |
1001 | | |
1002 | | let data = b"\ |
1003 | | <bigaf>\n\ |
1004 | | 0 0 \ |
1005 | | 0 0 \ |
1006 | | 0 128 \ |
1007 | | 6 0 \ |
1008 | | 0 \0\0\0\0\0\0\0\0\0\0\0\0\ |
1009 | | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ |
1010 | | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ |
1011 | | \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; |
1012 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
1013 | | assert_eq!(archive.kind(), ArchiveKind::AixBig); |
1014 | | |
1015 | | let data = b"\ |
1016 | | !<thin>\n\ |
1017 | | / 4 `\n\ |
1018 | | 0000"; |
1019 | | let archive = ArchiveFile::parse(&data[..]).unwrap(); |
1020 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
1021 | | assert!(archive.is_thin()); |
1022 | | } |
1023 | | |
1024 | | #[test] |
1025 | | fn gnu_names() { |
1026 | | let data = b"\ |
1027 | | !<arch>\n\ |
1028 | | // 18 `\n\ |
1029 | | 0123456789abcdef/\n\ |
1030 | | s p a c e/ 0 0 0 644 4 `\n\ |
1031 | | 0000\ |
1032 | | 0123456789abcde/0 0 0 644 3 `\n\ |
1033 | | odd\n\ |
1034 | | /0 0 0 0 644 4 `\n\ |
1035 | | even"; |
1036 | | let data = &data[..]; |
1037 | | let archive = ArchiveFile::parse(data).unwrap(); |
1038 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
1039 | | let mut members = archive.members(); |
1040 | | |
1041 | | let member = members.next().unwrap().unwrap(); |
1042 | | assert_eq!(member.name(), b"s p a c e"); |
1043 | | assert_eq!(member.data(data).unwrap(), &b"0000"[..]); |
1044 | | |
1045 | | let member = members.next().unwrap().unwrap(); |
1046 | | assert_eq!(member.name(), b"0123456789abcde"); |
1047 | | assert_eq!(member.data(data).unwrap(), &b"odd"[..]); |
1048 | | |
1049 | | let member = members.next().unwrap().unwrap(); |
1050 | | assert_eq!(member.name(), b"0123456789abcdef"); |
1051 | | assert_eq!(member.data(data).unwrap(), &b"even"[..]); |
1052 | | |
1053 | | assert!(members.next().is_none()); |
1054 | | } |
1055 | | |
1056 | | #[test] |
1057 | | fn thin_gnu_names() { |
1058 | | let data = b"\ |
1059 | | !<thin>\n\ |
1060 | | // 18 `\n\ |
1061 | | 0123456789/abcde/\n\ |
1062 | | s p a c e/ 0 0 0 644 4 `\n\ |
1063 | | 0123456789abcde/0 0 0 644 3 `\n\ |
1064 | | /0 0 0 0 644 4 `\n\ |
1065 | | "; |
1066 | | let data = &data[..]; |
1067 | | let archive = ArchiveFile::parse(data).unwrap(); |
1068 | | assert_eq!(archive.kind(), ArchiveKind::Gnu); |
1069 | | let mut members = archive.members(); |
1070 | | |
1071 | | let member = members.next().unwrap().unwrap(); |
1072 | | assert_eq!(member.name(), b"s p a c e"); |
1073 | | assert!(member.is_thin()); |
1074 | | assert_eq!(member.size(), 4); |
1075 | | assert_eq!(member.data(data).unwrap(), &[]); |
1076 | | |
1077 | | let member = members.next().unwrap().unwrap(); |
1078 | | assert_eq!(member.name(), b"0123456789abcde"); |
1079 | | assert!(member.is_thin()); |
1080 | | assert_eq!(member.size(), 3); |
1081 | | assert_eq!(member.data(data).unwrap(), &[]); |
1082 | | |
1083 | | let member = members.next().unwrap().unwrap(); |
1084 | | assert_eq!(member.name(), b"0123456789/abcde"); |
1085 | | assert!(member.is_thin()); |
1086 | | assert_eq!(member.size(), 4); |
1087 | | assert_eq!(member.data(data).unwrap(), &[]); |
1088 | | |
1089 | | assert!(members.next().is_none()); |
1090 | | } |
1091 | | |
1092 | | #[test] |
1093 | | fn bsd_names() { |
1094 | | let data = b"\ |
1095 | | !<arch>\n\ |
1096 | | 0123456789abcde 0 0 0 644 3 `\n\ |
1097 | | odd\n\ |
1098 | | #1/16 0 0 0 644 20 `\n\ |
1099 | | 0123456789abcdefeven"; |
1100 | | let data = &data[..]; |
1101 | | let archive = ArchiveFile::parse(data).unwrap(); |
1102 | | assert_eq!(archive.kind(), ArchiveKind::Unknown); |
1103 | | let mut members = archive.members(); |
1104 | | |
1105 | | let member = members.next().unwrap().unwrap(); |
1106 | | assert_eq!(member.name(), b"0123456789abcde"); |
1107 | | assert_eq!(member.data(data).unwrap(), &b"odd"[..]); |
1108 | | |
1109 | | let member = members.next().unwrap().unwrap(); |
1110 | | assert_eq!(member.name(), b"0123456789abcdef"); |
1111 | | assert_eq!(member.data(data).unwrap(), &b"even"[..]); |
1112 | | |
1113 | | assert!(members.next().is_none()); |
1114 | | } |
1115 | | |
1116 | | #[test] |
1117 | | fn aix_names() { |
1118 | | let data = b"\ |
1119 | | <bigaf>\n\ |
1120 | | 396 0 0 \ |
1121 | | 128 262 0 \ |
1122 | | 4 262 0 \ |
1123 | | 1662610370 223 1 644 16 \ |
1124 | | 0123456789abcdef`\nord\n\ |
1125 | | 4 396 128 \ |
1126 | | 1662610374 223 1 644 16 \ |
1127 | | fedcba9876543210`\nrev\n\ |
1128 | | 94 0 262 \ |
1129 | | 0 0 0 0 0 \ |
1130 | | `\n2 128 \ |
1131 | | 262 0123456789abcdef\0fedcba9876543210\0"; |
1132 | | let data = &data[..]; |
1133 | | let archive = ArchiveFile::parse(data).unwrap(); |
1134 | | assert_eq!(archive.kind(), ArchiveKind::AixBig); |
1135 | | let mut members = archive.members(); |
1136 | | |
1137 | | let member = members.next().unwrap().unwrap(); |
1138 | | assert_eq!(member.name(), b"0123456789abcdef"); |
1139 | | assert_eq!(member.data(data).unwrap(), &b"ord\n"[..]); |
1140 | | |
1141 | | let member = members.next().unwrap().unwrap(); |
1142 | | assert_eq!(member.name(), b"fedcba9876543210"); |
1143 | | assert_eq!(member.data(data).unwrap(), &b"rev\n"[..]); |
1144 | | |
1145 | | assert!(members.next().is_none()); |
1146 | | } |
1147 | | } |