diff options
Diffstat (limited to 'archive-zip.c')
| -rw-r--r-- | archive-zip.c | 130 | 
1 files changed, 120 insertions, 10 deletions
diff --git a/archive-zip.c b/archive-zip.c index 4bde019bce..9db47357b0 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -5,6 +5,8 @@  #include "archive.h"  #include "streaming.h"  #include "utf8.h" +#include "userdiff.h" +#include "xdiff-interface.h"  static int zip_date;  static int zip_time; @@ -14,7 +16,9 @@ static unsigned int zip_dir_size;  static unsigned int zip_offset;  static unsigned int zip_dir_offset; -static unsigned int zip_dir_entries; +static uint64_t zip_dir_entries; + +static unsigned int max_creator_version;  #define ZIP_DIRECTORY_MIN_SIZE	(1024 * 1024)  #define ZIP_STREAM	(1 <<  3) @@ -84,6 +88,28 @@ struct zip_extra_mtime {  	unsigned char _end[1];  }; +struct zip64_dir_trailer { +	unsigned char magic[4]; +	unsigned char record_size[8]; +	unsigned char creator_version[2]; +	unsigned char version[2]; +	unsigned char disk[4]; +	unsigned char directory_start_disk[4]; +	unsigned char entries_on_this_disk[8]; +	unsigned char entries[8]; +	unsigned char size[8]; +	unsigned char offset[8]; +	unsigned char _end[1]; +}; + +struct zip64_dir_trailer_locator { +	unsigned char magic[4]; +	unsigned char disk[4]; +	unsigned char offset[8]; +	unsigned char number_of_disks[4]; +	unsigned char _end[1]; +}; +  /*   * On ARM, padding is added at the end of the struct, so a simple   * sizeof(struct ...) reports two bytes more than the payload size @@ -96,6 +122,12 @@ struct zip_extra_mtime {  #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 ZIP64_DIR_TRAILER_SIZE	offsetof(struct zip64_dir_trailer, _end) +#define ZIP64_DIR_TRAILER_RECORD_SIZE \ +	(ZIP64_DIR_TRAILER_SIZE - \ +	 offsetof(struct zip64_dir_trailer, creator_version)) +#define ZIP64_DIR_TRAILER_LOCATOR_SIZE \ +	offsetof(struct zip64_dir_trailer_locator, _end)  static void copy_le16(unsigned char *dest, unsigned int n)  { @@ -111,6 +143,31 @@ static void copy_le32(unsigned char *dest, unsigned int n)  	dest[3] = 0xff & (n >> 030);  } +static void copy_le64(unsigned char *dest, uint64_t n) +{ +	dest[0] = 0xff & n; +	dest[1] = 0xff & (n >> 010); +	dest[2] = 0xff & (n >> 020); +	dest[3] = 0xff & (n >> 030); +	dest[4] = 0xff & (n >> 040); +	dest[5] = 0xff & (n >> 050); +	dest[6] = 0xff & (n >> 060); +	dest[7] = 0xff & (n >> 070); +} + +static uint64_t clamp_max(uint64_t n, uint64_t max, int *clamped) +{ +	if (n <= max) +		return n; +	*clamped = 1; +	return max; +} + +static void copy_le16_clamp(unsigned char *dest, uint64_t n, int *clamped) +{ +	copy_le16(dest, clamp_max(n, 0xffff, clamped)); +} +  static void *zlib_deflate_raw(void *data, unsigned long size,  			      int compression_level,  			      unsigned long *compressed_size) @@ -120,7 +177,6 @@ static void *zlib_deflate_raw(void *data, unsigned long size,  	void *buffer;  	int result; -	memset(&stream, 0, sizeof(stream));  	git_deflate_init_raw(&stream, compression_level);  	maxsize = git_deflate_bound(&stream, size);  	buffer = xmalloc(maxsize); @@ -189,6 +245,16 @@ static int has_only_ascii(const char *s)  	}  } +static int entry_is_binary(const char *path, const void *buffer, size_t size) +{ +	struct userdiff_driver *driver = userdiff_find_by_path(path); +	if (!driver) +		driver = userdiff_find_by_name("default"); +	if (driver->binary != -1) +		return driver->binary; +	return buffer_is_binary(buffer, size); +} +  #define STREAM_BUFFER_SIZE (1024 * 16)  static int write_zip_entry(struct archiver_args *args, @@ -210,6 +276,9 @@ static int write_zip_entry(struct archiver_args *args,  	struct git_istream *stream = NULL;  	unsigned long flags = 0;  	unsigned long size; +	int is_binary = -1; +	const char *path_without_prefix = path + args->baselen; +	unsigned int creator_version = 0;  	crc = crc32(0, NULL, 0); @@ -238,6 +307,8 @@ static int write_zip_entry(struct archiver_args *args,  		method = 0;  		attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :  			(mode & 0111) ? ((mode) << 16) : 0; +		if (S_ISLNK(mode) || (mode & 0111)) +			creator_version = 0x0317;  		if (S_ISREG(mode) && args->compression_level != 0 && size > 0)  			method = 8; @@ -256,6 +327,8 @@ static int write_zip_entry(struct archiver_args *args,  				return error("cannot read %s",  					     sha1_to_hex(sha1));  			crc = crc32(crc, buffer, size); +			is_binary = entry_is_binary(path_without_prefix, +						    buffer, size);  			out = buffer;  		}  		compressed_size = (method == 0) ? size : 0; @@ -264,6 +337,9 @@ static int write_zip_entry(struct archiver_args *args,  				sha1_to_hex(sha1));  	} +	if (creator_version > max_creator_version) +		max_creator_version = creator_version; +  	if (buffer && method == 8) {  		out = deflated = zlib_deflate_raw(buffer, size,  						  args->compression_level, @@ -288,8 +364,7 @@ static int write_zip_entry(struct archiver_args *args,  	}  	copy_le32(dirent.magic, 0x02014b50); -	copy_le16(dirent.creator_version, -		S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0); +	copy_le16(dirent.creator_version, creator_version);  	copy_le16(dirent.version, 10);  	copy_le16(dirent.flags, flags);  	copy_le16(dirent.compression_method, method); @@ -300,7 +375,6 @@ static int write_zip_entry(struct archiver_args *args,  	copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE);  	copy_le16(dirent.comment_length, 0);  	copy_le16(dirent.disk, 0); -	copy_le16(dirent.attr1, 0);  	copy_le32(dirent.attr2, attr2);  	copy_le32(dirent.offset, zip_offset); @@ -328,6 +402,9 @@ static int write_zip_entry(struct archiver_args *args,  			if (readlen <= 0)  				break;  			crc = crc32(crc, buf, readlen); +			if (is_binary == -1) +				is_binary = entry_is_binary(path_without_prefix, +							    buf, readlen);  			write_or_die(1, buf, readlen);  		}  		close_istream(stream); @@ -349,7 +426,6 @@ static int write_zip_entry(struct archiver_args *args,  		size_t out_len;  		unsigned char compressed[STREAM_BUFFER_SIZE * 2]; -		memset(&zstream, 0, sizeof(zstream));  		git_deflate_init_raw(&zstream, args->compression_level);  		compressed_size = 0; @@ -361,6 +437,9 @@ static int write_zip_entry(struct archiver_args *args,  			if (readlen <= 0)  				break;  			crc = crc32(crc, buf, readlen); +			if (is_binary == -1) +				is_binary = entry_is_binary(path_without_prefix, +							    buf, readlen);  			zstream.next_in = buf;  			zstream.avail_in = readlen; @@ -405,6 +484,8 @@ static int write_zip_entry(struct archiver_args *args,  	free(deflated);  	free(buffer); +	copy_le16(dirent.attr1, !is_binary); +  	memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);  	zip_dir_offset += ZIP_DIR_HEADER_SIZE;  	memcpy(zip_dir + zip_dir_offset, path, pathlen); @@ -416,23 +497,52 @@ static int write_zip_entry(struct archiver_args *args,  	return 0;  } +static void write_zip64_trailer(void) +{ +	struct zip64_dir_trailer trailer64; +	struct zip64_dir_trailer_locator locator64; + +	copy_le32(trailer64.magic, 0x06064b50); +	copy_le64(trailer64.record_size, ZIP64_DIR_TRAILER_RECORD_SIZE); +	copy_le16(trailer64.creator_version, max_creator_version); +	copy_le16(trailer64.version, 45); +	copy_le32(trailer64.disk, 0); +	copy_le32(trailer64.directory_start_disk, 0); +	copy_le64(trailer64.entries_on_this_disk, zip_dir_entries); +	copy_le64(trailer64.entries, zip_dir_entries); +	copy_le64(trailer64.size, zip_dir_offset); +	copy_le64(trailer64.offset, zip_offset); + +	copy_le32(locator64.magic, 0x07064b50); +	copy_le32(locator64.disk, 0); +	copy_le64(locator64.offset, zip_offset + zip_dir_offset); +	copy_le32(locator64.number_of_disks, 1); + +	write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE); +	write_or_die(1, &locator64, ZIP64_DIR_TRAILER_LOCATOR_SIZE); +} +  static void write_zip_trailer(const unsigned char *sha1)  {  	struct zip_dir_trailer trailer; +	int clamped = 0;  	copy_le32(trailer.magic, 0x06054b50);  	copy_le16(trailer.disk, 0);  	copy_le16(trailer.directory_start_disk, 0); -	copy_le16(trailer.entries_on_this_disk, zip_dir_entries); -	copy_le16(trailer.entries, zip_dir_entries); +	copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries, +			&clamped); +	copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped);  	copy_le32(trailer.size, zip_dir_offset);  	copy_le32(trailer.offset, zip_offset); -	copy_le16(trailer.comment_length, sha1 ? 40 : 0); +	copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);  	write_or_die(1, zip_dir, zip_dir_offset); +	if (clamped) +		write_zip64_trailer();  	write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);  	if (sha1) -		write_or_die(1, sha1_to_hex(sha1), 40); +		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);  }  static void dos_time(time_t *time, int *dos_date, int *dos_time)  | 
