summaryrefslogtreecommitdiff
path: root/libarchive/archive_read_support_format_tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_read_support_format_tar.c')
-rw-r--r--libarchive/archive_read_support_format_tar.c210
1 files changed, 138 insertions, 72 deletions
diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c
index c6f405da6e3fe..b0521a627ce31 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -151,6 +151,8 @@ struct tar {
struct archive_string_conv *sconv_default;
int init_default_conversion;
int compat_2x;
+ int process_mac_extensions;
+ int read_concatenated_archives;
};
static int archive_block_is_null(const char *p);
@@ -200,7 +202,7 @@ static int archive_read_format_tar_read_header(struct archive_read *,
struct archive_entry *);
static int checksum(struct archive_read *, const void *);
static int pax_attribute(struct archive_read *, struct tar *,
- struct archive_entry *, char *key, char *value);
+ struct archive_entry *, const char *key, const char *value);
static int pax_header(struct archive_read *, struct tar *,
struct archive_entry *, char *attr);
static void pax_time(const char *, int64_t *sec, long *nanos);
@@ -241,6 +243,10 @@ archive_read_support_format_tar(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_read_support_format_tar");
tar = (struct tar *)calloc(1, sizeof(*tar));
+#ifdef HAVE_COPYFILE_H
+ /* Set this by default on Mac OS. */
+ tar->process_mac_extensions = 1;
+#endif
if (tar == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate tar data");
@@ -254,7 +260,9 @@ archive_read_support_format_tar(struct archive *_a)
archive_read_format_tar_read_data,
archive_read_format_tar_skip,
NULL,
- archive_read_format_tar_cleanup);
+ archive_read_format_tar_cleanup,
+ NULL,
+ NULL);
if (r != ARCHIVE_OK)
free(tar);
@@ -368,7 +376,7 @@ archive_read_format_tar_options(struct archive_read *a,
tar = (struct tar *)(a->format->data);
if (strcmp(key, "compat-2x") == 0) {
/* Handle UTF-8 filnames as libarchive 2.x */
- tar->compat_2x = (val != NULL)?1:0;
+ tar->compat_2x = (val != NULL && val[0] != 0);
tar->init_default_conversion = tar->compat_2x;
return (ARCHIVE_OK);
} else if (strcmp(key, "hdrcharset") == 0) {
@@ -385,6 +393,12 @@ archive_read_format_tar_options(struct archive_read *a,
ret = ARCHIVE_FATAL;
}
return (ret);
+ } else if (strcmp(key, "mac-ext") == 0) {
+ tar->process_mac_extensions = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "read_concatenated_archives") == 0) {
+ tar->read_concatenated_archives = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
@@ -397,7 +411,7 @@ archive_read_format_tar_options(struct archive_read *a,
* how much unconsumed data we have floating around, and to consume
* anything outstanding since we're going to do read_aheads
*/
-static void
+static void
tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)
{
if (*unconsumed) {
@@ -442,6 +456,7 @@ archive_read_format_tar_read_header(struct archive_read *a,
static int default_dev;
struct tar *tar;
const char *p;
+ const wchar_t *wp;
int r;
size_t l, unconsumed = 0;
@@ -492,27 +507,22 @@ archive_read_format_tar_read_header(struct archive_read *a,
}
}
- if (r == ARCHIVE_OK) {
+ if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) {
/*
* "Regular" entry with trailing '/' is really
* directory: This is needed for certain old tar
* variants and even for some broken newer ones.
*/
- const wchar_t *wp;
- wp = archive_entry_pathname_w(entry);
- if (wp != NULL) {
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
l = wcslen(wp);
- if (archive_entry_filetype(entry) == AE_IFREG
- && wp[l-1] == L'/')
+ if (l > 0 && wp[l - 1] == L'/') {
archive_entry_set_filetype(entry, AE_IFDIR);
- } else {
- p = archive_entry_pathname(entry);
- if (p == NULL)
- return (ARCHIVE_FAILED);
+ }
+ } else if ((p = archive_entry_pathname(entry)) != NULL) {
l = strlen(p);
- if (archive_entry_filetype(entry) == AE_IFREG
- && p[l-1] == '/')
+ if (l > 0 && p[l - 1] == '/') {
archive_entry_set_filetype(entry, AE_IFDIR);
+ }
}
}
return (r);
@@ -594,8 +604,12 @@ archive_read_format_tar_skip(struct archive_read *a)
/* Do not consume the hole of a sparse file. */
request = 0;
for (p = tar->sparse_list; p != NULL; p = p->next) {
- if (!p->hole)
+ if (!p->hole) {
+ if (p->remaining >= INT64_MAX - request) {
+ return ARCHIVE_FATAL;
+ }
request += p->remaining;
+ }
}
if (request > tar->entry_bytes_remaining)
request = tar->entry_bytes_remaining;
@@ -629,36 +643,50 @@ tar_read_header(struct archive_read *a, struct tar *tar,
const struct archive_entry_header_ustar *header;
const struct archive_entry_header_gnutar *gnuheader;
- tar_flush_unconsumed(a, unconsumed);
+ /* Loop until we find a workable header record. */
+ for (;;) {
+ tar_flush_unconsumed(a, unconsumed);
- /* Read 512-byte header record */
- h = __archive_read_ahead(a, 512, &bytes);
- if (bytes < 0)
- return ((int)bytes);
- if (bytes == 0) { /* EOF at a block boundary. */
- /* Some writers do omit the block of nulls. <sigh> */
- return (ARCHIVE_EOF);
- }
- if (bytes < 512) { /* Short block at EOF; this is bad. */
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Truncated tar archive");
- return (ARCHIVE_FATAL);
- }
- *unconsumed = 512;
+ /* Read 512-byte header record */
+ h = __archive_read_ahead(a, 512, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) { /* EOF at a block boundary. */
+ /* Some writers do omit the block of nulls. <sigh> */
+ return (ARCHIVE_EOF);
+ }
+ if (bytes < 512) { /* Short block at EOF; this is bad. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = 512;
- /* Check for end-of-archive mark. */
- if (h[0] == 0 && archive_block_is_null(h)) {
- /* Try to consume a second all-null record, as well. */
- tar_flush_unconsumed(a, unconsumed);
- h = __archive_read_ahead(a, 512, NULL);
- if (h != NULL)
- __archive_read_consume(a, 512);
- archive_clear_error(&a->archive);
+ /* Header is workable if it's not an end-of-archive mark. */
+ if (h[0] != 0 || !archive_block_is_null(h))
+ break;
+
+ /* Ensure format is set for archives with only null blocks. */
if (a->archive.archive_format_name == NULL) {
a->archive.archive_format = ARCHIVE_FORMAT_TAR;
a->archive.archive_format_name = "tar";
}
- return (ARCHIVE_EOF);
+
+ if (!tar->read_concatenated_archives) {
+ /* Try to consume a second all-null record, as well. */
+ tar_flush_unconsumed(a, unconsumed);
+ h = __archive_read_ahead(a, 512, NULL);
+ if (h != NULL && h[0] == 0 && archive_block_is_null(h))
+ __archive_read_consume(a, 512);
+ archive_clear_error(&a->archive);
+ return (ARCHIVE_EOF);
+ }
+
+ /*
+ * We're reading concatenated archives, ignore this block and
+ * loop to get the next.
+ */
}
/*
@@ -693,6 +721,8 @@ tar_read_header(struct archive_read *a, struct tar *tar,
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
a->archive.archive_format_name = "POSIX pax interchange format";
err = header_pax_global(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ return (err);
break;
case 'K': /* Long link name (GNU tar, others) */
err = header_longlink(a, tar, entry, h, unconsumed);
@@ -745,9 +775,9 @@ tar_read_header(struct archive_read *a, struct tar *tar,
* extensions for both the AppleDouble extension entry and the
* regular entry.
*/
- /* TODO: Should this be disabled on non-Mac platforms? */
if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
- tar->header_recursion_depth == 0) {
+ tar->header_recursion_depth == 0 &&
+ tar->process_mac_extensions) {
int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);
if (err2 < err)
err = err2;
@@ -790,12 +820,20 @@ checksum(struct archive_read *a, const void *h)
{
const unsigned char *bytes;
const struct archive_entry_header_ustar *header;
- int check, i, sum;
+ int check, sum;
+ size_t i;
(void)a; /* UNUSED */
bytes = (const unsigned char *)h;
header = (const struct archive_entry_header_ustar *)h;
+ /* Checksum field must hold an octal number */
+ for (i = 0; i < sizeof(header->checksum); ++i) {
+ char c = header->checksum[i];
+ if (c != ' ' && c != '\0' && (c < '0' || c > '7'))
+ return 0;
+ }
+
/*
* Test the checksum. Note that POSIX specifies _unsigned_
* bytes for this calculation.
@@ -1287,7 +1325,7 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
if (wp[0] == '/' && wp[1] != L'\0')
wname = wp + 1;
}
- /*
+ /*
* If last path element starts with "._", then
* this is a Mac extension.
*/
@@ -1302,7 +1340,7 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
if (p[0] == '/' && p[1] != '\0')
name = p + 1;
}
- /*
+ /*
* If last path element starts with "._", then
* this is a Mac extension.
*/
@@ -1626,7 +1664,7 @@ pax_header(struct archive_read *a, struct tar *tar,
static int
pax_attribute_xattr(struct archive_entry *entry,
- char *name, char *value)
+ const char *name, const char *value)
{
char *name_decoded;
void *value_decoded;
@@ -1672,7 +1710,7 @@ pax_attribute_xattr(struct archive_entry *entry,
*/
static int
pax_attribute(struct archive_read *a, struct tar *tar,
- struct archive_entry *entry, char *key, char *value)
+ struct archive_entry *entry, const char *key, const char *value)
{
int64_t s;
long n;
@@ -2089,6 +2127,10 @@ gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,
else
tar->sparse_list = p;
tar->sparse_last = p;
+ if (remaining < 0 || offset < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data");
+ return (ARCHIVE_FATAL);
+ }
p->offset = offset;
p->remaining = remaining;
return (ARCHIVE_OK);
@@ -2422,9 +2464,10 @@ tar_atol(const char *p, size_t char_cnt)
static int64_t
tar_atol_base_n(const char *p, size_t char_cnt, int base)
{
- int64_t l, limit, last_digit_limit;
+ int64_t l, maxval, limit, last_digit_limit;
int digit, sign;
+ maxval = INT64_MAX;
limit = INT64_MAX / base;
last_digit_limit = INT64_MAX % base;
@@ -2441,6 +2484,10 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base)
sign = -1;
p++;
char_cnt--;
+
+ maxval = INT64_MIN;
+ limit = -(INT64_MIN / base);
+ last_digit_limit = INT64_MIN % base;
}
l = 0;
@@ -2448,8 +2495,7 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base)
digit = *p - '0';
while (digit >= 0 && digit < base && char_cnt != 0) {
if (l>limit || (l == limit && digit > last_digit_limit)) {
- l = INT64_MAX; /* Truncate on overflow. */
- break;
+ return maxval; /* Truncate on overflow. */
}
l = (l * base) + digit;
digit = *++p - '0';
@@ -2472,36 +2518,56 @@ tar_atol10(const char *p, size_t char_cnt)
}
/*
- * Parse a base-256 integer. This is just a straight signed binary
- * value in big-endian order, except that the high-order bit is
- * ignored.
+ * Parse a base-256 integer. This is just a variable-length
+ * twos-complement signed binary value in big-endian order, except
+ * that the high-order bit is ignored. The values here can be up to
+ * 12 bytes, so we need to be careful about overflowing 64-bit
+ * (8-byte) integers.
+ *
+ * This code unashamedly assumes that the local machine uses 8-bit
+ * bytes and twos-complement arithmetic.
*/
static int64_t
tar_atol256(const char *_p, size_t char_cnt)
{
- int64_t l, upper_limit, lower_limit;
+ uint64_t l;
const unsigned char *p = (const unsigned char *)_p;
+ unsigned char c, neg;
+
+ /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */
+ c = *p;
+ if (c & 0x40) {
+ neg = 0xff;
+ c |= 0x80;
+ l = ~ARCHIVE_LITERAL_ULL(0);
+ } else {
+ neg = 0;
+ c &= 0x7f;
+ l = 0;
+ }
- upper_limit = INT64_MAX / 256;
- lower_limit = INT64_MIN / 256;
+ /* If more than 8 bytes, check that we can ignore
+ * high-order bits without overflow. */
+ while (char_cnt > sizeof(int64_t)) {
+ --char_cnt;
+ if (c != neg)
+ return neg ? INT64_MIN : INT64_MAX;
+ c = *++p;
+ }
- /* Pad with 1 or 0 bits, depending on sign. */
- if ((0x40 & *p) == 0x40)
- l = (int64_t)-1;
- else
- l = 0;
- l = (l << 6) | (0x3f & *p++);
+ /* c is first byte that fits; if sign mismatch, return overflow */
+ if ((c ^ neg) & 0x80) {
+ return neg ? INT64_MIN : INT64_MAX;
+ }
+
+ /* Accumulate remaining bytes. */
while (--char_cnt > 0) {
- if (l > upper_limit) {
- l = INT64_MAX; /* Truncate on overflow */
- break;
- } else if (l < lower_limit) {
- l = INT64_MIN;
- break;
- }
- l = (l << 8) | (0xff & (int64_t)*p++);
+ l = (l << 8) | c;
+ c = *++p;
}
- return (l);
+ l = (l << 8) | c;
+ /* Return signed twos-complement value. */
+ return (int64_t)(l);
}
/*