]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
zip: Allow backslash as path separator 970/head
authorJohn Starks <jostarks@microsoft.com>
Thu, 16 Nov 2017 00:10:49 +0000 (16:10 -0800)
committerJohn Starks <jostarks@microsoft.com>
Thu, 16 Nov 2017 00:13:04 +0000 (16:13 -0800)
Some Windows zip archivers, such as PowerShell's Compress-Archive, use
backslash as the path separator in violation of the zip specification.
Info-Zip works around this by supporting this for archives that report
"version made by" of 0 (MS-DOS).

This change adds the same workaround to libarchive and adds a test of
this case using a zip file created with PowerShell.

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

index f54a61897c44274ad39754fc347e9fb8173b0959..461402f11054a8097c523876acc8023d6f008eb9 100644 (file)
@@ -681,6 +681,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_compat_zip_5.zip.uu \
        libarchive/test/test_compat_zip_6.zip.uu \
        libarchive/test/test_compat_zip_7.xps.uu \
+       libarchive/test/test_compat_zip_8.zip.uu \
        libarchive/test/test_compat_zstd_1.tar.zst.uu \
        libarchive/test/test_fuzz.cab.uu \
        libarchive/test/test_fuzz.lzh.uu \
index 69e388ead3c664729ed35345e8317715deed5e87..784160070ddebbdfd0d0f62b23d61bd2f9d02ea5 100644 (file)
@@ -886,6 +886,24 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
                zip_entry->mode |= 0664;
        }
 
+       /* Windows archivers sometimes use backslash as the directory separator.
+          Normalize to slash. */
+       if (zip_entry->system == 0 &&
+           (wp = archive_entry_pathname_w(entry)) != NULL) {
+               if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
+                       size_t i;
+                       struct archive_wstring s;
+                       archive_string_init(&s);
+                       archive_wstrcpy(&s, wp);
+                       for (i = 0; i < archive_strlen(&s); i++) {
+                               if (s.s[i] == '\\')
+                                       s.s[i] = '/';
+                       }
+                       archive_entry_copy_pathname_w(entry, s.s);
+                       archive_wstring_free(&s);
+               }
+       }
+
        /* Make sure that entries with a trailing '/' are marked as directories
         * even if the External File Attributes contains bogus values.  If this
         * is not a directory and there is no type, assume regularfile. */
index bb6d92ef498c67bf2909e83876bd7fea4d7fc06e..39152206c1bc6d5a7e6a0e71b7342abe748c67ab 100644 (file)
@@ -422,3 +422,29 @@ DEFINE_TEST(test_compat_zip_7)
        }
        free(p);
 }
+
+/**
+ * A file with backslash path separators instead of slashes.
+ * PowerShell's Compress-Archive cmdlet produces such archives.
+ */
+DEFINE_TEST(test_compat_zip_8)
+{
+       const char *refname = "test_compat_zip_8.zip";
+       struct archive *a;
+       struct archive_entry *ae;
+       void *p;
+       size_t s;
+
+       extract_reference_file(refname);
+       p = slurpfile(&s, refname);
+
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
+       assertEqualIntA(a, ARCHIVE_OK, read_open_memory_minimal(a, p, s, 7));
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       /* This file is in the archive as arc\test */
+       assertEqualString("arc/test", archive_entry_pathname(ae));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+       free(p);
+}
diff --git a/libarchive/test/test_compat_zip_8.zip.uu b/libarchive/test/test_compat_zip_8.zip.uu
new file mode 100644 (file)
index 0000000..316b6f6
--- /dev/null
@@ -0,0 +1,6 @@
+begin 666 test_compat_zip_8.zip\r
+M4$L#!!0````(`%A\;TOY6""D$`````X````(````87)C7'1E<W3[_Z^$(96A\r
+MF*&$@9>!BP$`4$L!`A0`%`````@`6'QO2_E8(*00````#@````@`````````\r
+H`````````````&%R8UQT97-T4$L%!@`````!``$`-@```#8`````````\r
+`\r
+end\r