Overhaul of 'ar' support:
* use "AR_GNU" as the format name instead of AR_SVR4 (it's what everyone is going to call it anyway) * Simplify numeric parsing to unsigned (none of the numeric values should ever be negative); don't run off end of numeric fields. * Finish parsing the common header fields before the next I/O request (which might dump the contents) * Be smarter about format guessing and trimming filenames. * Most of the magic values are only used in one place, so just inline them. * Many more comments. * Be smarter about handling damaged entries; return something reasonable. * Call it a "filename table" instead of a "string table" * Update tests. Enable selection of 'ar', 'arbsd', and 'argnu' formats by name (this allows bsdtar to create ar format archives). The 'ar' writer still needs some work; it should reject entries that aren't regular files and should probably also strip leading paths from filenames.
This commit is contained in:
parent
d7e5af0175
commit
7cead8e1b5
@ -59,6 +59,7 @@ SRCS= archive.h \
|
|||||||
archive_read_support_compression_gzip.c \
|
archive_read_support_compression_gzip.c \
|
||||||
archive_read_support_compression_none.c \
|
archive_read_support_compression_none.c \
|
||||||
archive_read_support_format_all.c \
|
archive_read_support_format_all.c \
|
||||||
|
archive_read_support_format_ar.c \
|
||||||
archive_read_support_format_cpio.c \
|
archive_read_support_format_cpio.c \
|
||||||
archive_read_support_format_empty.c \
|
archive_read_support_format_empty.c \
|
||||||
archive_read_support_format_iso9660.c \
|
archive_read_support_format_iso9660.c \
|
||||||
|
@ -178,7 +178,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data);
|
|||||||
#define ARCHIVE_FORMAT_ZIP 0x50000
|
#define ARCHIVE_FORMAT_ZIP 0x50000
|
||||||
#define ARCHIVE_FORMAT_EMPTY 0x60000
|
#define ARCHIVE_FORMAT_EMPTY 0x60000
|
||||||
#define ARCHIVE_FORMAT_AR 0x70000
|
#define ARCHIVE_FORMAT_AR 0x70000
|
||||||
#define ARCHIVE_FORMAT_AR_SVR4 (ARCHIVE_FORMAT_AR | 1)
|
#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
|
||||||
#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
|
#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
#include "archive_platform.h"
|
#include "archive_platform.h"
|
||||||
__FBSDID("$FreeBSD$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
#ifdef HAVE_ERRNO_H
|
#ifdef HAVE_ERRNO_H
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#endif
|
#endif
|
||||||
@ -70,16 +73,6 @@ struct ar {
|
|||||||
#define AR_fmag_offset 58
|
#define AR_fmag_offset 58
|
||||||
#define AR_fmag_size 2
|
#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'
|
#define isdigit(x) (x) >= '0' && (x) <= '9'
|
||||||
|
|
||||||
static int archive_read_format_ar_bid(struct archive_read *a);
|
static int archive_read_format_ar_bid(struct archive_read *a);
|
||||||
@ -89,32 +82,12 @@ static int archive_read_format_ar_read_data(struct archive_read *a,
|
|||||||
static int archive_read_format_ar_skip(struct archive_read *a);
|
static int archive_read_format_ar_skip(struct archive_read *a);
|
||||||
static int archive_read_format_ar_read_header(struct archive_read *a,
|
static int archive_read_format_ar_read_header(struct archive_read *a,
|
||||||
struct archive_entry *e);
|
struct archive_entry *e);
|
||||||
static int64_t ar_atol8(const char *p, unsigned char_cnt);
|
static uint64_t ar_atol8(const char *p, unsigned char_cnt);
|
||||||
static int64_t ar_atol10(const char *p, unsigned char_cnt);
|
static uint64_t ar_atol10(const char *p, unsigned char_cnt);
|
||||||
static int ar_parse_string_table(struct archive_read *, struct ar *,
|
static int ar_parse_gnu_filename_table(struct archive_read *, struct ar *,
|
||||||
const void *, size_t);
|
const void *, size_t);
|
||||||
|
static int ar_parse_common_header(struct ar *ar, struct archive_entry *,
|
||||||
/*
|
const char *h);
|
||||||
* 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
|
int
|
||||||
archive_read_support_format_ar(struct archive *_a)
|
archive_read_support_format_ar(struct archive *_a)
|
||||||
@ -177,16 +150,15 @@ archive_read_format_ar_bid(struct archive_read *a)
|
|||||||
if (ar->bid > 0)
|
if (ar->bid > 0)
|
||||||
return (ar->bid);
|
return (ar->bid);
|
||||||
|
|
||||||
bytes_read = (a->compression_read_ahead)(a, &h, SARMAG);
|
|
||||||
if (bytes_read < SARMAG)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify the global header.
|
* Verify the 8-byte file signature.
|
||||||
* TODO: Do we need to check more than this?
|
* TODO: Do we need to check more than this?
|
||||||
*/
|
*/
|
||||||
if (strncmp((const char*)h, ARMAG, SARMAG) == 0) {
|
bytes_read = (a->compression_read_ahead)(a, &h, 8);
|
||||||
ar->bid = SARMAG;
|
if (bytes_read < 8)
|
||||||
|
return (-1);
|
||||||
|
if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
|
||||||
|
ar->bid = 64;
|
||||||
return (ar->bid);
|
return (ar->bid);
|
||||||
}
|
}
|
||||||
return (-1);
|
return (-1);
|
||||||
@ -196,199 +168,247 @@ static int
|
|||||||
archive_read_format_ar_read_header(struct archive_read *a,
|
archive_read_format_ar_read_header(struct archive_read *a,
|
||||||
struct archive_entry *entry)
|
struct archive_entry *entry)
|
||||||
{
|
{
|
||||||
int r;
|
char filename[AR_name_size + 1];
|
||||||
size_t bsd_append;
|
|
||||||
ssize_t bytes;
|
|
||||||
int64_t nval;
|
|
||||||
size_t tab_size;
|
|
||||||
char *fname, *p;
|
|
||||||
struct ar *ar;
|
struct ar *ar;
|
||||||
|
uint64_t number; /* Used to hold parsed numbers before validation. */
|
||||||
|
ssize_t bytes_read;
|
||||||
|
size_t bsd_name_length, entry_size;
|
||||||
|
char *p;
|
||||||
const void *b;
|
const void *b;
|
||||||
const char *h;
|
const char *h;
|
||||||
|
int r;
|
||||||
|
|
||||||
bsd_append = 0;
|
ar = (struct ar*)*(a->pformat_data);
|
||||||
|
|
||||||
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) {
|
if (a->archive.file_position == 0) {
|
||||||
/*
|
/*
|
||||||
* We are now at the beginning of the archive,
|
* We are now at the beginning of the archive,
|
||||||
* so we need first consume the ar global header.
|
* so we need first consume the ar global header.
|
||||||
*/
|
*/
|
||||||
(a->compression_read_consume)(a, SARMAG);
|
(a->compression_read_consume)(a, 8);
|
||||||
|
/* Set a default format code for now. */
|
||||||
|
a->archive.archive_format = ARCHIVE_FORMAT_AR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read 60-byte header */
|
/* Read the header for the next file entry. */
|
||||||
bytes = (a->compression_read_ahead)(a, &b, 60);
|
bytes_read = (a->compression_read_ahead)(a, &b, 60);
|
||||||
if (bytes < 60) {
|
if (bytes_read < 60) {
|
||||||
/*
|
/* Broken header. */
|
||||||
* We just encountered an incomplete ar file,
|
|
||||||
* though the _bid function accepted it.
|
|
||||||
*/
|
|
||||||
return (ARCHIVE_EOF);
|
return (ARCHIVE_EOF);
|
||||||
}
|
}
|
||||||
(a->compression_read_consume)(a, 60);
|
(a->compression_read_consume)(a, 60);
|
||||||
|
|
||||||
h = (const char *)b;
|
h = (const char *)b;
|
||||||
|
|
||||||
/* Consistency check */
|
/* Verify the magic signature on the file header. */
|
||||||
if (strncmp(h + AR_fmag_offset, ARFMAG, SARFMAG) != 0) {
|
if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
|
||||||
archive_set_error(&a->archive, EINVAL,
|
archive_set_error(&a->archive, EINVAL,
|
||||||
"Consistency check failed");
|
"Consistency check failed");
|
||||||
return (ARCHIVE_WARN);
|
return (ARCHIVE_WARN);
|
||||||
}
|
}
|
||||||
|
|
||||||
ar = (struct ar*)*(a->pformat_data);
|
/* Copy filename into work buffer. */
|
||||||
|
strncpy(filename, h + AR_name_offset, AR_name_size);
|
||||||
|
filename[AR_name_size] = '\0';
|
||||||
|
|
||||||
if (strncmp(h + AR_name_offset, "//", 2) == 0) {
|
/*
|
||||||
|
* Guess the format variant based on the filename.
|
||||||
|
*/
|
||||||
|
if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
|
||||||
|
/* We don't already know the variant, so let's guess. */
|
||||||
/*
|
/*
|
||||||
* An archive member with ar_name "//" is an archive
|
* Biggest clue is presence of '/': GNU starts special
|
||||||
* string table.
|
* filenames with '/', appends '/' as terminator to
|
||||||
|
* non-special names, so anything with '/' should be
|
||||||
|
* GNU except for BSD long filenames.
|
||||||
*/
|
*/
|
||||||
nval = ar_atol10(h + AR_size_offset, AR_size_size);
|
if (strncmp(filename, "#1/", 3) == 0)
|
||||||
if (nval < 0 || nval > SIZE_MAX) {
|
a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
|
||||||
|
else if (strchr(filename, '/') != NULL)
|
||||||
|
a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
|
||||||
|
else if (strncmp(filename, "__.SYMDEF", 9) == 0)
|
||||||
|
a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
|
||||||
|
/*
|
||||||
|
* XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
|
||||||
|
* if name exactly fills 16-byte field? If so, we
|
||||||
|
* can't assume entries without '/' are BSD. XXX
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update format name from the code. */
|
||||||
|
if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
|
||||||
|
a->archive.archive_format_name = "ar (GNU/SVR4)";
|
||||||
|
else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
|
||||||
|
a->archive.archive_format_name = "ar (BSD)";
|
||||||
|
else
|
||||||
|
a->archive.archive_format_name = "ar";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove trailing spaces from the filename. GNU and BSD
|
||||||
|
* variants both pad filename area out with spaces.
|
||||||
|
* This will only be wrong if GNU/SVR4 'ar' implementations
|
||||||
|
* omit trailing '/' for 16-char filenames and we have
|
||||||
|
* a 16-char filename that ends in ' '.
|
||||||
|
*/
|
||||||
|
p = filename + AR_name_size - 1;
|
||||||
|
while (p >= filename && *p == ' ') {
|
||||||
|
*p = '\0';
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove trailing slash unless first character is '/'.
|
||||||
|
* (BSD entries never end in '/', so this will only trim
|
||||||
|
* GNU-format entries. GNU special entries start with '/'
|
||||||
|
* and are not terminated in '/', so we don't trim anything
|
||||||
|
* that starts with '/'.)
|
||||||
|
*/
|
||||||
|
if (filename[0] != '/' && *p == '/')
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* '//' is the GNU filename table.
|
||||||
|
* Later entries can refer to names in this table.
|
||||||
|
*/
|
||||||
|
if (strcmp(filename, "//") == 0) {
|
||||||
|
/* This must come before any call to _read_ahead. */
|
||||||
|
ar_parse_common_header(ar, entry, h);
|
||||||
|
archive_entry_copy_pathname(entry, filename);
|
||||||
|
archive_entry_set_mode(entry,
|
||||||
|
S_IFREG | (archive_entry_mode(entry) & 0777));
|
||||||
|
/* Get the size of the filename table. */
|
||||||
|
number = ar_atol10(h + AR_size_offset, AR_size_size);
|
||||||
|
if (number > SIZE_MAX) {
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||||
"String table too large");
|
"Filename table too large");
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
}
|
}
|
||||||
tab_size = (size_t)nval;
|
entry_size = (size_t)number;
|
||||||
bytes = (a->compression_read_ahead)(a, &b, tab_size);
|
/* Read the filename table into memory. */
|
||||||
if (bytes <= 0)
|
bytes_read = (a->compression_read_ahead)(a, &b, entry_size);
|
||||||
|
if (bytes_read <= 0)
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
if (bytes < nval) {
|
if ((size_t)bytes_read < entry_size) {
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||||
"Truncated input file");
|
"Truncated input file");
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Don't consume the contents, so the client will
|
||||||
|
* also get a shot at reading it.
|
||||||
|
*/
|
||||||
|
|
||||||
r = ar_parse_string_table(a, ar, b, tab_size);
|
/* Parse the filename table. */
|
||||||
if (r == ARCHIVE_OK) {
|
return (ar_parse_gnu_filename_table(a, ar, b, entry_size));
|
||||||
/*
|
|
||||||
* Archive string table only have ar_name and ar_size fileds
|
|
||||||
* in its header.
|
|
||||||
*/
|
|
||||||
archive_entry_copy_pathname(entry, "//");
|
|
||||||
h = (const char *)b;
|
|
||||||
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])) {
|
/*
|
||||||
|
* GNU variant handles long filenames by storing /<number>
|
||||||
|
* to indicate a name stored in the filename table.
|
||||||
|
*/
|
||||||
|
if (filename[0] == '/' && isdigit(filename[1])) {
|
||||||
|
number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
|
||||||
/*
|
/*
|
||||||
* Archive member is common format with SVR4/GNU variant.
|
* If we can't look up the real name, warn and return
|
||||||
* "/" followed by one or more digit(s) in the ar_name
|
* the entry with the wrong name.
|
||||||
* filed indicates an index to the string table.
|
|
||||||
*/
|
*/
|
||||||
if (ar->strtab == NULL) {
|
if (ar->strtab == NULL || number > ar->strtab_size) {
|
||||||
archive_set_error(&a->archive, EINVAL,
|
archive_set_error(&a->archive, EINVAL,
|
||||||
"String table does not exist");
|
"Can't find long filename for entry");
|
||||||
|
archive_entry_copy_pathname(entry, filename);
|
||||||
|
/* Parse the time, owner, mode, size fields. */
|
||||||
|
ar_parse_common_header(ar, entry, h);
|
||||||
return (ARCHIVE_WARN);
|
return (ARCHIVE_WARN);
|
||||||
}
|
}
|
||||||
|
|
||||||
nval = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
|
archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
|
||||||
if (nval < 0 || nval > ar->strtab_size) {
|
/* Parse the time, owner, mode, size fields. */
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
return (ar_parse_common_header(ar, entry, h));
|
||||||
"String table overflow");
|
|
||||||
return (ARCHIVE_FATAL);
|
|
||||||
}
|
|
||||||
archive_entry_copy_pathname(entry, &ar->strtab[(size_t)nval]);
|
|
||||||
goto remain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(h + AR_name_offset, AR_EFMT1, SAR_EFMT1) == 0) {
|
/*
|
||||||
/*
|
* BSD handles long filenames by storing "#1/" followed by the
|
||||||
* Archive member is common format with BSD variant.
|
* length of filename as a decimal number, then prepends the
|
||||||
* AR_EFMT1 is followed by one or more digit(s) indicating
|
* the filename to the file contents.
|
||||||
* the length of the real filename which is appended
|
*/
|
||||||
* to the header.
|
if (strncmp(filename, "#1/", 3) == 0) {
|
||||||
*/
|
/* Parse the time, owner, mode, size fields. */
|
||||||
nval = ar_atol10(h + AR_name_offset + SAR_EFMT1,
|
/* This must occur before _read_ahead is called again. */
|
||||||
AR_name_size - SAR_EFMT1);
|
ar_parse_common_header(ar, entry, h);
|
||||||
if (nval < 0 || nval >= SIZE_MAX) {
|
|
||||||
|
/* Parse the size of the name, adjust the file size. */
|
||||||
|
number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
|
||||||
|
if ((off_t)number > ar->entry_bytes_remaining) {
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||||
"Bad input file size");
|
"Bad input file size");
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
}
|
}
|
||||||
bsd_append = (size_t)nval;
|
bsd_name_length = (size_t)number;
|
||||||
bytes = (a->compression_read_ahead)(a, &b, bsd_append);
|
ar->entry_bytes_remaining -= bsd_name_length;
|
||||||
if (bytes <= 0)
|
/* Adjust file size reported to client. */
|
||||||
|
archive_entry_set_size(entry, ar->entry_bytes_remaining);
|
||||||
|
|
||||||
|
/* Read the long name into memory. */
|
||||||
|
bytes_read = (a->compression_read_ahead)(a, &b, bsd_name_length);
|
||||||
|
if (bytes_read <= 0)
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
if (bytes < nval) {
|
if ((size_t)bytes_read < bsd_name_length) {
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||||
"Truncated input file");
|
"Truncated input file");
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
}
|
}
|
||||||
|
(a->compression_read_consume)(a, bsd_name_length);
|
||||||
|
|
||||||
(a->compression_read_consume)(a, bsd_append);
|
/* Store it in the entry. */
|
||||||
|
p = (char *)malloc(bsd_name_length + 1);
|
||||||
fname = (char *)malloc(bsd_append + 1);
|
if (p == NULL) {
|
||||||
if (fname == NULL) {
|
|
||||||
archive_set_error(&a->archive, ENOMEM,
|
archive_set_error(&a->archive, ENOMEM,
|
||||||
"Can't allocate fname buffer");
|
"Can't allocate fname buffer");
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
}
|
}
|
||||||
strncpy(fname, b, bsd_append);
|
strncpy(p, b, bsd_name_length);
|
||||||
fname[bsd_append] = '\0';
|
p[bsd_name_length] = '\0';
|
||||||
archive_entry_copy_pathname(entry, fname);
|
archive_entry_copy_pathname(entry, p);
|
||||||
free(fname);
|
free(p);
|
||||||
fname = NULL;
|
return (ARCHIVE_OK);
|
||||||
|
|
||||||
goto remain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "/" followed by one or more spaces indicate a
|
* "/" is the SVR4/GNU archive symbol table.
|
||||||
* SVR4/GNU archive symbol table.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
if (strncmp(h + AR_name_offset, "/ ", 2) == 0) {
|
if (strcmp(filename, "/") == 0) {
|
||||||
archive_entry_copy_pathname(entry, "/");
|
archive_entry_copy_pathname(entry, "/");
|
||||||
goto remain;
|
/* Parse the time, owner, mode, size fields. */
|
||||||
}
|
r = ar_parse_common_header(ar, entry, h);
|
||||||
/*
|
/* Force the file type to a regular file. */
|
||||||
* "__.SYMDEF" indicates a BSD archive symbol table.
|
archive_entry_set_mode(entry,
|
||||||
*/
|
S_IFREG | (archive_entry_mode(entry) & 0777));
|
||||||
if (strncmp(h + AR_name_offset, "__.SYMDEF", 9) == 0) {
|
return (r);
|
||||||
archive_entry_copy_pathname(entry, "__.SYMDEF");
|
|
||||||
goto remain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Otherwise, the ar_name fields stores the real
|
* "__.SYMDEF" is a BSD archive symbol table.
|
||||||
* 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);
|
if (strcmp(filename, "__.SYMDEF") == 0) {
|
||||||
strncpy(fname, h + AR_name_offset, AR_name_size);
|
archive_entry_copy_pathname(entry, filename);
|
||||||
fname[AR_name_size] = '\0';
|
/* Parse the time, owner, mode, size fields. */
|
||||||
|
return (ar_parse_common_header(ar, entry, h));
|
||||||
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)
|
* Otherwise, this is a standard entry. The filename
|
||||||
*p = '\0';
|
* has already been trimmed as much as possible, based
|
||||||
archive_entry_copy_pathname(entry, fname);
|
* on our current knowledge of the format.
|
||||||
free(fname);
|
*/
|
||||||
fname = NULL;
|
archive_entry_copy_pathname(entry, filename);
|
||||||
|
return (ar_parse_common_header(ar, entry, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
|
||||||
|
const char *h)
|
||||||
|
{
|
||||||
|
uint64_t n;
|
||||||
|
|
||||||
remain:
|
|
||||||
/* Copy remaining header */
|
/* Copy remaining header */
|
||||||
archive_entry_set_mtime(entry,
|
archive_entry_set_mtime(entry,
|
||||||
(time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
|
(time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
|
||||||
@ -398,28 +418,12 @@ remain:
|
|||||||
(gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
|
(gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
|
||||||
archive_entry_set_mode(entry,
|
archive_entry_set_mode(entry,
|
||||||
(mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
|
(mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
|
||||||
nval = ar_atol10(h + AR_size_offset, AR_size_size);
|
n = ar_atol10(h + AR_size_offset, AR_size_size);
|
||||||
|
|
||||||
ar->entry_offset = 0;
|
ar->entry_offset = 0;
|
||||||
ar->entry_padding = nval % 2;
|
ar->entry_padding = n % 2;
|
||||||
|
archive_entry_set_size(entry, n);
|
||||||
/*
|
ar->entry_bytes_remaining = n;
|
||||||
* 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 > nval) {
|
|
||||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
|
||||||
"Truncated input file");
|
|
||||||
return (ARCHIVE_FATAL);
|
|
||||||
}
|
|
||||||
if (bsd_append > 0)
|
|
||||||
nval -= bsd_append;
|
|
||||||
|
|
||||||
archive_entry_set_size(entry, nval);
|
|
||||||
ar->entry_bytes_remaining = nval;
|
|
||||||
|
|
||||||
return (ARCHIVE_OK);
|
return (ARCHIVE_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +445,6 @@ archive_read_format_ar_read_data(struct archive_read *a,
|
|||||||
}
|
}
|
||||||
if (bytes_read < 0)
|
if (bytes_read < 0)
|
||||||
return (ARCHIVE_FATAL);
|
return (ARCHIVE_FATAL);
|
||||||
/* XXX I don't get this. */
|
|
||||||
if (bytes_read > ar->entry_bytes_remaining)
|
if (bytes_read > ar->entry_bytes_remaining)
|
||||||
bytes_read = (ssize_t)ar->entry_bytes_remaining;
|
bytes_read = (ssize_t)ar->entry_bytes_remaining;
|
||||||
*size = bytes_read;
|
*size = bytes_read;
|
||||||
@ -496,7 +499,7 @@ archive_read_format_ar_skip(struct archive_read *a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ar_parse_string_table(struct archive_read *a, struct ar *ar,
|
ar_parse_gnu_filename_table(struct archive_read *a, struct ar *ar,
|
||||||
const void *h, size_t size)
|
const void *h, size_t size)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
@ -550,27 +553,23 @@ bad_string_table:
|
|||||||
return (ARCHIVE_WARN);
|
return (ARCHIVE_WARN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t
|
static uint64_t
|
||||||
ar_atol8(const char *p, unsigned char_cnt)
|
ar_atol8(const char *p, unsigned char_cnt)
|
||||||
{
|
{
|
||||||
int64_t l, limit, last_digit_limit;
|
static const uint64_t max_uint64 = ~(uint64_t)0;
|
||||||
int digit, sign, base;
|
uint64_t l, limit, last_digit_limit;
|
||||||
|
unsigned int digit, base;
|
||||||
|
|
||||||
base = 8;
|
base = 8;
|
||||||
limit = max_int64 / base;
|
limit = max_uint64 / base;
|
||||||
last_digit_limit = max_int64 % base;
|
last_digit_limit = max_uint64 % base;
|
||||||
|
|
||||||
while (*p == ' ' || *p == '\t')
|
while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
|
||||||
p++;
|
p++;
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
p++;
|
|
||||||
} else
|
|
||||||
sign = 1;
|
|
||||||
|
|
||||||
l = 0;
|
l = 0;
|
||||||
digit = *p - '0';
|
digit = *p - '0';
|
||||||
while (digit >= 0 && digit < base && char_cnt-- > 0) {
|
while (*p >= '0' && digit < base && char_cnt-- > 0) {
|
||||||
if (l>limit || (l == limit && digit > last_digit_limit)) {
|
if (l>limit || (l == limit && digit > last_digit_limit)) {
|
||||||
l = max_uint64; /* Truncate on overflow. */
|
l = max_uint64; /* Truncate on overflow. */
|
||||||
break;
|
break;
|
||||||
@ -578,34 +577,25 @@ ar_atol8(const char *p, unsigned char_cnt)
|
|||||||
l = (l * base) + digit;
|
l = (l * base) + digit;
|
||||||
digit = *++p - '0';
|
digit = *++p - '0';
|
||||||
}
|
}
|
||||||
return (sign < 0) ? -l : l;
|
return (l);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static uint64_t
|
||||||
* XXX This is not really correct for negative numbers,
|
|
||||||
* as min_int64_t can never be returned. That one is unused BTW.
|
|
||||||
*/
|
|
||||||
static int64_t
|
|
||||||
ar_atol10(const char *p, unsigned char_cnt)
|
ar_atol10(const char *p, unsigned char_cnt)
|
||||||
{
|
{
|
||||||
int64_t l, limit, last_digit_limit;
|
static const uint64_t max_uint64 = ~(uint64_t)0;
|
||||||
int base, digit, sign;
|
uint64_t l, limit, last_digit_limit;
|
||||||
|
unsigned int base, digit;
|
||||||
|
|
||||||
base = 10;
|
base = 10;
|
||||||
limit = max_int64 / base;
|
limit = max_uint64 / base;
|
||||||
last_digit_limit = max_int64 % base;
|
last_digit_limit = max_uint64 % base;
|
||||||
|
|
||||||
while (*p == ' ' || *p == '\t')
|
while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
|
||||||
p++;
|
p++;
|
||||||
if (*p == '-') {
|
|
||||||
sign = -1;
|
|
||||||
p++;
|
|
||||||
} else
|
|
||||||
sign = 1;
|
|
||||||
|
|
||||||
l = 0;
|
l = 0;
|
||||||
digit = *p - '0';
|
digit = *p - '0';
|
||||||
while (digit >= 0 && digit < base && char_cnt-- > 0) {
|
while (*p >= '0' && digit < base && char_cnt-- > 0) {
|
||||||
if (l > limit || (l == limit && digit > last_digit_limit)) {
|
if (l > limit || (l == limit && digit > last_digit_limit)) {
|
||||||
l = max_uint64; /* Truncate on overflow. */
|
l = max_uint64; /* Truncate on overflow. */
|
||||||
break;
|
break;
|
||||||
@ -613,5 +603,5 @@ ar_atol10(const char *p, unsigned char_cnt)
|
|||||||
l = (l * base) + digit;
|
l = (l * base) + digit;
|
||||||
digit = *++p - '0';
|
digit = *++p - '0';
|
||||||
}
|
}
|
||||||
return (sign < 0) ? -l : l;
|
return (l);
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ archive_write_set_format_ar_svr4(struct archive *_a)
|
|||||||
struct archive_write *a = (struct archive_write *)_a;
|
struct archive_write *a = (struct archive_write *)_a;
|
||||||
int r = __archive_write_set_format_ar(a);
|
int r = __archive_write_set_format_ar(a);
|
||||||
if (r == ARCHIVE_OK) {
|
if (r == ARCHIVE_OK) {
|
||||||
a->archive_format = ARCHIVE_FORMAT_AR_SVR4;
|
a->archive_format = ARCHIVE_FORMAT_AR_GNU;
|
||||||
a->archive_format_name = "ar (GNU/SVR4)";
|
a->archive_format_name = "ar (GNU/SVR4)";
|
||||||
}
|
}
|
||||||
return (r);
|
return (r);
|
||||||
@ -197,7 +197,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, entry is a normal archive member. */
|
/* Otherwise, entry is a normal archive member. */
|
||||||
if (a->archive_format == ARCHIVE_FORMAT_AR_SVR4) {
|
if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) {
|
||||||
/*
|
/*
|
||||||
* SVR4/GNU variant use a "/" to mark then end of the filename,
|
* SVR4/GNU variant use a "/" to mark then end of the filename,
|
||||||
* make it possible to have embedded spaces in the filename.
|
* make it possible to have embedded spaces in the filename.
|
||||||
|
@ -44,6 +44,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
static
|
static
|
||||||
struct { const char *name; int (*setter)(struct archive *); } names[] =
|
struct { const char *name; int (*setter)(struct archive *); } names[] =
|
||||||
{
|
{
|
||||||
|
{ "arbsd", archive_write_set_format_ar_bsd },
|
||||||
|
{ "ar", archive_write_set_format_ar_bsd },
|
||||||
|
{ "argnu", archive_write_set_format_ar_svr4 },
|
||||||
|
{ "arsvr4", archive_write_set_format_ar_svr4 },
|
||||||
{ "cpio", archive_write_set_format_cpio },
|
{ "cpio", archive_write_set_format_cpio },
|
||||||
{ "pax", archive_write_set_format_pax },
|
{ "pax", archive_write_set_format_pax },
|
||||||
{ "posix", archive_write_set_format_pax },
|
{ "posix", archive_write_set_format_pax },
|
||||||
|
@ -64,17 +64,22 @@ DEFINE_TEST(test_read_format_ar)
|
|||||||
assertA(0 == archive_read_support_format_all(a));
|
assertA(0 == archive_read_support_format_all(a));
|
||||||
assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
|
assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
|
||||||
|
|
||||||
/* First we meet the string table */
|
/* Filename table. */
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
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(0 == strcmp("//", archive_entry_pathname(ae)));
|
||||||
assert(40 == archive_entry_size(ae));
|
assertEqualInt(0, archive_entry_mtime(ae));
|
||||||
assertA(40 == archive_read_data(a, buff, 45));
|
assertEqualInt(0, archive_entry_uid(ae));
|
||||||
|
assertEqualInt(0, archive_entry_gid(ae));
|
||||||
|
assertEqualInt(40, archive_entry_size(ae));
|
||||||
|
assertEqualIntA(a, 40, archive_read_data(a, buff, 50));
|
||||||
|
assert(0 == memcmp(buff, "yyytttsssaaafff.o/\nhhhhjjjjkkkkllll.o/\n\n", 40));
|
||||||
|
|
||||||
/* First Entry */
|
/* First Entry */
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
assertA(0 == archive_read_next_header(a, &ae));
|
||||||
assert(0 == strcmp("yyytttsssaaafff.o", archive_entry_pathname(ae)));
|
assert(0 == strcmp("yyytttsssaaafff.o", archive_entry_pathname(ae)));
|
||||||
|
assertEqualInt(1175465652, archive_entry_mtime(ae));
|
||||||
|
assertEqualInt(1001, archive_entry_uid(ae));
|
||||||
|
assertEqualInt(0, archive_entry_gid(ae));
|
||||||
assert(8 == archive_entry_size(ae));
|
assert(8 == archive_entry_size(ae));
|
||||||
assertA(8 == archive_read_data(a, buff, 10));
|
assertA(8 == archive_read_data(a, buff, 10));
|
||||||
assert(0 == memcmp(buff, "55667788", 8));
|
assert(0 == memcmp(buff, "55667788", 8));
|
||||||
@ -82,6 +87,9 @@ DEFINE_TEST(test_read_format_ar)
|
|||||||
/* Second Entry */
|
/* Second Entry */
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
assertA(0 == archive_read_next_header(a, &ae));
|
||||||
assert(0 == strcmp("gghh.o", archive_entry_pathname(ae)));
|
assert(0 == strcmp("gghh.o", archive_entry_pathname(ae)));
|
||||||
|
assertEqualInt(1175465668, archive_entry_mtime(ae));
|
||||||
|
assertEqualInt(1001, archive_entry_uid(ae));
|
||||||
|
assertEqualInt(0, archive_entry_gid(ae));
|
||||||
assert(4 == archive_entry_size(ae));
|
assert(4 == archive_entry_size(ae));
|
||||||
assertA(4 == archive_read_data(a, buff, 10));
|
assertA(4 == archive_read_data(a, buff, 10));
|
||||||
assert(0 == memcmp(buff, "3333", 4));
|
assert(0 == memcmp(buff, "3333", 4));
|
||||||
@ -89,6 +97,9 @@ DEFINE_TEST(test_read_format_ar)
|
|||||||
/* Third Entry */
|
/* Third Entry */
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
assertA(0 == archive_read_next_header(a, &ae));
|
||||||
assert(0 == strcmp("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae)));
|
assert(0 == strcmp("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae)));
|
||||||
|
assertEqualInt(1175465713, archive_entry_mtime(ae));
|
||||||
|
assertEqualInt(1001, archive_entry_uid(ae));
|
||||||
|
assertEqualInt(0, archive_entry_gid(ae));
|
||||||
assert(9 == archive_entry_size(ae));
|
assert(9 == archive_entry_size(ae));
|
||||||
assertA(9 == archive_read_data(a, buff, 9));
|
assertA(9 == archive_read_data(a, buff, 9));
|
||||||
assert(0 == memcmp(buff, "987654321", 9));
|
assert(0 == memcmp(buff, "987654321", 9));
|
||||||
|
@ -46,7 +46,7 @@ DEFINE_TEST(test_write_format_ar)
|
|||||||
assertA(0 == archive_write_set_compression_none(a));
|
assertA(0 == archive_write_set_compression_none(a));
|
||||||
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
|
assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
|
||||||
|
|
||||||
/* write the string table */
|
/* write the filename table */
|
||||||
assert((ae = archive_entry_new()) != NULL);
|
assert((ae = archive_entry_new()) != NULL);
|
||||||
archive_entry_copy_pathname(ae, "//");
|
archive_entry_copy_pathname(ae, "//");
|
||||||
archive_entry_set_size(ae, strlen(strtab));
|
archive_entry_set_size(ae, strlen(strtab));
|
||||||
@ -89,9 +89,10 @@ DEFINE_TEST(test_write_format_ar)
|
|||||||
assertA(0 == archive_read_open_memory(a, buff, used));
|
assertA(0 == archive_read_open_memory(a, buff, used));
|
||||||
|
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
assertA(0 == archive_read_next_header(a, &ae));
|
||||||
|
assertEqualInt(0, archive_entry_mtime(ae));
|
||||||
assert(0 == strcmp("//", archive_entry_pathname(ae)));
|
assert(0 == strcmp("//", archive_entry_pathname(ae)));
|
||||||
assert(strlen(strtab) == archive_entry_size(ae));
|
assertEqualInt(strlen(strtab), archive_entry_size(ae));
|
||||||
assertA(strlen(strtab) == archive_read_data(a, buff2, strlen(strtab)));
|
assertEqualIntA(a, strlen(strtab), archive_read_data(a, buff2, 100));
|
||||||
assert(0 == memcmp(buff2, strtab, strlen(strtab)));
|
assert(0 == memcmp(buff2, strtab, strlen(strtab)));
|
||||||
|
|
||||||
assertA(0 == archive_read_next_header(a, &ae));
|
assertA(0 == archive_read_next_header(a, &ae));
|
||||||
@ -152,7 +153,7 @@ DEFINE_TEST(test_write_format_ar)
|
|||||||
|
|
||||||
assert(0 == archive_read_next_header(a, &ae));
|
assert(0 == archive_read_next_header(a, &ae));
|
||||||
assert(0 == strcmp("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae)));
|
assert(0 == strcmp("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae)));
|
||||||
assert(5 == archive_entry_size(ae));
|
assertEqualInt(5, archive_entry_size(ae));
|
||||||
assertA(5 == archive_read_data(a, buff2, 10));
|
assertA(5 == archive_read_data(a, buff2, 10));
|
||||||
assert(0 == memcmp(buff2, "12345", 5));
|
assert(0 == memcmp(buff2, "12345", 5));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user