]> granicus.if.org Git - python/commitdiff
bpo-34341: Fix appending to ZIP archives with the ZIP64 extension. (GH-8683)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 17 Sep 2018 12:36:40 +0000 (15:36 +0300)
committerGitHub <noreply@github.com>
Mon, 17 Sep 2018 12:36:40 +0000 (15:36 +0300)
Lib/test/test_zipfile.py
Lib/zipfile.py
Misc/NEWS.d/next/Library/2018-08-06-11-01-18.bpo-34341.E0b9p2.rst [new file with mode: 0644]

index 4c6f57c07590885c371b46e6e764d0cc2df4c6e6..7b8922f4e054aebb33cca9dd645b54e8e9ad8755 100644 (file)
@@ -770,6 +770,20 @@ class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
         with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
             self.assertEqual(zipfp.namelist(), ["absolute"])
 
+    def test_append(self):
+        # Test that appending to the Zip64 archive doesn't change
+        # extra fields of existing entries.
+        with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp:
+            zipfp.writestr("strfile", self.data)
+        with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
+            zinfo = zipfp.getinfo("strfile")
+            extra = zinfo.extra
+        with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp:
+            zipfp.writestr("strfile2", self.data)
+        with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
+            zinfo = zipfp.getinfo("strfile")
+            self.assertEqual(zinfo.extra, extra)
+
 @requires_zlib
 class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
                                    unittest.TestCase):
index 7f237783773dc33548b257d4062841a5e4499558..89df90b25209f830ba657c24ffd58888d82c59f9 100644 (file)
@@ -159,6 +159,27 @@ _CD64_NUMBER_ENTRIES_TOTAL = 7
 _CD64_DIRECTORY_SIZE = 8
 _CD64_OFFSET_START_CENTDIR = 9
 
+_EXTRA_FIELD_STRUCT = struct.Struct('<HH')
+
+def _strip_extra(extra, xids):
+    # Remove Extra Fields with specified IDs.
+    unpack = _EXTRA_FIELD_STRUCT.unpack
+    modified = False
+    buffer = []
+    start = i = 0
+    while i + 4 <= len(extra):
+        xid, xlen = unpack(extra[i : i + 4])
+        j = i + 4 + xlen
+        if xid in xids:
+            if i != start:
+                buffer.append(extra[start : i])
+            start = j
+            modified = True
+        i = j
+    if not modified:
+        return extra
+    return b''.join(buffer)
+
 def _check_zipfile(fp):
     try:
         if _EndRecData(fp):
@@ -1819,6 +1840,7 @@ class ZipFile:
             min_version = 0
             if extra:
                 # Append a ZIP64 field to the extra's
+                extra_data = _strip_extra(extra_data, (1,))
                 extra_data = struct.pack(
                     '<HH' + 'Q'*len(extra),
                     1, 8*len(extra), *extra) + extra_data
diff --git a/Misc/NEWS.d/next/Library/2018-08-06-11-01-18.bpo-34341.E0b9p2.rst b/Misc/NEWS.d/next/Library/2018-08-06-11-01-18.bpo-34341.E0b9p2.rst
new file mode 100644 (file)
index 0000000..46a9cde
--- /dev/null
@@ -0,0 +1,2 @@
+Appending to the ZIP archive with the ZIP64 extension no longer grows the
+size of extra fields of existing entries.