]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Read Zip64 EOCD; work around an Info-Zip bug.
authorTim Kientzle <kientzle@acm.org>
Fri, 27 Dec 2013 08:41:48 +0000 (00:41 -0800)
committerTim Kientzle <kientzle@acm.org>
Fri, 27 Dec 2013 08:41:48 +0000 (00:41 -0800)
libarchive/archive_read_support_format_zip.c

index 83e1d97997b4808854904c1595eb5c2431ee3c46..f895b192300f03ee1bbf5953582291f54f1aa336 100644 (file)
@@ -162,9 +162,6 @@ static time_t       zip_time(const char *);
 static const char *compression_name(int compression);
 static void    process_extra(const char *, size_t, struct zip_entry *);
 
-int    archive_read_support_format_zip_streamable(struct archive *);
-int    archive_read_support_format_zip_seekable(struct archive *);
-
 int
 archive_read_support_format_zip_streamable(struct archive *_a)
 {
@@ -302,13 +299,11 @@ read_eocd(struct zip *zip, const char *p, int64_t current_offset)
        /* Sanity-check the EOCD we've found. */
 
        /* This must be the first volume. */
-       if (archive_le16dec(p + 4) != 0) {
+       if (archive_le16dec(p + 4) != 0)
                return 0;
-       }
        /* Central directory must be on this volume. */
-       if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) {
+       if (archive_le16dec(p + 4) != archive_le16dec(p + 6))
                return 0;
-       }
        /* All central directory entries must be on this volume. */
        if (archive_le16dec(p + 10) != archive_le16dec(p + 8))
                return 0;
@@ -328,13 +323,48 @@ read_eocd(struct zip *zip, const char *p, int64_t current_offset)
 }
 
 static int
-read_zip64_eocd(struct zip *zip, const char *p, int64_t current_offset)
+read_zip64_eocd(struct archive_read *a, struct zip *zip,
+    const char *p, int64_t current_offset)
 {
-       (void)zip; /* UNUSED */
-       (void)p; /* UNUSED */
-       (void)current_offset; /* UNUSED */
-       /* Not yet supported. */
-       return 0;
+       int64_t eocd64_offset;
+       int64_t eocd64_size;
+
+       /* Sanity-check the locator record. */
+
+       /* Central dir must be on first volume. */
+       if (archive_le32dec(p + 4) != 0)
+               return 0;
+       /* Must be only a single volume. */
+       if (archive_le32dec(p + 16) != 1)
+               return 0;
+
+       /* Find the Zip64 EOCD record. */
+       eocd64_offset = archive_le64dec(p + 8);
+       current_offset = __archive_read_seek(a, eocd64_offset, SEEK_SET);
+       if (current_offset < 0)
+               return 0;
+       if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
+               return 0;
+       /* Make sure we can read all of it. */
+       eocd64_size = archive_le64dec(p + 4) + 12;
+       if (eocd64_size < 56 || eocd64_size > 16384)
+               return 0;
+       if ((p = __archive_read_ahead(a, eocd64_size, NULL)) == NULL)
+               return 0;
+
+       /* Sanity-check the EOCD64 */
+       if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
+               return 0;
+       if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
+               return 0;
+       /* CD can't be split. */
+       if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
+               return 0;
+
+       /* Save the central directory offset for later use. */
+       zip->central_directory_offset = archive_le64dec(p + 48);
+
+       return 32;
 }
 
 
@@ -364,6 +394,10 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
                return 0;
        if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
                return 0;
+       /* TODO: Rework this to search backwards from the end.  We
+        * normally expect the EOCD record to be at the very end, so
+        * that should be significantly faster.  Tricky part: Make
+        * sure we still prefer the Zip64 locator if it's present. */
        for (i = 0; i <= tail - 22;) {
                switch (p[i + 3]) {
                case 'P': i += 3; break;
@@ -379,7 +413,8 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
                        break;
                case 007:
                        if (memcmp(p + i, "PK\006\007", 4) == 0) {
-                               int ret = read_zip64_eocd(zip, p + i, current_offset + i);
+                               int ret = read_zip64_eocd(a, zip,
+                                   p + i, current_offset + i);
                                if (ret > 0)
                                        return (ret);
                        }
@@ -1189,6 +1224,13 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
        }
        zip_read_consume(a, filename_length);
 
+       /* Work around a bug in Info-Zip: When reading from a pipe, it
+        * stats the pipe instead of synthesizing a file entry. */
+       if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) {
+               zip_entry->mode &= ~ AE_IFMT;
+               zip_entry->mode |= AE_IFREG;
+       }
+
        if ((zip_entry->mode & AE_IFMT) == 0) {
                /* Especially in streaming mode, we can end up
                   here without having seen proper mode information.