git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] archive-zip: Add zip64 headers when file size is too large for 32 bits

From
Peter Krefting <peter@softwolves.pp.se>
Date
Apr 22, 2017, 19:22 UTC
Message-ID
<alpine.DEB.2.11.1704222019420.12779@perkele.intern.softwolves.pp.se>
In-Reply-To
<3C736801-6BB8-41CC-88FF-C42FC853A736@blackthorn-media.com>

If the size of the files in the archive cannot be expressed in 32 bits, or the offset in the zip file itself, add zip64 local headers with the actual size. If we do find such entries, we also set a flag to force the creation of a zip64 end of central directory record.

Signed-off-by: Peter Krefting <peter@softwolves.pp.se>
---
  archive-zip.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
  1 file changed, 41 insertions(+), 9 deletions(-)
> git archive, when writing a zip file, has a silent 4GB file size 
> limit (on the inputs as well as the output), as it doesn’t fully 
> support zip64.

Yeah, it seems that the zip64 support that was added was to support more than 65535 files, but it did not add support for 64-bit file sizes or ZIP archives.

Try the below patch, it seems to work for me with a repository with two files of 4 Gbyte plus a few bytes. Haven't tested the case where the archive itself is larger than 4 Gbyte, but that ought to work, too.

diff --git a/archive-zip.c b/archive-zip.c
index b429a8d..c76a9b4 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -10,6 +10,7 @@

  static int zip_date;
  static int zip_time;
+static int zip_zip64;

  static unsigned char *zip_dir;
  static unsigned int zip_dir_size;
@@ -88,6 +89,16 @@ struct zip_extra_mtime {
  	unsigned char _end[1];
  };

+struct zip_extra_zip64 {
+	unsigned char magic[2];
+	unsigned char extra_size[2];
+	unsigned char size[8];
+	unsigned char compressed_size[8];
+	unsigned char offset[8];
+	unsigned char disk[4];
+	unsigned char _end[1];
+};
+
  struct zip64_dir_trailer {
  	unsigned char magic[4];
  	unsigned char record_size[8];
@@ -122,6 +133,9 @@ struct zip64_dir_trailer_locator {
  #define ZIP_EXTRA_MTIME_SIZE	offsetof(struct zip_extra_mtime, _end)
  #define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \
  	(ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags))
+#define ZIP_EXTRA_ZIP64_SIZE	offsetof(struct zip_extra_zip64, _end)
+#define ZIP_EXTRA_ZIP64_PAYLOAD_SIZE \
+	(ZIP_EXTRA_ZIP64_SIZE - offsetof(struct zip_extra_zip64, size))
  #define ZIP64_DIR_TRAILER_SIZE	offsetof(struct zip64_dir_trailer, _end)
  #define ZIP64_DIR_TRAILER_RECORD_SIZE \
  	(ZIP64_DIR_TRAILER_SIZE - \
@@ -219,19 +233,25 @@ static void set_zip_dir_data_desc(struct zip_dir_header *header,
  				  unsigned long compressed_size,
  				  unsigned long crc)
  {
+	int clamped = 0;
  	copy_le32(header->crc32, crc);
-	copy_le32(header->compressed_size, compressed_size);
-	copy_le32(header->size, size);
+	copy_le32(header->compressed_size, clamp_max(compressed_size, 0xFFFFFFFFU, &clamped));
+	copy_le32(header->size, clamp_max(size, 0xFFFFFFFFU, &clamped));
+	if (clamped)
+		zip_zip64 = 1;
  }

  static void set_zip_header_data_desc(struct zip_local_header *header,
  				     unsigned long size,
  				     unsigned long compressed_size,
-				     unsigned long crc)
+				     unsigned long crc,
+				     int *clamped)
  {
  	copy_le32(header->crc32, crc);
-	copy_le32(header->compressed_size, compressed_size);
-	copy_le32(header->size, size);
+	copy_le32(header->compressed_size, clamp_max(compressed_size, 0xFFFFFFFFU, clamped));
+	copy_le32(header->size, clamp_max(size, 0xFFFFFFFFU, clamped));
+	if (clamped)
+		zip_zip64 = 1;
  }

  static int has_only_ascii(const char *s)
@@ -279,6 +299,7 @@ static int write_zip_entry(struct archiver_args *args,
  	int is_binary = -1;
  	const char *path_without_prefix = path + args->baselen;
  	unsigned int creator_version = 0;
+	int clamped = 0;

  	crc = crc32(0, NULL, 0);

@@ -376,7 +397,7 @@ static int write_zip_entry(struct archiver_args *args,
  	copy_le16(dirent.comment_length, 0);
  	copy_le16(dirent.disk, 0);
  	copy_le32(dirent.attr2, attr2);
-	copy_le32(dirent.offset, zip_offset);
+	copy_le32(dirent.offset, clamp_max(zip_offset, 0xFFFFFFFFU, &clamped));

  	copy_le32(header.magic, 0x04034b50);
  	copy_le16(header.version, 10);
@@ -384,15 +405,26 @@ static int write_zip_entry(struct archiver_args *args,
  	copy_le16(header.compression_method, method);
  	copy_le16(header.mtime, zip_time);
  	copy_le16(header.mdate, zip_date);
-	set_zip_header_data_desc(&header, size, compressed_size, crc);
+	set_zip_header_data_desc(&header, size, compressed_size, crc, &clamped);
  	copy_le16(header.filename_length, pathlen);
-	copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE);
+	copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE + (clamped ? ZIP_EXTRA_ZIP64_SIZE : 0));
  	write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
  	zip_offset += ZIP_LOCAL_HEADER_SIZE;
  	write_or_die(1, path, pathlen);
  	zip_offset += pathlen;
  	write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE);
  	zip_offset += ZIP_EXTRA_MTIME_SIZE;
+	if (clamped) {
+		struct zip_extra_zip64 extra_zip64;
+		copy_le16(extra_zip64.magic, 0x0001);
+		copy_le16(extra_zip64.extra_size, ZIP_EXTRA_ZIP64_PAYLOAD_SIZE);
+		copy_le64(extra_zip64.size, size);
+		copy_le64(extra_zip64.compressed_size, compressed_size);
+		copy_le64(extra_zip64.offset, zip_offset);
+		copy_le32(extra_zip64.disk, 0);
+		write_or_die(1, &extra_zip64, ZIP_EXTRA_ZIP64_SIZE);
+		zip_offset += ZIP_EXTRA_ZIP64_SIZE;
+	}
  	if (stream && method == 0) {
  		unsigned char buf[STREAM_BUFFER_SIZE];
  		ssize_t readlen;
@@ -538,7 +570,7 @@ static void write_zip_trailer(const unsigned char *sha1)
  	copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);

  	write_or_die(1, zip_dir, zip_dir_offset);
-	if (clamped)
+	if (clamped || zip_zip64)
  		write_zip64_trailer();
  	write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
  	if (sha1)
-- 
2.1.4
Previous: Keith GoldfarbNext: Johannes Sixt
Message 2 of 44 in “Git archive doesn't fully support zip64”
  1. Keith GoldfarbApr 21, 2017
  2. archive-zip: Add zip64 headers when file size is too large for 32 bitsPeter Krefting, Apr 22, 2017
  3. Johannes SixtApr 22, 2017
  4. archive-zip: Add zip64 headers when file size is too large for 32 bitsPeter Krefting, Apr 22, 2017
  5. Johannes SixtApr 23, 2017
  6. Peter KreftingApr 23, 2017
  7. Johannes SixtApr 23, 2017
  8. Peter KreftingApr 24, 2017
  9. René ScharfeApr 24, 2017
  10. 0/5 archive-zip: support files and archives bigger than 4GBRené Scharfe, Apr 24, 2017
  11. 1/5 archive-zip: add tests for big ZIP archivesRené Scharfe, Apr 24, 2017
  12. 2/5 archive-zip: use strbuf for ZIP directoryRené Scharfe, Apr 24, 2017
  13. Junio C HamanoApr 25, 2017
  14. René ScharfeApr 25, 2017
  15. 3/5 archive-zip: write ZIP dir entry directly to strbufRené Scharfe, Apr 24, 2017
  16. 4/5 archive-zip: support archives bigger than 4GBRené Scharfe, Apr 24, 2017
  17. Peter KreftingApr 24, 2017
  18. René ScharfeApr 24, 2017
  19. René ScharfeApr 24, 2017
  20. Johannes SixtApr 24, 2017
  21. René ScharfeApr 24, 2017
  22. Peter KreftingApr 25, 2017
  23. René ScharfeApr 25, 2017
  24. Peter KreftingApr 26, 2017
  25. René ScharfeApr 26, 2017
  26. Peter KreftingApr 27, 2017
  27. René ScharfeApr 27, 2017
  28. Peter KreftingApr 28, 2017
  29. 5/5 archive-zip: support files bigger than 4GBRené Scharfe, Apr 24, 2017
  30. Keith GoldfarbApr 24, 2017
  31. Junio C HamanoApr 25, 2017
  32. René ScharfeApr 25, 2017
  33. Torsten BögershausenApr 29, 2017
  34. René ScharfeApr 29, 2017
  35. Torsten BögershausenApr 30, 2017
  36. René ScharfeApr 30, 2017
  37. Torsten BögershausenApr 30, 2017
  38. Johannes SixtApr 30, 2017
  39. René ScharfeApr 30, 2017
  40. Junio C HamanoApr 30, 2017
  41. René ScharfeMay 1, 2017
  42. René ScharfeApr 23, 2017
  43. Peter KreftingApr 23, 2017
  44. Johannes SixtApr 23, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.