@@ -326,7 +326,7 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
326
326
# compress_size Size of the compressed file
327
327
# file_size Size of the uncompressed file
328
328
329
- def FileHeader (self ):
329
+ def FileHeader (self , zip64 = None ):
330
330
"""Return the per-file header as a string."""
331
331
dt = self .date_time
332
332
dosdate = (dt [0 ] - 1980 ) << 9 | dt [1 ] << 5 | dt [2 ]
@@ -341,12 +341,17 @@ def FileHeader(self):
341
341
342
342
extra = self .extra
343
343
344
- if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT :
345
- # File is larger than what fits into a 4 byte integer,
346
- # fall back to the ZIP64 extension
344
+ if zip64 is None :
345
+ zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT
346
+ if zip64 :
347
347
fmt = '<HHQQ'
348
348
extra = extra + struct .pack (fmt ,
349
349
1 , struct .calcsize (fmt )- 4 , file_size , compress_size )
350
+ if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT :
351
+ if not zip64 :
352
+ raise LargeZipFile ("Filesize would require ZIP64 extensions" )
353
+ # File is larger than what fits into a 4 byte integer,
354
+ # fall back to the ZIP64 extension
350
355
file_size = 0xffffffff
351
356
compress_size = 0xffffffff
352
357
self .extract_version = max (45 , self .extract_version )
@@ -1135,20 +1140,23 @@ def write(self, filename, arcname=None, compress_type=None):
1135
1140
zinfo .CRC = 0
1136
1141
self .filelist .append (zinfo )
1137
1142
self .NameToInfo [zinfo .filename ] = zinfo
1138
- self .fp .write (zinfo .FileHeader ())
1143
+ self .fp .write (zinfo .FileHeader (False ))
1139
1144
return
1140
1145
1141
1146
with open (filename , "rb" ) as fp :
1142
1147
# Must overwrite CRC and sizes with correct data later
1143
1148
zinfo .CRC = CRC = 0
1144
1149
zinfo .compress_size = compress_size = 0
1145
- zinfo .file_size = file_size = 0
1146
- self .fp .write (zinfo .FileHeader ())
1150
+ # Compressed size can be larger than uncompressed size
1151
+ zip64 = self ._allowZip64 and \
1152
+ zinfo .file_size * 1.05 > ZIP64_LIMIT
1153
+ self .fp .write (zinfo .FileHeader (zip64 ))
1147
1154
if zinfo .compress_type == ZIP_DEFLATED :
1148
1155
cmpr = zlib .compressobj (zlib .Z_DEFAULT_COMPRESSION ,
1149
1156
zlib .DEFLATED , - 15 )
1150
1157
else :
1151
1158
cmpr = None
1159
+ file_size = 0
1152
1160
while 1 :
1153
1161
buf = fp .read (1024 * 8 )
1154
1162
if not buf :
@@ -1168,11 +1176,16 @@ def write(self, filename, arcname=None, compress_type=None):
1168
1176
zinfo .compress_size = file_size
1169
1177
zinfo .CRC = CRC
1170
1178
zinfo .file_size = file_size
1171
- # Seek backwards and write CRC and file sizes
1179
+ if not zip64 and self ._allowZip64 :
1180
+ if file_size > ZIP64_LIMIT :
1181
+ raise RuntimeError ('File size has increased during compressing' )
1182
+ if compress_size > ZIP64_LIMIT :
1183
+ raise RuntimeError ('Compressed size larger than uncompressed size' )
1184
+ # Seek backwards and write file header (which will now include
1185
+ # correct CRC and file sizes)
1172
1186
position = self .fp .tell () # Preserve current position in file
1173
- self .fp .seek (zinfo .header_offset + 14 , 0 )
1174
- self .fp .write (struct .pack ("<LLL" , zinfo .CRC , zinfo .compress_size ,
1175
- zinfo .file_size ))
1187
+ self .fp .seek (zinfo .header_offset , 0 )
1188
+ self .fp .write (zinfo .FileHeader (zip64 ))
1176
1189
self .fp .seek (position , 0 )
1177
1190
self .filelist .append (zinfo )
1178
1191
self .NameToInfo [zinfo .filename ] = zinfo
@@ -1212,14 +1225,18 @@ def writestr(self, zinfo_or_arcname, data, compress_type=None):
1212
1225
zinfo .compress_size = len (data ) # Compressed size
1213
1226
else :
1214
1227
zinfo .compress_size = zinfo .file_size
1215
- zinfo .header_offset = self .fp .tell () # Start of header data
1216
- self .fp .write (zinfo .FileHeader ())
1228
+ zip64 = zinfo .file_size > ZIP64_LIMIT or \
1229
+ zinfo .compress_size > ZIP64_LIMIT
1230
+ if zip64 and not self ._allowZip64 :
1231
+ raise LargeZipFile ("Filesize would require ZIP64 extensions" )
1232
+ self .fp .write (zinfo .FileHeader (zip64 ))
1217
1233
self .fp .write (data )
1218
- self .fp .flush ()
1219
1234
if zinfo .flag_bits & 0x08 :
1220
1235
# Write CRC and file sizes after the file data
1221
- self .fp .write (struct .pack ("<LLL" , zinfo .CRC , zinfo .compress_size ,
1236
+ fmt = '<LQQ' if zip64 else '<LLL'
1237
+ self .fp .write (struct .pack (fmt , zinfo .CRC , zinfo .compress_size ,
1222
1238
zinfo .file_size ))
1239
+ self .fp .flush ()
1223
1240
self .filelist .append (zinfo )
1224
1241
self .NameToInfo [zinfo .filename ] = zinfo
1225
1242
0 commit comments