928da1d79108ba783ec2afba2b8c6f8d5653f19b
   1/*
   2 * Copyright (c) 2006 Rene Scharfe
   3 */
   4#include "cache.h"
   5#include "archive.h"
   6#include "streaming.h"
   7#include "commit.h"
   8#include "utf8.h"
   9
  10static int zip_date;
  11static int zip_time;
  12
  13static unsigned char *zip_dir;
  14static unsigned int zip_dir_size;
  15
  16static unsigned int zip_offset;
  17static unsigned int zip_dir_offset;
  18static unsigned int zip_dir_entries;
  19
  20#define ZIP_DIRECTORY_MIN_SIZE  (1024 * 1024)
  21#define ZIP_STREAM      (1 <<  3)
  22#define ZIP_UTF8        (1 << 11)
  23
  24struct zip_local_header {
  25        unsigned char magic[4];
  26        unsigned char version[2];
  27        unsigned char flags[2];
  28        unsigned char compression_method[2];
  29        unsigned char mtime[2];
  30        unsigned char mdate[2];
  31        unsigned char crc32[4];
  32        unsigned char compressed_size[4];
  33        unsigned char size[4];
  34        unsigned char filename_length[2];
  35        unsigned char extra_length[2];
  36        unsigned char _end[1];
  37};
  38
  39struct zip_data_desc {
  40        unsigned char magic[4];
  41        unsigned char crc32[4];
  42        unsigned char compressed_size[4];
  43        unsigned char size[4];
  44        unsigned char _end[1];
  45};
  46
  47struct zip_dir_header {
  48        unsigned char magic[4];
  49        unsigned char creator_version[2];
  50        unsigned char version[2];
  51        unsigned char flags[2];
  52        unsigned char compression_method[2];
  53        unsigned char mtime[2];
  54        unsigned char mdate[2];
  55        unsigned char crc32[4];
  56        unsigned char compressed_size[4];
  57        unsigned char size[4];
  58        unsigned char filename_length[2];
  59        unsigned char extra_length[2];
  60        unsigned char comment_length[2];
  61        unsigned char disk[2];
  62        unsigned char attr1[2];
  63        unsigned char attr2[4];
  64        unsigned char offset[4];
  65        unsigned char _end[1];
  66};
  67
  68struct zip_dir_trailer {
  69        unsigned char magic[4];
  70        unsigned char disk[2];
  71        unsigned char directory_start_disk[2];
  72        unsigned char entries_on_this_disk[2];
  73        unsigned char entries[2];
  74        unsigned char size[4];
  75        unsigned char offset[4];
  76        unsigned char comment_length[2];
  77        unsigned char _end[1];
  78};
  79
  80/*
  81 * On ARM, padding is added at the end of the struct, so a simple
  82 * sizeof(struct ...) reports two bytes more than the payload size
  83 * we're interested in.
  84 */
  85#define ZIP_LOCAL_HEADER_SIZE   offsetof(struct zip_local_header, _end)
  86#define ZIP_DATA_DESC_SIZE      offsetof(struct zip_data_desc, _end)
  87#define ZIP_DIR_HEADER_SIZE     offsetof(struct zip_dir_header, _end)
  88#define ZIP_DIR_TRAILER_SIZE    offsetof(struct zip_dir_trailer, _end)
  89
  90static void copy_le16(unsigned char *dest, unsigned int n)
  91{
  92        dest[0] = 0xff & n;
  93        dest[1] = 0xff & (n >> 010);
  94}
  95
  96static void copy_le32(unsigned char *dest, unsigned int n)
  97{
  98        dest[0] = 0xff & n;
  99        dest[1] = 0xff & (n >> 010);
 100        dest[2] = 0xff & (n >> 020);
 101        dest[3] = 0xff & (n >> 030);
 102}
 103
 104static void *zlib_deflate(void *data, unsigned long size,
 105                int compression_level, unsigned long *compressed_size)
 106{
 107        git_zstream stream;
 108        unsigned long maxsize;
 109        void *buffer;
 110        int result;
 111
 112        memset(&stream, 0, sizeof(stream));
 113        git_deflate_init(&stream, compression_level);
 114        maxsize = git_deflate_bound(&stream, size);
 115        buffer = xmalloc(maxsize);
 116
 117        stream.next_in = data;
 118        stream.avail_in = size;
 119        stream.next_out = buffer;
 120        stream.avail_out = maxsize;
 121
 122        do {
 123                result = git_deflate(&stream, Z_FINISH);
 124        } while (result == Z_OK);
 125
 126        if (result != Z_STREAM_END) {
 127                free(buffer);
 128                return NULL;
 129        }
 130
 131        git_deflate_end(&stream);
 132        *compressed_size = stream.total_out;
 133
 134        return buffer;
 135}
 136
 137static void write_zip_data_desc(unsigned long size,
 138                                unsigned long compressed_size,
 139                                unsigned long crc)
 140{
 141        struct zip_data_desc trailer;
 142
 143        copy_le32(trailer.magic, 0x08074b50);
 144        copy_le32(trailer.crc32, crc);
 145        copy_le32(trailer.compressed_size, compressed_size);
 146        copy_le32(trailer.size, size);
 147        write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
 148}
 149
 150static void set_zip_dir_data_desc(struct zip_dir_header *header,
 151                                  unsigned long size,
 152                                  unsigned long compressed_size,
 153                                  unsigned long crc)
 154{
 155        copy_le32(header->crc32, crc);
 156        copy_le32(header->compressed_size, compressed_size);
 157        copy_le32(header->size, size);
 158}
 159
 160static void set_zip_header_data_desc(struct zip_local_header *header,
 161                                     unsigned long size,
 162                                     unsigned long compressed_size,
 163                                     unsigned long crc)
 164{
 165        copy_le32(header->crc32, crc);
 166        copy_le32(header->compressed_size, compressed_size);
 167        copy_le32(header->size, size);
 168}
 169
 170#define STREAM_BUFFER_SIZE (1024 * 16)
 171
 172static int write_zip_entry(struct archiver_args *args,
 173                           const unsigned char *sha1,
 174                           const char *path, size_t pathlen,
 175                           unsigned int mode)
 176{
 177        struct zip_local_header header;
 178        struct zip_dir_header dirent;
 179        unsigned int creator_version = 0;
 180        unsigned long attr2 = 0;
 181        unsigned long compressed_size;
 182        unsigned long crc;
 183        unsigned long direntsize;
 184        int method;
 185        unsigned char *out;
 186        void *deflated = NULL;
 187        void *buffer;
 188        struct git_istream *stream = NULL;
 189        unsigned long flags = 0;
 190        unsigned long size;
 191
 192        crc = crc32(0, NULL, 0);
 193
 194        if (has_non_ascii(path)) {
 195                if (is_utf8(path))
 196                        flags |= ZIP_UTF8;
 197                else
 198                        warning("Path is not valid UTF-8: %s", path);
 199        }
 200
 201        if (pathlen > 0xffff) {
 202                return error("path too long (%d chars, SHA1: %s): %s",
 203                                (int)pathlen, sha1_to_hex(sha1), path);
 204        }
 205
 206        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
 207                method = 0;
 208                attr2 = 16;
 209                out = NULL;
 210                size = 0;
 211                compressed_size = 0;
 212                buffer = NULL;
 213                size = 0;
 214        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
 215                enum object_type type = sha1_object_info(sha1, &size);
 216
 217                method = 0;
 218                if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
 219                        method = 8;
 220                if (S_ISLNK(mode) || (mode & 0111) || (flags & ZIP_UTF8)) {
 221                        creator_version = 0x033f;
 222                        attr2 = mode;
 223                        if (S_ISLNK(mode))
 224                                attr2 |= 0777;
 225                        attr2 <<= 16;
 226                }
 227                compressed_size = size;
 228
 229                if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
 230                    size > big_file_threshold) {
 231                        stream = open_istream(sha1, &type, &size, NULL);
 232                        if (!stream)
 233                                return error("cannot stream blob %s",
 234                                             sha1_to_hex(sha1));
 235                        flags |= ZIP_STREAM;
 236                        out = buffer = NULL;
 237                } else {
 238                        buffer = sha1_file_to_archive(args, path, sha1, mode,
 239                                                      &type, &size);
 240                        if (!buffer)
 241                                return error("cannot read %s",
 242                                             sha1_to_hex(sha1));
 243                        crc = crc32(crc, buffer, size);
 244                        out = buffer;
 245                }
 246        } else {
 247                return error("unsupported file mode: 0%o (SHA1: %s)", mode,
 248                                sha1_to_hex(sha1));
 249        }
 250
 251        if (buffer && method == 8) {
 252                deflated = zlib_deflate(buffer, size, args->compression_level,
 253                                &compressed_size);
 254                if (deflated && compressed_size - 6 < size) {
 255                        /* ZLIB --> raw compressed data (see RFC 1950) */
 256                        /* CMF and FLG ... */
 257                        out = (unsigned char *)deflated + 2;
 258                        compressed_size -= 6;   /* ... and ADLER32 */
 259                } else {
 260                        method = 0;
 261                        compressed_size = size;
 262                }
 263        }
 264
 265        /* make sure we have enough free space in the dictionary */
 266        direntsize = ZIP_DIR_HEADER_SIZE + pathlen;
 267        while (zip_dir_size < zip_dir_offset + direntsize) {
 268                zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
 269                zip_dir = xrealloc(zip_dir, zip_dir_size);
 270        }
 271
 272        copy_le32(dirent.magic, 0x02014b50);
 273        copy_le16(dirent.creator_version, creator_version);
 274        copy_le16(dirent.version, 10);
 275        copy_le16(dirent.flags, flags);
 276        copy_le16(dirent.compression_method, method);
 277        copy_le16(dirent.mtime, zip_time);
 278        copy_le16(dirent.mdate, zip_date);
 279        set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
 280        copy_le16(dirent.filename_length, pathlen);
 281        copy_le16(dirent.extra_length, 0);
 282        copy_le16(dirent.comment_length, 0);
 283        copy_le16(dirent.disk, 0);
 284        copy_le16(dirent.attr1, 0);
 285        copy_le32(dirent.attr2, attr2);
 286        copy_le32(dirent.offset, zip_offset);
 287
 288        copy_le32(header.magic, 0x04034b50);
 289        copy_le16(header.version, 10);
 290        copy_le16(header.flags, flags);
 291        copy_le16(header.compression_method, method);
 292        copy_le16(header.mtime, zip_time);
 293        copy_le16(header.mdate, zip_date);
 294        if (flags & ZIP_STREAM)
 295                set_zip_header_data_desc(&header, 0, 0, 0);
 296        else
 297                set_zip_header_data_desc(&header, size, compressed_size, crc);
 298        copy_le16(header.filename_length, pathlen);
 299        copy_le16(header.extra_length, 0);
 300        write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
 301        zip_offset += ZIP_LOCAL_HEADER_SIZE;
 302        write_or_die(1, path, pathlen);
 303        zip_offset += pathlen;
 304        if (stream && method == 0) {
 305                unsigned char buf[STREAM_BUFFER_SIZE];
 306                ssize_t readlen;
 307
 308                for (;;) {
 309                        readlen = read_istream(stream, buf, sizeof(buf));
 310                        if (readlen <= 0)
 311                                break;
 312                        crc = crc32(crc, buf, readlen);
 313                        write_or_die(1, buf, readlen);
 314                }
 315                close_istream(stream);
 316                if (readlen)
 317                        return readlen;
 318
 319                compressed_size = size;
 320                zip_offset += compressed_size;
 321
 322                write_zip_data_desc(size, compressed_size, crc);
 323                zip_offset += ZIP_DATA_DESC_SIZE;
 324
 325                set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
 326        } else if (stream && method == 8) {
 327                unsigned char buf[STREAM_BUFFER_SIZE];
 328                ssize_t readlen;
 329                git_zstream zstream;
 330                int result;
 331                size_t out_len;
 332                unsigned char compressed[STREAM_BUFFER_SIZE * 2];
 333
 334                memset(&zstream, 0, sizeof(zstream));
 335                git_deflate_init(&zstream, args->compression_level);
 336
 337                compressed_size = 0;
 338                zstream.next_out = compressed;
 339                zstream.avail_out = sizeof(compressed);
 340
 341                for (;;) {
 342                        readlen = read_istream(stream, buf, sizeof(buf));
 343                        if (readlen <= 0)
 344                                break;
 345                        crc = crc32(crc, buf, readlen);
 346
 347                        zstream.next_in = buf;
 348                        zstream.avail_in = readlen;
 349                        result = git_deflate(&zstream, 0);
 350                        if (result != Z_OK)
 351                                die("deflate error (%d)", result);
 352                        out = compressed;
 353                        if (!compressed_size)
 354                                out += 2;
 355                        out_len = zstream.next_out - out;
 356
 357                        if (out_len > 0) {
 358                                write_or_die(1, out, out_len);
 359                                compressed_size += out_len;
 360                                zstream.next_out = compressed;
 361                                zstream.avail_out = sizeof(compressed);
 362                        }
 363
 364                }
 365                close_istream(stream);
 366                if (readlen)
 367                        return readlen;
 368
 369                zstream.next_in = buf;
 370                zstream.avail_in = 0;
 371                result = git_deflate(&zstream, Z_FINISH);
 372                if (result != Z_STREAM_END)
 373                        die("deflate error (%d)", result);
 374
 375                git_deflate_end(&zstream);
 376                out = compressed;
 377                if (!compressed_size)
 378                        out += 2;
 379                out_len = zstream.next_out - out - 4;
 380                write_or_die(1, out, out_len);
 381                compressed_size += out_len;
 382                zip_offset += compressed_size;
 383
 384                write_zip_data_desc(size, compressed_size, crc);
 385                zip_offset += ZIP_DATA_DESC_SIZE;
 386
 387                set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
 388        } else if (compressed_size > 0) {
 389                write_or_die(1, out, compressed_size);
 390                zip_offset += compressed_size;
 391        }
 392
 393        free(deflated);
 394        free(buffer);
 395
 396        memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
 397        zip_dir_offset += ZIP_DIR_HEADER_SIZE;
 398        memcpy(zip_dir + zip_dir_offset, path, pathlen);
 399        zip_dir_offset += pathlen;
 400        zip_dir_entries++;
 401
 402        return 0;
 403}
 404
 405static void write_zip_trailer(const unsigned char *sha1)
 406{
 407        struct zip_dir_trailer trailer;
 408
 409        copy_le32(trailer.magic, 0x06054b50);
 410        copy_le16(trailer.disk, 0);
 411        copy_le16(trailer.directory_start_disk, 0);
 412        copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
 413        copy_le16(trailer.entries, zip_dir_entries);
 414        copy_le32(trailer.size, zip_dir_offset);
 415        copy_le32(trailer.offset, zip_offset);
 416        copy_le16(trailer.comment_length, sha1 ? 40 : 0);
 417
 418        write_or_die(1, zip_dir, zip_dir_offset);
 419        write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
 420        if (sha1)
 421                write_or_die(1, sha1_to_hex(sha1), 40);
 422}
 423
 424static void dos_time(time_t *time, int *dos_date, int *dos_time)
 425{
 426        struct tm *t = localtime(time);
 427
 428        *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 429                    (t->tm_year + 1900 - 1980) * 512;
 430        *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
 431}
 432
 433static int write_zip_archive(const struct archiver *ar,
 434                             struct archiver_args *args)
 435{
 436        int err;
 437
 438        dos_time(&args->time, &zip_date, &zip_time);
 439
 440        zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
 441        zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
 442
 443        err = write_archive_entries(args, write_zip_entry);
 444        if (!err)
 445                write_zip_trailer(args->commit_sha1);
 446
 447        free(zip_dir);
 448
 449        return err;
 450}
 451
 452static struct archiver zip_archiver = {
 453        "zip",
 454        write_zip_archive,
 455        ARCHIVER_WANT_COMPRESSION_LEVELS|ARCHIVER_REMOTE
 456};
 457
 458void init_zip_archiver(void)
 459{
 460        register_archiver(&zip_archiver);
 461}