]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
If we see junk when we're expecting a 'PK' signature
authorTim Kientzle <kientzle@gmail.com>
Sun, 7 Nov 2010 02:14:21 +0000 (22:14 -0400)
committerTim Kientzle <kientzle@gmail.com>
Sun, 7 Nov 2010 02:14:21 +0000 (22:14 -0400)
block, scan forward to see if we can find a suitable
signature.

This is necessary to read archives that have been modified
by some Zip utilities that update entries in-place without
compacting the entire archive.

Of course, this would be very natural if libarchive
used the Central directory.  But even when libarchive
does support the Central directory, this kind of logic
will still be useful for reading Zip archives in streaming
mode.

SVN-Revision: 2781

libarchive/archive_read_support_format_zip.c
libarchive/test/test_compat_zip.c
libarchive/test/test_compat_zip_2.zip.uu [new file with mode: 0644]

index 8cd1a1f8cbd5f9b29fe64d947f7778d5a671ad72..92a947bc75f42a30a2b8caaeac771e71671d38d6 100644 (file)
@@ -129,6 +129,7 @@ static int  archive_read_format_zip_read_data(struct archive_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, int64_t *offset);
 static int     zip_read_data_none(struct archive_read *a, const void **buff,
@@ -321,10 +322,17 @@ archive_read_format_zip_read_header(struct archive_read *a,
                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;
        }
 
        /*
@@ -378,6 +386,42 @@ archive_read_format_zip_read_header(struct archive_read *a,
        return (ARCHIVE_FATAL);
 }
 
+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)
@@ -924,6 +968,9 @@ process_extra(const void* extra, struct zip* zip)
                        if (datasize >= 4)
                                zip->gid = archive_le16dec(p + offset + 2);
                        break;
+               case 0x7875:
+                       /* Info-Zip Unix Extra Field (type 3) "ux". */
+                       break;
                default:
                        break;
                }
index 1c5747ddf46a4ae53128f228feac682efdd11acd..d7253eda705096f5204691b83d3183730b3dd932 100644 (file)
@@ -67,10 +67,43 @@ finish:
        assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
 
+/*
+ * 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();
 }
 
 
diff --git a/libarchive/test/test_compat_zip_2.zip.uu b/libarchive/test/test_compat_zip_2.zip.uu
new file mode 100644 (file)
index 0000000..f8ada31
--- /dev/null
@@ -0,0 +1,8 @@
+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@0````!F:6QE,5!+
+M`0(>`PH``````(&59CW'I`3)!@````8````%``````````````"D@2D```!F
+::6QE,E!+!08``````@`"`&8```!2````````
+`
+end