]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'rs/zip-with-uncompressed-size-in-the-header' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jan 2013 01:22:27 +0000 (17:22 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jan 2013 01:22:27 +0000 (17:22 -0800)
"git archive" did not record uncompressed size in the header when
streaming a zip archive, which confused some implementations of
unzip.

* rs/zip-with-uncompressed-size-in-the-header:
  archive-zip: write uncompressed size into header even with streaming

1  2 
archive-zip.c

diff --combined archive-zip.c
index 55f66b4060c64789bdb443f6c71f82a75ef22d0b,44b1dedd9cf0d41e64344470040e56efc82e9784..d3aef532b7e4b844f2ac9995b2e987d378737079
@@@ -4,7 -4,6 +4,7 @@@
  #include "cache.h"
  #include "archive.h"
  #include "streaming.h"
 +#include "utf8.h"
  
  static int zip_date;
  static int zip_time;
@@@ -17,8 -16,7 +17,8 @@@ static unsigned int zip_dir_offset
  static unsigned int zip_dir_entries;
  
  #define ZIP_DIRECTORY_MIN_SIZE        (1024 * 1024)
 -#define ZIP_STREAM (8)
 +#define ZIP_STREAM    (1 <<  3)
 +#define ZIP_UTF8      (1 << 11)
  
  struct zip_local_header {
        unsigned char magic[4];
@@@ -76,14 -74,6 +76,14 @@@ struct zip_dir_trailer 
        unsigned char _end[1];
  };
  
 +struct zip_extra_mtime {
 +      unsigned char magic[2];
 +      unsigned char extra_size[2];
 +      unsigned char flags[1];
 +      unsigned char mtime[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
@@@ -93,9 -83,6 +93,9 @@@
  #define ZIP_DATA_DESC_SIZE    offsetof(struct zip_data_desc, _end)
  #define ZIP_DIR_HEADER_SIZE   offsetof(struct zip_dir_header, _end)
  #define ZIP_DIR_TRAILER_SIZE  offsetof(struct zip_dir_trailer, _end)
 +#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))
  
  static void copy_le16(unsigned char *dest, unsigned int n)
  {
@@@ -177,17 -164,6 +177,17 @@@ static void set_zip_header_data_desc(st
        copy_le32(header->size, size);
  }
  
 +static int has_only_ascii(const char *s)
 +{
 +      for (;;) {
 +              int c = *s++;
 +              if (c == '\0')
 +                      return 1;
 +              if (!isascii(c))
 +                      return 0;
 +      }
 +}
 +
  #define STREAM_BUFFER_SIZE (1024 * 16)
  
  static int write_zip_entry(struct archiver_args *args,
  {
        struct zip_local_header header;
        struct zip_dir_header dirent;
 +      struct zip_extra_mtime extra;
        unsigned long attr2;
        unsigned long compressed_size;
        unsigned long crc;
  
        crc = crc32(0, NULL, 0);
  
 +      if (!has_only_ascii(path)) {
 +              if (is_utf8(path))
 +                      flags |= ZIP_UTF8;
 +              else
 +                      warning("Path is not valid UTF-8: %s", path);
 +      }
 +
        if (pathlen > 0xffff) {
                return error("path too long (%d chars, SHA1: %s): %s",
                                (int)pathlen, sha1_to_hex(sha1), path);
                        (mode & 0111) ? ((mode) << 16) : 0;
                if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
                        method = 8;
-               compressed_size = size;
+               compressed_size = (method == 0) ? size : 0;
  
                if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
                    size > big_file_threshold) {
                }
        }
  
 +      copy_le16(extra.magic, 0x5455);
 +      copy_le16(extra.extra_size, ZIP_EXTRA_MTIME_PAYLOAD_SIZE);
 +      extra.flags[0] = 1;     /* just mtime */
 +      copy_le32(extra.mtime, args->time);
 +
        /* make sure we have enough free space in the dictionary */
 -      direntsize = ZIP_DIR_HEADER_SIZE + pathlen;
 +      direntsize = ZIP_DIR_HEADER_SIZE + pathlen + ZIP_EXTRA_MTIME_SIZE;
        while (zip_dir_size < zip_dir_offset + direntsize) {
                zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
                zip_dir = xrealloc(zip_dir, zip_dir_size);
        copy_le16(dirent.mdate, zip_date);
        set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
        copy_le16(dirent.filename_length, pathlen);
 -      copy_le16(dirent.extra_length, 0);
 +      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_le16(header.compression_method, method);
        copy_le16(header.mtime, zip_time);
        copy_le16(header.mdate, zip_date);
-       if (flags & ZIP_STREAM)
-               set_zip_header_data_desc(&header, 0, 0, 0);
-       else
-               set_zip_header_data_desc(&header, size, compressed_size, crc);
+       set_zip_header_data_desc(&header, size, compressed_size, crc);
        copy_le16(header.filename_length, pathlen);
 -      copy_le16(header.extra_length, 0);
 +      copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE);
        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 (stream && method == 0) {
                unsigned char buf[STREAM_BUFFER_SIZE];
                ssize_t readlen;
        zip_dir_offset += ZIP_DIR_HEADER_SIZE;
        memcpy(zip_dir + zip_dir_offset, path, pathlen);
        zip_dir_offset += pathlen;
 +      memcpy(zip_dir + zip_dir_offset, &extra, ZIP_EXTRA_MTIME_SIZE);
 +      zip_dir_offset += ZIP_EXTRA_MTIME_SIZE;
        zip_dir_entries++;
  
        return 0;