1 /*- 2 * Copyright (c) 2003-2023 Tim Kientzle 3 * Copyright (c) 2008 Anselm Strauss 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "test.h" 28 29 /* 30 * Detailed byte-for-byte verification of the format of a zip archive 31 * written in streaming mode with Zip64 extensions enabled. 32 */ 33 34 DEFINE_TEST(test_write_format_zip64_stream) 35 { 36 struct archive *a; 37 struct archive_entry *ae; 38 size_t used, buffsize = 1000000; 39 unsigned long crc; 40 unsigned long compressed_size = 0; 41 __LA_MODE_T file_perm = 00644; 42 int zip_version = 45; 43 int zip_compression = 8; 44 short file_uid = 10, file_gid = 20; 45 unsigned char *buff, *buffend, *p; 46 unsigned char *central_header, *local_header, *eocd, *eocd_record; 47 unsigned char *extension_start, *extension_end; 48 unsigned char *data_start, *data_end; 49 char file_data[] = {'1', '2', '3', '4', '5', '6', '7', '8'}; 50 const char *file_name = "file"; 51 52 #ifndef HAVE_ZLIB_H 53 zip_compression = 0; 54 #endif 55 56 buff = malloc(buffsize); 57 58 /* Create a new archive in memory. */ 59 assert((a = archive_write_new()) != NULL); 60 assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); 61 assertEqualIntA(a, ARCHIVE_OK, 62 archive_write_set_options(a, "zip:zip64")); 63 assertEqualIntA(a, ARCHIVE_OK, 64 archive_write_set_options(a, "zip:experimental")); 65 assertEqualIntA(a, ARCHIVE_OK, 66 archive_write_open_memory(a, buff, buffsize, &used)); 67 68 assert((ae = archive_entry_new()) != NULL); 69 archive_entry_copy_pathname(ae, file_name); 70 archive_entry_set_mode(ae, AE_IFREG | file_perm); 71 archive_entry_set_uid(ae, file_uid); 72 archive_entry_set_gid(ae, file_gid); 73 archive_entry_set_mtime(ae, 0, 0); 74 assertEqualInt(0, archive_write_header(a, ae)); 75 archive_entry_free(ae); 76 assertEqualInt(8, archive_write_data(a, file_data, sizeof(file_data))); 77 assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); 78 assertEqualInt(ARCHIVE_OK, archive_write_free(a)); 79 buffend = buff + used; 80 dumpfile("constructed.zip", buff, used); 81 82 /* Verify "End of Central Directory" record. */ 83 /* Get address of end-of-central-directory record. */ 84 eocd_record = p = buffend - 22; /* Assumes there is no zip comment field. */ 85 failure("End-of-central-directory begins with PK\\005\\006 signature"); 86 assertEqualMem(p, "PK\005\006", 4); 87 failure("This must be disk 0"); 88 assertEqualInt(i2le(p + 4), 0); 89 failure("Central dir must start on disk 0"); 90 assertEqualInt(i2le(p + 6), 0); 91 failure("All central dir entries are on this disk"); 92 assertEqualInt(i2le(p + 8), i2le(p + 10)); 93 eocd = buff + i4le(p + 12) + i4le(p + 16); 94 failure("no zip comment"); 95 assertEqualInt(i2le(p + 20), 0); 96 97 /* Get address of first entry in central directory. */ 98 central_header = p = buff + i4le(buffend - 6); 99 failure("Central file record at offset %u should begin with" 100 " PK\\001\\002 signature", 101 i4le(buffend - 10)); 102 103 /* Verify file entry in central directory. */ 104 assertEqualMem(p, "PK\001\002", 4); /* Signature */ 105 assertEqualInt(i2le(p + 4), 3 * 256 + zip_version); /* Version made by */ 106 assertEqualInt(i2le(p + 6), zip_version); /* Version needed to extract */ 107 assertEqualInt(i2le(p + 8), 8); /* Flags */ 108 assertEqualInt(i2le(p + 10), zip_compression); /* Compression method */ 109 assertEqualInt(i2le(p + 12), 0); /* File time */ 110 assertEqualInt(i2le(p + 14), 33); /* File date */ 111 crc = bitcrc32(0, file_data, sizeof(file_data)); 112 assertEqualInt(i4le(p + 16), crc); /* CRC-32 */ 113 compressed_size = i4le(p + 20); /* Compressed size */ 114 assertEqualInt(i4le(p + 24), sizeof(file_data)); /* Uncompressed size */ 115 assertEqualInt(i2le(p + 28), strlen(file_name)); /* Pathname length */ 116 /* assertEqualInt(i2le(p + 30), 28); */ /* Extra field length: See below */ 117 assertEqualInt(i2le(p + 32), 0); /* File comment length */ 118 assertEqualInt(i2le(p + 34), 0); /* Disk number start */ 119 assertEqualInt(i2le(p + 36), 0); /* Internal file attrs */ 120 assertEqualInt(i4le(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ 121 assertEqualInt(i4le(p + 42), 0); /* Offset of local header */ 122 assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */ 123 p = extension_start = central_header + 46 + strlen(file_name); 124 extension_end = extension_start + i2le(central_header + 30); 125 126 assertEqualInt(i2le(p), 0x7875); /* 'ux' extension header */ 127 assertEqualInt(i2le(p + 2), 11); /* 'ux' size */ 128 /* TODO: verify 'ux' contents */ 129 p += 4 + i2le(p + 2); 130 131 assertEqualInt(i2le(p), 0x5455); /* 'UT' extension header */ 132 assertEqualInt(i2le(p + 2), 5); /* 'UT' size */ 133 assertEqualInt(p[4], 1); /* 'UT' flags */ 134 assertEqualInt(i4le(p + 5), 0); /* 'UT' mtime */ 135 p += 4 + i2le(p + 2); 136 137 /* Note: We don't expect to see zip64 extension in the central 138 * directory, since the writer knows the actual full size by 139 * the time it is ready to write the central directory and has 140 * no reason to insert it then. Info-Zip seems to do the same 141 * thing. */ 142 143 /* Just in case: Report any extra extensions. */ 144 while (p < extension_end) { 145 failure("Unexpected extension 0x%04X", i2le(p)); 146 assert(0); 147 p += 4 + i2le(p + 2); 148 } 149 150 /* Should have run exactly to end of extra data. */ 151 assertEqualAddress(p, extension_end); 152 153 assertEqualAddress(p, eocd); 154 155 /* After Central dir, we find Zip64 eocd and Zip64 eocd locator. */ 156 assertEqualMem(p, "PK\006\006", 4); /* Zip64 eocd */ 157 assertEqualInt(i8le(p + 4), 44); /* We're using v1 Zip64 eocd */ 158 assertEqualInt(i2le(p + 12), 45); /* Written by Version 4.5 */ 159 assertEqualInt(i2le(p + 14), 45); /* Needs version 4.5 to extract */ 160 assertEqualInt(i4le(p + 16), 0); /* This is disk #0 */ 161 assertEqualInt(i4le(p + 20), 0); /* Dir starts on disk #0 */ 162 assertEqualInt(i8le(p + 24), 1); /* 1 entry on this disk */ 163 assertEqualInt(i8le(p + 32), 1); /* 1 entry total */ 164 assertEqualInt(i8le(p + 40), eocd - central_header); /* size of cd */ 165 assertEqualInt(i8le(p + 48), central_header - buff); /* start of cd */ 166 p += 12 + i8le(p + 4); 167 168 assertEqualMem(p, "PK\006\007", 4); /* Zip64 eocd locator */ 169 assertEqualInt(i4le(p + 4), 0); /* Zip64 eocd is on disk #0 */ 170 assertEqualInt(i8le(p + 8), eocd - buff); /* Offset of Zip64 eocd */ 171 assertEqualInt(i4le(p + 16), 1); /* 1 disk */ 172 p += 20; 173 174 /* Regular EOCD immediately follows Zip64 records. */ 175 assertEqualAddress(p, eocd_record); 176 177 /* Verify local header of file entry. */ 178 p = local_header = buff; 179 assertEqualMem(p, "PK\003\004", 4); /* Signature */ 180 assertEqualInt(i2le(p + 4), zip_version); /* Version needed to extract */ 181 assertEqualInt(i2le(p + 6), 8); /* Flags: bit 3 = length-at-end */ 182 assertEqualInt(i2le(p + 8), zip_compression); /* Compression method */ 183 assertEqualInt(i2le(p + 10), 0); /* File time */ 184 assertEqualInt(i2le(p + 12), 33); /* File date */ 185 assertEqualInt(i4le(p + 14), 0); /* CRC-32 */ 186 assertEqualInt(i4le(p + 18), 0); /* Compressed size must be zero for length-at-end */ 187 assertEqualInt(i4le(p + 22), 0); /* Uncompressed size must be zero for length-at-end */ 188 assertEqualInt(i2le(p + 26), strlen(file_name)); /* Pathname length */ 189 assertEqualInt(i2le(p + 28), 37); /* Extra field length */ 190 assertEqualMem(p + 30, file_name, strlen(file_name)); /* Pathname */ 191 p = extension_start = local_header + 30 + strlen(file_name); 192 extension_end = extension_start + i2le(local_header + 28); 193 194 assertEqualInt(i2le(p), 0x7875); /* 'ux' extension header */ 195 assertEqualInt(i2le(p + 2), 11); /* 'ux' size */ 196 assertEqualInt(p[4], 1); /* 'ux' version */ 197 assertEqualInt(p[5], 4); /* 'ux' uid size */ 198 assertEqualInt(i4le(p + 6), file_uid); /* 'Ux' UID */ 199 assertEqualInt(p[10], 4); /* 'ux' gid size */ 200 assertEqualInt(i4le(p + 11), file_gid); /* 'Ux' GID */ 201 p += 4 + i2le(p + 2); 202 203 assertEqualInt(i2le(p), 0x5455); /* 'UT' extension header */ 204 assertEqualInt(i2le(p + 2), 5); /* 'UT' size */ 205 assertEqualInt(p[4], 1); /* 'UT' flags */ 206 assertEqualInt(i4le(p + 5), 0); /* 'UT' mtime */ 207 p += 4 + i2le(p + 2); 208 209 assertEqualInt(i2le(p), 0x6c78); /* 'xl' experimental extension header */ 210 assertEqualInt(i2le(p + 2), 9); /* size */ 211 assertEqualInt(p[4], 7); /* bitmap of included fields */ 212 assertEqualInt(i2le(p + 5) >> 8, 3); /* system & version made by */ 213 assertEqualInt(i2le(p + 7), 0); /* internal file attributes */ 214 assertEqualInt(i4le(p + 9) >> 16 & 01777, file_perm); /* external file attributes */ 215 p += 4 + i2le(p + 2); 216 217 /* Just in case: Report any extra extensions. */ 218 while (p < extension_end) { 219 failure("Unexpected extension 0x%04X", i2le(p)); 220 assert(0); 221 p += 4 + i2le(p + 2); 222 } 223 224 /* Should have run exactly to end of extra data. */ 225 assertEqualAddress(p, extension_end); 226 data_start = p; 227 228 /* Data descriptor should follow compressed data. */ 229 while (p < central_header && memcmp(p, "PK\007\010", 4) != 0) 230 ++p; 231 data_end = p; 232 assertEqualInt(data_end - data_start, compressed_size); 233 assertEqualMem(p, "PK\007\010", 4); 234 assertEqualInt(i4le(p + 4), crc); /* CRC-32 */ 235 assertEqualInt(i8le(p + 8), compressed_size); /* compressed size */ 236 assertEqualInt(i8le(p + 16), sizeof(file_data)); /* uncompressed size */ 237 238 /* Central directory should immediately follow the only entry. */ 239 assertEqualAddress(p + 24, central_header); 240 241 free(buff); 242 } 243