From: Tim Kientzle Date: Sun, 1 Dec 2013 22:45:56 +0000 (-0800) Subject: Refactor Zip writer. X-Git-Tag: v3.1.900a~327^2~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc6df8f01d99f00f69ca8e521d881998e702683a;p=thirdparty%2Flibarchive.git Refactor Zip writer. 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... --- diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 38c6b31b7..bd01caa5c 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -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. */ diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index bf704e6d4..cb5718d31 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -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); +} diff --git a/libarchive/test/main.c b/libarchive/test/main.c index a8bbef0c7..6fb34f8ae 100644 --- a/libarchive/test/main.c +++ b/libarchive/test/main.c @@ -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) diff --git a/libarchive/test/test.h b/libarchive/test/test.h index 7ca2da7ac..51c56814c 100644 --- a/libarchive/test/test.h +++ b/libarchive/test/test.h @@ -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 *); diff --git a/libarchive/test/test_write_format_zip.c b/libarchive/test/test_write_format_zip.c index 6f1ec5c3e..8aca0be83 100644 --- a/libarchive/test/test_write_format_zip.c +++ b/libarchive/test/test_write_format_zip.c @@ -32,14 +32,16 @@ __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. */ diff --git a/libarchive/test/test_write_format_zip_no_compression.c b/libarchive/test/test_write_format_zip_no_compression.c index 9d47f9b74..7bfceb969 100644 --- a/libarchive/test/test_write_format_zip_no_compression.c +++ b/libarchive/test/test_write_format_zip_no_compression.c @@ -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 */ } diff --git a/libarchive/test/test_write_zip_set_compression_store.c b/libarchive/test/test_write_zip_set_compression_store.c index f77eb1be0..d46679439 100644 --- a/libarchive/test/test_write_zip_set_compression_store.c +++ b/libarchive/test/test_write_zip_set_compression_store.c @@ -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 @@ -23,7 +23,12 @@ * 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 */ }