git
/
archive-zip.c
659 строк · 17.3 Кб
1/*
2* Copyright (c) 2006 Rene Scharfe
3*/
4
5#define USE_THE_REPOSITORY_VARIABLE6
7#include "git-compat-util.h"8#include "config.h"9#include "archive.h"10#include "gettext.h"11#include "git-zlib.h"12#include "hex.h"13#include "streaming.h"14#include "utf8.h"15#include "object-store-ll.h"16#include "strbuf.h"17#include "userdiff.h"18#include "write-or-die.h"19#include "xdiff-interface.h"20#include "date.h"21
22static int zip_date;23static int zip_time;24
25/* We only care about the "buf" part here. */
26static struct strbuf zip_dir;27
28static uintmax_t zip_offset;29static uint64_t zip_dir_entries;30
31static unsigned int max_creator_version;32
33#define ZIP_STREAM (1 << 3)34#define ZIP_UTF8 (1 << 11)35
36enum zip_method {37ZIP_METHOD_STORE = 0,38ZIP_METHOD_DEFLATE = 839};40
41struct zip_local_header {42unsigned char magic[4];43unsigned char version[2];44unsigned char flags[2];45unsigned char compression_method[2];46unsigned char mtime[2];47unsigned char mdate[2];48unsigned char crc32[4];49unsigned char compressed_size[4];50unsigned char size[4];51unsigned char filename_length[2];52unsigned char extra_length[2];53unsigned char _end[1];54};55
56struct zip_data_desc {57unsigned char magic[4];58unsigned char crc32[4];59unsigned char compressed_size[4];60unsigned char size[4];61unsigned char _end[1];62};63
64struct zip64_data_desc {65unsigned char magic[4];66unsigned char crc32[4];67unsigned char compressed_size[8];68unsigned char size[8];69unsigned char _end[1];70};71
72struct zip_dir_trailer {73unsigned char magic[4];74unsigned char disk[2];75unsigned char directory_start_disk[2];76unsigned char entries_on_this_disk[2];77unsigned char entries[2];78unsigned char size[4];79unsigned char offset[4];80unsigned char comment_length[2];81unsigned char _end[1];82};83
84struct zip_extra_mtime {85unsigned char magic[2];86unsigned char extra_size[2];87unsigned char flags[1];88unsigned char mtime[4];89unsigned char _end[1];90};91
92struct zip64_extra {93unsigned char magic[2];94unsigned char extra_size[2];95unsigned char size[8];96unsigned char compressed_size[8];97unsigned char _end[1];98};99
100struct zip64_dir_trailer {101unsigned char magic[4];102unsigned char record_size[8];103unsigned char creator_version[2];104unsigned char version[2];105unsigned char disk[4];106unsigned char directory_start_disk[4];107unsigned char entries_on_this_disk[8];108unsigned char entries[8];109unsigned char size[8];110unsigned char offset[8];111unsigned char _end[1];112};113
114struct zip64_dir_trailer_locator {115unsigned char magic[4];116unsigned char disk[4];117unsigned char offset[8];118unsigned char number_of_disks[4];119unsigned char _end[1];120};121
122/*
123* On ARM, padding is added at the end of the struct, so a simple
124* sizeof(struct ...) reports two bytes more than the payload size
125* we're interested in.
126*/
127#define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end)128#define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end)129#define ZIP64_DATA_DESC_SIZE offsetof(struct zip64_data_desc, _end)130#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end)131#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end)132#define ZIP_EXTRA_MTIME_SIZE offsetof(struct zip_extra_mtime, _end)133#define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \134(ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags))135#define ZIP64_EXTRA_SIZE offsetof(struct zip64_extra, _end)136#define ZIP64_EXTRA_PAYLOAD_SIZE \137(ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size))138#define ZIP64_DIR_TRAILER_SIZE offsetof(struct zip64_dir_trailer, _end)139#define ZIP64_DIR_TRAILER_RECORD_SIZE \140(ZIP64_DIR_TRAILER_SIZE - \141offsetof(struct zip64_dir_trailer, creator_version))142#define ZIP64_DIR_TRAILER_LOCATOR_SIZE \143offsetof(struct zip64_dir_trailer_locator, _end)144
145static void copy_le16(unsigned char *dest, unsigned int n)146{
147dest[0] = 0xff & n;148dest[1] = 0xff & (n >> 010);149}
150
151static void copy_le32(unsigned char *dest, unsigned int n)152{
153dest[0] = 0xff & n;154dest[1] = 0xff & (n >> 010);155dest[2] = 0xff & (n >> 020);156dest[3] = 0xff & (n >> 030);157}
158
159static void copy_le64(unsigned char *dest, uint64_t n)160{
161dest[0] = 0xff & n;162dest[1] = 0xff & (n >> 010);163dest[2] = 0xff & (n >> 020);164dest[3] = 0xff & (n >> 030);165dest[4] = 0xff & (n >> 040);166dest[5] = 0xff & (n >> 050);167dest[6] = 0xff & (n >> 060);168dest[7] = 0xff & (n >> 070);169}
170
171static uint64_t clamp_max(uint64_t n, uint64_t max, int *clamped)172{
173if (n <= max)174return n;175*clamped = 1;176return max;177}
178
179static void copy_le16_clamp(unsigned char *dest, uint64_t n, int *clamped)180{
181copy_le16(dest, clamp_max(n, 0xffff, clamped));182}
183
184static void copy_le32_clamp(unsigned char *dest, uint64_t n, int *clamped)185{
186copy_le32(dest, clamp_max(n, 0xffffffff, clamped));187}
188
189static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n)190{
191while (size-- > 0) {192strbuf_addch(sb, n & 0xff);193n >>= 8;194}195return -!!n;196}
197
198static uint32_t clamp32(uintmax_t n)199{
200const uintmax_t max = 0xffffffff;201return (n < max) ? n : max;202}
203
204static void *zlib_deflate_raw(void *data, unsigned long size,205int compression_level,206unsigned long *compressed_size)207{
208git_zstream stream;209unsigned long maxsize;210void *buffer;211int result;212
213git_deflate_init_raw(&stream, compression_level);214maxsize = git_deflate_bound(&stream, size);215buffer = xmalloc(maxsize);216
217stream.next_in = data;218stream.avail_in = size;219stream.next_out = buffer;220stream.avail_out = maxsize;221
222do {223result = git_deflate(&stream, Z_FINISH);224} while (result == Z_OK);225
226if (result != Z_STREAM_END) {227free(buffer);228return NULL;229}230
231git_deflate_end(&stream);232*compressed_size = stream.total_out;233
234return buffer;235}
236
237static void write_zip_data_desc(unsigned long size,238unsigned long compressed_size,239unsigned long crc)240{
241if (size >= 0xffffffff || compressed_size >= 0xffffffff) {242struct zip64_data_desc trailer;243copy_le32(trailer.magic, 0x08074b50);244copy_le32(trailer.crc32, crc);245copy_le64(trailer.compressed_size, compressed_size);246copy_le64(trailer.size, size);247write_or_die(1, &trailer, ZIP64_DATA_DESC_SIZE);248zip_offset += ZIP64_DATA_DESC_SIZE;249} else {250struct zip_data_desc trailer;251copy_le32(trailer.magic, 0x08074b50);252copy_le32(trailer.crc32, crc);253copy_le32(trailer.compressed_size, compressed_size);254copy_le32(trailer.size, size);255write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);256zip_offset += ZIP_DATA_DESC_SIZE;257}258}
259
260static void set_zip_header_data_desc(struct zip_local_header *header,261unsigned long size,262unsigned long compressed_size,263unsigned long crc)264{
265copy_le32(header->crc32, crc);266copy_le32(header->compressed_size, compressed_size);267copy_le32(header->size, size);268}
269
270static int has_only_ascii(const char *s)271{
272for (;;) {273int c = *s++;274if (c == '\0')275return 1;276if (!isascii(c))277return 0;278}279}
280
281static int entry_is_binary(struct index_state *istate, const char *path,282const void *buffer, size_t size)283{
284struct userdiff_driver *driver = userdiff_find_by_path(istate, path);285if (!driver)286driver = userdiff_find_by_name("default");287if (driver->binary != -1)288return driver->binary;289return buffer_is_binary(buffer, size);290}
291
292#define STREAM_BUFFER_SIZE (1024 * 16)293
294static int write_zip_entry(struct archiver_args *args,295const struct object_id *oid,296const char *path, size_t pathlen,297unsigned int mode,298void *buffer, unsigned long size)299{
300struct zip_local_header header;301uintmax_t offset = zip_offset;302struct zip_extra_mtime extra;303struct zip64_extra extra64;304size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE;305int need_zip64_extra = 0;306unsigned long attr2;307unsigned long compressed_size;308unsigned long crc;309enum zip_method method;310unsigned char *out;311void *deflated = NULL;312struct git_istream *stream = NULL;313unsigned long flags = 0;314int is_binary = -1;315const char *path_without_prefix = path + args->baselen;316unsigned int creator_version = 0;317unsigned int version_needed = 10;318size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE;319size_t zip64_dir_extra_payload_size = 0;320
321crc = crc32(0, NULL, 0);322
323if (!has_only_ascii(path)) {324if (is_utf8(path))325flags |= ZIP_UTF8;326else327warning(_("path is not valid UTF-8: %s"), path);328}329
330if (pathlen > 0xffff) {331return error(_("path too long (%d chars, SHA1: %s): %s"),332(int)pathlen, oid_to_hex(oid), path);333}334
335if (S_ISDIR(mode) || S_ISGITLINK(mode)) {336method = ZIP_METHOD_STORE;337attr2 = 16;338out = NULL;339compressed_size = 0;340} else if (S_ISREG(mode) || S_ISLNK(mode)) {341method = ZIP_METHOD_STORE;342attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :343(mode & 0111) ? ((mode) << 16) : 0;344if (S_ISLNK(mode) || (mode & 0111))345creator_version = 0x0317;346if (S_ISREG(mode) && args->compression_level != 0 && size > 0)347method = ZIP_METHOD_DEFLATE;348
349if (!buffer) {350enum object_type type;351stream = open_istream(args->repo, oid, &type, &size,352NULL);353if (!stream)354return error(_("cannot stream blob %s"),355oid_to_hex(oid));356flags |= ZIP_STREAM;357out = NULL;358} else {359crc = crc32(crc, buffer, size);360is_binary = entry_is_binary(args->repo->index,361path_without_prefix,362buffer, size);363out = buffer;364}365compressed_size = (method == ZIP_METHOD_STORE) ? size : 0;366} else {367return error(_("unsupported file mode: 0%o (SHA1: %s)"), mode,368oid_to_hex(oid));369}370
371if (creator_version > max_creator_version)372max_creator_version = creator_version;373
374if (buffer && method == ZIP_METHOD_DEFLATE) {375out = deflated = zlib_deflate_raw(buffer, size,376args->compression_level,377&compressed_size);378if (!out || compressed_size >= size) {379out = buffer;380method = ZIP_METHOD_STORE;381compressed_size = size;382}383}384
385copy_le16(extra.magic, 0x5455);386copy_le16(extra.extra_size, ZIP_EXTRA_MTIME_PAYLOAD_SIZE);387extra.flags[0] = 1; /* just mtime */388copy_le32(extra.mtime, args->time);389
390if (size > 0xffffffff || compressed_size > 0xffffffff)391need_zip64_extra = 1;392if (stream && size > 0x7fffffff)393need_zip64_extra = 1;394
395if (need_zip64_extra)396version_needed = 45;397
398copy_le32(header.magic, 0x04034b50);399copy_le16(header.version, version_needed);400copy_le16(header.flags, flags);401copy_le16(header.compression_method, method);402copy_le16(header.mtime, zip_time);403copy_le16(header.mdate, zip_date);404if (need_zip64_extra) {405set_zip_header_data_desc(&header, 0xffffffff, 0xffffffff, crc);406header_extra_size += ZIP64_EXTRA_SIZE;407} else {408set_zip_header_data_desc(&header, size, compressed_size, crc);409}410copy_le16(header.filename_length, pathlen);411copy_le16(header.extra_length, header_extra_size);412write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);413zip_offset += ZIP_LOCAL_HEADER_SIZE;414write_or_die(1, path, pathlen);415zip_offset += pathlen;416write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE);417zip_offset += ZIP_EXTRA_MTIME_SIZE;418if (need_zip64_extra) {419copy_le16(extra64.magic, 0x0001);420copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE);421copy_le64(extra64.size, size);422copy_le64(extra64.compressed_size, compressed_size);423write_or_die(1, &extra64, ZIP64_EXTRA_SIZE);424zip_offset += ZIP64_EXTRA_SIZE;425}426
427if (stream && method == ZIP_METHOD_STORE) {428unsigned char buf[STREAM_BUFFER_SIZE];429ssize_t readlen;430
431for (;;) {432readlen = read_istream(stream, buf, sizeof(buf));433if (readlen <= 0)434break;435crc = crc32(crc, buf, readlen);436if (is_binary == -1)437is_binary = entry_is_binary(args->repo->index,438path_without_prefix,439buf, readlen);440write_or_die(1, buf, readlen);441}442close_istream(stream);443if (readlen)444return readlen;445
446compressed_size = size;447zip_offset += compressed_size;448
449write_zip_data_desc(size, compressed_size, crc);450} else if (stream && method == ZIP_METHOD_DEFLATE) {451unsigned char buf[STREAM_BUFFER_SIZE];452ssize_t readlen;453git_zstream zstream;454int result;455size_t out_len;456unsigned char compressed[STREAM_BUFFER_SIZE * 2];457
458git_deflate_init_raw(&zstream, args->compression_level);459
460compressed_size = 0;461zstream.next_out = compressed;462zstream.avail_out = sizeof(compressed);463
464for (;;) {465readlen = read_istream(stream, buf, sizeof(buf));466if (readlen <= 0)467break;468crc = crc32(crc, buf, readlen);469if (is_binary == -1)470is_binary = entry_is_binary(args->repo->index,471path_without_prefix,472buf, readlen);473
474zstream.next_in = buf;475zstream.avail_in = readlen;476result = git_deflate(&zstream, 0);477if (result != Z_OK)478die(_("deflate error (%d)"), result);479out_len = zstream.next_out - compressed;480
481if (out_len > 0) {482write_or_die(1, compressed, out_len);483compressed_size += out_len;484zstream.next_out = compressed;485zstream.avail_out = sizeof(compressed);486}487
488}489close_istream(stream);490if (readlen)491return readlen;492
493zstream.next_in = buf;494zstream.avail_in = 0;495result = git_deflate(&zstream, Z_FINISH);496if (result != Z_STREAM_END)497die("deflate error (%d)", result);498
499git_deflate_end(&zstream);500out_len = zstream.next_out - compressed;501write_or_die(1, compressed, out_len);502compressed_size += out_len;503zip_offset += compressed_size;504
505write_zip_data_desc(size, compressed_size, crc);506} else if (compressed_size > 0) {507write_or_die(1, out, compressed_size);508zip_offset += compressed_size;509}510
511free(deflated);512
513if (compressed_size > 0xffffffff || size > 0xffffffff ||514offset > 0xffffffff) {515if (compressed_size >= 0xffffffff)516zip64_dir_extra_payload_size += 8;517if (size >= 0xffffffff)518zip64_dir_extra_payload_size += 8;519if (offset >= 0xffffffff)520zip64_dir_extra_payload_size += 8;521zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size;522}523
524strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */525strbuf_add_le(&zip_dir, 2, creator_version);526strbuf_add_le(&zip_dir, 2, version_needed);527strbuf_add_le(&zip_dir, 2, flags);528strbuf_add_le(&zip_dir, 2, method);529strbuf_add_le(&zip_dir, 2, zip_time);530strbuf_add_le(&zip_dir, 2, zip_date);531strbuf_add_le(&zip_dir, 4, crc);532strbuf_add_le(&zip_dir, 4, clamp32(compressed_size));533strbuf_add_le(&zip_dir, 4, clamp32(size));534strbuf_add_le(&zip_dir, 2, pathlen);535strbuf_add_le(&zip_dir, 2, zip_dir_extra_size);536strbuf_add_le(&zip_dir, 2, 0); /* comment length */537strbuf_add_le(&zip_dir, 2, 0); /* disk */538strbuf_add_le(&zip_dir, 2, !is_binary);539strbuf_add_le(&zip_dir, 4, attr2);540strbuf_add_le(&zip_dir, 4, clamp32(offset));541strbuf_add(&zip_dir, path, pathlen);542strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE);543if (zip64_dir_extra_payload_size) {544strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */545strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size);546if (size >= 0xffffffff)547strbuf_add_le(&zip_dir, 8, size);548if (compressed_size >= 0xffffffff)549strbuf_add_le(&zip_dir, 8, compressed_size);550if (offset >= 0xffffffff)551strbuf_add_le(&zip_dir, 8, offset);552}553zip_dir_entries++;554
555return 0;556}
557
558static void write_zip64_trailer(void)559{
560struct zip64_dir_trailer trailer64;561struct zip64_dir_trailer_locator locator64;562
563copy_le32(trailer64.magic, 0x06064b50);564copy_le64(trailer64.record_size, ZIP64_DIR_TRAILER_RECORD_SIZE);565copy_le16(trailer64.creator_version, max_creator_version);566copy_le16(trailer64.version, 45);567copy_le32(trailer64.disk, 0);568copy_le32(trailer64.directory_start_disk, 0);569copy_le64(trailer64.entries_on_this_disk, zip_dir_entries);570copy_le64(trailer64.entries, zip_dir_entries);571copy_le64(trailer64.size, zip_dir.len);572copy_le64(trailer64.offset, zip_offset);573
574copy_le32(locator64.magic, 0x07064b50);575copy_le32(locator64.disk, 0);576copy_le64(locator64.offset, zip_offset + zip_dir.len);577copy_le32(locator64.number_of_disks, 1);578
579write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE);580write_or_die(1, &locator64, ZIP64_DIR_TRAILER_LOCATOR_SIZE);581}
582
583static void write_zip_trailer(const struct object_id *oid)584{
585struct zip_dir_trailer trailer;586int clamped = 0;587
588copy_le32(trailer.magic, 0x06054b50);589copy_le16(trailer.disk, 0);590copy_le16(trailer.directory_start_disk, 0);591copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries,592&clamped);593copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped);594copy_le32(trailer.size, zip_dir.len);595copy_le32_clamp(trailer.offset, zip_offset, &clamped);596copy_le16(trailer.comment_length, oid ? the_hash_algo->hexsz : 0);597
598write_or_die(1, zip_dir.buf, zip_dir.len);599if (clamped)600write_zip64_trailer();601write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);602if (oid)603write_or_die(1, oid_to_hex(oid), the_hash_algo->hexsz);604}
605
606static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)607{
608time_t time;609struct tm tm;610
611if (date_overflows(*timestamp))612die(_("timestamp too large for this system: %"PRItime),613*timestamp);614time = (time_t)*timestamp;615localtime_r(&time, &tm);616*timestamp = time;617
618*dos_date = tm.tm_mday + (tm.tm_mon + 1) * 32 +619(tm.tm_year + 1900 - 1980) * 512;620*dos_time = tm.tm_sec / 2 + tm.tm_min * 32 + tm.tm_hour * 2048;621}
622
623static int archive_zip_config(const char *var, const char *value,624const struct config_context *ctx UNUSED,625void *data UNUSED)626{
627return userdiff_config(var, value);628}
629
630static int write_zip_archive(const struct archiver *ar UNUSED,631struct archiver_args *args)632{
633int err;634
635git_config(archive_zip_config, NULL);636
637dos_time(&args->time, &zip_date, &zip_time);638
639strbuf_init(&zip_dir, 0);640
641err = write_archive_entries(args, write_zip_entry);642if (!err)643write_zip_trailer(args->commit_oid);644
645strbuf_release(&zip_dir);646
647return err;648}
649
650static struct archiver zip_archiver = {651.name = "zip",652.write_archive = write_zip_archive,653.flags = ARCHIVER_WANT_COMPRESSION_LEVELS|ARCHIVER_REMOTE,654};655
656void init_zip_archiver(void)657{
658register_archiver(&zip_archiver);659}
660