summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2007-04-03 05:34:36 +0000
committerkientzle <kientzle@FreeBSD.org>2007-04-03 05:34:36 +0000
commitb356476feba52a0a45c8e89ebac5d6724a4e6d78 (patch)
treeee7ab4b960264dddb2ad2c445c0e861e145e0ca2 /lib
parentba7e53058343ca70fa1013a3af10139eac74e8c4 (diff)
downloadFreeBSD-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.in6
-rw-r--r--lib/libarchive/archive_read_support_format_ar.c585
-rw-r--r--lib/libarchive/archive_write_set_format_ar.c492
-rw-r--r--lib/libarchive/libarchive-formats.516
-rw-r--r--lib/libarchive/test/test_read_format_ar.c104
-rw-r--r--lib/libarchive/test/test_write_format_ar.c173
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
+}
OpenPOWER on IntegriCloud