summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_read_support_format_cpio.c
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2007-12-30 04:58:22 +0000
committerkientzle <kientzle@FreeBSD.org>2007-12-30 04:58:22 +0000
commit2b8395148ff378e52a5e00c0d2ac0123258b759b (patch)
tree743c28601a2502c30676ab2adfbbb665fa3f38b7 /lib/libarchive/archive_read_support_format_cpio.c
parentb1a3a611be764b44dbdd47a17ec5076bcfbe6e88 (diff)
downloadFreeBSD-src-2b8395148ff378e52a5e00c0d2ac0123258b759b.zip
FreeBSD-src-2b8395148ff378e52a5e00c0d2ac0123258b759b.tar.gz
Update libarchive to 2.4.10. This includes a number of improvements
that I've been working on but put off committing until after the RELENG_7 branch, including: * New manpages: cpio.5 mtree.5 * New archive_entry_strmode() * New archive_entry_link_resolver() * New read support: mtree format * Internal API change: read format auction only runs once * Running the auction only once allowed simplifying a lot of bid logic. * Cpio robustness: search for next header after a sync error * Support device nodes on ISO9660 images * Eliminate a lot of unnecessary copies for uncompressed archives * Corrected handling of new GNU --sparse --posix formats * Correctly handle a zero-byte write to a compressed archive * Fixed memory leaks Many of these improvements were motivated by the upcoming bsdcpio front-end. There have also been extensive improvements to the libarchive_test test harness, which I'll commit separately.
Diffstat (limited to 'lib/libarchive/archive_read_support_format_cpio.c')
-rw-r--r--lib/libarchive/archive_read_support_format_cpio.c182
1 files changed, 175 insertions, 7 deletions
diff --git a/lib/libarchive/archive_read_support_format_cpio.c b/lib/libarchive/archive_read_support_format_cpio.c
index 7eccc87..5034764 100644
--- a/lib/libarchive/archive_read_support_format_cpio.c
+++ b/lib/libarchive/archive_read_support_format_cpio.c
@@ -118,6 +118,8 @@ static int archive_read_format_cpio_read_data(struct archive_read *,
static int archive_read_format_cpio_read_header(struct archive_read *,
struct archive_entry *);
static int be4(const unsigned char *);
+static int find_odc_header(struct archive_read *);
+static int find_newc_header(struct archive_read *);
static int header_bin_be(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int header_bin_le(struct archive_read *, struct cpio *,
@@ -126,6 +128,8 @@ static int header_newc(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
static int header_odc(struct archive_read *, struct cpio *,
struct archive_entry *, size_t *, size_t *);
+static int is_octal(const char *, size_t);
+static int is_hex(const char *, size_t);
static int le4(const unsigned char *);
static void record_hardlink(struct cpio *cpio, struct archive_entry *entry);
@@ -161,13 +165,14 @@ archive_read_support_format_cpio(struct archive *_a)
static int
archive_read_format_cpio_bid(struct archive_read *a)
{
- int bid, bytes_read;
+ int bytes_read;
const void *h;
const unsigned char *p;
struct cpio *cpio;
+ int bid;
cpio = (struct cpio *)(a->format->data);
- bid = 0;
+
bytes_read = (a->decompressor->read_ahead)(a, &h, 6);
/* Convert error code into error return. */
if (bytes_read < 0)
@@ -176,6 +181,7 @@ archive_read_format_cpio_bid(struct archive_read *a)
return (-1);
p = (const unsigned char *)h;
+ bid = 0;
if (memcmp(p, "070707", 6) == 0) {
/* ASCII cpio archive (odc, POSIX.1) */
cpio->read_header = header_odc;
@@ -231,7 +237,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
cpio = (struct cpio *)(a->format->data);
r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
- if (r != ARCHIVE_OK)
+ if (r < ARCHIVE_WARN)
return (r);
/* Read name from buffer. */
@@ -266,7 +272,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
/* Detect and record hardlinks to previously-extracted entries. */
record_hardlink(cpio, entry);
- return (ARCHIVE_OK);
+ return (r);
}
static int
@@ -306,6 +312,82 @@ archive_read_format_cpio_read_data(struct archive_read *a,
}
}
+/*
+ * Skip forward to the next cpio newc header by searching for the
+ * 07070[12] string. This should be generalized and merged with
+ * find_odc_header below.
+ */
+static int
+is_hex(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if (*p < '0' || (*p > '9' && *p < 'a') || *p > 'f') {
+ return (0);
+ }
+ ++p;
+ }
+ return (1);
+}
+
+static int
+find_newc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, bytes, skipped = 0;
+
+ for (;;) {
+ bytes = (a->decompressor->read_ahead)(a, &h, 2048);
+ if (bytes < sizeof(struct cpio_newc_header))
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("07070", p, 5) == 0
+ && (p[5] == '1' || p[5] == '2')
+ && is_hex(p, sizeof(struct cpio_newc_header)))
+ return (ARCHIVE_OK);
+
+ /*
+ * Scan ahead until we find something that looks
+ * like an odc header.
+ */
+ while (p + sizeof(struct cpio_newc_header) < q) {
+ switch (p[5]) {
+ case '1':
+ case '2':
+ if (memcmp("07070", p, 5) == 0
+ && is_hex(p, sizeof(struct cpio_newc_header))) {
+ skip = p - (const char *)h;
+ (a->decompressor->consume)(a, skip);
+ skipped += skip;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ (a->decompressor->consume)(a, skip);
+ skipped += skip;
+ }
+}
+
static int
header_newc(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
@@ -313,6 +395,11 @@ header_newc(struct archive_read *a, struct cpio *cpio,
const void *h;
const struct cpio_newc_header *header;
size_t bytes;
+ int r;
+
+ r = find_newc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
/* Read fixed-size portion of header. */
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header));
@@ -357,7 +444,81 @@ header_newc(struct archive_read *a, struct cpio *cpio,
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
/* Pad file contents to a multiple of 4. */
cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
- return (ARCHIVE_OK);
+ return (r);
+}
+
+/*
+ * Skip forward to the next cpio odc header by searching for the
+ * 070707 string. This is a hand-optimized search that could
+ * probably be easily generalized to handle all character-based
+ * cpio variants.
+ */
+static int
+is_octal(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if (*p < '0' || *p > '7')
+ return (0);
+ ++p;
+ }
+ return (1);
+}
+
+static int
+find_odc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, bytes, skipped = 0;
+
+ for (;;) {
+ bytes = (a->decompressor->read_ahead)(a, &h, 512);
+ if (bytes < sizeof(struct cpio_odc_header))
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("070707", p, 6) == 0
+ && is_octal(p, sizeof(struct cpio_odc_header)))
+ return (ARCHIVE_OK);
+
+ /*
+ * Scan ahead until we find something that looks
+ * like an odc header.
+ */
+ while (p + sizeof(struct cpio_odc_header) < q) {
+ switch (p[5]) {
+ case '7':
+ if (memcmp("070707", p, 6) == 0
+ && is_octal(p, sizeof(struct cpio_odc_header))) {
+ skip = p - (const char *)h;
+ (a->decompressor->consume)(a, skip);
+ skipped += skip;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ (a->decompressor->consume)(a, skip);
+ skipped += skip;
+ }
}
static int
@@ -365,12 +526,18 @@ header_odc(struct archive_read *a, struct cpio *cpio,
struct archive_entry *entry, size_t *namelength, size_t *name_pad)
{
const void *h;
+ int r;
const struct cpio_odc_header *header;
size_t bytes;
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
a->archive.archive_format_name = "POSIX octet-oriented cpio";
+ /* Find the start of the next header. */
+ r = find_odc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
/* Read fixed-size portion of header. */
bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header));
if (bytes < sizeof(struct cpio_odc_header))
@@ -400,7 +567,7 @@ header_odc(struct archive_read *a, struct cpio *cpio,
atol8(header->c_filesize, sizeof(header->c_filesize));
archive_entry_set_size(entry, cpio->entry_bytes_remaining);
cpio->entry_padding = 0;
- return (ARCHIVE_OK);
+ return (r);
}
static int
@@ -574,7 +741,7 @@ record_hardlink(struct cpio *cpio, struct archive_entry *entry)
*/
for (le = cpio->links_head; le; le = le->next) {
if (le->dev == dev && le->ino == ino) {
- archive_entry_set_hardlink(entry, le->name);
+ archive_entry_copy_hardlink(entry, le->name);
if (--le->links <= 0) {
if (le->previous != NULL)
@@ -583,6 +750,7 @@ record_hardlink(struct cpio *cpio, struct archive_entry *entry)
le->next->previous = le->previous;
if (cpio->links_head == le)
cpio->links_head = le->next;
+ free(le->name);
free(le);
}
OpenPOWER on IntegriCloud