ignore padding at beginning or end. Fix Issue #257.
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
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;
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
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
--- /dev/null
+/*-
+ * 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);
+}
+
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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