svn commit: r214905 - in head/lib/libarchive: . test

Tim Kientzle kientzle at FreeBSD.org
Sun Nov 7 03:40:37 UTC 2010


Author: kientzle
Date: Sun Nov  7 03:40:37 2010
New Revision: 214905
URL: http://svn.freebsd.org/changeset/base/214905

Log:
  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

Added:
  head/lib/libarchive/test/test_compat_zip_2.zip.uu   (contents, props changed)
Modified:
  head/lib/libarchive/archive_read_support_format_zip.c
  head/lib/libarchive/test/test_compat_zip.c

Modified: head/lib/libarchive/archive_read_support_format_zip.c
==============================================================================
--- head/lib/libarchive/archive_read_support_format_zip.c	Sun Nov  7 03:26:22 2010	(r214904)
+++ head/lib/libarchive/archive_read_support_format_zip.c	Sun Nov  7 03:40:37 2010	(r214905)
@@ -128,6 +128,7 @@ static int	archive_read_format_zip_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(stru
 		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;
 	}
 
 	/*
@@ -375,6 +383,42 @@ archive_read_format_zip_read_header(stru
 }
 
 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 
 			if (datasize >= 4)
 				zip->gid = archive_le16dec(p + offset + 2);
 			break;
+		case 0x7875:
+			/* Info-Zip Unix Extra Field (type 3) "ux". */
+			break;
 		default:
 			break;
 		}

Modified: head/lib/libarchive/test/test_compat_zip.c
==============================================================================
--- head/lib/libarchive/test/test_compat_zip.c	Sun Nov  7 03:26:22 2010	(r214904)
+++ head/lib/libarchive/test/test_compat_zip.c	Sun Nov  7 03:40:37 2010	(r214905)
@@ -71,10 +71,43 @@ finish:
 #endif
 }
 
+/*
+ * Verify that we skip junk between entries.  The compat_zip_2.zip file
+ * has several bytes of junk between 'file1' and 'file2'.  Such
+ * junk is routinely introduced by some Zip writers when they manipulate
+ * existing zip archives.
+ */
+static void
+test_compat_zip_2(void)
+{
+	char name[] = "test_compat_zip_2.zip";
+	struct archive_entry *ae;
+	struct archive *a;
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
+	extract_reference_file(name);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+	/* Read first entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("file1", archive_entry_pathname(ae));
+
+	/* Read first entry. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("file2", archive_entry_pathname(ae));
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
 
 DEFINE_TEST(test_compat_zip)
 {
 	test_compat_zip_1();
+	test_compat_zip_2();
 }
 
 

Added: head/lib/libarchive/test/test_compat_zip_2.zip.uu
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libarchive/test/test_compat_zip_2.zip.uu	Sun Nov  7 03:40:37 2010	(r214905)
@@ -0,0 +1,10 @@
+$FreeBSD$
+
+begin 644 test_compat_zip_2.zip
+M4$L#!`H``````'V59CT````````````````%````9FEL93$M2E5.2RU02P,$
+M"@``````@95F/<>D!,D&````!@````4```!F:6QE,F9I;&4R"E!+`0(>`PH`
+M`````'V59CT````````````````%``````````````"D at 0````!F:6QE,5!+
+M`0(>`PH``````(&59CW'I`3)!@````8````%``````````````"D at 2D```!F
+::6QE,E!+!08``````@`"`&8```!2````````
+`
+end


More information about the svn-src-head mailing list