From bf0a4481f914a4a5a548e3dfd946be2f40f7728a Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Mon, 3 Sep 2007 18:47:14 +0000 Subject: [PATCH] Import libarchive-2.2.7. This brings support for mtree files, gtar 1.17 sparse files, and refines suid/sgid restore handling. --- contrib/libarchive-2/NEWS | 13 + contrib/libarchive-2/libarchive/archive.h.in | 4 + .../libarchive-2/libarchive/archive_entry.c | 8 + .../libarchive-2/libarchive/archive_entry.h | 1 + .../archive_read_support_format_all.c | 1 + .../archive_read_support_format_mtree.c | 708 ++++++++++++++++++ .../archive_read_support_format_tar.c | 112 +-- .../libarchive/archive_write_disk.c | 30 +- contrib/libarchive-2/tar/bsdtar.c | 2 +- contrib/libarchive-2/version | 2 +- 10 files changed, 824 insertions(+), 57 deletions(-) create mode 100644 contrib/libarchive-2/libarchive/archive_read_support_format_mtree.c diff --git a/contrib/libarchive-2/NEWS b/contrib/libarchive-2/NEWS index 758d9e55e3..9f621a9d08 100644 --- a/contrib/libarchive-2/NEWS +++ b/contrib/libarchive-2/NEWS @@ -1,4 +1,14 @@ +Sep 01, 2007: libarchive 2.2.7 released +Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage + (A little experimental still.) +Aug 18, 2007: Read gtar 1.17 --posix --sparse entries. +Aug 13, 2007: Refined suid/sgid restore handling; it is no longer + an error if suid/sgid bits are dropped when you request + perm restore but don't request owner restore. +Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio + (Still very incomplete!) + Aug 05, 2007: libarchive 2.2.6 released Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg @@ -8,6 +18,9 @@ Aug 05, 2007: Several bug fixes from FreeBSD CVS repo. Jul 13, 2007: libarchive 2.2.5 released Jul 12, 2007: libarchive 2.2.4 released +Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and + fixing several critical security bugs. Details available at + http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc May 26, 2007: libarchive 2.2.3 released May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some diff --git a/contrib/libarchive-2/libarchive/archive.h.in b/contrib/libarchive-2/libarchive/archive.h.in index 843276f72d..eb374437d1 100644 --- a/contrib/libarchive-2/libarchive/archive.h.in +++ b/contrib/libarchive-2/libarchive/archive.h.in @@ -218,6 +218,9 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_FORMAT_AR 0x70000 #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) +#define ARCHIVE_FORMAT_MTREE 0x80000 +#define ARCHIVE_FORMAT_MTREE_V1 (ARCHIVE_FORMAT_MTREE | 1) +#define ARCHIVE_FORMAT_MTREE_V2 (ARCHIVE_FORMAT_MTREE | 2) /*- * Basic outline for reading an archive: @@ -254,6 +257,7 @@ int archive_read_support_format_cpio(struct archive *); int archive_read_support_format_empty(struct archive *); int archive_read_support_format_gnutar(struct archive *); int archive_read_support_format_iso9660(struct archive *); +int archive_read_support_format_mtree(struct archive *); int archive_read_support_format_tar(struct archive *); int archive_read_support_format_zip(struct archive *); diff --git a/contrib/libarchive-2/libarchive/archive_entry.c b/contrib/libarchive-2/libarchive/archive_entry.c index 0ef2a18dd7..6b83abb4fb 100644 --- a/contrib/libarchive-2/libarchive/archive_entry.c +++ b/contrib/libarchive-2/libarchive/archive_entry.c @@ -779,6 +779,14 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) aes_copy_wcs(&entry->ae_pathname, name); } +void +archive_entry_set_perm(struct archive_entry *entry, mode_t p) +{ + entry->stat_valid = 0; + entry->ae_stat.aest_mode &= AE_IFMT; + entry->ae_stat.aest_mode |= ~AE_IFMT & p; +} + void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { diff --git a/contrib/libarchive-2/libarchive/archive_entry.h b/contrib/libarchive-2/libarchive/archive_entry.h index a6f3291b65..95fe8c61c7 100644 --- a/contrib/libarchive-2/libarchive/archive_entry.h +++ b/contrib/libarchive-2/libarchive/archive_entry.h @@ -148,6 +148,7 @@ void archive_entry_set_nlink(struct archive_entry *, unsigned int); void archive_entry_set_pathname(struct archive_entry *, const char *); void archive_entry_copy_pathname(struct archive_entry *, const char *); void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); +void archive_entry_set_perm(struct archive_entry *, mode_t); void archive_entry_set_rdev(struct archive_entry *, dev_t); void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); void archive_entry_set_rdevminor(struct archive_entry *, dev_t); diff --git a/contrib/libarchive-2/libarchive/archive_read_support_format_all.c b/contrib/libarchive-2/libarchive/archive_read_support_format_all.c index 3fdfa05285..c9165c8945 100644 --- a/contrib/libarchive-2/libarchive/archive_read_support_format_all.c +++ b/contrib/libarchive-2/libarchive/archive_read_support_format_all.c @@ -35,6 +35,7 @@ archive_read_support_format_all(struct archive *a) archive_read_support_format_cpio(a); archive_read_support_format_empty(a); archive_read_support_format_iso9660(a); + archive_read_support_format_mtree(a); archive_read_support_format_tar(a); archive_read_support_format_zip(a); return (ARCHIVE_OK); diff --git a/contrib/libarchive-2/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive-2/libarchive/archive_read_support_format_mtree.c new file mode 100644 index 0000000000..6b6ca92593 --- /dev/null +++ b/contrib/libarchive-2/libarchive/archive_read_support_format_mtree.c @@ -0,0 +1,708 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#include +/* #include */ /* See archive_platform.h */ +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" +#include "archive_string.h" + +struct mtree_entry { + struct mtree_entry *next; + char *name; + char *option_start; + char *option_end; + char full; + char used; +}; + +struct mtree { + struct archive_string line; + size_t buffsize; + char *buff; + off_t offset; + int fd; + int bid; + int filetype; + int archive_format; + const char *archive_format_name; + struct mtree_entry *entries; + struct mtree_entry *this_entry; + struct archive_string current_dir; + struct archive_string contents_name; +}; + +static int bid(struct archive_read *); +static int cleanup(struct archive_read *); +static void parse_escapes(char *, struct mtree_entry *); +static int parse_setting(struct archive_read *, struct mtree *, + struct archive_entry *, char *, char *); +static int read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset); +static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); +static int skip(struct archive_read *a); +static int read_header(struct archive_read *, + struct archive_entry *); +static int64_t mtree_atol10(char **); +static int64_t mtree_atol8(char **); + +int +archive_read_support_format_mtree(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct mtree *mtree; + int r; + + mtree = (struct mtree *)malloc(sizeof(*mtree)); + if (mtree == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate mtree data"); + return (ARCHIVE_FATAL); + } + memset(mtree, 0, sizeof(*mtree)); + mtree->bid = -1; + mtree->fd = -1; + + r = __archive_read_register_format(a, mtree, + bid, read_header, read_data, skip, cleanup); + + if (r != ARCHIVE_OK) + free(mtree); + return (ARCHIVE_OK); +} + +static int +cleanup(struct archive_read *a) +{ + struct mtree *mtree; + struct mtree_entry *p, *q; + + mtree = (struct mtree *)(a->format->data); + p = mtree->entries; + while (p != NULL) { + q = p->next; + free(p->name); + /* + * Note: option_start, option_end are pointers into + * the block that p->name points to. So we should + * not try to free them! + */ + free(p); + p = q; + } + archive_string_free(&mtree->line); + archive_string_free(&mtree->current_dir); + archive_string_free(&mtree->contents_name); + free(mtree->buff); + free(mtree); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + + +static int +bid(struct archive_read *a) +{ + struct mtree *mtree; + ssize_t bytes_read; + const void *h; + const char *signature = "#mtree"; + const char *p; + + mtree = (struct mtree *)(a->format->data); + if (mtree->bid != -1) + return (mtree->bid); + + /* Now let's look at the actual header and see if it matches. */ + bytes_read = (a->decompressor->read_ahead)(a, &h, strlen(signature)); + + if (bytes_read <= 0) + return (bytes_read); + + p = h; + mtree->bid = 0; + while (bytes_read > 0 && *signature != '\0') { + if (*p != *signature) + return (mtree->bid = 0); + mtree->bid += 8; + p++; + signature++; + bytes_read--; + } + return (mtree->bid); +} + +/* + * The extended mtree format permits multiple lines specifying + * attributes for each file. Practically speaking, that means we have + * to read the entire mtree file into memory up front. + */ +static int +read_mtree(struct archive_read *a, struct mtree *mtree) +{ + ssize_t len; + char *p; + struct mtree_entry *mentry; + struct mtree_entry *last_mentry = NULL; + + mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1; + mtree->archive_format_name = "mtree"; + + for (;;) { + len = readline(a, mtree, &p, 256); + if (len == 0) { + mtree->this_entry = mtree->entries; + return (ARCHIVE_OK); + } + if (len < 0) + return (len); + /* Leading whitespace is never significant, ignore it. */ + while (*p == ' ' || *p == '\t') { + ++p; + --len; + } + /* Skip content lines and blank lines. */ + if (*p == '#') + continue; + if (*p == '\r' || *p == '\n' || *p == '\0') + continue; + mentry = malloc(sizeof(*mentry)); + if (mentry == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + memset(mentry, 0, sizeof(*mentry)); + /* Add this entry to list. */ + if (last_mentry == NULL) { + last_mentry = mtree->entries = mentry; + } else { + last_mentry->next = mentry; + } + last_mentry = mentry; + + /* Copy line over onto heap. */ + mentry->name = malloc(len + 1); + if (mentry->name == NULL) { + free(mentry); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + strcpy(mentry->name, p); + mentry->option_end = mentry->name + len; + /* Find end of name. */ + p = mentry->name; + while (*p != ' ' && *p != '\n' && *p != '\0') + ++p; + *p++ = '\0'; + parse_escapes(mentry->name, mentry); + /* Find start of options and record it. */ + while (p < mentry->option_end && (*p == ' ' || *p == '\t')) + ++p; + mentry->option_start = p; + /* Null terminate each separate option. */ + while (++p < mentry->option_end) + if (*p == ' ' || *p == '\t' || *p == '\n') + *p = '\0'; + } +} + +static int +read_header(struct archive_read *a, struct archive_entry *entry) +{ + struct stat st; + struct mtree *mtree; + struct mtree_entry *mentry, *mentry2; + char *p, *q; + int r = ARCHIVE_OK, r1; + + mtree = (struct mtree *)(a->format->data); + + if (mtree->fd >= 0) { + close(mtree->fd); + mtree->fd = -1; + } + + if (mtree->entries == NULL) { + r = read_mtree(a, mtree); + if (r != ARCHIVE_OK) + return (r); + } + + a->archive.archive_format = mtree->archive_format; + a->archive.archive_format_name = mtree->archive_format_name; + + for (;;) { + mentry = mtree->this_entry; + if (mentry == NULL) { + mtree->this_entry = NULL; + return (ARCHIVE_EOF); + } + mtree->this_entry = mentry->next; + if (mentry->used) + continue; + mentry->used = 1; + if (strcmp(mentry->name, "..") == 0) { + if (archive_strlen(&mtree->current_dir) > 0) { + /* Roll back current path. */ + p = mtree->current_dir.s + + mtree->current_dir.length - 1; + while (p >= mtree->current_dir.s && *p != '/') + --p; + if (p >= mtree->current_dir.s) + --p; + mtree->current_dir.length + = p - mtree->current_dir.s + 1; + } + continue; + } + + mtree->filetype = AE_IFREG; + + /* Parse options. */ + p = mentry->option_start; + while (p < mentry->option_end) { + q = p + strlen(p); + r1 = parse_setting(a, mtree, entry, p, q); + if (r1 != ARCHIVE_OK) + r = r1; + p = q + 1; + } + + if (mentry->full) { + archive_entry_copy_pathname(entry, mentry->name); + /* + * "Full" entries are allowed to have multiple + * lines and those lines aren't required to be + * adjacent. We don't support multiple lines + * for "relative" entries nor do we make any + * attempt to merge data from separate + * "relative" and "full" entries. (Merging + * "relative" and "full" entries would require + * dealing with pathname canonicalization, + * which is a very tricky subject.) + */ + mentry2 = mentry->next; + while (mentry2 != NULL) { + if (mentry2->full + && !mentry2->used + && strcmp(mentry->name, mentry2->name) == 0) { + /* + * Add those options as well; + * later lines override + * earlier ones. + */ + p = mentry2->option_start; + while (p < mentry2->option_end) { + q = p + strlen(p); + r1 = parse_setting(a, mtree, entry, p, q); + if (r1 != ARCHIVE_OK) + r = r1; + p = q + 1; + } + mentry2->used = 1; + } + mentry2 = mentry2->next; + } + } else { + /* + * Relative entries require us to construct + * the full path and possibly update the + * current directory. + */ + size_t n = archive_strlen(&mtree->current_dir); + if (n > 0) + archive_strcat(&mtree->current_dir, "/"); + archive_strcat(&mtree->current_dir, mentry->name); + archive_entry_copy_pathname(entry, mtree->current_dir.s); + if (archive_entry_filetype(entry) != AE_IFDIR) + mtree->current_dir.length = n; + } + + /* + * Try to open and stat the file to get the real size. + * It would be nice to avoid this here so that getting + * a listing of an mtree wouldn't require opening + * every referenced contents file. But then we + * wouldn't know the actual contents size, so I don't + * see a really viable way around this. (Also, we may + * want to someday pull other unspecified info from + * the contents file on disk.) + */ + if (archive_strlen(&mtree->contents_name) > 0) { + mtree->fd = open(mtree->contents_name.s, O_RDONLY); + if (mtree->fd < 0) { + archive_set_error(&a->archive, errno, + "Can't open content=\"%s\"", + mtree->contents_name.s); + r = ARCHIVE_WARN; + } + } else { + /* If the specified path opens, use it. */ + mtree->fd = open(mtree->current_dir.s, O_RDONLY); + /* But don't fail if it's not there. */ + } + + /* + * If there is a contents file on disk, use that size; + * otherwise leave it as-is (it might have been set from + * the mtree size= keyword). + */ + if (mtree->fd >= 0) { + fstat(mtree->fd, &st); + archive_entry_set_size(entry, st.st_size); + } + + return r; + } +} + +static int +parse_setting(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, char *key, char *end) +{ + char *val; + + + if (end == key) + return (ARCHIVE_OK); + if (*key == '\0') + return (ARCHIVE_OK); + + val = strchr(key, '='); + if (val == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed attribute \"%s\" (%d)", key, key[0]); + return (ARCHIVE_WARN); + } + + *val = '\0'; + ++val; + + switch (key[0]) { + case 'c': + if (strcmp(key, "content") == 0) { + parse_escapes(val, NULL); + archive_strcpy(&mtree->contents_name, val); + break; + } + case 'g': + if (strcmp(key, "gid") == 0) { + archive_entry_set_gid(entry, mtree_atol10(&val)); + break; + } + if (strcmp(key, "gname") == 0) { + archive_entry_copy_gname(entry, val); + break; + } + case 'm': + if (strcmp(key, "mode") == 0) { + if (val[0] == '0') { + archive_entry_set_perm(entry, + mtree_atol8(&val)); + } else + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Symbolic mode \"%s\" unsupported", val); + break; + } + case 't': + if (strcmp(key, "type") == 0) { + switch (val[0]) { + case 'b': + if (strcmp(val, "block") == 0) { + mtree->filetype = AE_IFBLK; + break; + } + case 'c': + if (strcmp(val, "char") == 0) { + mtree->filetype = AE_IFCHR; + break; + } + case 'd': + if (strcmp(val, "dir") == 0) { + mtree->filetype = AE_IFDIR; + break; + } + case 'f': + if (strcmp(val, "fifo") == 0) { + mtree->filetype = AE_IFIFO; + break; + } + if (strcmp(val, "file") == 0) { + mtree->filetype = AE_IFREG; + break; + } + case 'l': + if (strcmp(val, "link") == 0) { + mtree->filetype = AE_IFLNK; + break; + } + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized file type \"%s\"", val); + return (ARCHIVE_WARN); + } + archive_entry_set_filetype(entry, mtree->filetype); + break; + } + if (strcmp(key, "time") == 0) { + archive_entry_set_mtime(entry, mtree_atol10(&val), 0); + break; + } + case 'u': + if (strcmp(key, "uid") == 0) { + archive_entry_set_uid(entry, mtree_atol10(&val)); + break; + } + if (strcmp(key, "uname") == 0) { + archive_entry_copy_uname(entry, val); + break; + } + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized key %s=%s", key, val); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +static int +read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct mtree *mtree; + + mtree = (struct mtree *)(a->format->data); + if (mtree->fd < 0) { + *buff = NULL; + *offset = 0; + *size = 0; + return (ARCHIVE_EOF); + } + if (mtree->buff == NULL) { + mtree->buffsize = 64 * 1024; + mtree->buff = malloc(mtree->buffsize); + if (mtree->buff == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + } + } + + *buff = mtree->buff; + *offset = mtree->offset; + bytes_read = read(mtree->fd, mtree->buff, mtree->buffsize); + if (bytes_read < 0) { + archive_set_error(&a->archive, errno, "Can't read"); + return (ARCHIVE_WARN); + } + if (bytes_read == 0) { + *size = 0; + return (ARCHIVE_EOF); + } + mtree->offset += bytes_read; + *size = (size_t)bytes_read; + return (ARCHIVE_OK); +} + +/* Skip does nothing except possibly close the contents file. */ +static int +skip(struct archive_read *a) +{ + struct mtree *mtree; + + mtree = (struct mtree *)(a->format->data); + if (mtree->fd >= 0) { + close(mtree->fd); + mtree->fd = -1; + } + return (ARCHIVE_OK); +} + +/* + * Since parsing octal escapes always makes strings shorter, + * we can always do this conversion in-place. + */ +static void +parse_escapes(char *src, struct mtree_entry *mentry) +{ + char *dest = src; + char c; + + while (*src != '\0') { + c = *src++; + if (c == '/' && mentry != NULL) + mentry->full = 1; + if (c == '\\') { + if (src[0] >= '0' && src[0] <= '3' + && src[1] >= '0' && src[1] <= '7' + && src[2] >= '0' && src[2] <= '7') { + c = (src[0] - '0') << 6; + c |= (src[1] - '0') << 3; + c |= (src[2] - '0'); + src += 3; + } + } + *dest++ = c; + } + *dest = '\0'; +} + +/* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +mtree_atol8(char **p) +{ + int64_t l, limit, last_digit_limit; + int digit, base; + + base = 8; + limit = INT64_MAX / base; + last_digit_limit = INT64_MAX % base; + + l = 0; + digit = **p - '0'; + while (digit >= 0 && digit < base) { + if (l>limit || (l == limit && digit > last_digit_limit)) { + l = INT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++(*p) - '0'; + } + return (l); +} + +/* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +mtree_atol10(char **p) +{ + int64_t l, limit, last_digit_limit; + int base, digit, sign; + + base = 10; + limit = INT64_MAX / base; + last_digit_limit = INT64_MAX % base; + + if (**p == '-') { + sign = -1; + ++(*p); + } else + sign = 1; + + l = 0; + digit = **p - '0'; + while (digit >= 0 && digit < base) { + if (l > limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++(*p) - '0'; + } + return (sign < 0) ? -l : l; +} + +/* + * Returns length of line (including trailing newline) + * or negative on error. 'start' argument is updated to + * point to first character of line. + */ +static ssize_t +readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) +{ + ssize_t bytes_read; + ssize_t total_size = 0; + const void *t; + const char *s; + void *p; + + /* Accumulate line in a line buffer. */ + for (;;) { + /* Read some more. */ + bytes_read = (a->decompressor->read_ahead)(a, &t, 1); + if (bytes_read == 0) + return (0); + if (bytes_read < 0) + return (ARCHIVE_FATAL); + s = t; /* Start of line? */ + p = memchr(t, '\n', bytes_read); + /* If we found '\n', trim the read. */ + if (p != NULL) { + bytes_read = 1 + ((const char *)p) - s; + } + if (total_size + bytes_read + 1 > limit) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Line too long"); + return (ARCHIVE_FATAL); + } + if (archive_string_ensure(&mtree->line, + total_size + bytes_read + 1) == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate working buffer"); + return (ARCHIVE_FATAL); + } + memcpy(mtree->line.s + total_size, t, bytes_read); + (a->decompressor->consume)(a, bytes_read); + total_size += bytes_read; + /* Null terminate. */ + mtree->line.s[total_size] = '\0'; + /* If we found '\n', clean up and return. */ + if (p != NULL) { + *start = mtree->line.s; + return (total_size); + } + } +} diff --git a/contrib/libarchive-2/libarchive/archive_read_support_format_tar.c b/contrib/libarchive-2/libarchive/archive_read_support_format_tar.c index 9124592ae8..5e7748eb1b 100644 --- a/contrib/libarchive-2/libarchive/archive_read_support_format_tar.c +++ b/contrib/libarchive-2/libarchive/archive_read_support_format_tar.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.60 2007/07/15 19:13:59 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.61 2007/08/18 21:53:25 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -72,6 +72,8 @@ static size_t wcslen(const wchar_t *s) #include "archive_private.h" #include "archive_read_private.h" +#define tar_min(a,b) ((a) < (b) ? (a) : (b)) + /* * Layout of POSIX 'ustar' tar header. */ @@ -172,6 +174,7 @@ static int archive_block_is_null(const unsigned char *p); static char *base64_decode(const wchar_t *, size_t, size_t *); static void gnu_add_sparse_entry(struct tar *, off_t offset, off_t remaining); +static void gnu_clear_sparse_list(struct tar *); static int gnu_sparse_old_read(struct archive_read *, struct tar *, const struct archive_entry_header_gnutar *header); static void gnu_sparse_old_parse(struct tar *, @@ -211,7 +214,8 @@ static int pax_attribute(struct tar *, struct archive_entry *, static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, char *attr); static void pax_time(const wchar_t *, int64_t *sec, long *nanos); -static ssize_t readline(struct archive_read *, struct tar *, const char **); +static ssize_t readline(struct archive_read *, struct tar *, const char **, + ssize_t limit); static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h); static int64_t tar_atol(const char *, unsigned); @@ -263,14 +267,9 @@ static int archive_read_format_tar_cleanup(struct archive_read *a) { struct tar *tar; - struct sparse_block *p; tar = (struct tar *)(a->format->data); - while (tar->sparse_list != NULL) { - p = tar->sparse_list; - tar->sparse_list = p->next; - free(p); - } + gnu_clear_sparse_list(tar); archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_name); archive_string_free(&tar->entry_linkname); @@ -423,7 +422,6 @@ archive_read_format_tar_read_header(struct archive_read *a, const char *p; int r; size_t l; - ssize_t size; /* Assign default device/inode values. */ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ @@ -445,22 +443,6 @@ archive_read_format_tar_read_header(struct archive_read *a, r = tar_read_header(a, tar, entry); - /* - * Yuck. See comments for gnu_sparse_10_read for why this - * is here and not in _read_data where it "should" go. - */ - if (tar->sparse_gnu_pending - && tar->sparse_gnu_major == 1 - && tar->sparse_gnu_minor == 0) { - tar->sparse_gnu_pending = 0; - /* Read initial sparse map. */ - size = gnu_sparse_10_read(a, tar); - if (size < 0) - return (size); - tar->entry_bytes_remaining -= size; - tar->entry_padding += size; - } - /* * "non-sparse" files are really just sparse files with * a single block. @@ -497,11 +479,12 @@ archive_read_format_tar_read_data(struct archive_read *a, if (tar->sparse_gnu_pending) { if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) { - /* - * We should parse the sparse data - * here, but have to parse it as part of the - * header because of a bug in GNU tar 1.16.1. - */ + tar->sparse_gnu_pending = 0; + /* Read initial sparse map. */ + bytes_read = gnu_sparse_10_read(a, tar); + tar->entry_bytes_remaining -= bytes_read; + if (bytes_read < 0) + return (bytes_read); } else { *size = 0; *offset = 0; @@ -559,7 +542,6 @@ archive_read_format_tar_skip(struct archive_read *a) { off_t bytes_skipped; struct tar* tar; - struct sparse_block *p; tar = (struct tar *)(a->format->data); @@ -577,12 +559,7 @@ archive_read_format_tar_skip(struct archive_read *a) tar->entry_padding = 0; /* Free the sparse list. */ - while (tar->sparse_list != NULL) { - p = tar->sparse_list; - tar->sparse_list = p->next; - free(p); - } - tar->sparse_last = NULL; + gnu_clear_sparse_list(tar); return (ARCHIVE_OK); } @@ -1650,6 +1627,19 @@ gnu_add_sparse_entry(struct tar *tar, off_t offset, off_t remaining) p->remaining = remaining; } +static void +gnu_clear_sparse_list(struct tar *tar) +{ + struct sparse_block *p; + + while (tar->sparse_list != NULL) { + p = tar->sparse_list; + tar->sparse_list = p->next; + free(p); + } + tar->sparse_last = NULL; +} + /* * GNU tar old-format sparse data. * @@ -1793,7 +1783,7 @@ gnu_sparse_01_parse(struct tar *tar, const wchar_t *p) */ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, - ssize_t *total_read) + ssize_t *remaining) { int64_t l, limit, last_digit_limit; const char *p; @@ -1804,10 +1794,16 @@ gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; - bytes_read = readline(a, tar, &p); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - *total_read += bytes_read; + /* + * Skip any lines starting with '#'; GNU tar specs + * don't require this, but they should. + */ + do { + bytes_read = readline(a, tar, &p, tar_min(*remaining, 100)); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + *remaining -= bytes_read; + } while (p[0] == '#'); l = 0; while (bytes_read > 0) { @@ -1828,32 +1824,39 @@ gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, } /* - * Returns number of bytes consumed to read the sparse block data. + * Returns length (in bytes) of the sparse data description + * that was read. */ static ssize_t gnu_sparse_10_read(struct archive_read *a, struct tar *tar) { - ssize_t bytes_read = 0; + ssize_t remaining, bytes_read; int entries; off_t offset, size, to_skip; + /* Clear out the existing sparse list. */ + gnu_clear_sparse_list(tar); + + remaining = tar->entry_bytes_remaining; + /* Parse entries. */ - entries = gnu_sparse_10_atol(a, tar, &bytes_read); + entries = gnu_sparse_10_atol(a, tar, &remaining); if (entries < 0) return (ARCHIVE_FATAL); /* Parse the individual entries. */ while (entries-- > 0) { /* Parse offset/size */ - offset = gnu_sparse_10_atol(a, tar, &bytes_read); + offset = gnu_sparse_10_atol(a, tar, &remaining); if (offset < 0) return (ARCHIVE_FATAL); - size = gnu_sparse_10_atol(a, tar, &bytes_read); + size = gnu_sparse_10_atol(a, tar, &remaining); if (size < 0) return (ARCHIVE_FATAL); /* Add a new sparse entry. */ gnu_add_sparse_entry(tar, offset, size); } /* Skip rest of block... */ + bytes_read = tar->entry_bytes_remaining - remaining; to_skip = 0x1ff & -bytes_read; if (to_skip != (a->decompressor->skip)(a, to_skip)) return (ARCHIVE_FATAL); @@ -2004,7 +2007,8 @@ tar_atol256(const char *_p, unsigned char_cnt) * when possible. */ static ssize_t -readline(struct archive_read *a, struct tar *tar, const char **start) +readline(struct archive_read *a, struct tar *tar, const char **start, + ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; @@ -2020,12 +2024,24 @@ readline(struct archive_read *a, struct tar *tar, const char **start) /* If we found '\n' in the read buffer, return pointer to that. */ if (p != NULL) { bytes_read = 1 + ((const char *)p) - s; + if (bytes_read > limit) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Line too long"); + return (ARCHIVE_FATAL); + } (a->decompressor->consume)(a, bytes_read); *start = s; return (bytes_read); } /* Otherwise, we need to accumulate in a line buffer. */ for (;;) { + if (total_size + bytes_read > limit) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Line too long"); + return (ARCHIVE_FATAL); + } if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); diff --git a/contrib/libarchive-2/libarchive/archive_write_disk.c b/contrib/libarchive-2/libarchive/archive_write_disk.c index 331bfe3b1c..e3b3a328cc 100644 --- a/contrib/libarchive-2/libarchive/archive_write_disk.c +++ b/contrib/libarchive-2/libarchive/archive_write_disk.c @@ -25,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.13 2007/07/15 19:13:59 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.14 2007/08/12 17:35:05 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -1541,15 +1541,28 @@ set_mode(struct archive_write_disk *a, int mode) } if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; - archive_set_error(&a->archive, -1, "Can't restore SGID bit"); - r = ARCHIVE_WARN; + if (a->flags & ARCHIVE_EXTRACT_OWNER) { + /* + * This is only an error if you + * requested owner restore. If you + * didn't, we'll try to restore + * sgid/suid, but won't consider it a + * problem if we can't. + */ + archive_set_error(&a->archive, -1, + "Can't restore SGID bit"); + r = ARCHIVE_WARN; + } } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; - archive_set_error(&a->archive, -1, "Can't restore SUID bit"); - r = ARCHIVE_WARN; + if (a->flags & ARCHIVE_EXTRACT_OWNER) { + archive_set_error(&a->archive, -1, + "Can't restore SUID bit"); + r = ARCHIVE_WARN; + } } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; @@ -1561,8 +1574,11 @@ set_mode(struct archive_write_disk *a, int mode) */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; - archive_set_error(&a->archive, -1, "Can't make file SUID"); - r = ARCHIVE_WARN; + if (a->flags & ARCHIVE_EXTRACT_OWNER) { + archive_set_error(&a->archive, -1, + "Can't make file SUID"); + r = ARCHIVE_WARN; + } } a->todo &= ~TODO_SUID_CHECK; } diff --git a/contrib/libarchive-2/tar/bsdtar.c b/contrib/libarchive-2/tar/bsdtar.c index 4f6d3c2aef..4ff4267e40 100644 --- a/contrib/libarchive-2/tar/bsdtar.c +++ b/contrib/libarchive-2/tar/bsdtar.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.75 2007/05/29 05:39:10 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.76 2007/08/01 03:15:35 kientzle Exp $"); #ifdef HAVE_SYS_PARAM_H #include diff --git a/contrib/libarchive-2/version b/contrib/libarchive-2/version index 1506473e24..b539adea59 100644 --- a/contrib/libarchive-2/version +++ b/contrib/libarchive-2/version @@ -1 +1 @@ -2.2.6 \ No newline at end of file +2.2.7 \ No newline at end of file -- 2.28.0