def _create_zip(self, input_path, output_file): """ Zips file to create the .dbc archive """ def _get_directories(p): p = p.parent while p != input_path: yield p.relative_to(input_path) p = p.parent directories = list( set( itertools.chain( *[_get_directories(x) for x in input_path.glob("**/*.*")]))) output_file.parent.mkdir(parents=True, exist_ok=True) with ZipFile(output_file, "w", compression=ZIP_DEFLATED) as zipf: for directory in directories: zi = ZipInfo(str(directory) + "/") # zi.external_attr = 0x10 zi.compress_type = ZIP_DEFLATED zipf.writestr(zi, "") for path in input_path.glob("**/*.*"): base_path = path.relative_to(input_path) zi = ZipInfo(str(base_path)) zi.compress_type = ZIP_DEFLATED zi.flag_bits = None # zi.external_attr = 0x20 zipf.writestr(zi, path.read_text())
def make_file_entry(name=None, date_time=None, mode=MODE_FILE | MODE_ARCHIVE): tt = date_time.timetuple() file = ZipInfo() file.filename = name file.orig_filename = file.filename file.date_time = date_time.isocalendar() + (tt.tm_hour, tt.tm_min, tt.tm_sec) file.compress_type = 8 file.create_system = 0 file.create_version = 20 file.extract_version = 20 file.flag_bits = 2 file.external_attr = mode return file
def compress_buffer(self, arcname, buffer): zinfo = ZipInfo(filename=arcname, date_time=time.localtime(time.time())[:6]) zinfo.compress_type = ZIP_DEFLATED zinfo.compress_size = 0 zinfo.flag_bits = 0x08 zinfo.external_attr = 0o600 << 16 zinfo.file_size = 0 zinfo.header_offset = self._data_p zinfo.CRC = crc = 0 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) yield self.__get_data(zinfo.FileHeader()) buffer.seek(0) while 1: buf = buffer.read(CHUNK_SIZE) if not buf: break zinfo.file_size += len(buf) crc = zlib.crc32(buf, crc) & 0xffffffff if cmpr: buf = cmpr.compress(buf) zinfo.compress_size += len(buf) yield self.__get_data(buf) if cmpr: buf = cmpr.flush() zinfo.compress_size += len(buf) yield self.__get_data(buf) else: zinfo.compress_size = zinfo.file_size zinfo.CRC = crc zip64 = zinfo.file_size > ZIP64_LIMIT or zinfo.compress_size > ZIP64_LIMIT fmt = '<4sLQQ' if zip64 else '<4sLLL' data_descriptor = struct.pack(fmt, stringDataDescriptor, zinfo.CRC, zinfo.compress_size, zinfo.file_size) yield self.__get_data(data_descriptor) self._filelist.append(zinfo)
def compress_file(self, filename, arcname): st = os.stat(filename) zinfo = ZipInfo(arcname, time.localtime(time.time())[:6]) zinfo.external_attr = (st[0] & 0xFFFF) << 16 zinfo.compress_type = ZIP_DEFLATED zinfo.flag_bits = 0x08 zinfo.header_offset = self._data_p cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) with open(filename, "rb") as fp: zinfo.CRC = crc = 0 zinfo.compress_size = 0 zinfo.file_size = 0 yield self.__get_data(zinfo.FileHeader()) while 1: buf = fp.read(CHUNK_SIZE) if not buf: break zinfo.file_size += len(buf) crc = zlib.crc32(buf, crc) & 0xffffffff if cmpr: buf = cmpr.compress(buf) zinfo.compress_size += len(buf) yield self.__get_data(buf) if cmpr: buf = cmpr.flush() zinfo.compress_size += len(buf) yield self.__get_data(buf) else: zinfo.compress_size = zinfo.file_size zinfo.CRC = crc zip64 = zinfo.file_size > ZIP64_LIMIT or zinfo.compress_size > ZIP64_LIMIT fmt = '<4sLQQ' if zip64 else '<4sLLL' data_descriptor = struct.pack(fmt, stringDataDescriptor, zinfo.CRC, zinfo.compress_size, zinfo.file_size) yield self.__get_data(data_descriptor) self._filelist.append(zinfo)
def compress_stream(self, arcname, datagen): zinfo = ZipInfo(arcname, time.localtime(time.time())[:6]) zinfo.external_attr = 0o600 << 16 zinfo.compress_type = ZIP_DEFLATED zinfo.flag_bits = 0x08 zinfo.header_offset = self._data_p cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) zinfo.CRC = crc = 0 zinfo.compress_size = 0 zinfo.file_size = 0 yield self.__get_data(zinfo.FileHeader()) for buf in datagen: if not buf: continue zinfo.file_size += len(buf) crc = zlib.crc32(buf, crc) & 0xffffffff if cmpr: buf = cmpr.compress(buf) zinfo.compress_size += len(buf) yield self.__get_data(buf) if cmpr: buf = cmpr.flush() zinfo.compress_size += len(buf) yield self.__get_data(buf) else: zinfo.compress_size = zinfo.file_size zinfo.CRC = crc zip64 = zinfo.file_size > ZIP64_LIMIT fmt = '<4sLQQ' if zip64 else '<4sLLL' data_descriptor = struct.pack(fmt, stringDataDescriptor, zinfo.CRC, zinfo.compress_size, zinfo.file_size) yield self.__get_data(data_descriptor) self._filelist.append(zinfo)
def write_iter(self, filename, arcname=None, compress_type=None): """Put the bytes from filename into the archive under the name arcname.""" from zipfile import stat, time, ZipInfo, ZIP_DEFLATED, crc32, struct if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes if compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x00 zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo yield zinfo.FileHeader() #self.fp.write(zinfo.FileHeader()) return with open(filename, "rb") as fp: # 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 cmpr = None while 1: buf = fp.read(1024 * 8) if not buf: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff zinfo.compress_size = file_size zinfo.file_size = file_size zinfo.CRC = CRC #self.fp.write(zinfo.FileHeader()) yield zinfo.FileHeader() fp.seek(0) while 1: buf = fp.read(1024 * 8) if not buf: break #self.fp.write(buf) yield buf self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo
def my_zip_write(self, filename, arcname=None, compress_type=None, date_time=None): """Copy of zipfile.ZipFile.write() with some modifications - Allow overriding the timestamp for reproducible builds - Calculate a SHA256 hash of the file as we write it and return the hash object. """ if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) if date_time is None: mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes if isdir: zinfo.compress_type = zipfile.ZIP_STORED elif compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x00 self.fp.seek(getattr(self, 'start_dir', 0)) zinfo.header_offset = self.fp.tell() # Start of header bytes if zinfo.compress_type == ZIP_LZMA: # Compressed data includes an end-of-stream (EOS) marker zinfo.flag_bits |= 0x02 self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 zinfo.external_attr |= 0x10 # MS-DOS directory flag self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader(False)) self.start_dir = self.fp.tell() return hashsum = hashlib.sha256() cmpr = _get_compressor(zinfo.compress_type) with open(filename, "rb") as fp: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 # 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: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff hashsum.update(buf) if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size zinfo.CRC = CRC zinfo.file_size = file_size 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) self.start_dir = self.fp.tell() # Preserve current position in file self.fp.seek(zinfo.header_offset, 0) self.fp.write(zinfo.FileHeader(zip64)) self.fp.seek(self.start_dir, 0) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo return hashsum
def write(self, filename, arcname=None, compress_type=None): """Put the bytes from filename into the archive under the name arcname.""" if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes if compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x00 zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader(False)) return with open(filename, "rb") as fp: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 # 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)) if zinfo.compress_type == ZIP_DEFLATED: cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) else: cmpr = None file_size = 0 while 1: buf = fp.read(1024 * 1024) if not buf: break file_size = file_size + len(buf) CRC = zlib.crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size zinfo.CRC = CRC zinfo.file_size = file_size 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, 0) self.fp.write(zinfo.FileHeader(zip64)) self.fp.seek(position, 0) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo
def write(self, filename, arcname=None, compress_type=None): """ Fixed version of write supporting bitflag 0x08 to write crc and size at end of file. """ if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes if isdir: zinfo.compress_type = ZIP_STORED elif compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x00 zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 zinfo.external_attr |= 0x10 # MS-DOS directory flag self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader()) self.start_dir = self.fp.tell() return zinfo.flag_bits |= 0x08 with open(filename, "rb") as fp: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 try: # Python > 2.7.3 # 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)) except TypeError: # Python <= 2.7.3 zip64 = zinfo.file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT self.fp.write(zinfo.FileHeader()) if zinfo.compress_type == ZIP_DEFLATED: cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) else: cmpr = None file_size = 0 while 1: buf = fp.read(CHUNK_SIZE) if not buf: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size zinfo.CRC = CRC zinfo.file_size = file_size 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') # Write CRC and file sizes after the file data fmt = b'<LQQ' if zip64 else b'<LLL' self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.start_dir = self.fp.tell() self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo
def write(self, filename, arcname=None, compress_type=None): """Put the bytes from filename into the archive under the name arcname. The file is written in strictly sequential fashion - no seeking.""" # This code is a tweaked version of ZipFile.write ... # TODO: add an alternative version that works with a stream rather than a filename. if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes if compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x08 # Use trailing data descriptor for file sizes and CRC zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader()) return with open(filename, "rb") as fp: # The CRC and sizes in the file header are zero ... zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 zinfo.file_size = file_size = 0 self.fp.write(zinfo.FileHeader()) if zinfo.compress_type == ZIP_DEFLATED: cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) else: cmpr = None while 1: buf = fp.read(1024 * 8) if not buf: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size # Write the data descriptor after the file containing the true sizes and CRC zinfo.CRC = CRC zinfo.file_size = file_size self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo
def write(self, filename, arcname=None, compress_type=None): """ Fixed version of write supporting bitflag 0x08 to write crc and size at end of file. """ if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes if isdir: zinfo.compress_type = ZIP_STORED elif compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x00 zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 zinfo.external_attr |= 0x10 # MS-DOS directory flag self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader()) self.start_dir = self.fp.tell() return zinfo.flag_bits |= 0x08 with open(filename, "rb") as fp: # Must overwrite CRC and sizes with correct data later zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 try: # Python > 2.7.3 # 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)) except TypeError: # Python <= 2.7.3 zip64 = zinfo.file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT self.fp.write(zinfo.FileHeader()) if zinfo.compress_type == ZIP_DEFLATED: cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) else: cmpr = None file_size = 0 while 1: buf = fp.read(CHUNK_SIZE) if not buf: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size zinfo.CRC = CRC zinfo.file_size = file_size 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') # Write CRC and file sizes after the file data fmt = b'<LQQ' if zip64 else b'<LLL' self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.start_dir = self.fp.tell() self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo
def fix_zip(self): if not self.broken: return False self.fp.seek(0, 2) file_len = self.fp.tell() mm = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ) offset = 0 file_list = {} cd_list = {} try: # pass one, parse the zip file while offset + 4 < file_len: hdr_off = mm.find(b"PK", offset) if hdr_off == -1: break hdr_type = mm[hdr_off:hdr_off + 4] if hdr_type == stringFileHeader: # local file header if hdr_off + sizeFileHeader > file_len: break fheader = mm[hdr_off:hdr_off + sizeFileHeader] fheader = struct.unpack(structFileHeader, fheader) start = hdr_off size = sizeFileHeader + fheader[_FH_COMPRESSED_SIZE] + fheader[_FH_FILENAME_LENGTH] + \ fheader[_FH_EXTRA_FIELD_LENGTH] name = mm[hdr_off + sizeFileHeader:hdr_off + sizeFileHeader + fheader[_FH_FILENAME_LENGTH]] file_list[name] = [start, size, fheader] offset = hdr_off + size elif hdr_type == stringCentralDir: if hdr_off + sizeCentralDir > file_len: break centdir = mm[hdr_off:hdr_off + sizeCentralDir] centdir = struct.unpack(structCentralDir, centdir) start = hdr_off size = sizeCentralDir + centdir[_CD_FILENAME_LENGTH] + centdir[_CD_EXTRA_FIELD_LENGTH] + \ centdir[_CD_COMMENT_LENGTH] name = mm[hdr_off + sizeCentralDir:hdr_off + sizeCentralDir + centdir[_CD_FILENAME_LENGTH]] cd_list[name] = [start, size, centdir] offset = hdr_off + size elif hdr_type == stringEndArchive: offset = hdr_off + sizeEndCentDir else: offset = hdr_off + 1 # Guesses last_cv = 20 last_ea = 0 last_cs = 0 last_dt = (0, 0) # Pass two, repair for filename, (start, end, centdir) in cd_list.items(): if filename not in file_list: continue if isinstance(filename, bytes): x = ZipInfo(filename.decode('utf-8', 'backslashreplace')) else: x = ZipInfo(filename) extra_off = start + sizeCentralDir x.extra = mm[extra_off:extra_off + centdir[_CD_EXTRA_FIELD_LENGTH]] extra_off += centdir[_CD_EXTRA_FIELD_LENGTH] x.comment = mm[extra_off:extra_off + centdir[_CD_EXTRA_FIELD_LENGTH]] x.header_offset = file_list[filename][0] (x.create_version, x.create_system, x.extract_version, x.reserved, x.flag_bits, x.compress_type, t, d, x.CRC, x.compress_size, x.file_size) = centdir[1:12] x.volume, x.internal_attr, x.external_attr = centdir[15:18] # Convert date/time code to (year, month, day, hour, min, sec) x._raw_time = t x.date_time = ((d >> 9) + 1980, (d >> 5) & 0xF, d & 0x1F, t >> 11, (t >> 5) & 0x3F, (t & 0x1F) * 2) last_ea = x.external_attr last_cs = x.create_system last_cv = x.create_version last_dt = (d, t) # noinspection PyProtectedMember x._decodeExtra() # x.filename = x._decodeFilename() self.filelist.append(x) self.NameToInfo[x.filename] = x for filename, (start, end, fheader) in file_list.items(): if filename in cd_list: continue x = ZipInfo(filename.decode('utf-8', 'backslashreplace')) x.extra = "" x.comment = "" x.header_offset = file_list[filename][0] x.create_version = last_cv x.create_system = last_cs x.extract_version = fheader[_FH_EXTRACT_VERSION] x.reserved = 0 x.flag_bits = fheader[_FH_GENERAL_PURPOSE_FLAG_BITS] x.compress_type = fheader[_FH_COMPRESSION_METHOD] d, t = last_dt x.CRC = fheader[_FH_CRC] x.compress_size = fheader[_FH_COMPRESSED_SIZE] x.file_size = fheader[_FH_UNCOMPRESSED_SIZE] x.volume = 0 x.internal_attr = 0 x.external_attr = last_ea # Convert date/time code to (year, month, day, hour, min, sec) x._raw_time = t x.date_time = ((d >> 9) + 1980, (d >> 5) & 0xF, d & 0x1F, t >> 11, (t >> 5) & 0x3F, (t & 0x1F) * 2) # noinspection PyProtectedMember x._decodeExtra() # x.filename = x._decodeFilename() self.filelist.append(x) self.NameToInfo[x.filename] = x finally: mm.close()
def write(self, filename, arcname=None, compress_type=None): """Put the bytes from filename into the archive under the name arcname. The file is written in strictly sequential fashion - no seeking.""" # This code is a tweaked version of ZipFile.write ... # TODO: add an alternative version that works with a stream rather than a filename. if not self.fp: raise RuntimeError( "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] # Create ZipInfo instance to store file information if arcname is None: arcname = filename arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) while arcname[0] in (os.sep, os.altsep): arcname = arcname[1:] if isdir: arcname += '/' zinfo = ZipInfo(arcname, date_time) zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes if compress_type is None: zinfo.compress_type = self.compression else: zinfo.compress_type = compress_type zinfo.file_size = st.st_size zinfo.flag_bits = 0x08 # Use trailing data descriptor for file sizes and CRC zinfo.header_offset = self.fp.tell() # Start of header bytes self._writecheck(zinfo) self._didModify = True if isdir: zinfo.file_size = 0 zinfo.compress_size = 0 zinfo.CRC = 0 self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo self.fp.write(zinfo.FileHeader()) return with open(filename, "rb") as fp: # The CRC and sizes in the file header are zero ... zinfo.CRC = CRC = 0 zinfo.compress_size = compress_size = 0 zinfo.file_size = file_size = 0 self.fp.write(zinfo.FileHeader()) if zinfo.compress_type == ZIP_DEFLATED: cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) else: cmpr = None while 1: buf = fp.read(1024 * 8) if not buf: break file_size = file_size + len(buf) CRC = crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf) self.fp.write(buf) if cmpr: buf = cmpr.flush() compress_size = compress_size + len(buf) self.fp.write(buf) zinfo.compress_size = compress_size else: zinfo.compress_size = file_size # Write the data descriptor after the file containing the true sizes and CRC zinfo.CRC = CRC zinfo.file_size = file_size self.fp.write( struct.pack("<LLL", zinfo.CRC, zinfo.compress_size, zinfo.file_size)) self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo