From c55d76dfd5abdb9b1794cb342bcc622ee472c3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Koles=C3=A1r=20Andr=C3=A1s?= Date: Thu, 15 Feb 2018 09:58:39 +0100 Subject: [PATCH 1/5] Fix missing size of Zip64 extended information extra field Extra fields need to have a 2-byte long size field after type field. Zip64 record in file header did not have this field. Fixes issue #44 for recent version of 7-Zip: * 18.01 reports OK. * 16.02 still reports Headers Errror. --- src/ZipArchive.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZipArchive.php b/src/ZipArchive.php index 7dd9e7f..f4d9867 100644 --- a/src/ZipArchive.php +++ b/src/ZipArchive.php @@ -210,7 +210,7 @@ protected function add_stream_file_header($name, $size, array $opt, $meth) // strip leading slashes from file name // (fixes bug in windows archive viewer) $name = preg_replace('/^\\/+/', '', $name); - $extra = pack('vVVVV', 1, 0, 0, 0, 0); + $extra = pack('vvVVVV', 1, 0x10, 0, 0, 0, 0); // create dos timestamp $opt['time'] = isset($opt['time']) ? $opt['time'] : time(); From 8b690ae01678f71cc7e1a79c44ecf0f276e6bf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Koles=C3=A1r=20Andr=C3=A1s?= Date: Thu, 15 Feb 2018 10:06:12 +0100 Subject: [PATCH 2/5] Set number of files in old 2-byte place if fits into there 7-Zip version 16.02 complaints with Headers Error if there is 0xFFFF. Even when Zip64 EOF has the correct value. Fixes #44 in 7-Zip version 16.02. --- src/ZipArchive.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ZipArchive.php b/src/ZipArchive.php index f4d9867..171a839 100644 --- a/src/ZipArchive.php +++ b/src/ZipArchive.php @@ -412,12 +412,15 @@ private function add_cdr_eof(array $opt = null) $comment = $opt['comment']; } + $num = count($this->files); + $num = $num > 0xFFFF ? 0xFFFF : $num; + $fields = array( // (from V,F of APPNOTE.TXT) array('V', 0x06054b50), // end of central file header signature array('v', 0xFFFF), // this disk number (0xFFFF to look in zip64 cdr) array('v', 0xFFFF), // number of disk with cdr (0xFFFF to look in zip64 cdr) - array('v', 0xFFFF), // number of entries in the cdr on this disk (0xFFFF to look in zip64 cdr)) - array('v', 0xFFFF), // number of entries in the cdr (0xFFFF to look in zip64 cdr) + array('v', $num), // number of entries in the cdr on this disk (0xFFFF to look in zip64 cdr)) + array('v', $num), // number of entries in the cdr (0xFFFF to look in zip64 cdr) array('V', 0xFFFFFFFF), // cdr size (0xFFFFFFFF to look in zip64 cdr) array('V', 0xFFFFFFFF), // cdr offset (0xFFFFFFFF to look in zip64 cdr) array('v', strlen($comment)), // zip file comment length From 508156b3db48ba302d8ea7c0e7b116c135d1b79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Koles=C3=A1r=20Andr=C3=A1s?= Date: Thu, 15 Feb 2018 16:59:17 +0100 Subject: [PATCH 3/5] Use sizes in short fields if they fit Only create Zip64 fields that are really needed. Only create Zip64 block if there is a value that did not fit into 32 bits. --- src/ZipArchive.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ZipArchive.php b/src/ZipArchive.php index 171a839..1f52a4d 100644 --- a/src/ZipArchive.php +++ b/src/ZipArchive.php @@ -297,13 +297,26 @@ private function add_cdr_file(array $args) list($len_low, $len_high) = $this->int64_split($len); list($ofs_low, $ofs_high) = $this->int64_split($ofs); + $zlen = $zlen_high ? 0xFFFFFFFF : $zlen_low; + $len = $len_high ? 0xFFFFFFFF : $len_low; + $ofs = $ofs_high ? 0xFFFFFFFF : $ofs_low; + // ZIP64, necessary for files over 4GB (incl. entire archive size) $extra_zip64 = ''; - $extra_zip64 .= pack('VV', $len_low, $len_high); - $extra_zip64 .= pack('VV', $zlen_low, $zlen_high); - $extra_zip64 .= pack('VV', $ofs_low, $ofs_high); + if ($len == 0xFFFFFFFF) + $extra_zip64 .= pack('VV', $len_low, $len_high); + + if ($zlen == 0xFFFFFFFF) + $extra_zip64 .= pack('VV', $zlen_low, $zlen_high); - $extra = pack('vv', 1, strlen($extra_zip64)) . $extra_zip64; + if ($ofs == 0xFFFFFFFF) + $extra_zip64 .= pack('VV', $ofs_low, $ofs_high); + + if (!empty($extra_zip64)) { + $extra = pack('vv', 1, strlen($extra_zip64)) . $extra_zip64; + } else { + $extra = ''; + } // get attributes $comment = isset($opt['comment']) ? $opt['comment'] : ''; @@ -319,15 +332,15 @@ private function add_cdr_file(array $args) array('v', $meth), // compresion method (deflate or store) array('V', $dts), // dos timestamp array('V', $crc), // crc32 of data - array('V', 0xFFFFFFFF), // compressed data length (zip64 - look in extra) - array('V', 0xFFFFFFFF), // uncompressed data length (zip64 - look in extra) + array('V', $zlen), // compressed data length (zip64 - look in extra) + array('V', $len), // uncompressed data length (zip64 - look in extra) array('v', strlen($name)), // filename length array('v', strlen($extra)), // extra data len array('v', strlen($comment)), // file comment length array('v', 0), // disk number start array('v', 0), // internal file attributes array('V', $file_attribute), // external file attributes, 0x10 for dir, 0x20 for file - array('V', 0xFFFFFFFF), // relative offset of local header (zip64 - look in extra) + array('V', $ofs), // relative offset of local header (zip64 - look in extra) ); // pack fields, then append name and comment From 3c5f30628169b02ea3dbab2c867461c873cdfd98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Koles=C3=A1r=20Andr=C3=A1s?= Date: Thu, 15 Feb 2018 17:01:21 +0100 Subject: [PATCH 4/5] Use short fields in CDR EOF --- src/ZipArchive.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ZipArchive.php b/src/ZipArchive.php index 1f52a4d..41183f8 100644 --- a/src/ZipArchive.php +++ b/src/ZipArchive.php @@ -428,14 +428,20 @@ private function add_cdr_eof(array $opt = null) $num = count($this->files); $num = $num > 0xFFFF ? 0xFFFF : $num; + list($cdr_len_low, $cdr_len_high) = $this->int64_split($this->cdr_len); + list($cdr_ofs_low, $cdr_ofs_high) = $this->int64_split($this->cdr_ofs); + + $cdr_len = $cdr_len_high ? 0xFFFFFFFF : $cdr_len_low; + $cdr_ofs = $cdr_ofs_high ? 0xFFFFFFFF : $cdr_ofs_low; + $fields = array( // (from V,F of APPNOTE.TXT) array('V', 0x06054b50), // end of central file header signature - array('v', 0xFFFF), // this disk number (0xFFFF to look in zip64 cdr) - array('v', 0xFFFF), // number of disk with cdr (0xFFFF to look in zip64 cdr) + array('v', 0x0000), // this disk number (0xFFFF to look in zip64 cdr) + array('v', 0x0000), // number of disk with cdr (0xFFFF to look in zip64 cdr) array('v', $num), // number of entries in the cdr on this disk (0xFFFF to look in zip64 cdr)) array('v', $num), // number of entries in the cdr (0xFFFF to look in zip64 cdr) - array('V', 0xFFFFFFFF), // cdr size (0xFFFFFFFF to look in zip64 cdr) - array('V', 0xFFFFFFFF), // cdr offset (0xFFFFFFFF to look in zip64 cdr) + array('V', $cdr_len), // cdr size (0xFFFFFFFF to look in zip64 cdr) + array('V', $cdr_ofs), // cdr offset (0xFFFFFFFF to look in zip64 cdr) array('v', strlen($comment)), // zip file comment length ); From e5177ab2a7780cdefb101b1909e6797d4bc18728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Koles=C3=A1r=20Andr=C3=A1s?= Date: Thu, 15 Feb 2018 17:02:05 +0100 Subject: [PATCH 5/5] Do not write Zip64 EOF and locator records if not needed If all values fit into old short EOF fields, skip Zip64. --- src/ZipArchive.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ZipArchive.php b/src/ZipArchive.php index 41183f8..593dd36 100644 --- a/src/ZipArchive.php +++ b/src/ZipArchive.php @@ -434,6 +434,11 @@ private function add_cdr_eof(array $opt = null) $cdr_len = $cdr_len_high ? 0xFFFFFFFF : $cdr_len_low; $cdr_ofs = $cdr_ofs_high ? 0xFFFFFFFF : $cdr_ofs_low; + if ($num == 0xFFFF || $cdr_len == 0xFFFFFFFF || $cdr_ofs == 0xFFFFFFFF) { + $this->add_cdr_eof_zip64(); + $this->add_cdr_eof_locator_zip64(); + } + $fields = array( // (from V,F of APPNOTE.TXT) array('V', 0x06054b50), // end of central file header signature array('v', 0x0000), // this disk number (0xFFFF to look in zip64 cdr) @@ -462,9 +467,6 @@ private function add_cdr(array $opt = null) $this->add_cdr_file($file); } - $this->add_cdr_eof_zip64(); - $this->add_cdr_eof_locator_zip64(); - $this->add_cdr_eof($opt); }