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...@0````!f:6QE,5!+
+M`0(>`PH``````(&59CW'I`3)!...@````8````%``````````````"d...@2d```!f
+::6QE,e!+!08```...@`"`&8```!2````````
+`
+end
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to