diff options
author | kientzle <kientzle@FreeBSD.org> | 2007-04-03 05:34:36 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2007-04-03 05:34:36 +0000 |
commit | b356476feba52a0a45c8e89ebac5d6724a4e6d78 (patch) | |
tree | ee7ab4b960264dddb2ad2c445c0e861e145e0ca2 /lib | |
parent | ba7e53058343ca70fa1013a3af10139eac74e8c4 (diff) | |
download | FreeBSD-src-b356476feba52a0a45c8e89ebac5d6724a4e6d78.zip FreeBSD-src-b356476feba52a0a45c8e89ebac5d6724a4e6d78.tar.gz |
'ar' format support for libarchive, contributed by Kai Wang.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libarchive/archive.h.in | 6 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_ar.c | 585 | ||||
-rw-r--r-- | lib/libarchive/archive_write_set_format_ar.c | 492 | ||||
-rw-r--r-- | lib/libarchive/libarchive-formats.5 | 16 | ||||
-rw-r--r-- | lib/libarchive/test/test_read_format_ar.c | 104 | ||||
-rw-r--r-- | lib/libarchive/test/test_write_format_ar.c | 173 |
6 files changed, 1376 insertions, 0 deletions
diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 5282418..afaa40dd 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -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/lib/libarchive/archive_read_support_format_ar.c b/lib/libarchive/archive_read_support_format_ar.c new file mode 100644 index 0000000..280d4bb --- /dev/null +++ b/lib/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$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#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 "!<arch>\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/lib/libarchive/archive_write_set_format_ar.c b/lib/libarchive/archive_write_set_format_ar.c new file mode 100644 index 0000000..a9c72fd --- /dev/null +++ b/lib/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$"); + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#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 "!<arch>\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/lib/libarchive/libarchive-formats.5 b/lib/libarchive/libarchive-formats.5 index dbd9469..dcd45e8 100644 --- a/lib/libarchive/libarchive-formats.5 +++ b/lib/libarchive/libarchive-formats.5 @@ -235,7 +235,23 @@ compressed with the .Dq deflate algorithm. Older zip compression algorithms are not supported. +.Ss Archive (library) file format +The Unix archives (commonly created by the +.Xr ar 1 +archiver) are mainly used as libraries of object files intended to +be loaded using the link-editor +.Xr ld 1 . +Altough it is capable of creating archives for any purpose, +nowadays it is used only to create static libraries and +for other purpose, +.Xr tar 1 +is the prefered way. The ar format has never been standardised, +currently there exist two commonly used format, one is +the GNU variant derived from SVR4, the other is the +BSD format first appeared in 4.4BSD. +Libarchive provides read and write support for both two variants. .Sh SEE ALSO +.Xr ar 1 , .Xr cpio 1 , .Xr mkisofs 1 , .Xr shar 1 , diff --git a/lib/libarchive/test/test_read_format_ar.c b/lib/libarchive/test/test_read_format_ar.c new file mode 100644 index 0000000..d3e4f0e --- /dev/null +++ b/lib/libarchive/test/test_read_format_ar.c @@ -0,0 +1,104 @@ +/*- + * 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 "test.h" +__FBSDID("$FreeBSD$"); + +/* + * This "archive" is created by "GNU ar". Here we try to verify + * our GNU format handling functionality. + */ +static unsigned char archive[] = { +'!','<','a','r','c','h','>',10,'/','/',' ',' ',' ',' ',' ',' ',' ', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', +' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', +' ',' ',' ',' ',' ','4','0',' ',' ',' ',' ',' ',' ',' ',' ','`',10, +'y','y','y','t','t','t','s','s','s','a','a','a','f','f','f','.','o', +'/',10,'h','h','h','h','j','j','j','j','k','k','k','k','l','l','l', +'l','.','o','/',10,10,'/','0',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', +' ',' ',' ',' ','1','1','7','5','4','6','5','6','5','2',' ',' ','1', +'0','0','1',' ',' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4', +' ',' ','8',' ',' ',' ',' ',' ',' ',' ',' ',' ','`',10,'5','5','6', +'6','7','7','8','8','g','g','h','h','.','o','/',' ',' ',' ',' ',' ', +' ',' ',' ',' ','1','1','7','5','4','6','5','6','6','8',' ',' ','1', +'0','0','1',' ',' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4', +' ',' ','4',' ',' ',' ',' ',' ',' ',' ',' ',' ','`',10,'3','3','3', +'3','/','1','9',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', +'1','1','7','5','4','6','5','7','1','3',' ',' ','1','0','0','1',' ', +' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4',' ',' ','9',' ', +' ',' ',' ',' ',' ',' ',' ',' ','`',10,'9','8','7','6','5','4','3', +'2','1',10}; + +char buff[64]; + +DEFINE_TEST(test_read_format_ar) +{ + struct archive_entry *ae; + struct archive *a; + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_compression_all(a)); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_open_memory(a, archive, sizeof(archive))); + + /* First we meet the string table */ + assertA(0 == archive_read_next_header(a, &ae)); + assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE); + assertA(archive_format(a) == ARCHIVE_FORMAT_AR); + assert(0 == strcmp("//", archive_entry_pathname(ae))); + assert(40 == archive_entry_size(ae)); + assertA(40 == archive_read_data(a, buff, 45)); + + /* First Entry */ + assertA(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("yyytttsssaaafff.o", archive_entry_pathname(ae))); + assert(8 == archive_entry_size(ae)); + assertA(8 == archive_read_data(a, buff, 10)); + assert(0 == memcmp(buff, "55667788", 8)); + + /* Second Entry */ + assertA(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("gghh.o", archive_entry_pathname(ae))); + assert(4 == archive_entry_size(ae)); + assertA(4 == archive_read_data(a, buff, 10)); + assert(0 == memcmp(buff, "3333", 4)); + + /* Third Entry */ + assertA(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae))); + assert(9 == archive_entry_size(ae)); + assertA(9 == archive_read_data(a, buff, 9)); + assert(0 == memcmp(buff, "987654321", 9)); + + /* Test EOF */ + assertA(1 == archive_read_next_header(a, &ae)); + assert(0 == archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif +} diff --git a/lib/libarchive/test/test_write_format_ar.c b/lib/libarchive/test/test_write_format_ar.c new file mode 100644 index 0000000..e54ab12 --- /dev/null +++ b/lib/libarchive/test/test_write_format_ar.c @@ -0,0 +1,173 @@ +/*- + * 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 "test.h" +__FBSDID("$FreeBSD$"); + +char buff[4096]; +char buff2[64]; +static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\n\n"; + +DEFINE_TEST(test_write_format_ar) +{ + struct archive_entry *ae; + struct archive* a; + size_t used; + + /* + * First we try to create a SVR4/GNU format archive. + */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_ar_svr4(a)); + assertA(0 == archive_write_set_compression_none(a)); + assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); + + /* write the string table */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "//"); + archive_entry_set_size(ae, strlen(strtab)); + assertA(0 == archive_write_header(a, ae)); + assertA(strlen(strtab) == archive_write_data(a, strtab, strlen(strtab))); + archive_entry_free(ae); + + /* write entries */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_mtime(ae, 1, 0); + assert(1 == archive_entry_mtime(ae)); + archive_entry_set_mode(ae, S_IFREG | 0755); + assert((S_IFREG | 0755) == archive_entry_mode(ae)); + archive_entry_copy_pathname(ae, "abcdefghijklmn.o"); + archive_entry_set_size(ae, 8); + assertA(0 == archive_write_header(a, ae)); + assertA(8 == archive_write_data(a, "87654321", 15)); + archive_entry_free(ae); + + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "ggghhhjjjrrrttt.o"); + archive_entry_set_size(ae, 7); + assertA(0 == archive_write_header(a, ae)); + assertA(7 == archive_write_data(a, "7777777", 7)); + + archive_entry_free(ae); + archive_write_close(a); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_write_finish(a)); +#elif + archive_write_finish(a); +#endif + + /* + * Now, read the data back. + */ + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_support_compression_all(a)); + assertA(0 == archive_read_open_memory(a, buff, used)); + + assertA(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("//", archive_entry_pathname(ae))); + assert(strlen(strtab) == archive_entry_size(ae)); + assertA(strlen(strtab) == archive_read_data(a, buff2, strlen(strtab))); + assert(0 == memcmp(buff2, strtab, strlen(strtab))); + + assertA(0 == archive_read_next_header(a, &ae)); + assert(1 == archive_entry_mtime(ae)); + assert(0 == strcmp("abcdefghijklmn.o", archive_entry_pathname(ae))); + assert(8 == archive_entry_size(ae)); + assertA(8 == archive_read_data(a, buff2, 10)); + assert(0 == memcmp(buff2, "87654321", 8)); + + assert(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("ggghhhjjjrrrttt.o", archive_entry_pathname(ae))); + assert(7 == archive_entry_size(ae)); + assertA(7 == archive_read_data(a, buff2, 11)); + assert(0 == memcmp(buff2, "7777777", 7)); + + assert(0 == archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif + + /* + * Then, we try to create a BSD format archive. + */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_ar_bsd(a)); + assertA(0 == archive_write_set_compression_none(a)); + assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); + + /* write a entry need long name extension */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "ttttyyyyuuuuiiii.o"); + archive_entry_set_size(ae, 5); + assertA(0 == archive_write_header(a, ae)); + assertA(5 == archive_write_data(a, "12345", 7)); + archive_entry_free(ae); + + /* write a entry with a short name */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "ttyy.o"); + archive_entry_set_size(ae, 6); + assertA(0 == archive_write_header(a, ae)); + assertA(6 == archive_write_data(a, "555555", 7)); + archive_entry_free(ae); + archive_write_close(a); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_write_finish(a)); +#elif + archive_write_finish(a); +#endif + + /* Now, Read the data back */ + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_format_all(a)); + assertA(0 == archive_read_support_compression_all(a)); + assertA(0 == archive_read_open_memory(a, buff, used)); + + assert(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae))); + assert(5 == archive_entry_size(ae)); + assertA(5 == archive_read_data(a, buff2, 10)); + assert(0 == memcmp(buff2, "12345", 5)); + + assert(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("ttyy.o", archive_entry_pathname(ae))); + assert(6 == archive_entry_size(ae)); + assertA(6 == archive_read_data(a, buff2, 10)); + assert(0 == memcmp(buff2, "555555", 6)); + + /* Test EOF */ + assertA(1 == archive_read_next_header(a, &ae)); + assert(0 == archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif +} |