Issue #21866: ZipFile.close() no longer writes ZIP64 central directory
records if allowZip64 is false.
This commit is contained in:
parent
8aa8c47db2
commit
cfbb394488
|
@ -18,7 +18,7 @@ import sys
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from tempfile import TemporaryFile
|
from tempfile import TemporaryFile
|
||||||
|
|
||||||
from test.support import TESTFN, run_unittest, requires_zlib
|
from test.support import TESTFN, requires_zlib
|
||||||
|
|
||||||
TESTFN2 = TESTFN + "2"
|
TESTFN2 = TESTFN + "2"
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class OtherTests(unittest.TestCase):
|
||||||
def testMoreThan64kFiles(self):
|
def testMoreThan64kFiles(self):
|
||||||
# This test checks that more than 64k files can be added to an archive,
|
# This test checks that more than 64k files can be added to an archive,
|
||||||
# and that the resulting archive can be read properly by ZipFile
|
# and that the resulting archive can be read properly by ZipFile
|
||||||
zipf = zipfile.ZipFile(TESTFN, mode="w", allowZip64=False)
|
zipf = zipfile.ZipFile(TESTFN, mode="w", allowZip64=True)
|
||||||
zipf.debug = 100
|
zipf.debug = 100
|
||||||
numfiles = (1 << 16) * 3//2
|
numfiles = (1 << 16) * 3//2
|
||||||
for i in range(numfiles):
|
for i in range(numfiles):
|
||||||
|
@ -105,14 +105,47 @@ class OtherTests(unittest.TestCase):
|
||||||
for i in range(numfiles):
|
for i in range(numfiles):
|
||||||
content = zipf2.read("foo%08d" % i).decode('ascii')
|
content = zipf2.read("foo%08d" % i).decode('ascii')
|
||||||
self.assertEqual(content, "%d" % (i**3 % 57))
|
self.assertEqual(content, "%d" % (i**3 % 57))
|
||||||
|
zipf2.close()
|
||||||
|
|
||||||
|
def testMoreThan64kFilesAppend(self):
|
||||||
|
zipf = zipfile.ZipFile(TESTFN, mode="w", allowZip64=False)
|
||||||
|
zipf.debug = 100
|
||||||
|
numfiles = (1 << 16) - 1
|
||||||
|
for i in range(numfiles):
|
||||||
|
zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles)
|
||||||
|
with self.assertRaises(zipfile.LargeZipFile):
|
||||||
|
zipf.writestr("foo%08d" % numfiles, b'')
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles)
|
||||||
zipf.close()
|
zipf.close()
|
||||||
|
|
||||||
|
zipf = zipfile.ZipFile(TESTFN, mode="a", allowZip64=False)
|
||||||
|
zipf.debug = 100
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles)
|
||||||
|
with self.assertRaises(zipfile.LargeZipFile):
|
||||||
|
zipf.writestr("foo%08d" % numfiles, b'')
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles)
|
||||||
|
zipf.close()
|
||||||
|
|
||||||
|
zipf = zipfile.ZipFile(TESTFN, mode="a", allowZip64=True)
|
||||||
|
zipf.debug = 100
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles)
|
||||||
|
numfiles2 = (1 << 16) * 3//2
|
||||||
|
for i in range(numfiles, numfiles2):
|
||||||
|
zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
|
||||||
|
self.assertEqual(len(zipf.namelist()), numfiles2)
|
||||||
|
zipf.close()
|
||||||
|
|
||||||
|
zipf2 = zipfile.ZipFile(TESTFN, mode="r")
|
||||||
|
self.assertEqual(len(zipf2.namelist()), numfiles2)
|
||||||
|
for i in range(numfiles2):
|
||||||
|
content = zipf2.read("foo%08d" % i).decode('ascii')
|
||||||
|
self.assertEqual(content, "%d" % (i**3 % 57))
|
||||||
|
zipf2.close()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
support.unlink(TESTFN)
|
support.unlink(TESTFN)
|
||||||
support.unlink(TESTFN2)
|
support.unlink(TESTFN2)
|
||||||
|
|
||||||
def test_main():
|
|
||||||
run_unittest(TestsWithSourceFile, OtherTests)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
unittest.main()
|
||||||
|
|
|
@ -50,7 +50,7 @@ error = BadZipfile = BadZipFile # Pre-3.2 compatibility names
|
||||||
|
|
||||||
|
|
||||||
ZIP64_LIMIT = (1 << 31) - 1
|
ZIP64_LIMIT = (1 << 31) - 1
|
||||||
ZIP_FILECOUNT_LIMIT = 1 << 16
|
ZIP_FILECOUNT_LIMIT = (1 << 16) - 1
|
||||||
ZIP_MAX_COMMENT = (1 << 16) - 1
|
ZIP_MAX_COMMENT = (1 << 16) - 1
|
||||||
|
|
||||||
# constants for Zip file compression methods
|
# constants for Zip file compression methods
|
||||||
|
@ -1304,13 +1304,17 @@ class ZipFile:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Attempt to write ZIP archive that was already closed")
|
"Attempt to write ZIP archive that was already closed")
|
||||||
_check_compression(zinfo.compress_type)
|
_check_compression(zinfo.compress_type)
|
||||||
if zinfo.file_size > ZIP64_LIMIT:
|
if not self._allowZip64:
|
||||||
if not self._allowZip64:
|
requires_zip64 = None
|
||||||
raise LargeZipFile("Filesize would require ZIP64 extensions")
|
if len(self.filelist) >= ZIP_FILECOUNT_LIMIT:
|
||||||
if zinfo.header_offset > ZIP64_LIMIT:
|
requires_zip64 = "Files count"
|
||||||
if not self._allowZip64:
|
elif zinfo.file_size > ZIP64_LIMIT:
|
||||||
raise LargeZipFile(
|
requires_zip64 = "Filesize"
|
||||||
"Zipfile size would require ZIP64 extensions")
|
elif zinfo.header_offset > ZIP64_LIMIT:
|
||||||
|
requires_zip64 = "Zipfile size"
|
||||||
|
if requires_zip64:
|
||||||
|
raise LargeZipFile(requires_zip64 +
|
||||||
|
" would require ZIP64 extensions")
|
||||||
|
|
||||||
def write(self, filename, arcname=None, compress_type=None):
|
def write(self, filename, arcname=None, compress_type=None):
|
||||||
"""Put the bytes from filename into the archive under the name
|
"""Put the bytes from filename into the archive under the name
|
||||||
|
@ -1464,10 +1468,8 @@ class ZipFile:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.mode in ("w", "a") and self._didModify: # write ending records
|
if self.mode in ("w", "a") and self._didModify: # write ending records
|
||||||
count = 0
|
|
||||||
pos1 = self.fp.tell()
|
pos1 = self.fp.tell()
|
||||||
for zinfo in self.filelist: # write central directory
|
for zinfo in self.filelist: # write central directory
|
||||||
count = count + 1
|
|
||||||
dt = zinfo.date_time
|
dt = zinfo.date_time
|
||||||
dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
|
dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
|
||||||
dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
|
dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
|
||||||
|
@ -1531,13 +1533,21 @@ class ZipFile:
|
||||||
|
|
||||||
pos2 = self.fp.tell()
|
pos2 = self.fp.tell()
|
||||||
# Write end-of-zip-archive record
|
# Write end-of-zip-archive record
|
||||||
centDirCount = count
|
centDirCount = len(self.filelist)
|
||||||
centDirSize = pos2 - pos1
|
centDirSize = pos2 - pos1
|
||||||
centDirOffset = pos1
|
centDirOffset = pos1
|
||||||
if (centDirCount >= ZIP_FILECOUNT_LIMIT or
|
requires_zip64 = None
|
||||||
centDirOffset > ZIP64_LIMIT or
|
if centDirCount > ZIP_FILECOUNT_LIMIT:
|
||||||
centDirSize > ZIP64_LIMIT):
|
requires_zip64 = "Files count"
|
||||||
|
elif centDirOffset > ZIP64_LIMIT:
|
||||||
|
requires_zip64 = "Central directory offset"
|
||||||
|
elif centDirSize > ZIP64_LIMIT:
|
||||||
|
requires_zip64 = "Central directory size"
|
||||||
|
if requires_zip64:
|
||||||
# Need to write the ZIP64 end-of-archive records
|
# Need to write the ZIP64 end-of-archive records
|
||||||
|
if not self._allowZip64:
|
||||||
|
raise LargeZipFile(requires_zip64 +
|
||||||
|
" would require ZIP64 extensions")
|
||||||
zip64endrec = struct.pack(
|
zip64endrec = struct.pack(
|
||||||
structEndArchive64, stringEndArchive64,
|
structEndArchive64, stringEndArchive64,
|
||||||
44, 45, 45, 0, 0, centDirCount, centDirCount,
|
44, 45, 45, 0, 0, centDirCount, centDirCount,
|
||||||
|
|
|
@ -13,6 +13,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #21866: ZipFile.close() no longer writes ZIP64 central directory
|
||||||
|
records if allowZip64 is false.
|
||||||
|
|
||||||
- Issue #22415: Fixed debugging output of the GROUPREF_EXISTS opcode in the re
|
- Issue #22415: Fixed debugging output of the GROUPREF_EXISTS opcode in the re
|
||||||
module. Removed trailing spaces in debugging output.
|
module. Removed trailing spaces in debugging output.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue