From f408e1d38d6cd5f22430d1db5032160e3472e099 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Sun, 7 Nov 2010 03:40:37 +0000 Subject: If the Zip reader doesn't see a PK signature block because there's inter-entry garbage, just scan forward to find the next one. This allows us to handle a lot of Zip archives that have been modified in-place. Thanks to: Gleb Kurtsou for sending me a sample archive --- lib/libarchive/archive_read_support_format_zip.c | 53 ++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'lib/libarchive/archive_read_support_format_zip.c') diff --git a/lib/libarchive/archive_read_support_format_zip.c b/lib/libarchive/archive_read_support_format_zip.c index 4a24cc80930b..0fa1fa391d58 100644 --- a/lib/libarchive/archive_read_support_format_zip.c +++ b/lib/libarchive/archive_read_support_format_zip.c @@ -128,6 +128,7 @@ static int archive_read_format_zip_read_data(struct archive_read *, static int archive_read_format_zip_read_data_skip(struct archive_read *a); static int archive_read_format_zip_read_header(struct archive_read *, struct archive_entry *); +static int search_next_signature(struct archive_read *); static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static int zip_read_data_none(struct archive_read *a, const void **buff, @@ -317,10 +318,17 @@ archive_read_format_zip_read_header(struct archive_read *a, signature = (const char *)h; } + /* If we don't see a PK signature here, scan forward. */ if (signature[0] != 'P' || signature[1] != 'K') { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Bad ZIP file"); - return (ARCHIVE_FATAL); + r = search_next_signature(a); + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Bad ZIP file"); + return (ARCHIVE_FATAL); + } + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) + return (ARCHIVE_FATAL); + signature = (const char *)h; } /* @@ -374,6 +382,42 @@ archive_read_format_zip_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } +static int +search_next_signature(struct archive_read *a) +{ + const void *h; + const char *p, *q; + size_t skip; + ssize_t bytes; + int64_t skipped = 0; + + for (;;) { + h = __archive_read_ahead(a, 4, &bytes); + if (h == NULL) + return (ARCHIVE_FATAL); + p = h; + q = p + bytes; + + while (p + 4 <= q) { + if (p[0] == 'P' && p[1] == 'K') { + if ((p[2] == '\001' && p[3] == '\002') + || (p[2] == '\003' && p[3] == '\004') + || (p[2] == '\005' && p[3] == '\006') + || (p[2] == '\007' && p[3] == '\010') + || (p[2] == '0' && p[3] == '0')) { + skip = p - (const char *)h; + __archive_read_consume(a, skip); + return (ARCHIVE_OK); + } + } + ++p; + } + skip = p - (const char *)h; + __archive_read_consume(a, skip); + skipped += skip; + } +} + static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) @@ -888,6 +932,9 @@ process_extra(const void* extra, struct zip* zip) if (datasize >= 4) zip->gid = archive_le16dec(p + offset + 2); break; + case 0x7875: + /* Info-Zip Unix Extra Field (type 3) "ux". */ + break; default: break; } -- cgit v1.3