1 //! Support for archive files.
2 
3 use crate::read::{self, Error, ReadError};
4 use crate::{archive, Bytes};
5 
6 /// The kind of archive format.
7 // TODO: Gnu64 and Darwin64 (and Darwin for writing)
8 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9 pub enum ArchiveKind {
10     /// There are no special files that indicate the archive format.
11     Unknown,
12     /// The GNU (or System V) archive format.
13     Gnu,
14     /// The BSD archive format.
15     Bsd,
16     /// The Windows COFF archive format.
17     Coff,
18 }
19 
20 /// A partially parsed archive file.
21 #[derive(Debug)]
22 pub struct ArchiveFile<'data> {
23     data: Bytes<'data>,
24     kind: ArchiveKind,
25     symbols: Bytes<'data>,
26     names: Bytes<'data>,
27 }
28 
29 impl<'data> ArchiveFile<'data> {
30     /// Parse the archive header and special members.
parse(data: &'data [u8]) -> read::Result<Self>31     pub fn parse(data: &'data [u8]) -> read::Result<Self> {
32         let data = Bytes(data);
33         let mut tail = data;
34 
35         let magic = tail
36             .read_bytes(archive::MAGIC.len())
37             .read_error("Invalid archive size")?;
38         if magic.0 != &archive::MAGIC[..] {
39             return Err(Error("Unsupported archive identifier"));
40         }
41 
42         let mut file = ArchiveFile {
43             data: tail,
44             kind: ArchiveKind::Unknown,
45             symbols: Bytes(&[]),
46             names: Bytes(&[]),
47         };
48 
49         // The first few members may be special, so parse them.
50         // GNU has:
51         // - "/": symbol table (optional)
52         // - "//": names table (optional)
53         // COFF has:
54         // - "/": first linker member
55         // - "/": second linker member
56         // - "//": names table
57         // BSD has:
58         // - "__.SYMDEF" or "__.SYMDEF SORTED": symbol table (optional)
59         if !tail.is_empty() {
60             let member = ArchiveMember::parse(&mut tail, Bytes(&[]))?;
61             if member.name == b"/" {
62                 // GNU symbol table (unless we later determine this is COFF).
63                 file.kind = ArchiveKind::Gnu;
64                 file.symbols = member.data;
65                 file.data = tail;
66 
67                 if !tail.is_empty() {
68                     let member = ArchiveMember::parse(&mut tail, Bytes(&[]))?;
69                     if member.name == b"/" {
70                         // COFF linker member.
71                         file.kind = ArchiveKind::Coff;
72                         file.symbols = member.data;
73                         file.data = tail;
74 
75                         if !tail.is_empty() {
76                             let member = ArchiveMember::parse(&mut tail, Bytes(&[]))?;
77                             if member.name == b"//" {
78                                 // COFF names table.
79                                 file.names = member.data;
80                                 file.data = tail;
81                             }
82                         }
83                     } else if member.name == b"//" {
84                         // GNU names table.
85                         file.names = member.data;
86                         file.data = tail;
87                     }
88                 }
89             } else if member.name == b"//" {
90                 // GNU names table.
91                 file.kind = ArchiveKind::Gnu;
92                 file.names = member.data;
93                 file.data = tail;
94             } else if member.name == b"__.SYMDEF" || member.name == b"__.SYMDEF SORTED" {
95                 // BSD symbol table.
96                 file.kind = ArchiveKind::Bsd;
97                 file.symbols = member.data;
98                 file.data = tail;
99             } else {
100                 // TODO: This could still be a BSD file. We leave this as unknown for now.
101             }
102         }
103         Ok(file)
104     }
105 
106     /// Return the archive format.
107     #[inline]
kind(&self) -> ArchiveKind108     pub fn kind(&self) -> ArchiveKind {
109         self.kind
110     }
111 
112     /// Iterate over the members of the archive.
113     ///
114     /// This does not return special members.
115     #[inline]
members(&self) -> ArchiveMemberIterator<'data>116     pub fn members(&self) -> ArchiveMemberIterator<'data> {
117         ArchiveMemberIterator {
118             data: self.data,
119             names: self.names,
120         }
121     }
122 }
123 
124 /// An iterator over the members of an archive.
125 #[derive(Debug)]
126 pub struct ArchiveMemberIterator<'data> {
127     data: Bytes<'data>,
128     names: Bytes<'data>,
129 }
130 
131 impl<'data> Iterator for ArchiveMemberIterator<'data> {
132     type Item = read::Result<ArchiveMember<'data>>;
133 
next(&mut self) -> Option<Self::Item>134     fn next(&mut self) -> Option<Self::Item> {
135         if self.data.is_empty() {
136             return None;
137         }
138         let member = ArchiveMember::parse(&mut self.data, self.names);
139         if member.is_err() {
140             self.data = Bytes(&[]);
141         }
142         Some(member)
143     }
144 }
145 
146 /// A partially parsed archive member.
147 #[derive(Debug)]
148 pub struct ArchiveMember<'data> {
149     header: &'data archive::Header,
150     name: &'data [u8],
151     data: Bytes<'data>,
152 }
153 
154 impl<'data> ArchiveMember<'data> {
155     /// Parse the archive member header, name, and file data.
156     ///
157     /// This reads the extended name (if any) and adjusts the file size.
parse(data: &mut Bytes<'data>, names: Bytes<'data>) -> read::Result<Self>158     fn parse(data: &mut Bytes<'data>, names: Bytes<'data>) -> read::Result<Self> {
159         let header = data
160             .read::<archive::Header>()
161             .read_error("Invalid archive member header")?;
162         if header.terminator != archive::TERMINATOR {
163             return Err(Error("Invalid archive terminator"));
164         }
165 
166         let size =
167             parse_usize_digits(&header.size, 10).read_error("Invalid archive member size")?;
168         let mut file_data = data
169             .read_bytes(size)
170             .read_error("Archive member size is too large")?;
171         // Entries are padded to an even number of bytes.
172         if (size & 1) != 0 {
173             data.skip(1).ok();
174         }
175 
176         let name = if header.name[0] == b'/' && (header.name[1] as char).is_digit(10) {
177             // Read file name from the names table.
178             parse_sysv_extended_name(&header.name[1..], names)
179                 .read_error("Invalid archive extended name offset")?
180         } else if &header.name[..3] == b"#1/" && (header.name[3] as char).is_digit(10) {
181             // Read file name from the start of the file data.
182             parse_bsd_extended_name(&header.name[3..], &mut file_data)
183                 .read_error("Invalid archive extended name length")?
184         } else if header.name[0] == b'/' {
185             let name_len =
186                 (header.name.iter().position(|&x| x == b' ')).unwrap_or_else(|| header.name.len());
187             &header.name[..name_len]
188         } else {
189             let name_len = (header.name.iter().position(|&x| x == b'/'))
190                 .or_else(|| header.name.iter().position(|&x| x == b' '))
191                 .unwrap_or_else(|| header.name.len());
192             &header.name[..name_len]
193         };
194 
195         Ok(ArchiveMember {
196             header,
197             name,
198             data: file_data,
199         })
200     }
201 
202     /// Return the raw header.
203     #[inline]
header(&self) -> &'data archive::Header204     pub fn header(&self) -> &'data archive::Header {
205         self.header
206     }
207 
208     /// Return the parsed file name.
209     ///
210     /// This may be an extended file name.
211     #[inline]
name(&self) -> &'data [u8]212     pub fn name(&self) -> &'data [u8] {
213         self.name
214     }
215 
216     /// Parse the file modification timestamp from the header.
217     #[inline]
date(&self) -> Option<usize>218     pub fn date(&self) -> Option<usize> {
219         parse_usize_digits(&self.header.date, 10)
220     }
221 
222     /// Parse the user ID from the header.
223     #[inline]
uid(&self) -> Option<usize>224     pub fn uid(&self) -> Option<usize> {
225         parse_usize_digits(&self.header.uid, 10)
226     }
227 
228     /// Parse the group ID from the header.
229     #[inline]
gid(&self) -> Option<usize>230     pub fn gid(&self) -> Option<usize> {
231         parse_usize_digits(&self.header.gid, 10)
232     }
233 
234     /// Parse the file mode from the header.
235     #[inline]
mode(&self) -> Option<usize>236     pub fn mode(&self) -> Option<usize> {
237         parse_usize_digits(&self.header.mode, 8)
238     }
239 
240     /// Return the file data.
241     #[inline]
data(&self) -> &'data [u8]242     pub fn data(&self) -> &'data [u8] {
243         self.data.0
244     }
245 }
246 
247 // Ignores bytes starting from the first space.
parse_usize_digits(digits: &[u8], radix: u32) -> Option<usize>248 fn parse_usize_digits(digits: &[u8], radix: u32) -> Option<usize> {
249     let len = digits
250         .iter()
251         .position(|&x| x == b' ')
252         .unwrap_or_else(|| digits.len());
253     let digits = &digits[..len];
254     if digits.is_empty() {
255         return None;
256     }
257     let mut result: usize = 0;
258     for &c in digits {
259         let x = (c as char).to_digit(radix)?;
260         result = result
261             .checked_mul(radix as usize)?
262             .checked_add(x as usize)?;
263     }
264     Some(result)
265 }
266 
parse_sysv_extended_name<'data>( digits: &[u8], mut names: Bytes<'data>, ) -> Result<&'data [u8], ()>267 fn parse_sysv_extended_name<'data>(
268     digits: &[u8],
269     mut names: Bytes<'data>,
270 ) -> Result<&'data [u8], ()> {
271     let offset = parse_usize_digits(digits, 10).ok_or(())?;
272     names.skip(offset)?;
273     let name = match names.0.iter().position(|&x| x == b'/' || x == 0) {
274         Some(len) => names.read_bytes(len)?,
275         None => names,
276     };
277     Ok(name.0)
278 }
279 
280 /// Modifies `data` to start after the extended name.
parse_bsd_extended_name<'data>( digits: &[u8], data: &mut Bytes<'data>, ) -> Result<&'data [u8], ()>281 fn parse_bsd_extended_name<'data>(
282     digits: &[u8],
283     data: &mut Bytes<'data>,
284 ) -> Result<&'data [u8], ()> {
285     let len = parse_usize_digits(digits, 10).ok_or(())?;
286     let mut name_data = data.read_bytes(len)?;
287     let name = match name_data.0.iter().position(|&x| x == 0) {
288         Some(len) => name_data.read_bytes(len)?,
289         None => name_data,
290     };
291     Ok(name.0)
292 }
293 
294 #[cfg(test)]
295 mod tests {
296     use super::*;
297 
298     #[test]
kind()299     fn kind() {
300         let data = b"!<arch>\n";
301         let archive = ArchiveFile::parse(data).unwrap();
302         assert_eq!(archive.kind(), ArchiveKind::Unknown);
303 
304         let data = b"\
305             !<arch>\n\
306             /                                               4         `\n\
307             0000";
308         let archive = ArchiveFile::parse(data).unwrap();
309         assert_eq!(archive.kind(), ArchiveKind::Gnu);
310 
311         let data = b"\
312             !<arch>\n\
313             //                                              4         `\n\
314             0000";
315         let archive = ArchiveFile::parse(data).unwrap();
316         assert_eq!(archive.kind(), ArchiveKind::Gnu);
317 
318         let data = b"\
319             !<arch>\n\
320             /                                               4         `\n\
321             0000\
322             //                                              4         `\n\
323             0000";
324         let archive = ArchiveFile::parse(data).unwrap();
325         assert_eq!(archive.kind(), ArchiveKind::Gnu);
326 
327         let data = b"\
328             !<arch>\n\
329             __.SYMDEF                                       4         `\n\
330             0000";
331         let archive = ArchiveFile::parse(data).unwrap();
332         assert_eq!(archive.kind(), ArchiveKind::Bsd);
333 
334         let data = b"\
335             !<arch>\n\
336             #1/9                                            13        `\n\
337             __.SYMDEF0000";
338         let archive = ArchiveFile::parse(data).unwrap();
339         assert_eq!(archive.kind(), ArchiveKind::Bsd);
340 
341         let data = b"\
342             !<arch>\n\
343             #1/16                                           20        `\n\
344             __.SYMDEF SORTED0000";
345         let archive = ArchiveFile::parse(data).unwrap();
346         assert_eq!(archive.kind(), ArchiveKind::Bsd);
347 
348         let data = b"\
349             !<arch>\n\
350             /                                               4         `\n\
351             0000\
352             /                                               4         `\n\
353             0000\
354             //                                              4         `\n\
355             0000";
356         let archive = ArchiveFile::parse(data).unwrap();
357         assert_eq!(archive.kind(), ArchiveKind::Coff);
358     }
359 
360     #[test]
gnu_names()361     fn gnu_names() {
362         let data = b"\
363             !<arch>\n\
364             //                                              18        `\n\
365             0123456789abcdef/\n\
366             0123456789abcde/0           0     0     644     3         `\n\
367             odd\n\
368             /0              0           0     0     644     4         `\n\
369             even";
370         let archive = ArchiveFile::parse(data).unwrap();
371         assert_eq!(archive.kind(), ArchiveKind::Gnu);
372         let mut members = archive.members();
373 
374         let member = members.next().unwrap().unwrap();
375         assert_eq!(member.name(), b"0123456789abcde");
376         assert_eq!(member.data(), b"odd");
377 
378         let member = members.next().unwrap().unwrap();
379         assert_eq!(member.name(), b"0123456789abcdef");
380         assert_eq!(member.data(), b"even");
381 
382         assert!(members.next().is_none());
383     }
384 
385     #[test]
bsd_names()386     fn bsd_names() {
387         let data = b"\
388             !<arch>\n\
389             0123456789abcde 0           0     0     644     3         `\n\
390             odd\n\
391             #1/16           0           0     0     644     20        `\n\
392             0123456789abcdefeven";
393         let archive = ArchiveFile::parse(data).unwrap();
394         assert_eq!(archive.kind(), ArchiveKind::Unknown);
395         let mut members = archive.members();
396 
397         let member = members.next().unwrap().unwrap();
398         assert_eq!(member.name(), b"0123456789abcde");
399         assert_eq!(member.data(), b"odd");
400 
401         let member = members.next().unwrap().unwrap();
402         assert_eq!(member.name(), b"0123456789abcdef");
403         assert_eq!(member.data(), b"even");
404 
405         assert!(members.next().is_none());
406     }
407 }
408