]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Refactor Zip writer.
authorTim Kientzle <kientzle@acm.org>
Sun, 1 Dec 2013 22:45:56 +0000 (14:45 -0800)
committerTim Kientzle <kientzle@acm.org>
Sun, 1 Dec 2013 22:45:56 +0000 (14:45 -0800)
Zip writer no longer preserves full archive_entry objects for
every entry; it just accumulates the actual bytes to be put
into the central directory.  Most of the central directory file
header is formatted at the same time as the local file header.
The header formatting is refactored to make it easier to support
variable-length extra data.

The tests are adjusted to match the new output:  We include more
detailed extra data in the central directory, we're more selective
about generating data descriptors (they're not needed for directory
entries, for instance), UT extra data now includes only the time
fields specified by the user, we're setting the "version required"
field more accurately.

There are some initial attempts to include Zip64 extensions
when appropriate; that still needs lots of work.  I'm not
yet sure how to test Zip64 support without generating gigantic
archives.  Hmmm...

libarchive/archive_read_support_format_zip.c
libarchive/archive_write_set_format_zip.c
libarchive/test/main.c
libarchive/test/test.h
libarchive/test/test_write_format_zip.c
libarchive/test/test_write_format_zip_no_compression.c
libarchive/test/test_write_zip_set_compression_store.c

index 38c6b31b7e21de543d67dfca1c1facbacca89f55..bd01caa5ce639b1a14c34bd88e364a54c333b9f1 100644 (file)
@@ -1760,6 +1760,7 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry)
                        /* Info-Zip Unix Extra Field (type 3) "ux". */
                        int uidsize = 0, gidsize = 0;
 
+                       /* TODO: support arbitrary uidsize/gidsize. */
                        if (datasize >= 1 && p[offset] == 1) {/* version=1 */
                                if (datasize >= 4) {
                                        /* get a uid size. */
index bf704e6d45902e3bb382cbf3d43c402a486edb4e..cb5718d3128b9a03d98d75ade2e5735f06382c4b 100644 (file)
@@ -77,25 +77,69 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 20
 #include "archive_crc32.h"
 #endif
 
-#define ZIP_SIGNATURE_LOCAL_FILE_HEADER 0x04034b50
-#define ZIP_SIGNATURE_DATA_DESCRIPTOR 0x08074b50
-#define ZIP_SIGNATURE_FILE_HEADER 0x02014b50
-#define ZIP_SIGNATURE_CENTRAL_DIRECTORY_END 0x06054b50
-#define ZIP_SIGNATURE_EXTRA_TIMESTAMP 0x5455
-#define ZIP_SIGNATURE_EXTRA_NEW_UNIX 0x7875
-#define ZIP_VERSION_EXTRACT 0x0014 /* ZIP version 2.0 is needed. */
-#define ZIP_VERSION_BY 0x0314 /* Made by UNIX, using ZIP version 2.0. */
-#define ZIP_FLAGS 0x08 /* Flagging bit 3 (count from 0) for using data descriptor. */
+#define ZIP_FLAGS_LENGTH_AT_END        (1<<3)
 #define ZIP_FLAGS_UTF8_NAME    (1 << 11)
 
+
 enum compression {
-       COMPRESSION_STORE = 0
-#ifdef HAVE_ZLIB_H
-       ,
+       COMPRESSION_UNSPECIFIED = -1,
+       COMPRESSION_STORE = 0,
        COMPRESSION_DEFLATE = 8
+};
+
+#ifdef HAVE_ZLIB_H
+#define COMPRESSION_DEFAULT    COMPRESSION_DEFLATE
+#else
+#define COMPRESSION_DEFAULT    COMPRESSION_STORE
+#endif
+
+struct cd_segment {
+       struct cd_segment *next;
+       size_t buff_size;
+       unsigned char *buff;
+       unsigned char *p;
+};
+
+struct zip {
+
+       int64_t entry_offset;
+       int64_t entry_compressed_size;
+       int64_t entry_uncompressed_size;
+       int64_t entry_compressed_written;
+       int64_t entry_uncompressed_written;
+       int64_t entry_uncompressed_limit;
+       struct archive_entry *entry;
+       uint32_t entry_crc32;
+       enum compression entry_compression;
+       int entry_flags;
+
+       unsigned char *file_header;
+       size_t file_header_extra_offset;
+
+       struct cd_segment *central_directory;
+       struct cd_segment *central_directory_last;
+       size_t central_directory_bytes;
+       size_t central_directory_entries;
+
+       int64_t written_bytes; /* Overall position in file. */
+
+       struct archive_string_conv *opt_sconv;
+       struct archive_string_conv *sconv_default;
+       enum compression requested_compression;
+       int init_default_conversion;
+       char force_zip64;
+
+#ifdef HAVE_ZLIB_H
+       z_stream stream;
+       size_t len_buf;
+       unsigned char *buf;
 #endif
 };
 
+/* Don't call this min or MIN, since those are already defined
+   on lots of platforms (but not all). */
+#define zipmin(a, b) ((a) > (b) ? (b) : (a))
+
 static ssize_t archive_write_zip_data(struct archive_write *,
                   const void *buff, size_t s);
 static int archive_write_zip_close(struct archive_write *);
@@ -108,109 +152,43 @@ static int archive_write_zip_options(struct archive_write *,
 static unsigned int dos_time(const time_t);
 static size_t path_length(struct archive_entry *);
 static int write_path(struct archive_entry *, struct archive_write *);
+static void copy_path(struct archive_entry *, unsigned char *);
+static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
 
-#define LOCAL_FILE_HEADER_SIGNATURE            0
-#define LOCAL_FILE_HEADER_VERSION              4
-#define LOCAL_FILE_HEADER_FLAGS                        6
-#define LOCAL_FILE_HEADER_COMPRESSION          8
-#define LOCAL_FILE_HEADER_TIMEDATE             10
-#define LOCAL_FILE_HEADER_CRC32                        14
-#define LOCAL_FILE_HEADER_COMPRESSED_SIZE      18
-#define LOCAL_FILE_HEADER_UNCOMPRESSED_SIZE    22
-#define LOCAL_FILE_HEADER_FILENAME_LENGTH      26
-#define LOCAL_FILE_HEADER_EXTRA_LENGTH         28
-#define SIZE_LOCAL_FILE_HEADER                 30
-
-#define FILE_HEADER_SIGNATURE                  0
-#define FILE_HEADER_VERSION_BY                 4
-#define FILE_HEADER_VERSION_EXTRACT            6
-#define FILE_HEADER_FLAGS                      8
-#define FILE_HEADER_COMPRESSION                        10
-#define FILE_HEADER_TIMEDATE                   12
-#define FILE_HEADER_CRC32                      16
-#define FILE_HEADER_COMPRESSED_SIZE            20
-#define FILE_HEADER_UNCOMPRESSED_SIZE          24
-#define FILE_HEADER_FILENAME_LENGTH            28
-#define FILE_HEADER_EXTRA_LENGTH               30
-#define FILE_HEADER_COMMENT_LENGTH             32
-#define FILE_HEADER_DISK_NUMBER                        34
-#define FILE_HEADER_ATTRIBUTES_INTERNAL                36
-#define FILE_HEADER_ATTRIBUTES_EXTERNAL                38
-#define FILE_HEADER_OFFSET                     42
-#define SIZE_FILE_HEADER                       46
-
-       /* Not mandatory, but recommended by specification. */
-#define DATA_DESCRIPTOR_SIGNATURE              0
-#define DATA_DESCRIPTOR_CRC32                  4
-#define DATA_DESCRIPTOR_COMPRESSED_SIZE                8
-#define DATA_DESCRIPTOR_UNCOMPRESSED_SIZE      12
-#define SIZE_DATA_DESCRIPTOR                   16
-
-#define EXTRA_DATA_LOCAL_TIME_ID               0
-#define EXTRA_DATA_LOCAL_TIME_SIZE             2
-#define EXTRA_DATA_LOCAL_TIME_FLAG             4
-#define EXTRA_DATA_LOCAL_MTIME                 5
-#define EXTRA_DATA_LOCAL_ATIME                 9
-#define EXTRA_DATA_LOCAL_CTIME                 13
-#define EXTRA_DATA_LOCAL_UNIX_ID               17
-#define EXTRA_DATA_LOCAL_UNIX_SIZE             19
-#define EXTRA_DATA_LOCAL_UNIX_VERSION          21
-#define EXTRA_DATA_LOCAL_UNIX_UID_SIZE         22
-#define EXTRA_DATA_LOCAL_UNIX_UID              23
-#define EXTRA_DATA_LOCAL_UNIX_GID_SIZE         27
-#define EXTRA_DATA_LOCAL_UNIX_GID              28
-#define SIZE_EXTRA_DATA_LOCAL                  32
-
-#define EXTRA_DATA_CENTRAL_TIME_ID             0
-#define EXTRA_DATA_CENTRAL_TIME_SIZE           2
-#define EXTRA_DATA_CENTRAL_TIME_FLAG           4
-#define EXTRA_DATA_CENTRAL_MTIME               5
-#define EXTRA_DATA_CENTRAL_UNIX_ID             9
-#define EXTRA_DATA_CENTRAL_UNIX_SIZE           11
-#define SIZE_EXTRA_DATA_CENTRAL                        13
-
-#define CENTRAL_DIRECTORY_END_SIGNATURE                0
-#define CENTRAL_DIRECTORY_END_DISK             4
-#define CENTRAL_DIRECTORY_END_START_DISK       6
-#define CENTRAL_DIRECTORY_END_ENTRIES_DISK     8
-#define CENTRAL_DIRECTORY_END_ENTRIES          10
-#define CENTRAL_DIRECTORY_END_SIZE             12
-#define CENTRAL_DIRECTORY_END_OFFSET           16
-#define CENTRAL_DIRECTORY_END_COMMENT_LENGTH   20
-#define SIZE_CENTRAL_DIRECTORY_END             22
-
-struct zip_entry {
-       int64_t offset;
-       int64_t compressed_size;
-       struct zip_entry *next;
-       struct archive_entry *entry;
-       uint32_t crc32;
-       enum compression compression;
-       int flags;
-};
+static unsigned char *
+cd_alloc(struct zip *zip, size_t length)
+{
+       unsigned char *p;
+
+       if (zip->central_directory == NULL
+           || (zip->central_directory_last->p + length 
+               > zip->central_directory_last->buff + zip->central_directory_last->buff_size)) {
+               struct cd_segment *segment = calloc(1, sizeof(*segment));
+               if (segment == NULL)
+                       return NULL;
+               segment->buff_size = 64 * 1024;
+               segment->buff = malloc(segment->buff_size);
+               if (segment->buff == NULL) {
+                       free(segment);
+                       return NULL;
+               }
+               segment->p = segment->buff;
 
-struct zip {
-       struct zip_entry entry; // Details about current entry.
-
-       uint8_t data_descriptor[SIZE_DATA_DESCRIPTOR];
-       int64_t offset;
-       int64_t written_bytes;
-       int64_t remaining_data_bytes;
-       struct zip_entry *central_directory;
-       struct zip_entry *central_directory_end;
-       struct archive_string_conv *opt_sconv;
-       struct archive_string_conv *sconv_default;
-       enum compression default_compression;
-       int flags;
-       int init_default_conversion;
-       char force_zip64;
+               if (zip->central_directory == NULL) {
+                       zip->central_directory
+                           = zip->central_directory_last
+                           = segment;
+               } else {
+                       zip->central_directory_last->next = segment;
+                       zip->central_directory_last = segment;
+               }
+       }
 
-#ifdef HAVE_ZLIB_H
-       z_stream stream;
-       size_t len_buf;
-       unsigned char *buf;
-#endif
-};
+       p = zip->central_directory_last->p;
+       zip->central_directory_last->p += length;
+       zip->central_directory_bytes += length;
+       return (p);
+}
 
 static int
 archive_write_zip_options(struct archive_write *a, const char *key,
@@ -226,14 +204,14 @@ archive_write_zip_options(struct archive_write *a, const char *key,
                            a->format_name);
                } else if (strcmp(val, "deflate") == 0) {
 #ifdef HAVE_ZLIB_H
-                       zip->default_compression = COMPRESSION_DEFLATE;
+                       zip->requested_compression = COMPRESSION_DEFLATE;
                        ret = ARCHIVE_OK;
 #else
                        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                            "deflate compression not supported");
 #endif
                } else if (strcmp(val, "store") == 0) {
-                       zip->default_compression = COMPRESSION_STORE;
+                       zip->requested_compression = COMPRESSION_STORE;
                        ret = ARCHIVE_OK;
                }
                return (ret);
@@ -269,7 +247,7 @@ archive_write_zip_set_compression_deflate(struct archive *_a)
        int ret = ARCHIVE_FAILED;
        
        archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
-               ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER,
+               ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
                "archive_write_zip_set_compression_deflate");
        if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -279,11 +257,12 @@ archive_write_zip_set_compression_deflate(struct archive *_a)
        } else {
 #ifdef HAVE_ZLIB_H
                struct zip *zip = a->format_data;
-               zip->default_compression = COMPRESSION_DEFLATE;
+               zip->requested_compression = COMPRESSION_DEFLATE;
                ret = ARCHIVE_OK;
 #else
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                        "deflate compression not supported");
+               ret = ARCHIVE_FAILED;
 #endif
        }
        return (ret);
@@ -297,7 +276,7 @@ archive_write_zip_set_compression_store(struct archive *_a)
        int ret = ARCHIVE_FAILED;
        
        archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
-               ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER,
+               ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
                "archive_write_zip_set_compression_deflate");
        if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -305,7 +284,7 @@ archive_write_zip_set_compression_store(struct archive *_a)
                        " with zip format");
                ret = ARCHIVE_FATAL;
        } else {
-               zip->default_compression = COMPRESSION_STORE;
+               zip->requested_compression = COMPRESSION_STORE;
                ret = ARCHIVE_OK;
        }
        return (ret);
@@ -330,14 +309,11 @@ archive_write_set_format_zip(struct archive *_a)
                    "Can't allocate zip data");
                return (ARCHIVE_FATAL);
        }
-       zip->central_directory = NULL;
-       zip->central_directory_end = NULL;
-       zip->offset = 0;
-       zip->written_bytes = 0;
-       zip->remaining_data_bytes = 0;
+
+       /* "Unspecified" lets us choose the appropriate compression. */
+       zip->requested_compression = COMPRESSION_UNSPECIFIED;
 
 #ifdef HAVE_ZLIB_H
-       zip->default_compression = COMPRESSION_DEFLATE;
        zip->len_buf = 65536;
        zip->buf = malloc(zip->len_buf);
        if (zip->buf == NULL) {
@@ -346,8 +322,6 @@ archive_write_set_format_zip(struct archive *_a)
                    "Can't allocate compression buffer");
                return (ARCHIVE_FATAL);
        }
-#else
-       zip->default_compression = COMPRESSION_STORE;
 #endif
 
        a->format_data = zip;
@@ -361,9 +335,6 @@ archive_write_set_format_zip(struct archive *_a)
        a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
        a->archive.archive_format_name = "ZIP";
 
-       archive_le32enc(&zip->data_descriptor[DATA_DESCRIPTOR_SIGNATURE],
-           ZIP_SIGNATURE_DATA_DESCRIPTOR);
-
        return (ARCHIVE_OK);
 }
 
@@ -382,16 +353,21 @@ is_all_ascii(const char *p)
 static int
 archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
 {
-       struct zip *zip;
-       uint8_t h[SIZE_LOCAL_FILE_HEADER];
-       uint8_t e[SIZE_EXTRA_DATA_LOCAL];
-       uint8_t *d;
-       struct archive_string_conv *sconv;
+       struct zip *zip = a->format_data;
+       unsigned char local_header[30];
+       unsigned char local_extra[64];
+       unsigned char *e;
+       unsigned char *cd_extra;
+       size_t filename_length;
+       const char *symlink = NULL;
+       size_t symlink_size;
+       struct archive_string_conv *sconv = get_sconv(a, zip);
        int ret, ret2 = ARCHIVE_OK;
        int64_t size;
        mode_t type;
+       int version_needed = 10;
 
-       /* Entries other than a regular file or a folder are skipped. */
+       /* Ignore types of entries that we don't support. */
        type = archive_entry_filetype(entry);
        if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -399,60 +375,40 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
                return ARCHIVE_FAILED;
        };
 
-       /* Directory entries should have a size of 0. */
-       if (type == AE_IFDIR)
+       /* Only regular files can have size > 0. */
+       if (type != AE_IFREG)
                archive_entry_set_size(entry, 0);
 
-       zip = a->format_data;
-       /* Setup default conversion. */
-       if (zip->opt_sconv == NULL && !zip->init_default_conversion) {
-               zip->sconv_default =
-                   archive_string_default_conversion_for_write(&(a->archive));
-               zip->init_default_conversion = 1;
-       }
 
-       if (zip->flags == 0) {
-               /* Initialize the general purpose flags. */
-               zip->flags = ZIP_FLAGS;
-               if (zip->opt_sconv != NULL) {
-                       if (strcmp(archive_string_conversion_charset_name(
-                           zip->opt_sconv), "UTF-8") == 0)
-                               zip->flags |= ZIP_FLAGS_UTF8_NAME;
-#if HAVE_NL_LANGINFO
-               } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
-                       zip->flags |= ZIP_FLAGS_UTF8_NAME;
-#endif
-               }
+       /* Reset information from last entry. */
+       zip->entry_offset = zip->written_bytes;
+       zip->entry_uncompressed_limit = INT64_MAX;
+       zip->entry_compressed_size = 0;
+       zip->entry_uncompressed_size = 0;
+       zip->entry_compressed_written = 0;
+       zip->entry_uncompressed_written = 0;
+       zip->entry_flags = 0;
+       zip->entry_crc32 = crc32(0, NULL, 0);
+       if (zip->entry != NULL) {
+               archive_entry_free(zip->entry);
+               zip->entry = NULL;
        }
-       d = zip->data_descriptor;
-       size = archive_entry_size(entry);
-       zip->remaining_data_bytes = size;
-
-       /* Clear out old entry information. */
-       if (zip->entry.entry != NULL) {
-               archive_entry_free(zip->entry.entry);
-       }
-       memset(&zip->entry, 0, sizeof(zip->entry));
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
        /* Make sure the path separators in pahtname, hardlink and symlink
         * are all slash '/', not the Windows path separator '\'. */
-       zip->entry.entry = __la_win_entry_in_posix_pathseparator(entry);
-       if (zip->entry.entry == entry)
-               zip->entry.entry = archive_entry_clone(entry);
+       zip->entry = __la_win_entry_in_posix_pathseparator(entry);
+       if (zip->entry == entry)
+               zip->entry = archive_entry_clone(entry);
 #else
-       zip->entry.entry = archive_entry_clone(entry);
+       zip->entry = archive_entry_clone(entry);
 #endif
-       if (zip->entry.entry == NULL) {
+       if (zip->entry == NULL) {
                archive_set_error(&a->archive, ENOMEM,
                    "Can't allocate zip header data");
                return (ARCHIVE_FATAL);
        }
-       zip->entry.flags = zip->flags;
-       if (zip->opt_sconv != NULL)
-               sconv = zip->opt_sconv;
-       else
-               sconv = zip->sconv_default;
+
        if (sconv != NULL) {
                const char *p;
                size_t len;
@@ -471,12 +427,12 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
                        ret2 = ARCHIVE_WARN;
                }
                if (len > 0)
-                       archive_entry_set_pathname(zip->entry.entry, p);
+                       archive_entry_set_pathname(zip->entry, p);
 
                /*
-                * Although there is no character-set regulation for Symlink,
-                * it is suitable to convert a character-set of Symlinke to
-                * what those of the Pathname has been converted to.
+                * There is no standard for symlink handling; we convert
+                * it using the same character-set translation that we use
+                * for filename.
                 */
                if (type == AE_IFLNK) {
                        if (archive_entry_symlink_l(entry, &p, &len, sconv)) {
@@ -486,139 +442,218 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
                                            " for Symlink");
                                        return (ARCHIVE_FATAL);
                                }
-                               /*
-                                * Even if the strng conversion failed,
-                                * we should not report the error since
-                                * thre is no regulation for.
-                                */
+                               /* No error if we can't convert. */
                        } else if (len > 0)
-                               archive_entry_set_symlink(zip->entry.entry, p);
+                               archive_entry_set_symlink(zip->entry, p);
+               }
+       }
+
+       /* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */
+       if (!is_all_ascii(archive_entry_pathname(zip->entry))) {
+               if (zip->opt_sconv != NULL) {
+                       if (strcmp(archive_string_conversion_charset_name(
+                                       zip->opt_sconv), "UTF-8") == 0)
+                               zip->entry_flags |= ZIP_FLAGS_UTF8_NAME;
+#if HAVE_NL_LANGINFO
+               } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
+                       zip->entry_flags |= ZIP_FLAGS_UTF8_NAME;
+#endif
                }
        }
-       /* If all characters in a filename are ASCII, Reset UTF-8 Name flag. */
-       if ((zip->entry.flags & ZIP_FLAGS_UTF8_NAME) != 0 &&
-           is_all_ascii(archive_entry_pathname(zip->entry.entry)))
-               zip->entry.flags &= ~ZIP_FLAGS_UTF8_NAME;
+       filename_length = path_length(zip->entry);
 
-       /* Initialize the CRC variable and potentially the local crc32(). */
-       zip->entry.crc32 = crc32(0, NULL, 0);
+       /* Determine appropriate compression and size for this entry. */
        if (type == AE_IFLNK) {
-               const char *p = archive_entry_symlink(zip->entry.entry);
-               if (p != NULL)
-                       size = strlen(p);
+               symlink = archive_entry_symlink(zip->entry);
+               if (symlink != NULL)
+                       symlink_size = strlen(symlink);
                else
-                       size = 0;
-               zip->remaining_data_bytes = 0;
-               archive_entry_set_size(zip->entry.entry, size);
-               zip->entry.compression = COMPRESSION_STORE;
-               zip->entry.compressed_size = size;
+                       symlink_size = 0;
+               zip->entry_uncompressed_limit = symlink_size;
+               zip->entry_compressed_size = symlink_size;
+               zip->entry_uncompressed_size = symlink_size;
+               zip->entry_crc32 = crc32(zip->entry_crc32,
+                   (const unsigned char *)symlink, symlink_size);
+               zip->entry_compression = COMPRESSION_STORE;
+               version_needed = 20;
+       } else if (type != AE_IFREG) {
+               zip->entry_compression = COMPRESSION_STORE;
+               zip->entry_uncompressed_limit = 0;
+               size = 0;
+               version_needed = 20;
+       } else if (archive_entry_size_is_set(zip->entry)) {
+               size = archive_entry_size(zip->entry);
+               zip->entry_uncompressed_limit = size;
+               zip->entry_compression = zip->requested_compression;
+               if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
+                       zip->entry_compression = COMPRESSION_DEFAULT;
+               }
+               if (zip->entry_compression == COMPRESSION_STORE) {
+                       zip->entry_compressed_size = size;
+                       zip->entry_uncompressed_size = size;
+                       version_needed = 10;
+               } else {
+                       version_needed = 20;
+               }
+               /* We may know the size, but never the CRC. */
+               zip->entry_flags |= ZIP_FLAGS_LENGTH_AT_END;
        } else {
-               zip->entry.compression = zip->default_compression;
-               zip->entry.compressed_size = 0;
+               /* Prefer deflate if it's available. */
+               zip->entry_compression = COMPRESSION_DEFAULT;
+               zip->entry_flags |= ZIP_FLAGS_LENGTH_AT_END;
+               if (zip->entry_compression == COMPRESSION_STORE) {
+                       version_needed = 10;
+               } else {
+                       version_needed = 20;
+               }
        }
 
-       /* Store the offset of this header for later use in central
-        * directory. */
-       zip->entry.offset = zip->written_bytes;
-
-       memset(h, 0, sizeof(h));
-       archive_le32enc(&h[LOCAL_FILE_HEADER_SIGNATURE],
-               ZIP_SIGNATURE_LOCAL_FILE_HEADER);
-       archive_le16enc(&h[LOCAL_FILE_HEADER_VERSION], ZIP_VERSION_EXTRACT);
-       archive_le16enc(&h[LOCAL_FILE_HEADER_FLAGS], zip->entry.flags);
-       archive_le16enc(&h[LOCAL_FILE_HEADER_COMPRESSION], zip->entry.compression);
-       archive_le32enc(&h[LOCAL_FILE_HEADER_TIMEDATE],
-               dos_time(archive_entry_mtime(zip->entry.entry)));
-       archive_le16enc(&h[LOCAL_FILE_HEADER_FILENAME_LENGTH],
-               (uint16_t)path_length(zip->entry.entry));
-
-       switch (zip->entry.compression) {
-       case COMPRESSION_STORE:
-               /* Setting compressed and uncompressed sizes even when
-                * specification says to set to zero when using data
-                * descriptors. Otherwise the end of the data for an
-                * entry is rather difficult to find. */
-               archive_le32enc(&h[LOCAL_FILE_HEADER_COMPRESSED_SIZE],
-                   (uint32_t)size);
-               archive_le32enc(&h[LOCAL_FILE_HEADER_UNCOMPRESSED_SIZE],
-                   (uint32_t)size);
-               break;
-#ifdef HAVE_ZLIB_H
-       case COMPRESSION_DEFLATE:
-               archive_le32enc(&h[LOCAL_FILE_HEADER_UNCOMPRESSED_SIZE],
-                   (uint32_t)size);
+       /* TODO: Set version_needed = 45 if zip64 gets triggered. */
+
+       /* Format the local header. */
+       memset(local_header, 0, sizeof(local_header));
+       memcpy(local_header, "PK\003\004", 4);
+       archive_le16enc(local_header + 4, version_needed);
+       archive_le16enc(local_header + 6, zip->entry_flags);
+       archive_le16enc(local_header + 8, zip->entry_compression);
+       archive_le32enc(local_header + 10, dos_time(archive_entry_mtime(zip->entry)));
+       archive_le32enc(local_header + 14, zip->entry_crc32);
+       archive_le32enc(local_header + 18,
+           zipmin(zip->entry_compressed_size, 0xffffffffLL));
+       archive_le32enc(local_header + 22,
+           zipmin(zip->entry_uncompressed_size, 0xffffffffLL));
+       archive_le16enc(local_header + 26, filename_length);
+
+       /* Format as much of central directory file header as we can: */
+       zip->file_header = cd_alloc(zip, 46);
+       /* If (zip->file_header == NULL) XXXX */
+       ++zip->central_directory_entries;
+       memset(zip->file_header, 0, 46);
+       /* "Made by PKZip 2.0 on Unix." */
+       /* TODO: Change extract-version to 4.5 if Zip64 gets triggered. */
+       memcpy(zip->file_header, "PK\001\002", 4);
+       archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed);
+       archive_le16enc(zip->file_header + 6, version_needed);
+       archive_le16enc(zip->file_header + 8, zip->entry_flags);
+       archive_le16enc(zip->file_header + 10, zip->entry_compression);
+       archive_le32enc(zip->file_header + 12, dos_time(archive_entry_mtime(zip->entry)));
+       archive_le16enc(zip->file_header + 28, filename_length);
+       /* Following Info-Zip, store mode in the "external attributes" field. */
+       archive_le32enc(zip->file_header + 38,
+           archive_entry_mode(zip->entry) << 16);
+       unsigned char *fn = cd_alloc(zip, filename_length);
+       /* If (fn == NULL) XXXX */
+       copy_path(zip->entry, fn);
+
+       /* Format extra data. */
+       memset(local_extra, 0, sizeof(local_extra));
+       e = local_extra;
+
+       /* UT timestamp, length depends on what timestamps are set. */
+       memcpy(e, "UT", 2);
+       archive_le16enc(e + 2,
+           1
+           + (archive_entry_mtime_is_set(entry) ? 4 : 0)
+           + (archive_entry_atime_is_set(entry) ? 4 : 0)
+           + (archive_entry_ctime_is_set(entry) ? 4 : 0));
+       e += 4;
+       *e++ =
+           (archive_entry_mtime_is_set(entry) ? 1 : 0)
+           | (archive_entry_atime_is_set(entry) ? 2 : 0)
+           | (archive_entry_ctime_is_set(entry) ? 4 : 0);
+       if (archive_entry_mtime_is_set(entry)) {
+               archive_le32enc(e, (uint32_t)archive_entry_mtime(entry));
+               e += 4;
+       }
+       if (archive_entry_atime_is_set(entry)) {
+               archive_le32enc(e, (uint32_t)archive_entry_atime(entry));
+               e += 4;
+       }
+       if (archive_entry_ctime_is_set(entry)) {
+               archive_le32enc(e, (uint32_t)archive_entry_ctime(entry));
+               e += 4;
+       }
 
-               zip->stream.zalloc = Z_NULL;
-               zip->stream.zfree = Z_NULL;
-               zip->stream.opaque = Z_NULL;
-               zip->stream.next_out = zip->buf;
-               zip->stream.avail_out = (uInt)zip->len_buf;
-               if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION,
-                   Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
-                       archive_set_error(&a->archive, ENOMEM,
-                           "Can't init deflate compressor");
-                       return (ARCHIVE_FATAL);
-               }
-               break;
-#endif
+       /* ux Unix extra data, length 11, version 1 */
+       /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */
+       memcpy(e, "ux\013\000\001", 5);
+       e += 5;
+       *e++ = 4; /* Length of following UID */
+       archive_le32enc(e, (uint32_t)archive_entry_uid(entry));
+       e += 4;
+       *e++ = 4; /* Length of following GID */
+       archive_le32enc(e, (uint32_t)archive_entry_gid(entry));
+       e += 4;
+
+       /* Copy UT and ux into central directory as well. */
+       /* (Zip64 extended data varies between local header and
+        * central directory, so we cannot just copy it.) */
+       zip->file_header_extra_offset = zip->central_directory_bytes;
+       cd_extra = cd_alloc(zip, e - local_extra);
+       memcpy(cd_extra, local_extra, e - local_extra);
+
+       /* Zip64 field with variable size. */
+#if 0  /* XXX THIS IS WRONG XXX */
+       if ((zip->entry_flags & ZIP_FLAGS_LENGTH_AT_END) == 0
+           && size > 0xffffffffLL) {
+               unsigned char *zip64_start = e;
+               memcpy(e, "\001\000\020\000", 4);
+               e += 4;
+               if (zip->entry_uncompressed_size > 0xffffffffLL)
+                       archive_le64enc(e, zip->entry_uncompressed_size);
+               e += 8;
+               if (zip->entry_compressed_size > 0xffffffffLL)
+                       archive_le64enc(e, zip->entry_compressed_size);
+               e += 8;
+               archive_le16enc(zip64_start + 2, e - zip64_start + 4);
        }
+#endif
 
-       /* Formatting extra data. */
-       archive_le16enc(&h[LOCAL_FILE_HEADER_EXTRA_LENGTH], sizeof(e));
-       archive_le16enc(&e[EXTRA_DATA_LOCAL_TIME_ID],
-               ZIP_SIGNATURE_EXTRA_TIMESTAMP);
-       archive_le16enc(&e[EXTRA_DATA_LOCAL_TIME_SIZE], 1 + 4 * 3);
-       e[EXTRA_DATA_LOCAL_TIME_FLAG] = 0x07;
-       archive_le32enc(&e[EXTRA_DATA_LOCAL_MTIME],
-           (uint32_t)archive_entry_mtime(entry));
-       archive_le32enc(&e[EXTRA_DATA_LOCAL_ATIME],
-           (uint32_t)archive_entry_atime(entry));
-       archive_le32enc(&e[EXTRA_DATA_LOCAL_CTIME],
-           (uint32_t)archive_entry_ctime(entry));
-
-       archive_le16enc(&e[EXTRA_DATA_LOCAL_UNIX_ID],
-               ZIP_SIGNATURE_EXTRA_NEW_UNIX);
-       archive_le16enc(&e[EXTRA_DATA_LOCAL_UNIX_SIZE], 1 + (1 + 4) * 2);
-       e[EXTRA_DATA_LOCAL_UNIX_VERSION] = 1;
-       e[EXTRA_DATA_LOCAL_UNIX_UID_SIZE] = 4;
-       archive_le32enc(&e[EXTRA_DATA_LOCAL_UNIX_UID],
-               (uint32_t)archive_entry_uid(zip->entry.entry));
-       e[EXTRA_DATA_LOCAL_UNIX_GID_SIZE] = 4;
-       archive_le32enc(&e[EXTRA_DATA_LOCAL_UNIX_GID],
-               (uint32_t)archive_entry_gid(zip->entry.entry));
-
-       archive_le32enc(&d[DATA_DESCRIPTOR_UNCOMPRESSED_SIZE],
-           (uint32_t)size);
-
-       ret = __archive_write_output(a, h, sizeof(h));
+       /* Update local header with size of extra data and write it all out: */
+       archive_le16enc(local_header + 28, e - local_extra);
+
+       ret = __archive_write_output(a, local_header, 30);
        if (ret != ARCHIVE_OK)
                return (ARCHIVE_FATAL);
-       zip->written_bytes += sizeof(h);
+       zip->written_bytes += 30;
 
-       ret = write_path(zip->entry.entry, a);
+       ret = write_path(zip->entry, a);
        if (ret <= ARCHIVE_OK)
                return (ARCHIVE_FATAL);
        zip->written_bytes += ret;
 
-       ret = __archive_write_output(a, e, sizeof(e));
+       ret = __archive_write_output(a, local_extra, e - local_extra);
        if (ret != ARCHIVE_OK)
                return (ARCHIVE_FATAL);
-       zip->written_bytes += sizeof(e);
-
-       if (type == AE_IFLNK) {
-               const unsigned char *p;
+       zip->written_bytes += e - local_extra;
 
-               p = (const unsigned char *)archive_entry_symlink(zip->entry.entry);
-               ret = __archive_write_output(a, p, (size_t)size);
+       /* For symlinks, write the body now. */
+       if (symlink != NULL) {
+               ret = __archive_write_output(a, symlink, (size_t)symlink_size);
                if (ret != ARCHIVE_OK)
                        return (ARCHIVE_FATAL);
-               zip->written_bytes += size;
-               zip->entry.crc32 = crc32(zip->entry.crc32, p, (unsigned)size);
+               zip->entry_compressed_written += symlink_size;
+               zip->entry_uncompressed_written += symlink_size;
+               zip->written_bytes += symlink_size;
        }
 
-       if (ret2 != ARCHIVE_OK)
-               return (ret2);
-       return (ARCHIVE_OK);
+#ifdef HAVE_ZLIB_H
+       if (zip->entry_compression == COMPRESSION_DEFLATE) {
+               zip->stream.zalloc = Z_NULL;
+               zip->stream.zfree = Z_NULL;
+               zip->stream.opaque = Z_NULL;
+               zip->stream.next_out = zip->buf;
+               zip->stream.avail_out = (uInt)zip->len_buf;
+               if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION,
+                   Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+                       archive_set_error(&a->archive, ENOMEM,
+                           "Can't init deflate compressor");
+                       return (ARCHIVE_FATAL);
+               }
+       }
+#endif
+
+       return (ret2);
 }
 
 static ssize_t
@@ -627,20 +662,19 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
        int ret;
        struct zip *zip = a->format_data;
 
-       if ((int64_t)s > zip->remaining_data_bytes)
-               s = (size_t)zip->remaining_data_bytes;
+       if ((int64_t)s > zip->entry_uncompressed_limit)
+               s = (size_t)zip->entry_uncompressed_limit;
+       zip->entry_uncompressed_written += s;
 
        if (s == 0) return 0;
 
-       switch (zip->entry.compression) {
+       switch (zip->entry_compression) {
        case COMPRESSION_STORE:
                ret = __archive_write_output(a, buff, s);
                if (ret != ARCHIVE_OK) return (ret);
                zip->written_bytes += s;
-               zip->remaining_data_bytes -= s;
-               zip->entry.compressed_size += s;
-               zip->entry.crc32 = crc32(zip->entry.crc32, buff, (unsigned)s);
-               return (s);
+               zip->entry_compressed_written += s;
+               break;
 #if HAVE_ZLIB_H
        case COMPRESSION_DEFLATE:
                zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
@@ -654,16 +688,13 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
                                        zip->len_buf);
                                if (ret != ARCHIVE_OK)
                                        return (ret);
-                               zip->entry.compressed_size += zip->len_buf;
+                               zip->entry_compressed_written += zip->len_buf;
                                zip->written_bytes += zip->len_buf;
                                zip->stream.next_out = zip->buf;
                                zip->stream.avail_out = (uInt)zip->len_buf;
                        }
                } while (zip->stream.avail_in != 0);
-               zip->remaining_data_bytes -= s;
-               /* If we have it, use zlib's fast crc32() */
-               zip->entry.crc32 = crc32(zip->entry.crc32, buff, (uInt)s);
-               return (s);
+               break;
 #endif
 
        default:
@@ -671,70 +702,92 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
                    "Invalid ZIP compression type");
                return ARCHIVE_FATAL;
        }
+
+       zip->entry_uncompressed_limit -= s;
+       zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)s);
+       return (s);
+
 }
 
 static int
 archive_write_zip_finish_entry(struct archive_write *a)
 {
-       /* Write the data descripter after file data has been written. */
-       int ret;
        struct zip *zip = a->format_data;
-       uint8_t *d = zip->data_descriptor;
-       struct zip_entry *l;
-#if HAVE_ZLIB_H
-       size_t reminder;
-#endif
+       int ret;
 
-       switch(zip->entry.compression) {
-       case COMPRESSION_STORE:
-               break;
 #if HAVE_ZLIB_H
-       case COMPRESSION_DEFLATE:
+       if (zip->entry_compression == COMPRESSION_DEFLATE) {
                for (;;) {
+                       size_t remainder;
                        ret = deflate(&zip->stream, Z_FINISH);
                        if (ret == Z_STREAM_ERROR)
                                return (ARCHIVE_FATAL);
-                       reminder = zip->len_buf - zip->stream.avail_out;
-                       ret = __archive_write_output(a, zip->buf, reminder);
+                       remainder = zip->len_buf - zip->stream.avail_out;
+                       ret = __archive_write_output(a, zip->buf, remainder);
                        if (ret != ARCHIVE_OK)
                                return (ret);
-                       zip->entry.compressed_size += reminder;
-                       zip->written_bytes += reminder;
+                       zip->entry_compressed_written += remainder;
+                       zip->written_bytes += remainder;
                        zip->stream.next_out = zip->buf;
                        if (zip->stream.avail_out != 0)
                                break;
                        zip->stream.avail_out = (uInt)zip->len_buf;
                }
                deflateEnd(&zip->stream);
-               break;
-#endif
        }
+#endif
 
-       archive_le32enc(&d[DATA_DESCRIPTOR_CRC32], zip->entry.crc32);
-       archive_le32enc(&d[DATA_DESCRIPTOR_COMPRESSED_SIZE],
-               (uint32_t)zip->entry.compressed_size);
-       ret = __archive_write_output(a, d, SIZE_DATA_DESCRIPTOR);
-       if (ret != ARCHIVE_OK)
-               return (ARCHIVE_FATAL);
-       zip->written_bytes += SIZE_DATA_DESCRIPTOR;
-
-       /* Add details to list for formatting central directory. */
-       /* TODO: Actually format it now and store the central dir data
-        * as a block of bytes. */
-       l = calloc(1, sizeof(*l));
-       if (l == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate zip header data");
-               return (ARCHIVE_FATAL);
+       /* Write trailing data descriptor. */
+       /* XXX IF ZIP64 XXX */
+       if ((zip->entry_flags & ZIP_FLAGS_LENGTH_AT_END) != 0) {
+               char d[24];
+               memcpy(d, "PK\007\010", 4);
+               archive_le32enc(d + 4, zip->entry_crc32);
+               archive_le32enc(d + 8, (uint32_t)zip->entry_compressed_written);
+               archive_le32enc(d + 12, (uint32_t)zip->entry_uncompressed_written);
+               ret = __archive_write_output(a, d, 16);
+               if (ret != ARCHIVE_OK)
+                       return (ARCHIVE_FATAL);
+               zip->written_bytes += 16;
        }
-       *l = zip->entry;
-       zip->entry.entry = NULL; // This has moved.
-       if (zip->central_directory == NULL) {
-               zip->central_directory = l;
-       } else {
-               zip->central_directory_end->next = l;
+
+       /* Append Zip64 extra data to central directory information. */
+       if (zip->entry_compressed_written > 0xffffffffLL
+           || zip->entry_uncompressed_written > 0xffffffffLL
+           || zip->entry_offset > 0xffffffffLL) {
+               unsigned char zip64[32];
+               unsigned char *z = zip64, *zd;
+               memcpy(z, "\001\000\000\000", 4);
+               z += 4;
+               if (zip->entry_uncompressed_written > 0xffffffffLL)
+                       archive_le64enc(z, zip->entry_uncompressed_written);
+               z += 8;
+               if (zip->entry_compressed_written > 0xffffffffLL)
+                       archive_le64enc(z, zip->entry_compressed_written);
+               z += 8;
+               if (zip->entry_offset > 0xffffffffLL)
+                       archive_le64enc(z, zip->entry_offset);
+               z += 8;
+               archive_le16enc(zip64 + 2, z - zip64 + 4);
+               zd = cd_alloc(zip, z - zip64);
+               if (zd == NULL) {
+                       archive_set_error(&a->archive, ENOMEM,
+                               "Can't allocate zip data");
+                       return (ARCHIVE_FATAL);
+               }
+               memcpy(zd, zip64, z - zip64);
        }
-       zip->central_directory_end = l;
+
+       /* Fix up central directory file header. */
+       archive_le32enc(zip->file_header + 16, zip->entry_crc32);
+       archive_le32enc(zip->file_header + 20,
+           zipmin(zip->entry_compressed_written, 0xffffffffLL));
+       archive_le32enc(zip->file_header + 24,
+           zipmin(zip->entry_uncompressed_written, 0xffffffffLL));
+       archive_le16enc(zip->file_header + 30,
+           zip->central_directory_bytes - zip->file_header_extra_offset);
+       archive_le32enc(zip->file_header + 42,
+           zipmin(zip->entry_offset, 0xffffffffLL));
 
        return (ARCHIVE_OK);
 }
@@ -742,97 +795,33 @@ archive_write_zip_finish_entry(struct archive_write *a)
 static int
 archive_write_zip_close(struct archive_write *a)
 {
-       struct zip *zip;
-       struct zip_entry *l;
-       uint8_t h[SIZE_FILE_HEADER];
-       uint8_t end[SIZE_CENTRAL_DIRECTORY_END];
-       uint8_t e[SIZE_EXTRA_DATA_CENTRAL];
+       uint8_t end[22];
        int64_t offset_start, offset_end;
-       int entries;
+       struct zip *zip = a->format_data;
+       struct cd_segment *segment;
        int ret;
 
-       zip = a->format_data;
-       l = zip->central_directory;
-
-       /*
-        * Formatting central directory file header fields that are
-        * fixed for all entries.
-        * Fields not used (and therefor 0) are:
-        *
-        *   - comment_length
-        *   - disk_number
-        *   - attributes_internal
-        */
-       memset(h, 0, sizeof(h));
-       archive_le32enc(&h[FILE_HEADER_SIGNATURE], ZIP_SIGNATURE_FILE_HEADER);
-       archive_le16enc(&h[FILE_HEADER_VERSION_BY], ZIP_VERSION_BY);
-       archive_le16enc(&h[FILE_HEADER_VERSION_EXTRACT], ZIP_VERSION_EXTRACT);
-
-       entries = 0;
        offset_start = zip->written_bytes;
-
-       /* Formatting individual header fields per entry and
-        * writing each entry. */
-       while (l != NULL) {
-               archive_le16enc(&h[FILE_HEADER_FLAGS], l->flags);
-               archive_le16enc(&h[FILE_HEADER_COMPRESSION], l->compression);
-               archive_le32enc(&h[FILE_HEADER_TIMEDATE],
-                       dos_time(archive_entry_mtime(l->entry)));
-               archive_le32enc(&h[FILE_HEADER_CRC32], l->crc32);
-               archive_le32enc(&h[FILE_HEADER_COMPRESSED_SIZE],
-                       (uint32_t)l->compressed_size);
-               archive_le32enc(&h[FILE_HEADER_UNCOMPRESSED_SIZE],
-                       (uint32_t)archive_entry_size(l->entry));
-               archive_le16enc(&h[FILE_HEADER_FILENAME_LENGTH],
-                       (uint16_t)path_length(l->entry));
-               archive_le16enc(&h[FILE_HEADER_EXTRA_LENGTH], sizeof(e));
-               archive_le16enc(&h[FILE_HEADER_ATTRIBUTES_EXTERNAL+2],
-                       archive_entry_mode(l->entry));
-               archive_le32enc(&h[FILE_HEADER_OFFSET], (uint32_t)l->offset);
-
-               /* Formatting extra data. */
-               archive_le16enc(&e[EXTRA_DATA_CENTRAL_TIME_ID],
-                       ZIP_SIGNATURE_EXTRA_TIMESTAMP);
-               archive_le16enc(&e[EXTRA_DATA_CENTRAL_TIME_SIZE], 1 + 4);
-               e[EXTRA_DATA_CENTRAL_TIME_FLAG] = 0x07;
-               archive_le32enc(&e[EXTRA_DATA_CENTRAL_MTIME],
-                       (uint32_t)archive_entry_mtime(l->entry));
-               archive_le16enc(&e[EXTRA_DATA_CENTRAL_UNIX_ID],
-                       ZIP_SIGNATURE_EXTRA_NEW_UNIX);
-               archive_le16enc(&e[EXTRA_DATA_CENTRAL_UNIX_SIZE], 0x0000);
-
-               ret = __archive_write_output(a, h, sizeof(h));
+       segment = zip->central_directory;
+       while (segment != NULL) {
+               ret = __archive_write_output(a,
+                   segment->buff, segment->p - segment->buff);
                if (ret != ARCHIVE_OK)
                        return (ARCHIVE_FATAL);
-               zip->written_bytes += sizeof(h);
-
-               ret = write_path(l->entry, a);
-               if (ret <= ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
-               zip->written_bytes += ret;
-
-               ret = __archive_write_output(a, e, sizeof(e));
-               if (ret != ARCHIVE_OK)
-                       return (ARCHIVE_FATAL);
-               zip->written_bytes += sizeof(e);
-
-               l = l->next;
-               entries++;
+               zip->written_bytes += segment->p - segment->buff;
+               segment = segment->next;
        }
        offset_end = zip->written_bytes;
 
-       /* Formatting end of central directory. */
+       /* TODO: If central dir info is too large, write Zip64 end-of-cd */
+
+       /* Format and write end of central directory. */
        memset(end, 0, sizeof(end));
-       archive_le32enc(&end[CENTRAL_DIRECTORY_END_SIGNATURE],
-               ZIP_SIGNATURE_CENTRAL_DIRECTORY_END);
-       archive_le16enc(&end[CENTRAL_DIRECTORY_END_ENTRIES_DISK], entries);
-       archive_le16enc(&end[CENTRAL_DIRECTORY_END_ENTRIES], entries);
-       archive_le32enc(&end[CENTRAL_DIRECTORY_END_SIZE],
-               (uint32_t)(offset_end - offset_start));
-       archive_le32enc(&end[CENTRAL_DIRECTORY_END_OFFSET],
-               (uint32_t)offset_start);
-
-       /* Writing end of central directory. */
+       memcpy(end, "PK\005\006", 4);
+       archive_le16enc(end + 8, zip->central_directory_entries);
+       archive_le16enc(end + 10, zip->central_directory_entries);
+       archive_le32enc(end + 12, (uint32_t)(offset_end - offset_start));
+       archive_le32enc(end + 16, (uint32_t)offset_start);
        ret = __archive_write_output(a, end, sizeof(end));
        if (ret != ARCHIVE_OK)
                return (ARCHIVE_FATAL);
@@ -844,18 +833,21 @@ static int
 archive_write_zip_free(struct archive_write *a)
 {
        struct zip *zip;
-       struct zip_entry *l;
+       struct cd_segment *segment;
 
        zip = a->format_data;
        while (zip->central_directory != NULL) {
-          l = zip->central_directory;
-          zip->central_directory = l->next;
-          archive_entry_free(l->entry);
-          free(l);
+               segment = zip->central_directory;
+               zip->central_directory = segment->next;
+               free(segment->buff);
+               free(segment);
        }
 #ifdef HAVE_ZLIB_H
        free(zip->buf);
 #endif
+       archive_entry_free(zip->entry);
+       /* TODO: Free opt_sconv, sconv_default */
+
        free(zip);
        a->format_data = NULL;
        return (ARCHIVE_OK);
@@ -928,7 +920,7 @@ write_path(struct archive_entry *entry, struct archive_write *archive)
                return (ARCHIVE_FATAL);
        written_bytes += strlen(path);
 
-       /* Folders are recognized by a traling slash. */
+       /* Folders are recognized by a trailing slash. */
        if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
                ret = __archive_write_output(archive, "/", 1);
                if (ret != ARCHIVE_OK)
@@ -938,3 +930,41 @@ write_path(struct archive_entry *entry, struct archive_write *archive)
 
        return ((int)written_bytes);
 }
+
+static void
+copy_path(struct archive_entry *entry, unsigned char *p)
+{
+       int ret;
+       const char *path;
+       size_t pathlen;
+       mode_t type;
+
+       path = archive_entry_pathname(entry);
+       pathlen = strlen(path);
+       type = archive_entry_filetype(entry);
+
+       memcpy(p, path, pathlen);
+
+       /* Folders are recognized by a trailing slash. */
+       if ((type == AE_IFDIR) & (path[pathlen - 1] != '/')) {
+               p[pathlen] = '/';
+               p[pathlen + 1] = '\0';
+       }
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a, struct zip *zip)
+{
+       struct archive_string_conv *sconv;
+
+       if (zip->opt_sconv != NULL)
+               return (zip->opt_sconv);
+
+       if (!zip->init_default_conversion) {
+               zip->sconv_default =
+                   archive_string_default_conversion_for_write(&(a->archive));
+               zip->init_default_conversion = 1;
+       }
+       return (zip->sconv_default);
+}
index a8bbef0c70097b2fcacfad9fe5a8dc7bbc37e647..6fb34f8ae3cfe33d63b2b8e1f20504c08c70519c 100644 (file)
@@ -2146,6 +2146,28 @@ slurpfile(size_t * sizep, const char *fmt, ...)
        return (p);
 }
 
+/*
+ * Slurp a file into memory for ease of comparison and testing.
+ * Returns size of file in 'sizep' if non-NULL, null-terminates
+ * data in memory for ease of use.
+ */
+void
+dumpfile(const char *filename, void *data, size_t len)
+{
+       ssize_t bytes_written;
+       FILE *f;
+
+       f = fopen(filename, "wb");
+       if (f == NULL) {
+               logprintf("Can't open file %s for writing\n", filename);
+               return;
+       }
+       bytes_written = fwrite(data, 1, len, f);
+       if (bytes_written < len)
+               logprintf("Can't write file %s\n", filename);
+       fclose(f);
+}
+
 /* Read a uuencoded file from the reference directory, decode, and
  * write the result into the current directory. */
 #define        UUDECODE(c) (((c) - 0x20) & 0x3f)
index 7ca2da7ac39e66e90e7d72343119519f405e5b8a..51c56814c1d91b64558542a3470349d8fa2f81fb 100644 (file)
@@ -303,6 +303,9 @@ int is_LargeInode(const char *);
 /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
 char *slurpfile(size_t *, const char *fmt, ...);
 
+/* Dump block of bytes to a file. */
+void dumpfile(const char *filename, void *, size_t);
+
 /* Extracts named reference file to the current directory. */
 void extract_reference_file(const char *);
 
index 6f1ec5c3eda5dede5fae94198f432b68b95fe1b3..8aca0be832beb998b213cdba5a4d6d8602f52cfa 100644 (file)
 __FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip.c 201247 2009-12-30 05:59:21Z kientzle $");
 
 static void
-verify_contents(struct archive *a, int expect_details)
+verify_contents(struct archive *a, int seeking)
 {
        char filedata[64];
        struct archive_entry *ae;
 
        /*
-        * Read and verify first file.
+        * Default compression options:
         */
+
+       /* Read and verify first file. */
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualInt(1, archive_entry_mtime(ae));
        /* Zip doesn't store high-resolution mtime. */
@@ -47,64 +49,255 @@ verify_contents(struct archive *a, int expect_details)
        assertEqualInt(0, archive_entry_atime(ae));
        assertEqualInt(0, archive_entry_ctime(ae));
        assertEqualString("file", archive_entry_pathname(ae));
-       if (expect_details) {
+       if (seeking) {
                assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
                assertEqualInt(8, archive_entry_size(ae));
        } else {
-               assertEqualInt(0, archive_entry_size(ae));
+               assertEqualInt(0, archive_entry_size_is_set(ae));
        }
        assertEqualIntA(a, 8,
            archive_read_data(a, filedata, sizeof(filedata)));
        assertEqualMem(filedata, "12345678", 8);
 
 
-       /*
-        * Read the second file back.
-        */
+       /* Read the second file back. */
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualInt(1, archive_entry_mtime(ae));
        assertEqualInt(0, archive_entry_mtime_nsec(ae));
        assertEqualInt(0, archive_entry_atime(ae));
        assertEqualInt(0, archive_entry_ctime(ae));
        assertEqualString("file2", archive_entry_pathname(ae));
-       if (expect_details) {
+       if (seeking) {
                assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
                assertEqualInt(4, archive_entry_size(ae));
        } else {
-               assertEqualInt(0, archive_entry_size(ae));
+               assertEqualInt(0, archive_entry_size_is_set(ae));
        }
        assertEqualIntA(a, 4,
            archive_read_data(a, filedata, sizeof(filedata)));
        assertEqualMem(filedata, "1234", 4);
 
-       /*
-        * Read the third file back.
-        */
+       /* Read the third file back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(2, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file3", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(5, archive_entry_size(ae));
+               assertEqualInt(AE_IFREG | 0621, archive_entry_mode(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size_is_set(ae));
+       }
+       assertEqualIntA(a, 5,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "mnopq", 5);
+
+       /* Read symlink. */
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualInt(1, archive_entry_mtime(ae));
        assertEqualInt(0, archive_entry_mtime_nsec(ae));
        assertEqualInt(0, archive_entry_atime(ae));
        assertEqualInt(0, archive_entry_ctime(ae));
        assertEqualString("symlink", archive_entry_pathname(ae));
-       if (expect_details) {
+       if (seeking) {
                assertEqualInt(AE_IFLNK | 0755, archive_entry_mode(ae));
                assertEqualInt(0, archive_entry_size(ae));
                assertEqualString("file1", archive_entry_symlink(ae));
        } else {
+               /* Streaming cannot read file type, so
+                * symlink body shows as regular file contents. */
                assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
+               assertEqualInt(5, archive_entry_size(ae));
+       }
+
+       /* Read the dir entry back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(11, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("dir/", archive_entry_pathname(ae));
+       if (seeking)
+               assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae));
+       assertEqualInt(0, archive_entry_size(ae));
+       assertEqualIntA(a, 0, archive_read_data(a, filedata, 10));
+
+#ifdef HAVE_ZLIB_H
+       /*
+        * Deflate compression option:
+        */
+
+       /* Read and verify first file. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       /* Zip doesn't store high-resolution mtime. */
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file_deflate", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
+               assertEqualInt(8, archive_entry_size(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size(ae));
+       }
+       assertEqualIntA(a, 8,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "12345678", 8);
+
+
+       /* Read the second file back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file2_deflate", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
+               assertEqualInt(4, archive_entry_size(ae));
+       } else {
                assertEqualInt(0, archive_entry_size(ae));
        }
+       assertEqualIntA(a, 4,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "1234", 4);
+
+       /* Read the third file back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(2, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file3_deflate", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(5, archive_entry_size(ae));
+               assertEqualInt(AE_IFREG | 0621, archive_entry_mode(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size_is_set(ae));
+       }
+       assertEqualIntA(a, 5,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "ghijk", 4);
+
+       /* Read symlink. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("symlink_deflate", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFLNK | 0755, archive_entry_mode(ae));
+               assertEqualInt(0, archive_entry_size(ae));
+               assertEqualString("file1", archive_entry_symlink(ae));
+       } else {
+               assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
+               assertEqualInt(5, archive_entry_size(ae));
+               assertEqualIntA(a, 5, archive_read_data(a, filedata, 10));
+               assertEqualMem(filedata, "file1", 5);
+       }
+
+       /* Read the dir entry back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(11, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("dir_deflate/", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae));
+       }
+       assertEqualInt(0, archive_entry_size(ae));
+       assertEqualIntA(a, 0, archive_read_data(a, filedata, 10));
+#endif
 
        /*
-        * Read the dir entry back.
+        * Store compression option:
         */
+
+       /* Read and verify first file. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       /* Zip doesn't store high-resolution mtime. */
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file_stored", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
+               assert(archive_entry_size_is_set(ae));
+               assertEqualInt(8, archive_entry_size(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size_is_set(ae));
+       }
+       assertEqualIntA(a, 8,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "12345678", 8);
+
+
+       /* Read the second file back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file2_stored", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(4, archive_entry_size(ae));
+               assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size_is_set(ae));
+       }
+       assertEqualIntA(a, 4,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "ACEG", 4);
+
+       /* Read the third file back. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(2, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file3_stored", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(5, archive_entry_size(ae));
+               assertEqualInt(AE_IFREG | 0621, archive_entry_mode(ae));
+       } else {
+               assertEqualInt(0, archive_entry_size_is_set(ae));
+       }
+       assertEqualIntA(a, 5,
+           archive_read_data(a, filedata, sizeof(filedata)));
+       assertEqualMem(filedata, "ijklm", 4);
+
+       /* Read symlink. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_mtime_nsec(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("symlink_stored", archive_entry_pathname(ae));
+       if (seeking) {
+               assertEqualInt(AE_IFLNK | 0755, archive_entry_mode(ae));
+               assertEqualInt(0, archive_entry_size(ae));
+               assertEqualString("file1", archive_entry_symlink(ae));
+       } else {
+               assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
+               assertEqualInt(5, archive_entry_size(ae));
+               assertEqualIntA(a, 5, archive_read_data(a, filedata, 10));
+               assertEqualMem(filedata, "file1", 5);
+       }
+
+       /* Read the dir entry back. */
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
        assertEqualInt(11, archive_entry_mtime(ae));
        assertEqualInt(0, archive_entry_mtime_nsec(ae));
        assertEqualInt(0, archive_entry_atime(ae));
        assertEqualInt(0, archive_entry_ctime(ae));
-       assertEqualString("dir/", archive_entry_pathname(ae));
-       if (expect_details)
+       assertEqualString("dir_stored/", archive_entry_pathname(ae));
+       if (seeking)
                assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae));
        assertEqualInt(0, archive_entry_size(ae));
        assertEqualIntA(a, 0, archive_read_data(a, filedata, 10));
@@ -129,30 +322,24 @@ DEFINE_TEST(test_write_format_zip)
        /* Create a new archive in memory. */
        assert((a = archive_write_new()) != NULL);
        assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
-#ifdef HAVE_ZLIB_H
-       compression_type = "deflate";
-#else
-       compression_type = "store";
-#endif
-       assertEqualIntA(a, ARCHIVE_OK,
-           archive_write_set_format_option(a, "zip", "compression", compression_type));
        assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_write_open_memory(a, buff, buffsize, &used));
 
+       /*
+        * First write things with the "default" compression.
+        * The library will choose "deflate" for most things if it's
+        * available, else "store".
+        */
+
        /*
         * Write a file to it.
         */
        assert((ae = archive_entry_new()) != NULL);
        archive_entry_set_mtime(ae, 1, 10);
-       assertEqualInt(1, archive_entry_mtime(ae));
-       assertEqualInt(10, archive_entry_mtime_nsec(ae));
        archive_entry_copy_pathname(ae, "file");
-       assertEqualString("file", archive_entry_pathname(ae));
        archive_entry_set_mode(ae, AE_IFREG | 0755);
-       assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
        archive_entry_set_size(ae, 8);
-
        assertEqualInt(0, archive_write_header(a, ae));
        archive_entry_free(ae);
        assertEqualInt(8, archive_write_data(a, "12345678", 9));
@@ -163,20 +350,27 @@ DEFINE_TEST(test_write_format_zip)
         */
        assert((ae = archive_entry_new()) != NULL);
        archive_entry_set_mtime(ae, 1, 10);
-       assertEqualInt(1, archive_entry_mtime(ae));
-       assertEqualInt(10, archive_entry_mtime_nsec(ae));
        archive_entry_copy_pathname(ae, "file2");
-       assertEqualString("file2", archive_entry_pathname(ae));
        archive_entry_set_mode(ae, AE_IFREG | 0755);
-       assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
        archive_entry_set_size(ae, 4);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(4, archive_write_data(a, "1234", 4));
 
+       /*
+        * Write a file with an unknown size.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 2, 15);
+       archive_entry_copy_pathname(ae, "file3");
+       archive_entry_set_mode(ae, AE_IFREG | 0621);
+       archive_entry_unset_size(ae);
        assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
        archive_entry_free(ae);
-       assertEqualInt(4, archive_write_data(a, "1234", 5));
+       assertEqualInt(5, archive_write_data(a, "mnopq", 5));
 
        /*
-        * Write symbolic like file to it.
+        * Write symbolic link.
         */
        assert((ae = archive_entry_new()) != NULL);
        archive_entry_set_mtime(ae, 1, 10);
@@ -201,17 +395,158 @@ DEFINE_TEST(test_write_format_zip)
        archive_entry_copy_pathname(ae, "dir");
        archive_entry_set_mode(ae, S_IFDIR | 0755);
        archive_entry_set_size(ae, 512);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       failure("size should be zero so that applications know not to write");
+       assertEqualInt(0, archive_entry_size(ae));
+       archive_entry_free(ae);
+       assertEqualIntA(a, 0, archive_write_data(a, "12345678", 9));
+
+       /*
+        * Force "deflate" compression if the platform supports it.
+        */
+#ifdef HAVE_ZLIB_H
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_zip_set_compression_deflate(a));
+
+       /*
+        * Write a file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file_deflate");
+       archive_entry_set_mode(ae, AE_IFREG | 0755);
+       archive_entry_set_size(ae, 8);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(8, archive_write_data(a, "12345678", 9));
+       assertEqualInt(0, archive_write_data(a, "1", 1));
 
+       /*
+        * Write another file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file2_deflate");
+       archive_entry_set_mode(ae, AE_IFREG | 0755);
+       archive_entry_set_size(ae, 4);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(4, archive_write_data(a, "1234", 4));
+
+       /*
+        * Write a file with an unknown size.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 2, 15);
+       archive_entry_copy_pathname(ae, "file3_deflate");
+       archive_entry_set_mode(ae, AE_IFREG | 0621);
+       archive_entry_unset_size(ae);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(5, archive_write_data(a, "ghijk", 5));
+
+       /*
+        * Write symbolic like file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "symlink_deflate");
+       archive_entry_copy_symlink(ae, "file1");
+       archive_entry_set_mode(ae, AE_IFLNK | 0755);
+       archive_entry_set_size(ae, 4);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+
+       /*
+        * Write a directory to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 11, 110);
+       archive_entry_copy_pathname(ae, "dir_deflate");
+       archive_entry_set_mode(ae, S_IFDIR | 0755);
+       archive_entry_set_size(ae, 512);
        assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
        failure("size should be zero so that applications know not to write");
        assertEqualInt(0, archive_entry_size(ae));
        archive_entry_free(ae);
        assertEqualIntA(a, 0, archive_write_data(a, "12345678", 9));
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
+#endif
+
+       /*
+        * Now write a bunch of entries with "store" compression.
+        */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_zip_set_compression_store(a));
+
+       /*
+        * Write a file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file_stored");
+       archive_entry_set_mode(ae, AE_IFREG | 0755);
+       archive_entry_set_size(ae, 8);
+       assertEqualInt(0, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(8, archive_write_data(a, "12345678", 9));
+       assertEqualInt(0, archive_write_data(a, "1", 1));
+
+       /*
+        * Write another file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file2_stored");
+       archive_entry_set_mode(ae, AE_IFREG | 0755);
+       archive_entry_set_size(ae, 4);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(4, archive_write_data(a, "ACEG", 4));
+
+       /*
+        * Write a file with an unknown size.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 2, 15);
+       archive_entry_copy_pathname(ae, "file3_stored");
+       archive_entry_set_mode(ae, AE_IFREG | 0621);
+       archive_entry_unset_size(ae);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertEqualInt(5, archive_write_data(a, "ijklm", 5));
+
+       /*
+        * Write symbolic like file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "symlink_stored");
+       archive_entry_copy_symlink(ae, "file1");
+       archive_entry_set_mode(ae, AE_IFLNK | 0755);
+       archive_entry_set_size(ae, 4);
+       assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
+       archive_entry_free(ae);
+
+       /*
+        * Write a directory to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 11, 110);
+       archive_entry_copy_pathname(ae, "dir_stored");
+       archive_entry_set_mode(ae, S_IFDIR | 0755);
+       archive_entry_set_size(ae, 512);
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+       failure("size should be zero so that applications know not to write");
+       assertEqualInt(0, archive_entry_size(ae));
+       archive_entry_free(ae);
+       assertEqualIntA(a, 0, archive_write_data(a, "12345678", 9));
+
 
        /* Close out the archive. */
        assertEqualInt(ARCHIVE_OK, archive_write_close(a));
        assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
+       dumpfile("constructed.zip", buff, used);
+
        /*
         * Now, read the data back.
         */
index 9d47f9b746f752466bf90be78296f216e788ab46..7bfceb9691bed37631aede730c9e914099efd55a 100644 (file)
@@ -114,8 +114,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        archive_entry_set_uid(entry, file_uid);
        archive_entry_set_gid(entry, file_gid);
        archive_entry_set_mtime(entry, t, 0);
-       archive_entry_set_atime(entry, t, 0);
-       archive_entry_set_ctime(entry, t, 0);
+       archive_entry_set_atime(entry, t + 3, 0);
        assertEqualIntA(a, 0, archive_write_header(a, entry));
        assertEqualIntA(a, sizeof(file_data1), archive_write_data(a, file_data1, sizeof(file_data1)));
        assertEqualIntA(a, sizeof(file_data2), archive_write_data(a, file_data2, sizeof(file_data2)));
@@ -129,8 +128,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        archive_entry_set_uid(entry, folder_uid);
        archive_entry_set_gid(entry, folder_gid);
        archive_entry_set_mtime(entry, t, 0);
-       archive_entry_set_atime(entry, t, 0);
-       archive_entry_set_ctime(entry, t, 0);
+       archive_entry_set_ctime(entry, t + 5, 0);
        assertEqualIntA(a, 0, archive_write_header(a, entry));
        archive_entry_free(entry);
 
@@ -138,6 +136,8 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
        assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
+       dumpfile("constructed.zip", buff, used);
+
        /* Remember the end of the archive in memory. */
        buffend = buff + used;
 
@@ -166,8 +166,8 @@ DEFINE_TEST(test_write_format_zip_no_compression)
 
        /* Verify file entry in central directory. */
        assertEqualMem(p, "PK\001\002", 4); /* Signature */
-       assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */
-       assertEqualInt(i2(p + 6), 20); /* Version needed to extract */
+       assertEqualInt(i2(p + 4), 3 * 256 + 10); /* Version made by */
+       assertEqualInt(i2(p + 6), 10); /* Version needed to extract */
        assertEqualInt(i2(p + 8), 8); /* Flags */
        assertEqualInt(i2(p + 10), 0); /* Compression method */
        assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
@@ -178,7 +178,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(p + 20), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */
        assertEqualInt(i4(p + 24), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */
        assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */
-       assertEqualInt(i2(p + 30), 13); /* Extra field length */
+       assertEqualInt(i2(p + 30), 28); /* Extra field length */
        assertEqualInt(i2(p + 32), 0); /* File comment length */
        assertEqualInt(i2(p + 34), 0); /* Disk number start */
        assertEqualInt(i2(p + 36), 0); /* Internal file attrs */
@@ -187,18 +187,20 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */
        p = p + 46 + strlen(file_name);
        assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(p + 2), 5); /* 'UT' size */
-       assertEqualInt(p[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(p + 2), 9); /* 'UT' size */
+       assertEqualInt(p[4], 3); /* 'UT' flags */
        assertEqualInt(i4(p + 5), t); /* 'UT' mtime */
-       p = p + 9;
+       assertEqualInt(i4(p + 9), t + 3); /* 'UT' atime */
+       p = p + 4 + i2(p + 2);
        assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */
-       assertEqualInt(i2(p + 2), 0); /* 'ux' size */
-       p = p + 4;
+       assertEqualInt(i2(p + 2), 11); /* 'ux' size */
+/* TODO */
+       p = p + 4 + i2(p + 2);
 
        /* Verify local header of file entry. */
        q = buff;
        assertEqualMem(q, "PK\003\004", 4); /* Signature */
-       assertEqualInt(i2(q + 4), 20); /* Version needed to extract */
+       assertEqualInt(i2(q + 4), 10); /* Version needed to extract */
        assertEqualInt(i2(q + 6), 8); /* Flags */
        assertEqualInt(i2(q + 8), 0); /* Compression method */
        assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
@@ -207,16 +209,15 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(q + 18), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */
        assertEqualInt(i4(q + 22), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */
        assertEqualInt(i2(q + 26), strlen(file_name)); /* Pathname length */
-       assertEqualInt(i2(q + 28), 32); /* Extra field length */
+       assertEqualInt(i2(q + 28), 28); /* Extra field length */
        assertEqualMem(q + 30, file_name, strlen(file_name)); /* Pathname */
        q = q + 30 + strlen(file_name);
        assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(q + 2), 13); /* 'UT' size */
-       assertEqualInt(q[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(q + 2), 9); /* 'UT' size */
+       assertEqualInt(q[4], 3); /* 'UT' flags */
        assertEqualInt(i4(q + 5), t); /* 'UT' mtime */
-       assertEqualInt(i4(q + 9), t); /* 'UT' atime */
-       assertEqualInt(i4(q + 13), t); /* 'UT' ctime */
-       q = q + 17;
+       assertEqualInt(i4(q + 9), t + 3); /* 'UT' atime */
+       q = q + 4 + i2(q + 2);
        assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */
        assertEqualInt(i2(q + 2), 11); /* 'ux' size */
        assertEqualInt(q[4], 1); /* 'ux' version */
@@ -224,7 +225,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(q + 6), file_uid); /* 'Ux' UID */
        assertEqualInt(q[10], 4); /* 'ux' gid size */
        assertEqualInt(i4(q + 11), file_gid); /* 'Ux' GID */
-       q = q + 15;
+       q = q + 4 + i2(q + 2);
 
        /* Verify data of file entry. */
        assertEqualMem(q, file_data1, sizeof(file_data1));
@@ -242,7 +243,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualMem(p, "PK\001\002", 4); /* Signature */
        assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */
        assertEqualInt(i2(p + 6), 20); /* Version needed to extract */
-       assertEqualInt(i2(p + 8), 8); /* Flags */
+       assertEqualInt(i2(p + 8), 0); /* Flags */
        assertEqualInt(i2(p + 10), 0); /* Compression method */
        assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
        assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */
@@ -251,7 +252,7 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(p + 20), 0); /* Compressed size */
        assertEqualInt(i4(p + 24), 0); /* Uncompressed size */
        assertEqualInt(i2(p + 28), strlen(folder_name)); /* Pathname length */
-       assertEqualInt(i2(p + 30), 13); /* Extra field length */
+       assertEqualInt(i2(p + 30), 28); /* Extra field length */
        assertEqualInt(i2(p + 32), 0); /* File comment length */
        assertEqualInt(i2(p + 34), 0); /* Disk number start */
        assertEqualInt(i2(p + 36), 0); /* Internal file attrs */
@@ -260,18 +261,24 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualMem(p + 46, folder_name, strlen(folder_name)); /* Pathname */
        p = p + 46 + strlen(folder_name);
        assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(p + 2), 5); /* 'UT' size */
-       assertEqualInt(p[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(p + 2), 9); /* 'UT' size */
+       assertEqualInt(p[4], 5); /* 'UT' flags */
        assertEqualInt(i4(p + 5), t); /* 'UT' mtime */
-       p = p + 9;
+       assertEqualInt(i4(p + 9), t + 5); /* 'UT' atime */
+       p = p + 4 + i2(p + 2);
        assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */
-       assertEqualInt(i2(p + 2), 0); /* 'ux' size */
-       /*p = p + 4;*/
+       assertEqualInt(i2(p + 2), 11); /* 'ux' size */
+       assertEqualInt(p[4], 1); /* 'ux' version */
+       assertEqualInt(p[5], 4); /* 'ux' uid size */
+       assertEqualInt(i4(p + 6), folder_uid); /* 'ux' UID */
+       assertEqualInt(p[10], 4); /* 'ux' gid size */
+       assertEqualInt(i4(p + 11), folder_gid); /* 'ux' GID */
+       /*p = p + 4 + i2(p + 2);*/
 
        /* Verify local header of folder entry. */
        assertEqualMem(q, "PK\003\004", 4); /* Signature */
        assertEqualInt(i2(q + 4), 20); /* Version needed to extract */
-       assertEqualInt(i2(q + 6), 8); /* Flags */
+       assertEqualInt(i2(q + 6), 0); /* Flags */
        assertEqualInt(i2(q + 8), 0); /* Compression method */
        assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
        assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */
@@ -279,16 +286,15 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(q + 18), 0); /* Compressed size */
        assertEqualInt(i4(q + 22), 0); /* Uncompressed size */
        assertEqualInt(i2(q + 26), strlen(folder_name)); /* Pathname length */
-       assertEqualInt(i2(q + 28), 32); /* Extra field length */
+       assertEqualInt(i2(q + 28), 28); /* Extra field length */
        assertEqualMem(q + 30, folder_name, strlen(folder_name)); /* Pathname */
        q = q + 30 + strlen(folder_name);
        assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(q + 2), 13); /* 'UT' size */
-       assertEqualInt(q[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(q + 2), 9); /* 'UT' size */
+       assertEqualInt(q[4], 5); /* 'UT' flags */
        assertEqualInt(i4(q + 5), t); /* 'UT' mtime */
-       assertEqualInt(i4(q + 9), t); /* 'UT' atime */
-       assertEqualInt(i4(q + 13), t); /* 'UT' ctime */
-       q = q + 17;
+       assertEqualInt(i4(q + 9), t + 5); /* 'UT' atime */
+       q = q + 4 + i2(q + 2);
        assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */
        assertEqualInt(i2(q + 2), 11); /* 'ux' size */
        assertEqualInt(q[4], 1); /* 'ux' version */
@@ -296,15 +302,9 @@ DEFINE_TEST(test_write_format_zip_no_compression)
        assertEqualInt(i4(q + 6), folder_uid); /* 'ux' UID */
        assertEqualInt(q[10], 4); /* 'ux' gid size */
        assertEqualInt(i4(q + 11), folder_gid); /* 'ux' GID */
-       q = q + 15;
+       q = q + 4 + i2(q + 2);
 
        /* There should not be any data in the folder entry,
-        * meaning next is the data descriptor header. */
-
-       /* Verify data descriptor of folder entry. */
-       assertEqualMem(q, "PK\007\010", 4); /* Signature */
-       assertEqualInt(i4(q + 4), crc); /* CRC-32 */
-       assertEqualInt(i4(q + 8), 0); /* Compressed size */
-       assertEqualInt(i4(q + 12), 0); /* Uncompressed size */
-       /*q = q + 16;*/
+        * so the first central directory entry should be next: */
+       assertEqualMem(q, "PK\001\002", 4); /* Signature */
 }
index f77eb1be0685ad64807a2062d587d46573250492..d466794394c475b4d36e9dcb8213f2c32e74f8a7 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2012 Matthias Brantner
+ * Copyright (c) 2008 Anselm Strauss
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/*
+ * Development supported by Google Summer of Code 2008.
+ */
+
 #include "test.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_format_zip_no_compression.c 201247 2009-12-30 05:59:21Z kientzle $");
 
 static unsigned long
 bitcrc32(unsigned long c, void *_p, size_t s)
@@ -93,6 +98,7 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        /* Create new ZIP archive in memory without padding. */
        assert((a = archive_write_new()) != NULL);
        assertA(0 == archive_write_set_format_zip(a));
+       assertA(0 == archive_write_set_options(a, "zip:compression=store"));
        assertA(0 == archive_write_add_filter_none(a));
        assertA(0 == archive_write_set_bytes_per_block(a, 1));
        assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
@@ -108,14 +114,11 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        archive_entry_set_uid(entry, file_uid);
        archive_entry_set_gid(entry, file_gid);
        archive_entry_set_mtime(entry, t, 0);
-       archive_entry_set_atime(entry, t, 0);
-       archive_entry_set_ctime(entry, t, 0);
-       archive_write_zip_set_compression_store(a);
+       archive_entry_set_atime(entry, t + 3, 0);
        assertEqualIntA(a, 0, archive_write_header(a, entry));
        assertEqualIntA(a, sizeof(file_data1), archive_write_data(a, file_data1, sizeof(file_data1)));
        assertEqualIntA(a, sizeof(file_data2), archive_write_data(a, file_data2, sizeof(file_data2)));
        archive_entry_free(entry);
-       archive_write_finish_entry(a);
 
        /* Folder */
        assert((entry = archive_entry_new()) != NULL);
@@ -125,17 +128,16 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        archive_entry_set_uid(entry, folder_uid);
        archive_entry_set_gid(entry, folder_gid);
        archive_entry_set_mtime(entry, t, 0);
-       archive_entry_set_atime(entry, t, 0);
-       archive_entry_set_ctime(entry, t, 0);
-       archive_write_zip_set_compression_store(a);
+       archive_entry_set_ctime(entry, t + 5, 0);
        assertEqualIntA(a, 0, archive_write_header(a, entry));
        archive_entry_free(entry);
-       archive_write_finish_entry(a);
 
        /* Close the archive . */
        assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
        assertEqualInt(ARCHIVE_OK, archive_write_free(a));
 
+       dumpfile("constructed.zip", buff, used);
+
        /* Remember the end of the archive in memory. */
        buffend = buff + used;
 
@@ -164,8 +166,8 @@ DEFINE_TEST(test_write_zip_set_compression_store)
 
        /* Verify file entry in central directory. */
        assertEqualMem(p, "PK\001\002", 4); /* Signature */
-       assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */
-       assertEqualInt(i2(p + 6), 20); /* Version needed to extract */
+       assertEqualInt(i2(p + 4), 3 * 256 + 10); /* Version made by */
+       assertEqualInt(i2(p + 6), 10); /* Version needed to extract */
        assertEqualInt(i2(p + 8), 8); /* Flags */
        assertEqualInt(i2(p + 10), 0); /* Compression method */
        assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
@@ -176,7 +178,7 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(p + 20), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */
        assertEqualInt(i4(p + 24), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */
        assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */
-       assertEqualInt(i2(p + 30), 13); /* Extra field length */
+       assertEqualInt(i2(p + 30), 28); /* Extra field length */
        assertEqualInt(i2(p + 32), 0); /* File comment length */
        assertEqualInt(i2(p + 34), 0); /* Disk number start */
        assertEqualInt(i2(p + 36), 0); /* Internal file attrs */
@@ -185,18 +187,20 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */
        p = p + 46 + strlen(file_name);
        assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(p + 2), 5); /* 'UT' size */
-       assertEqualInt(p[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(p + 2), 9); /* 'UT' size */
+       assertEqualInt(p[4], 3); /* 'UT' flags */
        assertEqualInt(i4(p + 5), t); /* 'UT' mtime */
-       p = p + 9;
+       assertEqualInt(i4(p + 9), t + 3); /* 'UT' atime */
+       p = p + 4 + i2(p + 2);
        assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */
-       assertEqualInt(i2(p + 2), 0); /* 'ux' size */
-       p = p + 4;
+       assertEqualInt(i2(p + 2), 11); /* 'ux' size */
+/* TODO */
+       p = p + 4 + i2(p + 2);
 
        /* Verify local header of file entry. */
        q = buff;
        assertEqualMem(q, "PK\003\004", 4); /* Signature */
-       assertEqualInt(i2(q + 4), 20); /* Version needed to extract */
+       assertEqualInt(i2(q + 4), 10); /* Version needed to extract */
        assertEqualInt(i2(q + 6), 8); /* Flags */
        assertEqualInt(i2(q + 8), 0); /* Compression method */
        assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
@@ -205,16 +209,15 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(q + 18), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */
        assertEqualInt(i4(q + 22), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */
        assertEqualInt(i2(q + 26), strlen(file_name)); /* Pathname length */
-       assertEqualInt(i2(q + 28), 32); /* Extra field length */
+       assertEqualInt(i2(q + 28), 28); /* Extra field length */
        assertEqualMem(q + 30, file_name, strlen(file_name)); /* Pathname */
        q = q + 30 + strlen(file_name);
        assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(q + 2), 13); /* 'UT' size */
-       assertEqualInt(q[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(q + 2), 9); /* 'UT' size */
+       assertEqualInt(q[4], 3); /* 'UT' flags */
        assertEqualInt(i4(q + 5), t); /* 'UT' mtime */
-       assertEqualInt(i4(q + 9), t); /* 'UT' atime */
-       assertEqualInt(i4(q + 13), t); /* 'UT' ctime */
-       q = q + 17;
+       assertEqualInt(i4(q + 9), t + 3); /* 'UT' atime */
+       q = q + 4 + i2(q + 2);
        assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */
        assertEqualInt(i2(q + 2), 11); /* 'ux' size */
        assertEqualInt(q[4], 1); /* 'ux' version */
@@ -222,7 +225,7 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(q + 6), file_uid); /* 'Ux' UID */
        assertEqualInt(q[10], 4); /* 'ux' gid size */
        assertEqualInt(i4(q + 11), file_gid); /* 'Ux' GID */
-       q = q + 15;
+       q = q + 4 + i2(q + 2);
 
        /* Verify data of file entry. */
        assertEqualMem(q, file_data1, sizeof(file_data1));
@@ -240,7 +243,7 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualMem(p, "PK\001\002", 4); /* Signature */
        assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */
        assertEqualInt(i2(p + 6), 20); /* Version needed to extract */
-       assertEqualInt(i2(p + 8), 8); /* Flags */
+       assertEqualInt(i2(p + 8), 0); /* Flags */
        assertEqualInt(i2(p + 10), 0); /* Compression method */
        assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
        assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */
@@ -249,7 +252,7 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(p + 20), 0); /* Compressed size */
        assertEqualInt(i4(p + 24), 0); /* Uncompressed size */
        assertEqualInt(i2(p + 28), strlen(folder_name)); /* Pathname length */
-       assertEqualInt(i2(p + 30), 13); /* Extra field length */
+       assertEqualInt(i2(p + 30), 28); /* Extra field length */
        assertEqualInt(i2(p + 32), 0); /* File comment length */
        assertEqualInt(i2(p + 34), 0); /* Disk number start */
        assertEqualInt(i2(p + 36), 0); /* Internal file attrs */
@@ -258,18 +261,24 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualMem(p + 46, folder_name, strlen(folder_name)); /* Pathname */
        p = p + 46 + strlen(folder_name);
        assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(p + 2), 5); /* 'UT' size */
-       assertEqualInt(p[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(p + 2), 9); /* 'UT' size */
+       assertEqualInt(p[4], 5); /* 'UT' flags */
        assertEqualInt(i4(p + 5), t); /* 'UT' mtime */
-       p = p + 9;
+       assertEqualInt(i4(p + 9), t + 5); /* 'UT' atime */
+       p = p + 4 + i2(p + 2);
        assertEqualInt(i2(p), 0x7875); /* 'ux' extension header */
-       assertEqualInt(i2(p + 2), 0); /* 'ux' size */
-       /*p = p + 4;*/
+       assertEqualInt(i2(p + 2), 11); /* 'ux' size */
+       assertEqualInt(p[4], 1); /* 'ux' version */
+       assertEqualInt(p[5], 4); /* 'ux' uid size */
+       assertEqualInt(i4(p + 6), folder_uid); /* 'ux' UID */
+       assertEqualInt(p[10], 4); /* 'ux' gid size */
+       assertEqualInt(i4(p + 11), folder_gid); /* 'ux' GID */
+       /*p = p + 4 + i2(p + 2);*/
 
        /* Verify local header of folder entry. */
        assertEqualMem(q, "PK\003\004", 4); /* Signature */
        assertEqualInt(i2(q + 4), 20); /* Version needed to extract */
-       assertEqualInt(i2(q + 6), 8); /* Flags */
+       assertEqualInt(i2(q + 6), 0); /* Flags */
        assertEqualInt(i2(q + 8), 0); /* Compression method */
        assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */
        assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */
@@ -277,16 +286,15 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(q + 18), 0); /* Compressed size */
        assertEqualInt(i4(q + 22), 0); /* Uncompressed size */
        assertEqualInt(i2(q + 26), strlen(folder_name)); /* Pathname length */
-       assertEqualInt(i2(q + 28), 32); /* Extra field length */
+       assertEqualInt(i2(q + 28), 28); /* Extra field length */
        assertEqualMem(q + 30, folder_name, strlen(folder_name)); /* Pathname */
        q = q + 30 + strlen(folder_name);
        assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */
-       assertEqualInt(i2(q + 2), 13); /* 'UT' size */
-       assertEqualInt(q[4], 7); /* 'UT' flags */
+       assertEqualInt(i2(q + 2), 9); /* 'UT' size */
+       assertEqualInt(q[4], 5); /* 'UT' flags */
        assertEqualInt(i4(q + 5), t); /* 'UT' mtime */
-       assertEqualInt(i4(q + 9), t); /* 'UT' atime */
-       assertEqualInt(i4(q + 13), t); /* 'UT' ctime */
-       q = q + 17;
+       assertEqualInt(i4(q + 9), t + 5); /* 'UT' atime */
+       q = q + 4 + i2(q + 2);
        assertEqualInt(i2(q), 0x7875); /* 'ux' extension header */
        assertEqualInt(i2(q + 2), 11); /* 'ux' size */
        assertEqualInt(q[4], 1); /* 'ux' version */
@@ -294,15 +302,9 @@ DEFINE_TEST(test_write_zip_set_compression_store)
        assertEqualInt(i4(q + 6), folder_uid); /* 'ux' UID */
        assertEqualInt(q[10], 4); /* 'ux' gid size */
        assertEqualInt(i4(q + 11), folder_gid); /* 'ux' GID */
-       q = q + 15;
+       q = q + 4 + i2(q + 2);
 
        /* There should not be any data in the folder entry,
-        * meaning next is the data descriptor header. */
-
-       /* Verify data descriptor of folder entry. */
-       assertEqualMem(q, "PK\007\010", 4); /* Signature */
-       assertEqualInt(i4(q + 4), crc); /* CRC-32 */
-       assertEqualInt(i4(q + 8), 0); /* Compressed size */
-       assertEqualInt(i4(q + 12), 0); /* Uncompressed size */
-       /*q = q + 16;*/
+        * so the first central directory entry should be next: */
+       assertEqualMem(q, "PK\001\002", 4); /* Signature */
 }