]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Support Zip64 extra data fields for handling large entries.
authorTim Kientzle <kientzle@acm.org>
Tue, 26 Nov 2013 18:03:01 +0000 (10:03 -0800)
committerTim Kientzle <kientzle@acm.org>
Tue, 26 Nov 2013 18:03:01 +0000 (10:03 -0800)
Process extra data fields for central directory and local file headers
so we get correct full size information in both cases.
Correct central directory vs. local file header sanity check
to compare full size information (including data picked out of
the extra data).

Note:  This does not yet support the Zip64 end-of-central-directory
marker so doesn't correctly handle very large archives.

libarchive/archive_read_support_format_zip.c
libarchive/test/CMakeLists.txt
libarchive/test/test_compat_zip.c
libarchive/test/test_read_format_zip_zip64.c [new file with mode: 0644]
libarchive/test/test_read_format_zip_zip64a.zip.uu [new file with mode: 0644]
libarchive/test/test_read_format_zip_zip64b.zip.uu [new file with mode: 0644]
libarchive/test/test_write_format_zip.c

index a261b35d7bf4caabfce1e4b9207eaaf3f4e43dcf..38c6b31b7e21de543d67dfca1c1facbacca89f55 100644 (file)
@@ -56,7 +56,6 @@ struct zip_entry {
        int64_t                 uncompressed_size;
        int64_t                 gid;
        int64_t                 uid;
-       struct archive_entry    *entry;
        struct archive_string   rsrcname;
        time_t                  mtime;
        time_t                  atime;
@@ -66,6 +65,7 @@ struct zip_entry {
        uint16_t                flags;
        char                    compression;
        char                    system;
+       char                    have_zip64;
 };
 
 struct zip {
@@ -411,6 +411,7 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
 {
        unsigned i;
        int64_t correction;
+       const char *p;
        static const struct archive_rb_tree_ops rb_ops = {
                &cmp_node, &cmp_key
        };
@@ -440,7 +441,7 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
                struct zip_entry *zip_entry = &zip->zip_entries[i];
                size_t filename_length, extra_length, comment_length;
                uint32_t external_attributes;
-               const char *name, *p, *r;
+               const char *name, *r;
 
                if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
                        return ARCHIVE_FATAL;
@@ -526,10 +527,19 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
                   find and read local file header to get the
                   filename) at the cost of requiring a lot of extra
                   space. */
-               /* We don't read the extra block here.  We assume it
-                  will be duplicated at the local file header. */
-               __archive_read_consume(a,
-                   46 + filename_length + extra_length + comment_length);
+               __archive_read_consume(a, 46 + filename_length);
+
+               /* Read and process the extra data. */
+               if ((p = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Truncated ZIP file header");
+                       return (ARCHIVE_FATAL);
+               }
+               process_extra(p, extra_length, zip_entry);
+               __archive_read_consume(a, extra_length);
+
+               /* Skip the comment, if any. */
+               __archive_read_consume(a, comment_length);
        }
 
        return ARCHIVE_OK;
@@ -957,6 +967,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
        size_t len, filename_length, extra_length;
        struct archive_string_conv *sconv;
        struct zip_entry *zip_entry = zip->entry;
+       struct zip_entry zip_entry_original = *zip_entry;
        uint32_t local_crc32;
        int64_t compressed_size, uncompressed_size;
        int ret = ARCHIVE_OK;
@@ -998,42 +1009,6 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
 
        zip_read_consume(a, 30);
 
-       if (zip->have_central_directory) {
-               /* If we read the central dir entry, we must have size
-                * information as well, so ignore the length-at-end flag. */
-               zip_entry->flags &= ~ZIP_LENGTH_AT_END;
-               /* If we have values from both the local file header
-                  and the central directory, warn about mismatches
-                  which might indicate a damaged file.  But some
-                  writers always put zero in the local header; don't
-                  bother warning about that. */
-               if (local_crc32 != 0 && local_crc32 != zip_entry->crc32) {
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Inconsistent CRC32 values");
-                       ret = ARCHIVE_WARN;
-               }
-               if (compressed_size != 0
-                   && compressed_size != zip_entry->compressed_size) {
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Inconsistent compressed size");
-                       ret = ARCHIVE_WARN;
-               }
-               if (uncompressed_size != 0
-                   && uncompressed_size != zip_entry->uncompressed_size) {
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Inconsistent uncompressed size");
-                       ret = ARCHIVE_WARN;
-               }
-       } else {
-               /* If we don't have the CD info, use whatever we do have. */
-               zip_entry->crc32 = local_crc32;
-               zip_entry->compressed_size = compressed_size;
-               zip_entry->uncompressed_size = uncompressed_size;
-       }
-
        /* Read the filename. */
        if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
@@ -1091,9 +1066,9 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
                                zip_entry->mode |= AE_IFREG;
                }
                if (zip_entry->mode == AE_IFDIR) {
-                       zip_entry->mode |= 0777;
+                       zip_entry->mode |= 0775;
                } else if (zip_entry->mode == AE_IFREG) {
-                       zip_entry->mode |= 0666;
+                       zip_entry->mode |= 0664;
                }
        }
 
@@ -1106,6 +1081,49 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
        process_extra(h, extra_length, zip_entry);
        zip_read_consume(a, extra_length);
 
+       if (zip->have_central_directory) {
+               /* If we read the central dir entry, we must have size
+                * information as well, so ignore the length-at-end flag. */
+               zip_entry->flags &= ~ZIP_LENGTH_AT_END;
+               /* If we have values from both the local file header
+                  and the central directory, warn about mismatches
+                  which might indicate a damaged file.  But some
+                  writers always put zero in the local header; don't
+                  bother warning about that. */
+               if (zip_entry->crc32 != 0
+                   && zip_entry->crc32 != zip_entry_original.crc32) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Inconsistent CRC32 values");
+                       ret = ARCHIVE_WARN;
+               }
+               if (zip_entry->compressed_size != 0
+                   && zip_entry->compressed_size != zip_entry_original.compressed_size) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Inconsistent compressed size: "
+                           "%jd in central directory, %jd in local header",
+                           (intmax_t)zip_entry_original.compressed_size,
+                           (intmax_t)zip_entry->compressed_size);
+                       ret = ARCHIVE_WARN;
+               }
+               if (zip_entry->uncompressed_size != 0
+                   && zip_entry->uncompressed_size != zip_entry_original.uncompressed_size) {
+                       archive_set_error(&a->archive,
+                           ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Inconsistent uncompressed size: "
+                           "%jd in central directory, %jd in local header",
+                           (intmax_t)zip_entry_original.uncompressed_size,
+                           (intmax_t)zip_entry->uncompressed_size);
+                       ret = ARCHIVE_WARN;
+               }
+       } else {
+               /* If we don't have the CD info, use whatever we do have. */
+               zip_entry->crc32 = local_crc32;
+               zip_entry->compressed_size = compressed_size;
+               zip_entry->uncompressed_size = uncompressed_size;
+       }
+
        /* Populate some additional entry fields: */
        archive_entry_set_mode(entry, zip_entry->mode);
        archive_entry_set_uid(entry, zip_entry->uid);
@@ -1247,7 +1265,7 @@ archive_read_format_zip_read_data(struct archive_read *a,
                    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
                        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                            "ZIP uncompressed data is wrong size "
-                           "(read %jd, expected %jd)",
+                           "(read %jd, expected %jd)\n",
                            (intmax_t)zip->entry_uncompressed_bytes_read,
                            (intmax_t)zip->entry->uncompressed_size);
                        return (ARCHIVE_WARN);
@@ -1281,9 +1299,10 @@ archive_read_format_zip_read_data(struct archive_read *a,
  * TODO: Technically, the PK\007\010 signature is optional.
  * In the original spec, the data descriptor contained CRC
  * and size fields but had no leading signature.  In practice,
- * newer writers seem to provide the signature pretty consistently,
- * but we might need to do something more complex here if
- * we want to handle older archives that lack that signature.
+ * newer writers seem to provide the signature pretty consistently.
+ *
+ * For uncompressed data, the PK\007\010 marker seems essential
+ * to be sure we've actually seen the end of the entry.
  *
  * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
  * zip->end_of_entry if it consumes all of the data.
@@ -1303,32 +1322,36 @@ zip_read_data_none(struct archive_read *a, const void **_buff,
        if (zip->entry->flags & ZIP_LENGTH_AT_END) {
                const char *p;
 
-               /* Grab at least 16 bytes. */
-               buff = __archive_read_ahead(a, 16, &bytes_avail);
-               if (bytes_avail < 16) {
+               /* Grab at least 24 bytes. */
+               buff = __archive_read_ahead(a, 24, &bytes_avail);
+               if (bytes_avail < 24) {
                        /* Zip archives have end-of-archive markers
                           that are longer than this, so a failure to get at
-                          least 16 bytes really does indicate a truncated
+                          least 24 bytes really does indicate a truncated
                           file. */
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
                            "Truncated ZIP file data");
                        return (ARCHIVE_FATAL);
                }
-               /* Check for a complete PK\007\010 signature. */
+               /* Check for a complete PK\007\010 signature, followed
+                * by the correct 4-byte CRC. */
                p = buff;
                if (p[0] == 'P' && p[1] == 'K' 
                    && p[2] == '\007' && p[3] == '\010'
-                   && archive_le32dec(p + 4) == zip->entry_crc32
-                   && archive_le32dec(p + 8) ==
-                           zip->entry_compressed_bytes_read
-                   && archive_le32dec(p + 12) ==
-                           zip->entry_uncompressed_bytes_read) {
-                       zip->entry->crc32 = archive_le32dec(p + 4);
-                       zip->entry->compressed_size = archive_le32dec(p + 8);
-                       zip->entry->uncompressed_size = archive_le32dec(p + 12);
+                   && archive_le32dec(p + 4) == zip->entry_crc32) {
+                       if (zip->entry->have_zip64) {
+                               zip->entry->crc32 = archive_le32dec(p + 4);
+                               zip->entry->compressed_size = archive_le64dec(p + 8);
+                               zip->entry->uncompressed_size = archive_le64dec(p + 16);
+                               zip->unconsumed = 24;
+                       } else {
+                               zip->entry->crc32 = archive_le32dec(p + 4);
+                               zip->entry->compressed_size = archive_le32dec(p + 8);
+                               zip->entry->uncompressed_size = archive_le32dec(p + 12);
+                               zip->unconsumed = 16;
+                       }
                        zip->end_of_entry = 1;
-                       zip->unconsumed = 16;
                        return (ARCHIVE_OK);
                }
                /* If not at EOF, ensure we consume at least one byte. */
@@ -1488,7 +1511,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
        if (zip->end_of_entry && (zip->entry->flags & ZIP_LENGTH_AT_END)) {
                const char *p;
 
-               if (NULL == (p = __archive_read_ahead(a, 16, NULL))) {
+               if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
                        archive_set_error(&a->archive,
                            ARCHIVE_ERRNO_FILE_FORMAT,
                            "Truncated ZIP end-of-file record");
@@ -1497,10 +1520,19 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
                /* Consume the optional PK\007\010 marker. */
                if (p[0] == 'P' && p[1] == 'K' &&
                    p[2] == '\007' && p[3] == '\010') {
-                       zip->entry->crc32 = archive_le32dec(p + 4);
-                       zip->entry->compressed_size = archive_le32dec(p + 8);
-                       zip->entry->uncompressed_size = archive_le32dec(p + 12);
-                       zip->unconsumed = 16;
+                       p += 4;
+                       zip->unconsumed = 4;
+               }
+               if (zip->entry->have_zip64) {
+                       zip->entry->crc32 = archive_le32dec(p);
+                       zip->entry->compressed_size = archive_le64dec(p + 4);
+                       zip->entry->uncompressed_size = archive_le64dec(p + 12);
+                       zip->unconsumed += 20;
+               } else {
+                       zip->entry->crc32 = archive_le32dec(p);
+                       zip->entry->compressed_size = archive_le32dec(p + 4);
+                       zip->entry->uncompressed_size = archive_le32dec(p + 8);
+                       zip->unconsumed += 12;
                }
        }
 
@@ -1568,7 +1600,10 @@ archive_read_format_zip_read_data_skip(struct archive_read *a)
                                else if (p[3] == '\007') { p += 1; }
                                else if (p[3] == '\010' && p[2] == '\007'
                                    && p[1] == 'K' && p[0] == 'P') {
-                                       zip_read_consume(a, p - buff + 16);
+                                       if (zip->entry->have_zip64)
+                                               zip_read_consume(a, p - buff + 24);
+                                       else
+                                               zip_read_consume(a, p - buff + 16);
                                        return ARCHIVE_OK;
                                } else { p += 4; }
                        }
@@ -1624,14 +1659,34 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry)
                switch (headerid) {
                case 0x0001:
                        /* Zip64 extended information extra field. */
-                       if (datasize >= 8 &&
-                           zip_entry->uncompressed_size == 0xffffffff)
+                       zip_entry->have_zip64 = 1;
+                       if (zip_entry->uncompressed_size == 0xffffffff) {
+                               if (datasize < 8)
+                                       break;
                                zip_entry->uncompressed_size =
                                    archive_le64dec(p + offset);
-                       if (datasize >= 16 &&
-                           zip_entry->compressed_size == 0xffffffff)
+                               offset += 8;
+                               datasize -= 8;
+                       }
+                       if (zip_entry->compressed_size == 0xffffffff) {
+                               if (datasize < 8)
+                                       break;
                                zip_entry->compressed_size =
-                                   archive_le64dec(p + offset + 8);
+                                   archive_le64dec(p + offset);
+                               offset += 8;
+                               datasize -= 8;
+                       }
+                       if (zip_entry->local_header_offset == 0xffffffff) {
+                               if (datasize < 8)
+                                       break;
+                               zip_entry->local_header_offset =
+                                   archive_le64dec(p + offset);
+                               offset += 8;
+                               datasize -= 8;
+                       }
+                       /* archive_le32dec(p + offset) gives disk
+                        * on which file starts, but we don't handle
+                        * multi-volume Zip files. */
                        break;
                case 0x5455:
                {
index 55de0ca65fb1d41598fedd02b29e695595bee44e..6bbabd121927bdc59cace90f94cdb9904c61eed1 100644 (file)
@@ -146,6 +146,7 @@ IF(ENABLE_TEST)
     test_read_format_zip_nofiletype.c
     test_read_format_zip_padded.c
     test_read_format_zip_sfx.c
+    test_read_format_zip_zip64.c
     test_read_large.c
     test_read_pax_truncated.c
     test_read_position.c
index 82cd023c09e8474081d1a3c745c51f719e954bcd..c55d82abd48481959d9ff6f585ecc91f19a37179 100644 (file)
@@ -238,19 +238,19 @@ test_compat_zip_5(void)
        assertEqualString("Metadata/Job_PT.xml", archive_entry_pathname(ae));
        assertEqualInt(3559, archive_entry_size(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
 
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualString("Metadata/MXDC_Empty_PT.xml", archive_entry_pathname(ae));
        assertEqualInt(456, archive_entry_size(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
 
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualString("Documents/1/Metadata/Page1_Thumbnail.JPG", archive_entry_pathname(ae));
        assertEqualInt(1495, archive_entry_size(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
        /* TODO: Read some of the file data and verify it.
           The code to read uncompressed Zip entries with "file at end" semantics
           is tricky and should be verified more carefully. */
@@ -298,21 +298,21 @@ test_compat_zip_5(void)
        assertEqualInt(0, archive_entry_size(ae));
        assert(!archive_entry_size_is_set(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
 
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualString("Metadata/MXDC_Empty_PT.xml", archive_entry_pathname(ae));
        assertEqualInt(0, archive_entry_size(ae));
        assert(!archive_entry_size_is_set(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
 
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualString("Documents/1/Metadata/Page1_Thumbnail.JPG", archive_entry_pathname(ae));
        assertEqualInt(0, archive_entry_size(ae));
        assert(!archive_entry_size_is_set(ae));
        assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
-       assertEqualInt(0666, archive_entry_perm(ae));
+       assertEqualInt(0664, archive_entry_perm(ae));
 
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualString("Documents/1/Pages/_rels/1.fpage.rels", archive_entry_pathname(ae));
diff --git a/libarchive/test/test_read_format_zip_zip64.c b/libarchive/test/test_read_format_zip_zip64.c
new file mode 100644 (file)
index 0000000..2dc4040
--- /dev/null
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 2013 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.
+ * 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$");
+
+/*
+ * Sample file was created with:
+ *    echo file0 | zip
+ * Info-Zip uses Zip64 extensions in this case.
+ */
+static void
+verify_file0_seek(struct archive *a)
+{
+       char *p;
+       size_t s;
+       struct archive_entry *ae;
+       char data[16];
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString("-", archive_entry_pathname(ae));
+       assertEqualInt(AE_IFREG | 0660, archive_entry_mode(ae));
+       assertEqualInt(6, archive_entry_size(ae));
+       assertEqualIntA(a, 6, archive_read_data(a, data, 16));
+       assertEqualMem(data, "file0\x0a", 6);
+
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+verify_file0_stream(struct archive *a)
+{
+       char *p;
+       size_t s;
+       struct archive_entry *ae;
+       char data[16];
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString("-", archive_entry_pathname(ae));
+       assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
+       assertEqualInt(0, archive_entry_size_is_set(ae));
+       assertEqualIntA(a, 6, archive_read_data(a, data, 16));
+       assertEqualMem(data, "file0\x0a", 6);
+
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_zip_zip64a)
+{
+       const char *refname = "test_read_format_zip_zip64a.zip";
+       struct archive *a;
+       char *p;
+       size_t s;
+
+       extract_reference_file(refname);
+       p = slurpfile(&s, refname);
+
+       /* First read with seeking. */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a));
+       assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1));
+       verify_file0_seek(a);
+
+       /* Then read streaming. */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_streamable(a));
+       assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1));
+       verify_file0_stream(a);
+       free(p);
+}
+
+DEFINE_TEST(test_read_format_zip_zip64b)
+{
+       const char *refname = "test_read_format_zip_zip64b.zip";
+       struct archive *a;
+       char *p;
+       size_t s;
+
+       extract_reference_file(refname);
+       p = slurpfile(&s, refname);
+
+       /* First read with seeking. */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a));
+       assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1));
+       verify_file0_seek(a);
+
+       /* Then read streaming. */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_streamable(a));
+       assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1));
+       verify_file0_stream(a);
+       free(p);
+}
+
diff --git a/libarchive/test/test_read_format_zip_zip64a.zip.uu b/libarchive/test/test_read_format_zip_zip64a.zip.uu
new file mode 100644 (file)
index 0000000..ae8764c
--- /dev/null
@@ -0,0 +1,7 @@
+begin 644 test_read_format_zip_zip64a.zip
+M4$L#!"T`"``(`$]Y>$,`````__________\!`!0`+0$`$```````````````
+M````````2\O,237@`@!02P<(1<8R^P@`````````!@````````!02P$"'@,M
+M``@`"`!/>7A#1<8R^P@````&`````0`````````!````L($`````+5!+!08`
+1`````0`!`"\```!3````````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_zip64b.zip.uu b/libarchive/test/test_read_format_zip_zip64b.zip.uu
new file mode 100644 (file)
index 0000000..99bef74
--- /dev/null
@@ -0,0 +1,7 @@
+begin 644 test_read_format_zip_zip64b.zip
+M4$L#!"T`"``(`$]Y>$,`````_____P8````!``P`+0$`"````````````$O+
+MS$DUX`(`4$L'"$7&,OL(``````````8`````````4$L!`AX#+0`(``@`3WEX
+M0T7&,OL(````!@````$``````````0```+"!`````"U02P4&``````$``0`O
+)````2P``````
+`
+end
index 53ce3acc853756166611191b0cc40ffdcd827610..6f1ec5c3eda5dede5fae94198f432b68b95fe1b3 100644 (file)
@@ -91,7 +91,7 @@ verify_contents(struct archive *a, int expect_details)
                assertEqualInt(0, archive_entry_size(ae));
                assertEqualString("file1", archive_entry_symlink(ae));
        } else {
-               assertEqualInt(AE_IFREG | 0666, archive_entry_mode(ae));
+               assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
                assertEqualInt(0, archive_entry_size(ae));
        }