From: Peter Avalos Date: Sat, 7 Apr 2007 14:04:55 +0000 (+0000) Subject: Merge from vendor branch LIBARCHIVE: X-Git-Tag: v2.0.1~3272 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/545c2f3aef5020e0851a470ed84f64057c8c7583?hp=59e60932e72d2afc7b3337b8bf9235cfca978aa1 Merge from vendor branch LIBARCHIVE: Import libarchive 2.0.28. --- diff --git a/contrib/libarchive-2.0/NEWS b/contrib/libarchive-2.0/NEWS index a9c8dbaf15..50249fcb51 100644 --- a/contrib/libarchive-2.0/NEWS +++ b/contrib/libarchive-2.0/NEWS @@ -1,4 +1,7 @@ +Apr 06, 2007: libarchive 2.0.28 released +Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang. + Apr 01, 2007: libarchive 2.0.27 released Apr 01, 2007: libarchive 2.0.26 released diff --git a/contrib/libarchive-2.0/libarchive/archive.h.in b/contrib/libarchive-2.0/libarchive/archive.h.in index a9ba787a7e..874bda71e7 100644 --- a/contrib/libarchive-2.0/libarchive/archive.h.in +++ b/contrib/libarchive-2.0/libarchive/archive.h.in @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.40 2007/03/11 10:29:52 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.41 2007/04/03 05:34:36 kientzle Exp $ */ #ifndef ARCHIVE_H_INCLUDED @@ -177,6 +177,9 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) #define ARCHIVE_FORMAT_ZIP 0x50000 #define ARCHIVE_FORMAT_EMPTY 0x60000 +#define ARCHIVE_FORMAT_AR 0x70000 +#define ARCHIVE_FORMAT_AR_SVR4 (ARCHIVE_FORMAT_AR | 1) +#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) /*- * Basic outline for reading an archive: @@ -206,6 +209,7 @@ int archive_read_support_compression_gzip(struct archive *); int archive_read_support_compression_none(struct archive *); int archive_read_support_format_all(struct archive *); +int archive_read_support_format_ar(struct archive *); int archive_read_support_format_cpio(struct archive *); int archive_read_support_format_empty(struct archive *); int archive_read_support_format_gnutar(struct archive *); @@ -374,6 +378,8 @@ int archive_write_set_format(struct archive *, int format_code); int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ +int archive_write_set_format_ar_bsd(struct archive *); +int archive_write_set_format_ar_svr4(struct archive *); int archive_write_set_format_cpio(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ int archive_write_set_format_pax(struct archive *); diff --git a/contrib/libarchive-2.0/libarchive/archive_entry.c b/contrib/libarchive-2.0/libarchive/archive_entry.c index 4564767f2d..0cdd2b0b5e 100644 --- a/contrib/libarchive-2.0/libarchive/archive_entry.c +++ b/contrib/libarchive-2.0/libarchive/archive_entry.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.40 2007/03/11 10:29:52 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.41 2007/04/05 05:20:13 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -250,7 +250,8 @@ aes_get_mbs(struct aes *aes) * chars encode to no more than 3 bytes. There must * be a better way... XXX */ - int mbs_length = wcslen(aes->aes_wcs) * 3 + 64; + size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64; + aes->aes_mbs_alloc = (char *)malloc(mbs_length); aes->aes_mbs = aes->aes_mbs_alloc; if (aes->aes_mbs == NULL) @@ -271,7 +272,8 @@ aes_get_wcs(struct aes *aes) * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ - int wcs_length = strlen(aes->aes_mbs); + size_t wcs_length = strlen(aes->aes_mbs); + aes->aes_wcs_alloc = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); aes->aes_wcs = aes->aes_wcs_alloc; @@ -1207,14 +1209,14 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; - /* FALL THROUGH */ + /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; - /* FALL THROUGH */ + /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); break; @@ -1648,7 +1650,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) const char *sp; unsigned long bits; struct flag *flag; - int length; + size_t length; bits = bitset | bitclear; length = 0; diff --git a/contrib/libarchive-2.0/libarchive/archive_read.3 b/contrib/libarchive-2.0/libarchive/archive_read.3 index 1a83ff4232..e50a28e15e 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read.3 +++ b/contrib/libarchive-2.0/libarchive/archive_read.3 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.31 2007/03/03 07:37:36 kientzle Exp $ +.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.33 2007/04/07 05:53:11 kientzle Exp $ .\" .Dd August 19, 2006 .Dt archive_read 3 @@ -50,7 +50,9 @@ .Nm archive_read_data , .Nm archive_read_data_block , .Nm archive_read_data_skip , +.\" #if ARCHIVE_API_VERSION < 3 .Nm archive_read_data_into_buffer , +.\" #endif .Nm archive_read_data_into_fd , .Nm archive_read_extract , .Nm archive_read_extract_set_progress_callback , @@ -103,8 +105,10 @@ .Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset" .Ft int .Fn archive_read_data_skip "struct archive *" +.\" #if ARCHIVE_API_VERSION < 3 .Ft int .Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len" +.\" #endif .Ft int .Fn archive_read_data_into_fd "struct archive *" "int fd" .Ft int @@ -224,11 +228,13 @@ and internal buffer optimizations. A convenience function that repeatedly calls .Fn archive_read_data_block to skip all of the data for this archive entry. +.\" #if ARCHIVE_API_VERSION < 3 .It Fn archive_read_data_into_buffer -A convenience function that repeatedly calls -.Fn archive_read_data_block -to copy the entire entry into the client-supplied buffer. -Note that the client is responsible for sizing the buffer appropriately. +This function is deprecated and will be removed. +Use +.Fn archive_read_data +instead. +.\" #endif .It Fn archive_read_data_into_fd A convenience function that repeatedly calls .Fn archive_read_data_block diff --git a/contrib/libarchive-2.0/libarchive/archive_read.c b/contrib/libarchive-2.0/libarchive/archive_read.c index 82b0e50461..5c9938f93e 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read.c +++ b/contrib/libarchive-2.0/libarchive/archive_read.c @@ -32,7 +32,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.32 2007/04/02 00:41:37 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.34 2007/04/05 15:51:19 cperciva Exp $"); #ifdef HAVE_ERRNO_H #include @@ -274,8 +274,8 @@ dummy_skip(struct archive_read * a, off_t request) return (ARCHIVE_FATAL); } if (bytes_read > request) - bytes_read = request; - (a->compression_read_consume)(a, bytes_read); + bytes_read = (ssize_t)request; + (a->compression_read_consume)(a, (size_t)bytes_read); request -= bytes_read; bytes_skipped += bytes_read; } @@ -448,7 +448,7 @@ archive_read_data(struct archive *_a, void *buff, size_t s) dest = (char *)buff; while (s > 0) { - if (a->read_data_remaining <= 0) { + if (a->read_data_remaining == 0) { read_buf = a->read_data_block; r = archive_read_data_block(&a->archive, &read_buf, &a->read_data_remaining, &a->read_data_offset); @@ -506,6 +506,22 @@ archive_read_data(struct archive *_a, void *buff, size_t s) return (bytes_read); } +#if ARCHIVE_API_VERSION < 3 +/* + * Obsolete function provided for compatibility only. Note that the API + * of this function doesn't allow the caller to detect if the remaining + * data from the archive entry is shorter than the buffer provided, or + * even if an error occurred while reading data. + */ +int +archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len) +{ + + archive_read_data(a, d, len); + return (ARCHIVE_OK); +} +#endif + /* * Skip over all remaining data in this entry. */ diff --git a/contrib/libarchive-2.0/libarchive/archive_read_extract.c b/contrib/libarchive-2.0/libarchive/archive_read_extract.c index a06671e479..08804d490a 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_extract.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_extract.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.54 2007/03/11 10:29:52 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.55 2007/04/07 03:37:59 cperciva Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -98,10 +98,6 @@ archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags) if (r == ARCHIVE_OK) /* If there's an FD, pour data into it. */ r = copy_data(_a, a->extract->ad); - if (r != ARCHIVE_OK) - archive_set_error(&a->archive, - archive_errno(extract->ad), - "%s", archive_error_string(extract->ad)); r2 = archive_write_finish_entry(a->extract->ad); /* Use the first message. */ if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) @@ -141,8 +137,11 @@ copy_data(struct archive *ar, struct archive *aw) if (r != ARCHIVE_OK) return (r); r = archive_write_data_block(aw, buff, size, offset); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + archive_set_error(ar, archive_errno(aw), + "%s", archive_error_string(aw)); return (r); + } } } diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c index 82c7d7dbb4..8651c98720 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c @@ -25,7 +25,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.14 2007/03/24 03:23:26 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.15 2007/04/05 05:18:16 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -253,10 +253,10 @@ static ssize_t read_ahead(struct archive_read *a, const void **p, size_t min) { struct private_data *state; - int read_avail, was_avail, ret; + size_t read_avail, was_avail; + int ret; state = (struct private_data *)a->compression_data; - was_avail = -1; if (!a->client_reader) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No read callback is registered? " @@ -275,13 +275,14 @@ read_ahead(struct archive_read *a, const void **p, size_t min) = state->uncompressed_buffer_size - read_avail; } - while (was_avail < read_avail && /* Made some progress. */ - read_avail < (int)min && /* Haven't satisfied min. */ - read_avail < (int)state->uncompressed_buffer_size) { /* !full */ + while (read_avail < min && /* Haven't satisfied min. */ + read_avail < state->uncompressed_buffer_size) { /* !full */ + was_avail = read_avail; if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK) return (ret); - was_avail = read_avail; read_avail = state->stream.next_out - state->read_next; + if (was_avail == read_avail) /* No progress? */ + break; } *p = state->read_next; @@ -346,12 +347,15 @@ drive_decompressor(struct archive_read *a, struct private_data *state) ssize_t ret; int decompressed, total_decompressed; char *output; + const void *read_buf; total_decompressed = 0; for (;;) { if (state->stream.avail_in == 0) { + read_buf = state->stream.next_in; ret = (a->client_reader)(&a->archive, a->client_data, - (const void **)&state->stream.next_in); + &read_buf); + state->stream.next_in = (void *)(uintptr_t)read_buf; if (ret < 0) { /* * TODO: Find a better way to handle diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c index 6f801d3d5a..6064389351 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c @@ -64,7 +64,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.8 2007/03/03 07:37:36 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.9 2007/04/05 05:18:16 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -275,10 +275,10 @@ static ssize_t read_ahead(struct archive_read *a, const void **p, size_t min) { struct private_data *state; - int read_avail, was_avail, ret; + size_t read_avail; + int ret; state = (struct private_data *)a->compression_data; - was_avail = -1; if (!a->client_reader) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No read callback is registered? " @@ -288,14 +288,14 @@ read_ahead(struct archive_read *a, const void **p, size_t min) read_avail = state->next_out - state->read_next; - if (read_avail < (int)min && state->end_of_stream) { + if (read_avail < min && state->end_of_stream) { if (state->end_of_stream == ARCHIVE_EOF) return (0); else return (-1); } - if (read_avail < (int)min) { + if (read_avail < min) { memmove(state->uncompressed_buffer, state->read_next, read_avail); state->read_next = (unsigned char *)state->uncompressed_buffer; @@ -303,7 +303,7 @@ read_ahead(struct archive_read *a, const void **p, size_t min) state->avail_out = state->uncompressed_buffer_size - read_avail; - while (read_avail < (int)state->uncompressed_buffer_size + while (read_avail < state->uncompressed_buffer_size && !state->end_of_stream) { if (state->stackp > state->stack) { *state->next_out++ = *--state->stackp; @@ -465,12 +465,14 @@ getbits(struct archive_read *a, struct private_data *state, int n) 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; - + const void *read_buf; while (state->bits_avail < n) { if (state->avail_in <= 0) { + read_buf = state->next_in; ret = (a->client_reader)(&a->archive, a->client_data, - (const void **)&state->next_in); + &read_buf); + state->next_in = read_buf; if (ret < 0) return (ARCHIVE_FATAL); if (ret == 0) diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c index ccda36e152..7386c9bca9 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c @@ -25,7 +25,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.13 2007/03/03 07:37:36 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.14 2007/04/05 05:18:16 kientzle Exp $"); #ifdef HAVE_ERRNO_H @@ -258,10 +258,10 @@ static ssize_t read_ahead(struct archive_read *a, const void **p, size_t min) { struct private_data *state; - int read_avail, was_avail, ret; + size_t read_avail, was_avail; + int ret; state = (struct private_data *)a->compression_data; - was_avail = -1; if (!a->client_reader) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "No read callback is registered? " @@ -280,13 +280,14 @@ read_ahead(struct archive_read *a, const void **p, size_t min) = state->uncompressed_buffer_size - read_avail; } - while (was_avail < read_avail && /* Made some progress. */ - read_avail < (int)min && /* Haven't satisfied min. */ - read_avail < (int)state->uncompressed_buffer_size) { /* !full */ + while (read_avail < min && /* Haven't satisfied min. */ + read_avail < state->uncompressed_buffer_size) { /* !full */ + was_avail = read_avail; if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK) return (ret); - was_avail = read_avail; read_avail = state->stream.next_out - state->read_next; + if (was_avail == read_avail) /* No progress? */ + break; } *p = state->read_next; @@ -349,10 +350,11 @@ static int drive_decompressor(struct archive_read *a, struct private_data *state) { ssize_t ret; - int decompressed, total_decompressed; + size_t decompressed, total_decompressed; int count, flags, header_state; unsigned char *output; unsigned char b; + const void *read_buf; flags = 0; count = 0; @@ -360,8 +362,10 @@ drive_decompressor(struct archive_read *a, struct private_data *state) total_decompressed = 0; for (;;) { if (state->stream.avail_in == 0) { + read_buf = state->stream.next_in; ret = (a->client_reader)(&a->archive, a->client_data, - (const void **)&state->stream.next_in); + &read_buf); + state->stream.next_in = (unsigned char *)(uintptr_t)read_buf; if (ret < 0) { /* * TODO: Find a better way to handle diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c index dde0aee30f..6a3a245d52 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.15 2007/03/03 07:37:36 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.16 2007/04/05 05:18:16 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -51,7 +51,7 @@ struct archive_decompress_none { size_t buffer_size; char *next; /* Current read location. */ size_t avail; /* Bytes in my buffer. */ - const char *client_buff; /* Client buffer information. */ + const void *client_buff; /* Client buffer information. */ size_t client_total; const char *client_next; size_t client_avail; @@ -130,7 +130,7 @@ archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t } /* Save reference to first block of data. */ - state->client_buff = (const char *)buff; + state->client_buff = buff; state->client_total = n; state->client_next = state->client_buff; state->client_avail = state->client_total; @@ -219,8 +219,7 @@ archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff, * aren't, hence the cast. */ bytes_read = (a->client_reader)(&a->archive, - a->client_data, - (const void **)&state->client_buff); + a->client_data, &state->client_buff); if (bytes_read < 0) { /* Read error. */ state->client_total = state->client_avail = 0; state->client_next = state->client_buff = NULL; diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c index 281e98ca4f..3fdfa05285 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c @@ -24,13 +24,14 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.8 2007/02/01 06:18:16 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9 2007/04/07 05:54:23 kientzle Exp $"); #include "archive.h" int archive_read_support_format_all(struct archive *a) { + archive_read_support_format_ar(a); archive_read_support_format_cpio(a); archive_read_support_format_empty(a); archive_read_support_format_iso9660(a); diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_ar.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_ar.c new file mode 100644 index 0000000000..b5ced6623c --- /dev/null +++ b/contrib/libarchive-2.0/libarchive/archive_read_support_format_ar.c @@ -0,0 +1,585 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 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 + * in this position and unchanged. + * 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: src/lib/libarchive/archive_read_support_format_ar.c,v 1.1 2007/04/03 05:34:36 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#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" + +struct ar { + int bid; + int has_strtab; + off_t entry_bytes_remaining; + off_t entry_offset; + off_t entry_padding; + char *strtab; +}; + +/* + * Define structure of the "ar" header. + */ +#define AR_name_offset 0 +#define AR_name_size 16 +#define AR_date_offset 16 +#define AR_date_size 12 +#define AR_uid_offset 28 +#define AR_uid_size 6 +#define AR_gid_offset 34 +#define AR_gid_size 6 +#define AR_mode_offset 40 +#define AR_mode_size 8 +#define AR_size_offset 48 +#define AR_size_size 10 +#define AR_fmag_offset 58 +#define AR_fmag_size 2 + +/* + * "ar" magic numbers. + */ +#define ARMAG "!\n" +#define SARMAG 8 /* strlen(ARMAG); */ +#define AR_EFMT1 "#1/" +#define SAR_EFMT1 3 /* strlen(AR_EFMT1); */ +#define ARFMAG "`\n" +#define SARFMAG 2 /* strlen(ARFMAG); */ + +#define isdigit(x) (x) >= '0' && (x) <= '9' + +static int archive_read_format_ar_bid(struct archive_read *a); +static int archive_read_format_ar_cleanup(struct archive_read *a); +static int archive_read_format_ar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset); +static int archive_read_format_ar_skip(struct archive_read *a); +static int archive_read_format_ar_read_header(struct archive_read *a, + struct archive_entry *e); +static int64_t ar_atol8(const char *p, unsigned char_cnt); +static int64_t ar_atol10(const char *p, unsigned char_cnt); +static int ar_parse_string_table(struct archive_read *, struct ar *, + const void *, ssize_t); + +/* + * ANSI C99 defines constants for these, but not everyone supports + * those constants, so I define a couple of static variables here and + * compute the values. These calculations should be portable to any + * 2s-complement architecture. + */ +#ifdef UINT64_MAX +static const uint64_t max_uint64 = UINT64_MAX; +#else +static const uint64_t max_uint64 = ~(uint64_t)0; +#endif +#ifdef INT64_MAX +static const int64_t max_int64 = INT64_MAX; +#else +static const int64_t max_int64 = (int64_t)((~(uint64_t)0) >> 1); +#endif +#ifdef INT64_MIN +static const int64_t min_int64 = INT64_MIN; +#else +static const int64_t min_int64 = (int64_t)(~((~(uint64_t)0) >> 1)); +#endif + +int +archive_read_support_format_ar(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct ar *ar; + int r; + + ar = (struct ar *)malloc(sizeof(*ar)); + if (ar == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate ar data"); + return (ARCHIVE_FATAL); + } + memset(ar, 0, sizeof(*ar)); + ar->bid = -1; + + r = __archive_read_register_format(a, + ar, + archive_read_format_ar_bid, + archive_read_format_ar_read_header, + archive_read_format_ar_read_data, + archive_read_format_ar_skip, + archive_read_format_ar_cleanup); + + if (r != ARCHIVE_OK) { + free(ar); + return (r); + } + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_cleanup(struct archive_read *a) +{ + struct ar *ar; + + ar = (struct ar *)*(a->pformat_data); + if (ar->has_strtab > 0) + free(ar->strtab); + free(ar); + *(a->pformat_data) = NULL; + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_bid(struct archive_read *a) +{ + struct ar *ar; + ssize_t bytes_read; + const void *h; + + if (a->archive.archive_format != 0 && + (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) != + ARCHIVE_FORMAT_AR) + return(0); + + ar = (struct ar *)*(a->pformat_data); + + if (ar->bid > 0) + return (ar->bid); + + bytes_read = (a->compression_read_ahead)(a, &h, SARMAG); + if (bytes_read < SARMAG) + return (-1); + + /* + * Verify the global header. + * TODO: Do we need to check more than this? + */ + if (strncmp((const char*)h, ARMAG, SARMAG) == 0) { + ar->bid = SARMAG; + return (ar->bid); + } + return (-1); +} + +static int +archive_read_format_ar_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + int r, bsd_append; + ssize_t bytes; + int64_t nval; + char *fname, *p; + struct ar *ar; + const void *b; + const char *h; + + bsd_append = 0; + + if (!a->archive.archive_format) { + a->archive.archive_format = ARCHIVE_FORMAT_AR; + a->archive.archive_format_name = "Unix Archiver"; + } + + if (a->archive.file_position == 0) { + /* + * We are now at the beginning of the archive, + * so we need first consume the ar global header. + */ + (a->compression_read_consume)(a, SARMAG); + } + + /* Read 60-byte header */ + bytes = (a->compression_read_ahead)(a, &b, 60); + if (bytes < 60) { + /* + * We just encountered an incomplete ar file, + * though the _bid function accepted it. + */ + return (ARCHIVE_EOF); + } + (a->compression_read_consume)(a, 60); + + h = (const char *)b; + + /* Consistency check */ + if (strncmp(h + AR_fmag_offset, ARFMAG, SARFMAG) != 0) { + archive_set_error(&a->archive, EINVAL, + "Consistency check failed"); + return (ARCHIVE_WARN); + } + + ar = (struct ar*)*(a->pformat_data); + + if (strncmp(h + AR_name_offset, "//", 2) == 0) { + /* + * An archive member with ar_name "//" is an archive + * string table. + */ + nval = ar_atol10(h + AR_size_offset, AR_size_size); + bytes = (a->compression_read_ahead)(a, &b, nval); + if (bytes <= 0) + return (ARCHIVE_FATAL); + if (bytes < nval) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file"); + return (ARCHIVE_FATAL); + } + + r = ar_parse_string_table(a, ar, b, nval); + if (r == ARCHIVE_OK) { + /* + * Archive string table only have ar_name and ar_size fileds + * in its header. + */ + archive_entry_copy_pathname(entry, "//"); + nval = ar_atol10(h + AR_size_offset, AR_size_size); + archive_entry_set_size(entry, nval); + + ar->entry_offset = 0; + ar->entry_bytes_remaining = nval; + ar->entry_padding = ar->entry_bytes_remaining % 2; + } + return (r); + } + + if (h[AR_name_offset] == '/' && isdigit(h[AR_name_offset + 1])) { + /* + * Archive member is common format with SVR4/GNU variant. + * "/" followed by one or more digit(s) in the ar_name + * filed indicates an index to the string table. + */ + if (ar->has_strtab > 0) { + nval = ar_atol10(h + AR_name_offset + 1, + AR_name_size - 1); + archive_entry_copy_pathname(entry, &ar->strtab[nval]); + } else { + archive_set_error(&a->archive, EINVAL, + "String table does not exist"); + return (ARCHIVE_WARN); + } + goto remain; + } + + if (strncmp(h + AR_name_offset, AR_EFMT1, SAR_EFMT1) == 0) { + /* + * Archive member is common format with BSD variant. + * AR_EFMT1 is followed by one or more digit(s) indicating + * the length of the real filename which is appended + * to the header. + */ + nval = ar_atol10(h + AR_name_offset + SAR_EFMT1, + AR_name_size - SAR_EFMT1); + bytes = (a->compression_read_ahead)(a, &b, nval); + if (bytes <= 0) + return (ARCHIVE_FATAL); + if (bytes < nval) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated input file"); + return (ARCHIVE_FATAL); + } + + (a->compression_read_consume)(a, nval); + + fname = (char *)malloc(nval + 1); + if (fname == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate fname buffer"); + return (ARCHIVE_FATAL); + } + strncpy(fname, b, nval); + fname[nval] = '\0'; + archive_entry_copy_pathname(entry, fname); + free(fname); + fname = NULL; + + bsd_append = nval; + goto remain; + } + + /* + * "/" followed by one or more spaces indicate a + * SVR4/GNU archive symbol table. + * + */ + if (strncmp(h + AR_name_offset, "/ ", 2) == 0) { + archive_entry_copy_pathname(entry, "/"); + goto remain; + } + /* + * "__.SYMDEF" indicates a BSD archive symbol table. + */ + if (strncmp(h + AR_name_offset, "__.SYMDEF", 9) == 0) { + archive_entry_copy_pathname(entry, "__.SYMDEF"); + goto remain; + } + + /* + * Otherwise, the ar_name fields stores the real + * filename. + * SVR4/GNU variant append a '/' to mark the end of + * filename, while BSD variant use a space. + */ + fname = (char *)malloc(AR_name_size + 1); + strncpy(fname, h + AR_name_offset, AR_name_size); + fname[AR_name_size] = '\0'; + + if ((p = strchr(fname, '/')) != NULL) { + /* SVR4/GNU format */ + *p = '\0'; + archive_entry_copy_pathname(entry, fname); + free(fname); + fname = NULL; + goto remain; + } + + /* BSD format */ + if ((p = strchr(fname, ' ')) != NULL) + *p = '\0'; + archive_entry_copy_pathname(entry, fname); + free(fname); + fname = NULL; + +remain: + /* Copy remaining header */ + archive_entry_set_mtime(entry, + ar_atol10(h + AR_date_offset, AR_date_size), 0); + archive_entry_set_uid(entry, + ar_atol10(h + AR_uid_offset, AR_uid_size)); + archive_entry_set_gid(entry, + ar_atol10(h + AR_gid_offset, AR_gid_size)); + archive_entry_set_mode(entry, + ar_atol8(h + AR_mode_offset, AR_mode_size)); + nval = ar_atol10(h + AR_size_offset, AR_size_size); + + ar->entry_offset = 0; + ar->entry_padding = nval % 2; + + /* + * For BSD variant, we should subtract the length of + * the appended filename string from ar_size to get the + * real file size. But remember we should do this only + * after we had calculated the padding. + */ + if (bsd_append > 0) + nval -= bsd_append; + + archive_entry_set_size(entry, nval); + ar->entry_bytes_remaining = nval; + + return (ARCHIVE_OK); +} + +static int +archive_read_format_ar_read_data(struct archive_read *a, + const void **buff, size_t *size, off_t *offset) +{ + ssize_t bytes_read; + struct ar *ar; + + ar = (struct ar *)*(a->pformat_data); + + if (ar->entry_bytes_remaining > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated ar archive"); + return (ARCHIVE_FATAL); + } + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (bytes_read > ar->entry_bytes_remaining) + bytes_read = ar->entry_bytes_remaining; + *size = bytes_read; + *offset = ar->entry_offset; + ar->entry_offset += bytes_read; + ar->entry_bytes_remaining -= bytes_read; + (a->compression_read_consume)(a, bytes_read); + return (ARCHIVE_OK); + } else { + while (ar->entry_padding > 0) { + bytes_read = (a->compression_read_ahead)(a, buff, 1); + if (bytes_read <= 0) + return (ARCHIVE_FATAL); + if (bytes_read > ar->entry_padding) + bytes_read = ar->entry_padding; + (a->compression_read_consume)(a, bytes_read); + ar->entry_padding -= bytes_read; + } + *buff = NULL; + *size = 0; + *offset = ar->entry_offset; + return (ARCHIVE_EOF); + } +} + +static int +archive_read_format_ar_skip(struct archive_read *a) +{ + off_t bytes_skipped; + struct ar* ar; + int r = ARCHIVE_OK; + const void *b; /* Dummy variables */ + size_t s; + off_t o; + + ar = (struct ar *)*(a->pformat_data); + if (a->compression_skip == NULL) { + while (r == ARCHIVE_OK) + r = archive_read_format_ar_read_data(a, &b, &s, &o); + return (r); + } + + bytes_skipped = (a->compression_skip)(a, ar->entry_bytes_remaining + + ar->entry_padding); + if (bytes_skipped < 0) + return (ARCHIVE_FATAL); + + ar->entry_bytes_remaining = 0; + ar->entry_padding = 0; + + return (ARCHIVE_OK); +} + +static int +ar_parse_string_table(struct archive_read *a, struct ar *ar, + const void *h, ssize_t size) +{ + char *p; + + if (ar->has_strtab > 0) { + archive_set_error(&a->archive, EINVAL, + "More than one string tables exist"); + return (ARCHIVE_WARN); + } + + ar->strtab = (char *)malloc(size); + if (ar->strtab == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate string table buffer"); + return (ARCHIVE_FATAL); + } + + (void)memcpy(ar->strtab, h, size); + p = ar->strtab; + while (p < ar->strtab + size - 1) { + if (*p == '/') { + *p++ = '\0'; + if (*p == '\n') + *p++ = '\0'; + else { + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); + free(ar->strtab); + return (ARCHIVE_WARN); + } + } else + p++; + } + /* + * Sanity check, last two chars must be `/\n' or '\n\n', + * depending on whether the string table is padded by a '\n' + * (string table produced by GNU ar always has a even size). + */ + if (p != ar->strtab + size && *p != '\n') { + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); + free(ar->strtab); + return (ARCHIVE_WARN); + } + + ar->has_strtab = 1; + return (ARCHIVE_OK); +} + +static int64_t +ar_atol8(const char *p, unsigned char_cnt) +{ + int64_t l, limit, last_digit_limit; + int digit, sign, base; + + base = 8; + limit = max_int64 / base; + last_digit_limit = max_int64 % base; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '-') { + sign = -1; + p++; + } else + sign = 1; + + l = 0; + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt-- > 0) { + if (l>limit || (l == limit && digit > last_digit_limit)) { + l = max_uint64; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (sign < 0) ? -l : l; +} + +static int64_t +ar_atol10(const char *p, unsigned char_cnt) +{ + int64_t l, limit, last_digit_limit; + int base, digit, sign; + + base = 10; + limit = max_int64 / base; + last_digit_limit = max_int64 % base; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '-') { + sign = -1; + p++; + } else + sign = 1; + + l = 0; + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt-- > 0) { + if (l > limit || (l == limit && digit > last_digit_limit)) { + l = max_uint64; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + } + return (sign < 0) ? -l : l; +} diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_tar.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_tar.c index c0537974bf..b57ac56701 100644 --- a/contrib/libarchive-2.0/libarchive/archive_read_support_format_tar.c +++ b/contrib/libarchive-2.0/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.50 2007/03/31 22:59:43 cperciva Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.52 2007/04/03 23:53:55 cperciva Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -524,15 +524,9 @@ archive_read_format_tar_read_data(struct archive_read *a, (a->compression_read_consume)(a, bytes_read); return (ARCHIVE_OK); } else { - while (tar->entry_padding > 0) { - bytes_read = (a->compression_read_ahead)(a, buff, 1); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - if (bytes_read > tar->entry_padding) - bytes_read = tar->entry_padding; - (a->compression_read_consume)(a, bytes_read); - tar->entry_padding -= bytes_read; - } + if ((a->compression_skip)(a, tar->entry_padding) < 0) + return (ARCHIVE_FATAL); + tar->entry_padding = 0; *buff = NULL; *size = 0; *offset = tar->entry_offset; @@ -1301,6 +1295,10 @@ pax_attribute(struct archive_entry *entry, struct stat *st, tar_atol10(value, wcslen(value))); else if (wcscmp(key, L"SCHILY.fflags")==0) archive_entry_copy_fflags_text_w(entry, value); + else if (wcscmp(key, L"SCHILY.dev")==0) + st->st_dev = tar_atol10(value, wcslen(value)); + else if (wcscmp(key, L"SCHILY.ino")==0) + st->st_ino = tar_atol10(value, wcslen(value)); else if (wcscmp(key, L"SCHILY.nlink")==0) st->st_nlink = tar_atol10(value, wcslen(value)); break; diff --git a/contrib/libarchive-2.0/libarchive/archive_write_disk.3 b/contrib/libarchive-2.0/libarchive/archive_write_disk.3 new file mode 100644 index 0000000000..880a399b94 --- /dev/null +++ b/contrib/libarchive-2.0/libarchive/archive_write_disk.3 @@ -0,0 +1,358 @@ +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.1 2007/03/03 07:37:36 kientzle Exp $ +.\" +.Dd March 2, 2007 +.Dt archive_write_disk 3 +.Os +.Sh NAME +.Nm archive_write_disk_new , +.Nm archive_write_disk_set_options , +.Nm archive_write_disk_set_skip_file , +.Nm archive_write_disk_set_group_lookup , +.Nm archive_write_disk_set_standard_lookup , +.Nm archive_write_disk_set_user_lookup , +.Nm archive_write_header , +.Nm archive_write_data , +.Nm archive_write_finish_entry , +.Nm archive_write_close , +.Nm archive_write_finish +.Nd functions for creating objects on disk +.Sh SYNOPSIS +.In archive.h +.Ft struct archive * +.Fn archive_write_disk_new "void" +.Ft int +.Fn archive_write_disk_set_options "struct archive *" "int flags" +.Ft int +.Fn archive_write_disk_set_skip_file "struct archive *" "dev_t" "ino_t" +.Ft int +.Fn archive_write_disk_set_group_lookup "struct archive *" "void *" "gid_t (*)(void *, const char *gname, gid_t gid)" "void (*cleanup)(void *)" +.Ft int +.Fn archive_write_disk_set_standard_lookup "struct archive *" +.Ft int +.Fn archive_write_disk_set_user_lookup "struct archive *" "void *" "uid_t (*)(void *, const char *uname, uid_t uid)" "void (*cleanup)(void *)" +.Ft int +.Fn archive_write_header "struct archive *" "struct archive_entry *" +.Ft ssize_t +.Fn archive_write_data "struct archive *" "const void *" "size_t" +.Ft int +.Fn archive_write_finish_entry "struct archive *" +.Ft int +.Fn archive_write_close "struct archive *" +.Ft int +.Fn archive_write_finish "struct archive *" +.Sh DESCRIPTION +These functions provide a complete API for creating objects on +disk from +.Tn struct archive_entry +descriptions. +They are most naturally used when extracting objects from an archive +using the +.Fn archive_read +interface. +The general process is to read +.Tn struct archive_entry +objects from an archive, then write those objects to a +.Tn struct archive +object created using the +.Fn archive_write_disk +family functions. +This interface is deliberately very similar to the +.Fn archive_write +interface used to write objects to a streaming archive. +.Bl -tag -width indent +.It Fn archive_write_disk_new +Allocates and initializes a +.Tn struct archive +object suitable for writing objects to disk. +.It Fn archive_write_disk_set_skip_file +Records the device and inode numbers of a file that should not be +overwritten. +This is typically used to ensure that an extraction process does not +overwrite the archive from which objects are being read. +This capability is technically unnecessary but can be a significant +performance optimization in practice. +.It Fn archive_write_disk_set_options +The options field consists of a bitwise OR of one or more of the +following values: +.Bl -tag -compact -width "indent" +.It Cm ARCHIVE_EXTRACT_OWNER +The user and group IDs should be set on the restored file. +By default, the user and group IDs are not restored. +.It Cm ARCHIVE_EXTRACT_PERM +Full permissions (including SGID, SUID, and sticky bits) should +be restored exactly as specified, without obeying the +current umask. +Note that SUID and SGID bits can only be restored if the +user and group ID of the object on disk are correct. +If +.Cm ARCHIVE_EXTRACT_OWNER +is not specified, then SUID and SGID bits will only be restored +if the default user and group IDs of newly-created objects on disk +happen to match those specified in the archive entry. +By default, only basic permissions are restored, and umask is obeyed. +.It Cm ARCHIVE_EXTRACT_TIME +The timestamps (mtime, ctime, and atime) should be restored. +By default, they are ignored. +Note that restoring of atime is not currently supported. +.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE +Existing files on disk will not be overwritten. +By default, existing regular files are truncated and overwritten; +existing directories will have their permissions updated; +other pre-existing objects are unlinked and recreated from scratch. +.It Cm ARCHIVE_EXTRACT_UNLINK +Existing files on disk will be unlinked before any attempt to +create them. +In some cases, this can prove to be a significant performance improvement. +By default, existing files are truncated and rewritten, but +the file is not recreated. +In particular, the default behavior does not break existing hard links. +.It Cm ARCHIVE_EXTRACT_ACL +Attempt to restore ACLs. +By default, extended ACLs are ignored. +.It Cm ARCHIVE_EXTRACT_FFLAGS +Attempt to restore extended file flags. +By default, file flags are ignored. +.It Cm ARCHIVE_EXTRACT_XATTR +Attempt to restore POSIX.1e extended attributes. +By default, they are ignored. +.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS +Refuse to extract any object whose final location would be altered +by a symlink on disk. +This is intended to help guard against a variety of mischief +caused by archives that (deliberately or otherwise) extract +files outside of the current directory. +The default is not to perform this check. +If +.Cm ARCHIVE_EXTRACT_UNLINK +is specified together with this option, the library will +remove any intermediate symlinks it finds and return an +error only if such symlink could not be removed. +.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT +Refuse to extract a path that contains a +.Pa .. +element anywhere within it. +The default is to not refuse such paths. +Note that paths ending in +.Pa .. +always cause an error, regardless of this flag. +.El +.It Fn archive_write_disk_set_group_lookup , Fn archive_write_disk_set_user_lookup +The +.Tn struct archive_entry +objects contain both names and ids that can be used to identify users +and groups. +These names and ids describe the ownership of the file itself and +also appear in ACL lists. +By default, the library uses the ids and ignores the names, but +this can be overridden by registering user and group lookup functions. +To register, you must provide a lookup function which +accepts both a name and id and returns a suitable id. +You may also provide a +.Tn void * +pointer to a private data structure and a cleanup function for +that data. +The cleanup function will be invoked when the +.Tn struct archive +object is destroyed. +.It Fn archive_write_disk_set_standard_lookup +This convenience function installs a standard set of user +and group lookup functions. +These functions use +.Xr getpwnam 3 +and +.Xr getgrnam 3 +to convert names to ids, defaulting to the ids if the names cannot +be looked up. +These functions also implement a simple memory cache to reduce +the number of calls to +.Xr getpwnam 3 +and +.Xr getgrnam 3 . +.It Fn archive_write_header +Build and write a header using the data in the provided +.Tn struct archive_entry +structure. +See +.Xr archive_entry 3 +for information on creating and populating +.Tn struct archive_entry +objects. +.It Fn archive_write_data +Write data corresponding to the header just written. +Returns number of bytes written or -1 on error. +.It Fn archive_write_finish_entry +Close out the entry just written. +Ordinarily, clients never need to call this, as it +is called automatically by +.Fn archive_write_next_header +and +.Fn archive_write_close +as needed. +.It Fn archive_write_close +Set any attributes that could not be set during the initial restore. +For example, directory timestamps are not restored initially because +restoring a subsequent file would alter that timestamp. +Similarly, non-writable directories are initially created with +write permissions (so that their contents can be restored). +The +.Nm +library maintains a list of all such deferred attributes and +sets them when this function is invoked. +.It Fn archive_write_finish +Invokes +.Fn archive_write_close +if it was not invoked manually, then releases all resources. +.El +More information about the +.Va struct archive +object and the overall design of the library can be found in the +.Xr libarchive 3 +overview. +Many of these functions are also documented under +.Xr archive_write 3 . +.Sh RETURN VALUES +Most functions return +.Cm ARCHIVE_OK +(zero) on success, or one of several non-zero +error codes for errors. +Specific error codes include: +.Cm ARCHIVE_RETRY +for operations that might succeed if retried, +.Cm ARCHIVE_WARN +for unusual conditions that do not prevent further operations, and +.Cm ARCHIVE_FATAL +for serious errors that make remaining operations impossible. +The +.Fn archive_errno +and +.Fn archive_error_string +functions can be used to retrieve an appropriate error code and a +textual error message. +.Pp +.Fn archive_write_disk_new +returns a pointer to a newly-allocated +.Tn struct archive +object. +.Pp +.Fn archive_write_data +returns a count of the number of bytes actually written. +On error, -1 is returned and the +.Fn archive_errno +and +.Fn archive_error_string +functions will return appropriate values. +.Sh SEE ALSO +.Xr archive_read 3 , +.Xr archive_write 3 , +.Xr tar 1 , +.Xr libarchive 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +The +.Nm archive_write_disk +interface was added to +.Nm libarchive 2.0 +and first appeared in +.Fx 6.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@acm.org . +.Sh BUGS +Directories are actually extracted in two distinct phases. +Directories are created during +.Fn archive_write_header , +but final permissions are not set until +.Fn archive_write_close . +This separation is necessary to correctly handle borderline +cases such as a non-writable directory containing +files, but can cause unexpected results. +In particular, directory permissions are not fully +restored until the archive is closed. +If you use +.Xr chdir 2 +to change the current directory between calls to +.Fn archive_read_extract +or before calling +.Fn archive_read_close , +you may confuse the permission-setting logic with +the result that directory permissions are restored +incorrectly. +.Pp +The library attempts to create objects with filenames longer than +.Cm PATH_MAX +by creating prefixes of the full path and changing the current directory. +Currently, this logic is limited in scope; the fixup pass does +not work correctly for such objects and the symlink security check +option disables the support for very long pathnames. +.Pp +Restoring the path +.Pa aa/../bb +does create each intermediate directory. +In particular, the directory +.Pa aa +is created as well as the final object +.Pa bb . +In theory, this can be exploited to create an entire directory heirarchy +with a single request. +Of course, this does not work if the +.Cm ARCHIVE_EXTRACT_NODOTDOT +option is specified. +.Pp +Implicit directories are always created obeying the current umask. +Explicit objects are created obeying the current umask unless +.Cm ARCHIVE_EXTRACT_PERM +is specified, in which case they current umask is ignored. +.Pp +SGID and SUID bits are restored only if the correct user and +group could be set. +If +.Cm ARCHIVE_EXTRACT_OWNER +is not specified, then no attempt is made to set the ownership. +In this case, SGID and SUID bits are restored only if the +user and group of the final object happen to match those specified +in the entry. +.Pp +The +.Dq standard +user-id and group-id lookup functions are not the defaults because +.Xr getgrnam 3 +and +.Xr getpwnam 3 +are sometimes too large for particular applications. +The current design allows the application author to use a more +compact implementation when appropriate. +.Pp +There should be a corresponding +.Nm archive_read_disk +interface that walks a directory heirarchy and returns archive +entry objects. \ No newline at end of file diff --git a/contrib/libarchive-2.0/libarchive/archive_write_set_format_ar.c b/contrib/libarchive-2.0/libarchive/archive_write_set_format_ar.c new file mode 100644 index 0000000000..714b489c20 --- /dev/null +++ b/contrib/libarchive-2.0/libarchive/archive_write_set_format_ar.c @@ -0,0 +1,492 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 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 + * in this position and unchanged. + * 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: src/lib/libarchive/archive_write_set_format_ar.c,v 1.1 2007/04/03 05:34:36 kientzle Exp $"); + +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#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_write_private.h" + +struct ar_w { + uint64_t entry_bytes_remaining; + uint64_t entry_padding; + int is_strtab; + int has_strtab; + char *strtab; +}; + +/* + * Define structure of the "ar" header. + */ +#define AR_name_offset 0 +#define AR_name_size 16 +#define AR_date_offset 16 +#define AR_date_size 12 +#define AR_uid_offset 28 +#define AR_uid_size 6 +#define AR_gid_offset 34 +#define AR_gid_size 6 +#define AR_mode_offset 40 +#define AR_mode_size 8 +#define AR_size_offset 48 +#define AR_size_size 10 +#define AR_fmag_offset 58 +#define AR_fmag_size 2 + +/* + * "ar" magic numbers. + */ +#define ARMAG "!\n" +#define SARMAG 8 /* strlen(ARMAG); */ +#define AR_EFMT1 "#1/" +#define SAR_EFMT1 3 /* strlen(AR_EFMT1); */ +#define ARFMAG "`\n" +#define SARFMAG 2 /* strlen(ARFMAG); */ + +static int __archive_write_set_format_ar(struct archive_write *); +static int archive_write_ar_header(struct archive_write *, + struct archive_entry *); +static ssize_t archive_write_ar_data(struct archive_write *, const void *buff, + size_t s); +static int archive_write_ar_destroy(struct archive_write *); +static int archive_write_ar_finish_entry(struct archive_write *); +static int format_octal(int64_t v, char *p, int s); +static int format_decimal(int64_t v, char *p, int s); + +int +archive_write_set_format_ar_bsd(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = __archive_write_set_format_ar(a); + if (r == ARCHIVE_OK) { + a->archive_format = ARCHIVE_FORMAT_AR_BSD; + a->archive_format_name = "ar (BSD)"; + } + return (r); +} + +int +archive_write_set_format_ar_svr4(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = __archive_write_set_format_ar(a); + if (r == ARCHIVE_OK) { + a->archive_format = ARCHIVE_FORMAT_AR_SVR4; + a->archive_format_name = "ar (GNU/SVR4)"; + } + return (r); +} + +/* + * Generic initialization. + */ +static int +__archive_write_set_format_ar(struct archive_write *a) +{ + struct ar_w *ar; + + /* If someone else was already registered, unregister them. */ + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + ar = (struct ar_w *)malloc(sizeof(*ar)); + if (ar == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); + return (ARCHIVE_FATAL); + } + memset(ar, 0, sizeof(*ar)); + a->format_data = ar; + + a->format_write_header = archive_write_ar_header; + a->format_write_data = archive_write_ar_data; + a->format_finish = NULL; + a->format_destroy = archive_write_ar_destroy; + a->format_finish_entry = archive_write_ar_finish_entry; + return (ARCHIVE_OK); +} + +static int +archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) +{ + int ret, append_fn; + char buff[60]; + char *ss, *se; + struct ar_w *ar; + const char *pp; + const struct stat *st; + + ret = 0; + append_fn = 0; + ar = (struct ar_w *)a->format_data; + ar->is_strtab = 0; + + if (a->archive.file_position == 0) { + /* + * We are now at the beginning of the archive, + * so we need first write the ar global header. + */ + (a->compression_write)(a, ARMAG, SARMAG); + } + + memset(buff, ' ', 60); + strncpy(&buff[AR_fmag_offset], ARFMAG, SARFMAG); + + pp = archive_entry_pathname(entry); + + if (strcmp(pp, "/") == 0 ) { + /* Entry is archive symbol table in GNU format */ + buff[AR_name_offset] = '/'; + goto stat; + } + if (strcmp(pp, "__.SYMDEF") == 0) { + /* Entry is archive symbol table in BSD format */ + strncpy(buff + AR_name_offset, "__.SYMDEF", 9); + goto stat; + } + if (strcmp(pp, "//") == 0) { + /* + * Entry is archive string table, inform that we should + * collect strtab in next _data call. + */ + ar->is_strtab = 1; + buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; + /* + * For archive string table, only ar_size filed should + * be set. + */ + goto size; + } + + /* Otherwise, entry is a normal archive member. */ + if (a->archive_format == ARCHIVE_FORMAT_AR_SVR4) { + /* + * SVR4/GNU variant use a "/" to mark then end of the filename, + * make it possible to have embedded spaces in the filename. + * So, the longest filename here (without extension) is + * actually 15 bytes. + */ + if (strlen(pp) <= 15) { + strncpy(&buff[AR_name_offset], pp, strlen(pp)); + buff[AR_name_offset + strlen(pp)] = '/'; + } else { + /* + * For filename longer than 15 bytes, GNU variant + * makes use of a string table and instead stores the + * offset of the real filename to in the ar_name field. + * The string table should have been written before. + */ + if (ar->has_strtab <= 0) { + archive_set_error(&a->archive, EINVAL, + "Can't find string table"); + return (ARCHIVE_WARN); + } + + se = (char *)malloc(strlen(pp) + 3); + if (se == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate filename buffer"); + return (ARCHIVE_FATAL); + } + + strncpy(se, pp, strlen(pp)); + strcpy(se + strlen(pp), "/\n"); + + ss = strstr(ar->strtab, se); + free(se); + + if (ss == NULL) { + archive_set_error(&a->archive, EINVAL, + "Invalid string table"); + return (ARCHIVE_WARN); + } + + /* + * GNU variant puts "/" followed by digits into + * ar_name field. These digits indicates the real + * filename string's offset to the string table. + */ + buff[AR_name_offset] = '/'; + if (format_decimal(ss - ar->strtab, + buff + AR_name_offset + 1, + AR_name_size - 1)) { + archive_set_error(&a->archive, ERANGE, + "string table offset too large"); + return (ARCHIVE_WARN); + } + } + } else if (a->archive_format == ARCHIVE_FORMAT_AR_BSD) { + /* + * BSD variant: for any file name which is more than + * 16 chars or contains one or more embedded space(s), the + * string "#1/" followed by the ASCII length of the name is + * put into the ar_name field. The file size (stored in the + * ar_size field) is incremented by the length of the name. + * The name is then written immediately following the + * archive header. + */ + if (strlen(pp) <= 16 && strchr(pp, ' ') == NULL) { + strncpy(&buff[AR_name_offset], pp, strlen(pp)); + buff[AR_name_offset + strlen(pp)] = ' '; + } + else { + strncpy(buff + AR_name_offset, AR_EFMT1, SAR_EFMT1); + if (format_decimal(strlen(pp), + buff + AR_name_offset + SAR_EFMT1, + AR_name_size - SAR_EFMT1)) { + archive_set_error(&a->archive, ERANGE, + "File name too long"); + return (ARCHIVE_WARN); + } + append_fn = 1; + archive_entry_set_size(entry, + archive_entry_size(entry) + strlen(pp)); + } + } + +stat: + st = archive_entry_stat(entry); + if (format_decimal(st->st_mtime, buff + AR_date_offset, AR_date_size)) { + archive_set_error(&a->archive, ERANGE, + "File modification time too large"); + return (ARCHIVE_WARN); + } + if (format_decimal(st->st_uid, buff + AR_uid_offset, AR_uid_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric user ID too large"); + return (ARCHIVE_WARN); + } + if (format_decimal(st->st_gid, buff + AR_gid_offset, AR_gid_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric group ID too large"); + return (ARCHIVE_WARN); + } + if (format_octal(st->st_mode, buff + AR_mode_offset, AR_mode_size)) { + archive_set_error(&a->archive, ERANGE, + "Numeric mode too large"); + return (ARCHIVE_WARN); + } + +size: + if (format_decimal(archive_entry_size(entry), buff + AR_size_offset, + AR_size_size)) { + archive_set_error(&a->archive, ERANGE, + "File size out of range"); + return (ARCHIVE_WARN); + } + + ret = (a->compression_write)(a, buff, 60); + if (ret != ARCHIVE_OK) + return (ret); + + ar->entry_bytes_remaining = archive_entry_size(entry); + ar->entry_padding = ar->entry_bytes_remaining % 2; + + if (append_fn > 0) { + ret = (a->compression_write)(a, pp, strlen(pp)); + if (ret != ARCHIVE_OK) + return (ret); + ar->entry_bytes_remaining -= strlen(pp); + } + + return (ARCHIVE_OK); +} + +static ssize_t +archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) +{ + struct ar_w *ar; + int ret; + + ar = (struct ar_w *)a->format_data; + if (s > ar->entry_bytes_remaining) + s = ar->entry_bytes_remaining; + + if (ar->is_strtab > 0) { + if (ar->has_strtab > 0) { + archive_set_error(&a->archive, EINVAL, + "More than one string tables exist"); + return (ARCHIVE_WARN); + } + + ar->strtab = (char *)malloc(s); + if (ar->strtab == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate strtab buffer"); + return (ARCHIVE_FATAL); + } + strncpy(ar->strtab, buff, s); + ar->has_strtab = 1; + } + + ret = (a->compression_write)(a, buff, s); + if (ret != ARCHIVE_OK) + return (ret); + + ar->entry_bytes_remaining -= s; + return (s); +} + +static int +archive_write_ar_destroy(struct archive_write *a) +{ + struct ar_w *ar; + + ar = (struct ar_w *)a->format_data; + + if (ar->has_strtab > 0) { + free(ar->strtab); + ar->strtab = NULL; + } + + free(ar); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_ar_finish_entry(struct archive_write *a) +{ + struct ar_w *ar; + int ret; + + ar = (struct ar_w *)a->format_data; + + if (ar->entry_bytes_remaining != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Entry remaining bytes larger than 0"); + return (ARCHIVE_WARN); + } + + if (ar->entry_padding == 0) { + return (ARCHIVE_OK); + } + + if (ar->entry_padding != 1) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Padding wrong size: %d should be 1 or 0", + ar->entry_padding); + return (ARCHIVE_WARN); + } + + ret = (a->compression_write)(a, "\n", 1); + return (ret); +} + +/* + * Format a number into the specified field using base-8. + * NB: This version is slightly different from the one in + * _ustar.c + */ +static int +format_octal(int64_t v, char *p, int s) +{ + int len; + char *h; + + len = s; + h = p; + + /* Octal values can't be negative, so use 0. */ + if (v < 0) { + while (len-- > 0) + *p++ = '0'; + return (-1); + } + + p += s; /* Start at the end and work backwards. */ + do { + *--p = (char)('0' + (v & 7)); + v >>= 3; + } while (--s > 0 && v > 0); + + if (v == 0) { + memmove(h, p, len - s); + p = h + len - s; + while (s-- > 0) + *p++ = ' '; + return (0); + } + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '7'; + + return (-1); +} + +/* + * Format a number into the specified field using base-10. + */ +static int +format_decimal(int64_t v, char *p, int s) +{ + int len; + char *h; + + len = s; + h = p; + + /* Negative values in ar header are meaningless , so use 0. */ + if (v < 0) { + while (len-- > 0) + *p++ = '0'; + return (-1); + } + + p += s; + do { + *--p = (char)('0' + (v % 10)); + v /= 10; + } while (--s > 0 && v > 0); + + if (v == 0) { + memmove(h, p, len - s); + p = h + len - s; + while (s-- > 0) + *p++ = ' '; + return (0); + } + /* If it overflowed, fill field with max value. */ + while (len-- > 0) + *p++ = '9'; + + return (-1); +} diff --git a/contrib/libarchive-2.0/libarchive/libarchive-formats.5 b/contrib/libarchive-2.0/libarchive/libarchive-formats.5 index 1b134e6bf0..cd34622b2c 100644 --- a/contrib/libarchive-2.0/libarchive/libarchive-formats.5 +++ b/contrib/libarchive-2.0/libarchive/libarchive-formats.5 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.12 2007/01/09 08:05:56 kientzle Exp $ +.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14 2007/04/05 05:07:53 kientzle Exp $ .\" .Dd April 27, 2004 .Dt libarchive-formats 3 @@ -235,7 +235,20 @@ compressed with the .Dq deflate algorithm. Older zip compression algorithms are not supported. +.Ss Archive (library) file format +The Unix archive format (commonly created by the +.Xr ar 1 +archiver) is a general-purpose format which is +used almost exclusively for object files to be +read by the link editor +.Xr ld 1 . +The ar format has never been standardised. +There are two common variants: +the GNU format derived from SVR4, +and the BSD format, which first appeared in 4.4BSD. +Libarchive provides read and write support for both variants. .Sh SEE ALSO +.Xr ar 1 , .Xr cpio 1 , .Xr mkisofs 1 , .Xr shar 1 , diff --git a/contrib/libarchive-2.0/tar/read.c b/contrib/libarchive-2.0/tar/read.c index 1a141bbee2..8649dcfee2 100644 --- a/contrib/libarchive-2.0/tar/read.c +++ b/contrib/libarchive-2.0/tar/read.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.28 2007/03/11 10:36:42 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.29 2007/04/07 05:56:40 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -122,19 +122,17 @@ read_archive(struct bsdtar *bsdtar, char mode) r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; - if (r == ARCHIVE_WARN) + if (r < ARCHIVE_OK) bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); - if (r == ARCHIVE_FATAL) { - bsdtar->return_value = 1; - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); - break; - } if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); bsdtar_warnc(bsdtar, 0, "Retrying..."); continue; } + if (r != ARCHIVE_OK) { + bsdtar->return_value = 1; + break; + } /* * Exclude entries that are too old. diff --git a/contrib/libarchive-2.0/version b/contrib/libarchive-2.0/version index 7cc919f925..650a6a5571 100644 --- a/contrib/libarchive-2.0/version +++ b/contrib/libarchive-2.0/version @@ -1 +1 @@ -2.0.27 \ No newline at end of file +2.0.28 \ No newline at end of file