bpo-5950: Support reading zips with comments in zipimport (#9548)
* bpo-5950: Support reading zips with comments in zipimport
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index 63e5672..e98b090 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -116,6 +116,9 @@
zinfo = ZipInfo(name, time.localtime(mtime))
zinfo.compress_type = self.compression
z.writestr(zinfo, data)
+ comment = kw.get("comment", None)
+ if comment is not None:
+ z.comment = comment
stuff = kw.get("stuff", None)
if stuff is not None:
@@ -665,6 +668,18 @@
with self.assertRaises(TypeError):
zipimport.zipimporter(memoryview(os.fsencode(filename)))
+ def testComment(self):
+ files = {TESTMOD + ".py": (NOW, test_src)}
+ self.doTest(".py", files, TESTMOD, comment=b"comment")
+
+ def testBeginningCruftAndComment(self):
+ files = {TESTMOD + ".py": (NOW, test_src)}
+ self.doTest(".py", files, TESTMOD, stuff=b"cruft" * 64, comment=b"hi")
+
+ def testLargestPossibleComment(self):
+ files = {TESTMOD + ".py": (NOW, test_src)}
+ self.doTest(".py", files, TESTMOD, comment=b"c" * ((1 << 16) - 1))
+
@support.requires_zlib
class CompressedZipImportTestCase(UncompressedZipImportTestCase):
diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 4017340..2c11f68f 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -38,6 +38,9 @@
_module_type = type(sys)
+END_CENTRAL_DIR_SIZE = 22
+STRING_END_ARCHIVE = b'PK\x05\x06'
+MAX_COMMENT_LEN = (1 << 16) - 1
class zipimporter:
"""zipimporter(archivepath) -> zipimporter object
@@ -354,16 +357,39 @@
with fp:
try:
- fp.seek(-22, 2)
+ fp.seek(-END_CENTRAL_DIR_SIZE, 2)
header_position = fp.tell()
- buffer = fp.read(22)
+ buffer = fp.read(END_CENTRAL_DIR_SIZE)
except OSError:
raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive)
- if len(buffer) != 22:
+ if len(buffer) != END_CENTRAL_DIR_SIZE:
raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive)
- if buffer[:4] != b'PK\x05\x06':
+ if buffer[:4] != STRING_END_ARCHIVE:
# Bad: End of Central Dir signature
- raise ZipImportError(f'not a Zip file: {archive!r}', path=archive)
+ # Check if there's a comment.
+ try:
+ fp.seek(0, 2)
+ file_size = fp.tell()
+ except OSError:
+ raise ZipImportError(f"can't read Zip file: {archive!r}",
+ path=archive)
+ max_comment_start = max(file_size - MAX_COMMENT_LEN -
+ END_CENTRAL_DIR_SIZE, 0)
+ try:
+ fp.seek(max_comment_start)
+ data = fp.read()
+ except OSError:
+ raise ZipImportError(f"can't read Zip file: {archive!r}",
+ path=archive)
+ pos = data.rfind(STRING_END_ARCHIVE)
+ if pos < 0:
+ raise ZipImportError(f'not a Zip file: {archive!r}',
+ path=archive)
+ buffer = data[pos:pos+END_CENTRAL_DIR_SIZE]
+ if len(buffer) != END_CENTRAL_DIR_SIZE:
+ raise ZipImportError(f"corrupt Zip file: {archive!r}",
+ path=archive)
+ header_position = file_size - len(data) + pos
header_size = _unpack_uint32(buffer[12:16])
header_offset = _unpack_uint32(buffer[16:20])