]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add tests to verify that the seeking Zip reader can correctly
authorTim Kientzle <kientzle@acm.org>
Sun, 24 Nov 2013 00:01:11 +0000 (16:01 -0800)
committerTim Kientzle <kientzle@acm.org>
Sun, 24 Nov 2013 00:01:11 +0000 (16:01 -0800)
ignore padding at beginning or end.  Fix Issue #257.

libarchive/archive_read_support_format_zip.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_format_zip_padded.c [new file with mode: 0644]
libarchive/test/test_read_format_zip_padded1.zip.uu [new file with mode: 0644]
libarchive/test/test_read_format_zip_padded2.zip.uu [new file with mode: 0644]
libarchive/test/test_read_format_zip_padded3.zip.uu [new file with mode: 0644]

index c9fae7f24b84d68ad197e0645e131096aac0d4d3..e6b9c76902dec8ad659f35b1213c4154a9490e86 100644 (file)
@@ -245,7 +245,7 @@ static int
 archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
 {
        struct zip *zip = (struct zip *)a->format->data;
-       int64_t filesize;
+       int64_t end_of_central_directory_offset;
        const char *p;
 
        /* If someone has already bid more than 32, then avoid
@@ -253,46 +253,46 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
        if (best_bid > 32)
                return (-1);
 
-       filesize = __archive_read_seek(a, -22, SEEK_END);
+       /* end-of-central-directory record is 22 bytes; first check
+        * for it at very end of file. */
+       end_of_central_directory_offset = __archive_read_seek(a, -22, SEEK_END);
        /* If we can't seek, then we can't bid. */
-       if (filesize <= 0)
+       if (end_of_central_directory_offset <= 0)
                return 0;
 
-       /* TODO: More robust search for end of central directory record. */
        if ((p = __archive_read_ahead(a, 22, NULL)) == NULL)
                return 0;
        /* First four bytes are signature for end of central directory
-          record.  Four zero bytes ensure this isn't a multi-volume
-          Zip file (which we don't yet support). */
+        * record.  Four zero bytes ensure this isn't a multi-volume
+        * Zip file (which we don't yet support). */
        if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0) {
                int64_t i, tail;
                int found;
 
                /*
-                * If there is a comment in end of central directory
-                * record, 22 bytes are too short. we have to read more
-                * to properly detect the record. Hopefully, a length
-                * of the comment is not longer than 16362 bytes(16K-22).
+                * End-of-central-directory isn't exactly at end of file.
+                * Try reading last 16k of file and search for it.
                 */
-               if (filesize + 22 > 1024 * 16) {
+               if (end_of_central_directory_offset + 22 > 1024 * 16) {
                        tail = 1024 * 16;
-                       filesize = __archive_read_seek(a, tail * -1, SEEK_END);
+                       end_of_central_directory_offset
+                           = __archive_read_seek(a, tail * -1, SEEK_END);
                } else {
-                       tail = filesize + 22;
-                       filesize = __archive_read_seek(a, 0, SEEK_SET);
+                       tail = end_of_central_directory_offset + 22;
+                       end_of_central_directory_offset
+                           = __archive_read_seek(a, 0, SEEK_SET);
                }
-               if (filesize < 0)
+               if (end_of_central_directory_offset < 0)
                        return 0;
                if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
                        return 0;
-               for (found = 0, i = 0;!found && i < tail - 22;) {
+               for (found = 0, i = 0; !found && i < tail - 22;) {
                        switch (p[i]) {
                        case 'P':
                                if (memcmp(p+i,
                                    "PK\005\006\000\000\000\000", 8) == 0) {
                                        p += i;
-                                       filesize += tail -
-                                           (22 + archive_le16dec(p+20));
+                                       end_of_central_directory_offset += i;
                                        found = 1;
                                } else
                                        i += 8;
@@ -313,14 +313,14 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
        zip->central_directory_entries = archive_le16dec(p + 10);
        zip->central_directory_size = archive_le32dec(p + 12);
        zip->central_directory_offset = archive_le32dec(p + 16);
-       zip->end_of_central_directory_offset = filesize;
+       zip->end_of_central_directory_offset = end_of_central_directory_offset;
 
        /* Just one volume, so central dir must all be on this volume. */
        if (zip->central_directory_entries != archive_le16dec(p + 8))
                return 0;
-       /* Central directory can't extend beyond end of this file. */
+       /* Central directory can't extend beyond start of EOCD record. */
        if (zip->central_directory_offset +
-           (int64_t)zip->central_directory_size > filesize)
+           (int64_t)zip->central_directory_size > end_of_central_directory_offset)
                return 0;
 
        /* This is just a tiny bit higher than the maximum returned by
index dadea4c6917afa69ecebb5cb894745378f34dd9a..560e7ff4c99c6916ee256e4bbefbc64e25d4c750 100644 (file)
@@ -143,6 +143,7 @@ IF(ENABLE_TEST)
     test_read_format_zip_comment_stored.c
     test_read_format_zip_filename.c
     test_read_format_zip_mac_metadata.c
+    test_read_format_zip_padded.c
     test_read_format_zip_sfx.c
     test_read_large.c
     test_read_pax_truncated.c
diff --git a/libarchive/test/test_read_format_zip_padded.c b/libarchive/test/test_read_format_zip_padded.c
new file mode 100644 (file)
index 0000000..dae88ab
--- /dev/null
@@ -0,0 +1,90 @@
+/*-
+ * 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$");
+
+static void
+verify_padded_archive(const char *refname)
+{
+       char *p;
+       size_t s;
+       struct archive *a;
+       struct archive_entry *ae;
+
+       extract_reference_file(refname);
+       p = slurpfile(&s, refname);
+
+       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));
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString("file0", archive_entry_pathname(ae));
+       assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae));
+       assertEqualInt(6, archive_entry_size(ae));
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString("file1", archive_entry_pathname(ae));
+       assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae));
+       assertEqualInt(6, archive_entry_size(ae));
+
+       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));
+}
+
+/*
+ * Read a zip file with padding in front.
+ * This is technically a malformed file, since the
+ * internal file offsets aren't adjusted to account
+ * for the added padding.  Unfortunately, some SFX
+ * creators do almost exactly this:
+ */
+DEFINE_TEST(test_read_format_zip_padded1)
+{
+       const char *refname = "test_read_format_zip_padded1.zip";
+       verify_padded_archive(refname);
+}
+
+/*
+ * Read a zip file with padding at end.
+ * Small amounts of end padding should just be ignored by the reader.
+ * (If there's too much, the reader won't find the central directory.)
+ */
+DEFINE_TEST(test_read_format_zip_padded2)
+{
+       const char *refname = "test_read_format_zip_padded2.zip";
+       verify_padded_archive(refname);
+}
+
+/*
+ * Read a zip file with padding at front and end.
+ */
+DEFINE_TEST(test_read_format_zip_padded3)
+{
+       const char *refname = "test_read_format_zip_padded3.zip";
+       verify_padded_archive(refname);
+}
+
diff --git a/libarchive/test/test_read_format_zip_padded1.zip.uu b/libarchive/test/test_read_format_zip_padded1.zip.uu
new file mode 100644 (file)
index 0000000..8cc3f41
--- /dev/null
@@ -0,0 +1,12 @@
+begin 644 test_read_format_zip_padded1.zip
+MBG[*5O,EN^?);45X-!:9?HM<L&-"$R%P5;$U[1.2WOE6X-QPZG5+AYE[P12?
+M"FRK!PH4+B,W<X.9)#HL;-A0&8G-R01RP!F$(04\L?C').!2[]D@SQO4OMU0
+M2P,$"@``````]WEW0T7&,OL&````!@````4`'`!F:6QE,%54"0`#(3>14B$W
+MD5)U>`L``03U`0``!!0```!F:6QE,`I02P,$"@``````^'EW0P3W*>(&````
+M!@````4`'`!F:6QE,554"0`#)#>14B0WD5)U>`L``03U`0``!!0```!F:6QE
+M,0I02P$"'@,*``````#W>7=#1<8R^P8````&````!0`8```````!````I($`
+M````9FEL93!55`4``R$WD5)U>`L``03U`0``!!0```!02P$"'@,*``````#X
+M>7=#!/<IX@8````&````!0`8```````!````I(%%````9FEL93%55`4``R0W
+GD5)U>`L``03U`0``!!0```!02P4&``````(``@"6````B@``````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_padded2.zip.uu b/libarchive/test/test_read_format_zip_padded2.zip.uu
new file mode 100644 (file)
index 0000000..3b57fe1
--- /dev/null
@@ -0,0 +1,15 @@
+begin 644 test_read_format_zip_padded2.zip
+M4$L#!`H``````/=Y=T-%QC+[!@````8````%`!P`9FEL93!55`D``R$WD5(A
+M-Y%2=7@+``$$]0$```04````9FEL93`*4$L#!`H``````/AY=T,$]RGB!@``
+M``8````%`!P`9FEL93%55`D``R0WD5(D-Y%2=7@+``$$]0$```04````9FEL
+M93$*4$L!`AX#"@``````]WEW0T7&,OL&````!@````4`&````````0```*2!
+M`````&9I;&4P550%``,A-Y%2=7@+``$$]0$```04````4$L!`AX#"@``````
+M^'EW0P3W*>(&````!@````4`&````````0```*2!10```&9I;&4Q550%``,D
+M-Y%2=7@+``$$]0$```04````4$L%!@`````"``(`E@```(H``````(.2J;)Q
+M[PX@J`9,J[C2AH>N<8_.US0'*2[BOW;O(NTEX5WU&F@8Z'+:Q]HTV@/M"D"9
+MPM-+&=&VJIL(Z1VGT?_2.G?,\!@N<R9]00R%?$2$)D],0%;E,M9/VHBL6L.2
+M95+G0_?,<4=]7\F\P3):J;8<^XD;T=4FG0DBZF[8@1<*@EBQWFF5X[5$+),9
+M?G^M&F[\EP_0Y($XR!8-B[.P/%X/O5+NHDZW]/B#<D3LG@&N)!@9P?Q7<JW^
+$*#^T_@``
+`
+end
diff --git a/libarchive/test/test_read_format_zip_padded3.zip.uu b/libarchive/test/test_read_format_zip_padded3.zip.uu
new file mode 100644 (file)
index 0000000..198ff7a
--- /dev/null
@@ -0,0 +1,17 @@
+begin 644 test_read_format_zip_padded3.zip
+MKN@)Y\;8.^`2RL>+_"U8I!K>[,)F8(HA0[3P7N"0[A;4]3QYC6?U,X[!@NM%
+MOQJNIW"H3++]CW52#?CS&A=VU8&T:-YPI'J&>3^,1.F=97>R\P]*YF).R*I0
+M2P,$"@``````]WEW0T7&,OL&````!@````4`'`!F:6QE,%54"0`#(3>14B$W
+MD5)U>`L``03U`0``!!0```!F:6QE,`I02P,$"@``````^'EW0P3W*>(&````
+M!@````4`'`!F:6QE,554"0`#)#>14B0WD5)U>`L``03U`0``!!0```!F:6QE
+M,0I02P$"'@,*``````#W>7=#1<8R^P8````&````!0`8```````!````I($`
+M````9FEL93!55`4``R$WD5)U>`L``03U`0``!!0```!02P$"'@,*``````#X
+M>7=#!/<IX@8````&````!0`8```````!````I(%%````9FEL93%55`4``R0W
+MD5)U>`L``03U`0``!!0```!02P4&``````(``@"6````B@``````D$,:4/1\
+MSHGT7I+41*.O21/O.+Z%^`_>)S-WGU>>J4G<M@[R9/6.-3MH@=5NJL1B-YF)
+MS`#V4J829[9>0Y?5'GJ1PMY->*=#3Y+U(_A)OCL1N.):>IH8%T>Y$?&=@(K;
+M(+HD_4?R.6`$<QHBNX.@EX3C$S(W=4-7(P5EH@`[:`M'_=IS(*H<1"7>;@@+
+M12?TRT^S)&!1W*^J06(&KL+*+>]_YL$5K0Q:G9J,1!%[\Q"5;/MD-K-YJW<H
+#'$O6
+`
+end