2 * Copyright (c) 2007 Kai Wang
3 * Copyright (c) 2007 Tim Kientzle
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "archive_platform.h"
29 __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.1 2007/04/03 05:34:36 kientzle Exp $");
42 #include "archive_entry.h"
43 #include "archive_private.h"
44 #include "archive_read_private.h"
49 off_t entry_bytes_remaining;
56 * Define structure of the "ar" header.
58 #define AR_name_offset 0
59 #define AR_name_size 16
60 #define AR_date_offset 16
61 #define AR_date_size 12
62 #define AR_uid_offset 28
64 #define AR_gid_offset 34
66 #define AR_mode_offset 40
67 #define AR_mode_size 8
68 #define AR_size_offset 48
69 #define AR_size_size 10
70 #define AR_fmag_offset 58
71 #define AR_fmag_size 2
76 #define ARMAG "!<arch>\n"
77 #define SARMAG 8 /* strlen(ARMAG); */
78 #define AR_EFMT1 "#1/"
79 #define SAR_EFMT1 3 /* strlen(AR_EFMT1); */
81 #define SARFMAG 2 /* strlen(ARFMAG); */
83 #define isdigit(x) (x) >= '0' && (x) <= '9'
85 static int archive_read_format_ar_bid(struct archive_read *a);
86 static int archive_read_format_ar_cleanup(struct archive_read *a);
87 static int archive_read_format_ar_read_data(struct archive_read *a,
88 const void **buff, size_t *size, off_t *offset);
89 static int archive_read_format_ar_skip(struct archive_read *a);
90 static int archive_read_format_ar_read_header(struct archive_read *a,
91 struct archive_entry *e);
92 static int64_t ar_atol8(const char *p, unsigned char_cnt);
93 static int64_t ar_atol10(const char *p, unsigned char_cnt);
94 static int ar_parse_string_table(struct archive_read *, struct ar *,
95 const void *, ssize_t);
98 * ANSI C99 defines constants for these, but not everyone supports
99 * those constants, so I define a couple of static variables here and
100 * compute the values. These calculations should be portable to any
101 * 2s-complement architecture.
104 static const uint64_t max_uint64 = UINT64_MAX;
106 static const uint64_t max_uint64 = ~(uint64_t)0;
109 static const int64_t max_int64 = INT64_MAX;
111 static const int64_t max_int64 = (int64_t)((~(uint64_t)0) >> 1);
114 static const int64_t min_int64 = INT64_MIN;
116 static const int64_t min_int64 = (int64_t)(~((~(uint64_t)0) >> 1));
120 archive_read_support_format_ar(struct archive *_a)
122 struct archive_read *a = (struct archive_read *)_a;
126 ar = (struct ar *)malloc(sizeof(*ar));
128 archive_set_error(&a->archive, ENOMEM,
129 "Can't allocate ar data");
130 return (ARCHIVE_FATAL);
132 memset(ar, 0, sizeof(*ar));
135 r = __archive_read_register_format(a,
137 archive_read_format_ar_bid,
138 archive_read_format_ar_read_header,
139 archive_read_format_ar_read_data,
140 archive_read_format_ar_skip,
141 archive_read_format_ar_cleanup);
143 if (r != ARCHIVE_OK) {
151 archive_read_format_ar_cleanup(struct archive_read *a)
155 ar = (struct ar *)*(a->pformat_data);
156 if (ar->has_strtab > 0)
159 *(a->pformat_data) = NULL;
164 archive_read_format_ar_bid(struct archive_read *a)
170 if (a->archive.archive_format != 0 &&
171 (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
175 ar = (struct ar *)*(a->pformat_data);
180 bytes_read = (a->compression_read_ahead)(a, &h, SARMAG);
181 if (bytes_read < SARMAG)
185 * Verify the global header.
186 * TODO: Do we need to check more than this?
188 if (strncmp((const char*)h, ARMAG, SARMAG) == 0) {
196 archive_read_format_ar_read_header(struct archive_read *a,
197 struct archive_entry *entry)
209 if (!a->archive.archive_format) {
210 a->archive.archive_format = ARCHIVE_FORMAT_AR;
211 a->archive.archive_format_name = "Unix Archiver";
214 if (a->archive.file_position == 0) {
216 * We are now at the beginning of the archive,
217 * so we need first consume the ar global header.
219 (a->compression_read_consume)(a, SARMAG);
222 /* Read 60-byte header */
223 bytes = (a->compression_read_ahead)(a, &b, 60);
226 * We just encountered an incomplete ar file,
227 * though the _bid function accepted it.
229 return (ARCHIVE_EOF);
231 (a->compression_read_consume)(a, 60);
235 /* Consistency check */
236 if (strncmp(h + AR_fmag_offset, ARFMAG, SARFMAG) != 0) {
237 archive_set_error(&a->archive, EINVAL,
238 "Consistency check failed");
239 return (ARCHIVE_WARN);
242 ar = (struct ar*)*(a->pformat_data);
244 if (strncmp(h + AR_name_offset, "//", 2) == 0) {
246 * An archive member with ar_name "//" is an archive
249 nval = ar_atol10(h + AR_size_offset, AR_size_size);
250 bytes = (a->compression_read_ahead)(a, &b, nval);
252 return (ARCHIVE_FATAL);
254 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
255 "Truncated input file");
256 return (ARCHIVE_FATAL);
259 r = ar_parse_string_table(a, ar, b, nval);
260 if (r == ARCHIVE_OK) {
262 * Archive string table only have ar_name and ar_size fileds
265 archive_entry_copy_pathname(entry, "//");
266 nval = ar_atol10(h + AR_size_offset, AR_size_size);
267 archive_entry_set_size(entry, nval);
269 ar->entry_offset = 0;
270 ar->entry_bytes_remaining = nval;
271 ar->entry_padding = ar->entry_bytes_remaining % 2;
276 if (h[AR_name_offset] == '/' && isdigit(h[AR_name_offset + 1])) {
278 * Archive member is common format with SVR4/GNU variant.
279 * "/" followed by one or more digit(s) in the ar_name
280 * filed indicates an index to the string table.
282 if (ar->has_strtab > 0) {
283 nval = ar_atol10(h + AR_name_offset + 1,
285 archive_entry_copy_pathname(entry, &ar->strtab[nval]);
287 archive_set_error(&a->archive, EINVAL,
288 "String table does not exist");
289 return (ARCHIVE_WARN);
294 if (strncmp(h + AR_name_offset, AR_EFMT1, SAR_EFMT1) == 0) {
296 * Archive member is common format with BSD variant.
297 * AR_EFMT1 is followed by one or more digit(s) indicating
298 * the length of the real filename which is appended
301 nval = ar_atol10(h + AR_name_offset + SAR_EFMT1,
302 AR_name_size - SAR_EFMT1);
303 bytes = (a->compression_read_ahead)(a, &b, nval);
305 return (ARCHIVE_FATAL);
307 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
308 "Truncated input file");
309 return (ARCHIVE_FATAL);
312 (a->compression_read_consume)(a, nval);
314 fname = (char *)malloc(nval + 1);
316 archive_set_error(&a->archive, ENOMEM,
317 "Can't allocate fname buffer");
318 return (ARCHIVE_FATAL);
320 strncpy(fname, b, nval);
322 archive_entry_copy_pathname(entry, fname);
331 * "/" followed by one or more spaces indicate a
332 * SVR4/GNU archive symbol table.
335 if (strncmp(h + AR_name_offset, "/ ", 2) == 0) {
336 archive_entry_copy_pathname(entry, "/");
340 * "__.SYMDEF" indicates a BSD archive symbol table.
342 if (strncmp(h + AR_name_offset, "__.SYMDEF", 9) == 0) {
343 archive_entry_copy_pathname(entry, "__.SYMDEF");
348 * Otherwise, the ar_name fields stores the real
350 * SVR4/GNU variant append a '/' to mark the end of
351 * filename, while BSD variant use a space.
353 fname = (char *)malloc(AR_name_size + 1);
354 strncpy(fname, h + AR_name_offset, AR_name_size);
355 fname[AR_name_size] = '\0';
357 if ((p = strchr(fname, '/')) != NULL) {
358 /* SVR4/GNU format */
360 archive_entry_copy_pathname(entry, fname);
367 if ((p = strchr(fname, ' ')) != NULL)
369 archive_entry_copy_pathname(entry, fname);
374 /* Copy remaining header */
375 archive_entry_set_mtime(entry,
376 ar_atol10(h + AR_date_offset, AR_date_size), 0);
377 archive_entry_set_uid(entry,
378 ar_atol10(h + AR_uid_offset, AR_uid_size));
379 archive_entry_set_gid(entry,
380 ar_atol10(h + AR_gid_offset, AR_gid_size));
381 archive_entry_set_mode(entry,
382 ar_atol8(h + AR_mode_offset, AR_mode_size));
383 nval = ar_atol10(h + AR_size_offset, AR_size_size);
385 ar->entry_offset = 0;
386 ar->entry_padding = nval % 2;
389 * For BSD variant, we should subtract the length of
390 * the appended filename string from ar_size to get the
391 * real file size. But remember we should do this only
392 * after we had calculated the padding.
397 archive_entry_set_size(entry, nval);
398 ar->entry_bytes_remaining = nval;
404 archive_read_format_ar_read_data(struct archive_read *a,
405 const void **buff, size_t *size, off_t *offset)
410 ar = (struct ar *)*(a->pformat_data);
412 if (ar->entry_bytes_remaining > 0) {
413 bytes_read = (a->compression_read_ahead)(a, buff, 1);
414 if (bytes_read == 0) {
415 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
416 "Truncated ar archive");
417 return (ARCHIVE_FATAL);
420 return (ARCHIVE_FATAL);
421 if (bytes_read > ar->entry_bytes_remaining)
422 bytes_read = ar->entry_bytes_remaining;
424 *offset = ar->entry_offset;
425 ar->entry_offset += bytes_read;
426 ar->entry_bytes_remaining -= bytes_read;
427 (a->compression_read_consume)(a, bytes_read);
430 while (ar->entry_padding > 0) {
431 bytes_read = (a->compression_read_ahead)(a, buff, 1);
433 return (ARCHIVE_FATAL);
434 if (bytes_read > ar->entry_padding)
435 bytes_read = ar->entry_padding;
436 (a->compression_read_consume)(a, bytes_read);
437 ar->entry_padding -= bytes_read;
441 *offset = ar->entry_offset;
442 return (ARCHIVE_EOF);
447 archive_read_format_ar_skip(struct archive_read *a)
452 const void *b; /* Dummy variables */
456 ar = (struct ar *)*(a->pformat_data);
457 if (a->compression_skip == NULL) {
458 while (r == ARCHIVE_OK)
459 r = archive_read_format_ar_read_data(a, &b, &s, &o);
463 bytes_skipped = (a->compression_skip)(a, ar->entry_bytes_remaining +
465 if (bytes_skipped < 0)
466 return (ARCHIVE_FATAL);
468 ar->entry_bytes_remaining = 0;
469 ar->entry_padding = 0;
475 ar_parse_string_table(struct archive_read *a, struct ar *ar,
476 const void *h, ssize_t size)
480 if (ar->has_strtab > 0) {
481 archive_set_error(&a->archive, EINVAL,
482 "More than one string tables exist");
483 return (ARCHIVE_WARN);
486 ar->strtab = (char *)malloc(size);
487 if (ar->strtab == NULL) {
488 archive_set_error(&a->archive, ENOMEM,
489 "Can't allocate string table buffer");
490 return (ARCHIVE_FATAL);
493 (void)memcpy(ar->strtab, h, size);
495 while (p < ar->strtab + size - 1) {
501 archive_set_error(&a->archive, EINVAL,
502 "Invalid string table");
504 return (ARCHIVE_WARN);
510 * Sanity check, last two chars must be `/\n' or '\n\n',
511 * depending on whether the string table is padded by a '\n'
512 * (string table produced by GNU ar always has a even size).
514 if (p != ar->strtab + size && *p != '\n') {
515 archive_set_error(&a->archive, EINVAL,
516 "Invalid string table");
518 return (ARCHIVE_WARN);
526 ar_atol8(const char *p, unsigned char_cnt)
528 int64_t l, limit, last_digit_limit;
529 int digit, sign, base;
532 limit = max_int64 / base;
533 last_digit_limit = max_int64 % base;
535 while (*p == ' ' || *p == '\t')
545 while (digit >= 0 && digit < base && char_cnt-- > 0) {
546 if (l>limit || (l == limit && digit > last_digit_limit)) {
547 l = max_uint64; /* Truncate on overflow. */
550 l = (l * base) + digit;
553 return (sign < 0) ? -l : l;
557 ar_atol10(const char *p, unsigned char_cnt)
559 int64_t l, limit, last_digit_limit;
560 int base, digit, sign;
563 limit = max_int64 / base;
564 last_digit_limit = max_int64 % base;
566 while (*p == ' ' || *p == '\t')
576 while (digit >= 0 && digit < base && char_cnt-- > 0) {
577 if (l > limit || (l == limit && digit > last_digit_limit)) {
578 l = max_uint64; /* Truncate on overflow. */
581 l = (l * base) + digit;
584 return (sign < 0) ? -l : l;