diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2013-01-14 22:38:17 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2013-01-14 22:38:17 (GMT) |
commit | 64bfcc4c224930a56a0a96726d02b624ebe85b7e (patch) | |
tree | 912a3bfc77b37707c36ccd3fcdd0ad07fe11f437 /Lib/zipfile.py | |
parent | 67da89446a0b3f008a15e6a510925921ca6f9303 (diff) | |
parent | 182d7cd531e565bfbd9e248290d6f868c688bf33 (diff) | |
download | cpython-64bfcc4c224930a56a0a96726d02b624ebe85b7e.zip cpython-64bfcc4c224930a56a0a96726d02b624ebe85b7e.tar.gz cpython-64bfcc4c224930a56a0a96726d02b624ebe85b7e.tar.bz2 |
Issue #9720: zipfile now writes correct local headers for files larger than 4 GiB.
Diffstat (limited to 'Lib/zipfile.py')
-rw-r--r-- | Lib/zipfile.py | 47 |
1 files changed, 32 insertions, 15 deletions
diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 68051c8..dcebf72 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -346,7 +346,7 @@ class ZipInfo (object): # compress_size Size of the compressed file # file_size Size of the uncompressed file - def FileHeader(self): + def FileHeader(self, zip64=None): """Return the per-file header as a string.""" dt = self.date_time dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] @@ -362,12 +362,17 @@ class ZipInfo (object): extra = self.extra min_version = 0 - if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT: - # File is larger than what fits into a 4 byte integer, - # fall back to the ZIP64 extension + if zip64 is None: + zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT + if zip64: fmt = '<HHQQ' extra = extra + struct.pack(fmt, 1, struct.calcsize(fmt)-4, file_size, compress_size) + if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT: + if not zip64: + raise LargeZipFile("Filesize would require ZIP64 extensions") + # File is larger than what fits into a 4 byte integer, + # fall back to the ZIP64 extension file_size = 0xffffffff compress_size = 0xffffffff min_version = ZIP64_VERSION @@ -1301,7 +1306,7 @@ class ZipFile: zinfo.CRC = 0 self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo - self.fp.write(zinfo.FileHeader()) + self.fp.write(zinfo.FileHeader(False)) return cmpr = _get_compressor(zinfo.compress_type) @@ -1309,8 +1314,11 @@ class ZipFile: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 - zinfo.file_size = file_size = 0 - self.fp.write(zinfo.FileHeader()) + # Compressed size can be larger than uncompressed size + zip64 = self._allowZip64 and \ + zinfo.file_size * 1.05 > ZIP64_LIMIT + self.fp.write(zinfo.FileHeader(zip64)) + file_size = 0 while 1: buf = fp.read(1024 * 8) if not buf: @@ -1330,11 +1338,16 @@ class ZipFile: zinfo.compress_size = file_size zinfo.CRC = CRC zinfo.file_size = file_size - # Seek backwards and write CRC and file sizes + if not zip64 and self._allowZip64: + if file_size > ZIP64_LIMIT: + raise RuntimeError('File size has increased during compressing') + if compress_size > ZIP64_LIMIT: + raise RuntimeError('Compressed size larger than uncompressed size') + # Seek backwards and write file header (which will now include + # correct CRC and file sizes) position = self.fp.tell() # Preserve current position in file - self.fp.seek(zinfo.header_offset + 14, 0) - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, - zinfo.file_size)) + self.fp.seek(zinfo.header_offset, 0) + self.fp.write(zinfo.FileHeader(zip64)) self.fp.seek(position, 0) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo @@ -1376,14 +1389,18 @@ class ZipFile: zinfo.compress_size = len(data) # Compressed size else: zinfo.compress_size = zinfo.file_size - zinfo.header_offset = self.fp.tell() # Start of header data - self.fp.write(zinfo.FileHeader()) + zip64 = zinfo.file_size > ZIP64_LIMIT or \ + zinfo.compress_size > ZIP64_LIMIT + if zip64 and not self._allowZip64: + raise LargeZipFile("Filesize would require ZIP64 extensions") + self.fp.write(zinfo.FileHeader(zip64)) self.fp.write(data) - self.fp.flush() if zinfo.flag_bits & 0x08: # Write CRC and file sizes after the file data - self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, + fmt = '<LQQ' if zip64 else '<LLL' + self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size, zinfo.file_size)) + self.fp.flush() self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo |