]> git.proxmox.com Git - rustc.git/blob - vendor/object-0.22.0/src/read/archive.rs
New upstream version 1.51.0+dfsg1
[rustc.git] / vendor / object-0.22.0 / src / read / archive.rs
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.
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]
108 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]
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
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.
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]
204 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]
212 pub fn name(&self) -> &'data [u8] {
213 self.name
214 }
215
216 /// Parse the file modification timestamp from the header.
217 #[inline]
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]
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]
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]
236 pub fn mode(&self) -> Option<usize> {
237 parse_usize_digits(&self.header.mode, 8)
238 }
239
240 /// Return the file data.
241 #[inline]
242 pub fn data(&self) -> &'data [u8] {
243 self.data.0
244 }
245 }
246
247 // Ignores bytes starting from the first space.
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
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.
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]
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]
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]
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 }