1228753Smm/*- 2228753Smm * Copyright (c) 2008 Anselm Strauss 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm/* 27228753Smm * Development supported by Google Summer of Code 2008. 28228753Smm */ 29228753Smm 30228753Smm#include "test.h" 31229592Smm__FBSDID("$FreeBSD$"); 32228753Smm 33228753Smmstatic unsigned long 34228753Smmbitcrc32(unsigned long c, void *_p, size_t s) 35228753Smm{ 36228753Smm /* This is a drop-in replacement for crc32() from zlib. 37228753Smm * Libarchive should be able to correctly generate 38228753Smm * uncompressed zip archives (including correct CRCs) even 39228753Smm * when zlib is unavailable, and this function helps us verify 40228753Smm * that. Yes, this is very, very slow and unsuitable for 41228753Smm * production use, but it's correct, compact, and works well 42228753Smm * enough for this particular usage. Libarchive internally 43228753Smm * uses a much more efficient implementation. */ 44228753Smm const unsigned char *p = _p; 45228753Smm int bitctr; 46228753Smm 47228753Smm if (p == NULL) 48228753Smm return (0); 49228753Smm 50228753Smm for (; s > 0; --s) { 51228753Smm c ^= *p++; 52228753Smm for (bitctr = 8; bitctr > 0; --bitctr) { 53228753Smm if (c & 1) c = (c >> 1); 54228753Smm else c = (c >> 1) ^ 0xedb88320; 55228753Smm c ^= 0x80000000; 56228753Smm } 57228753Smm } 58228753Smm return (c); 59228753Smm} 60228753Smm 61228753Smm/* Quick and dirty: Read 2-byte and 4-byte integers from Zip file. */ 62228753Smmstatic int i2(const char *p) { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); } 63228753Smmstatic int i4(const char *p) { return (i2(p) | (i2(p + 2) << 16)); } 64228753Smm 65228753SmmDEFINE_TEST(test_write_format_zip_no_compression) 66228753Smm{ 67228753Smm /* Buffer data */ 68228753Smm struct archive *a; 69228753Smm struct archive_entry *entry; 70228753Smm char buff[100000]; 71228753Smm const char *buffend; 72228753Smm /* p is the pointer to walk over the central directory, 73228753Smm * q walks over the local headers, the data and the data descriptors. */ 74228753Smm const char *p, *q; 75228753Smm size_t used; 76228753Smm 77228753Smm /* File data */ 78228753Smm char file_name[] = "file"; 79228753Smm char file_data1[] = {'1', '2', '3', '4', '5'}; 80228753Smm char file_data2[] = {'6', '7', '8', '9', '0'}; 81228753Smm int file_perm = 00644; 82228753Smm short file_uid = 10; 83228753Smm short file_gid = 20; 84228753Smm 85228753Smm /* Folder data */ 86228753Smm char folder_name[] = "folder/"; 87228753Smm int folder_perm = 00755; 88228753Smm short folder_uid = 30; 89228753Smm short folder_gid = 40; 90228753Smm 91228753Smm /* Time data */ 92228753Smm time_t t = time(NULL); 93228753Smm struct tm *tm = localtime(&t); 94228753Smm 95228753Smm /* Misc variables */ 96228753Smm unsigned long crc; 97228753Smm 98228753Smm /* Create new ZIP archive in memory without padding. */ 99228753Smm assert((a = archive_write_new()) != NULL); 100228753Smm assertA(0 == archive_write_set_format_zip(a)); 101228753Smm assertA(0 == archive_write_set_format_options(a, "zip:compression=store")); 102228753Smm assertA(0 == archive_write_set_compression_none(a)); 103228753Smm assertA(0 == archive_write_set_bytes_per_block(a, 1)); 104228753Smm assertA(0 == archive_write_set_bytes_in_last_block(a, 1)); 105228753Smm assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); 106228753Smm 107228753Smm /* Write entries. */ 108228753Smm 109228753Smm /* Regular file */ 110228753Smm assert((entry = archive_entry_new()) != NULL); 111228753Smm archive_entry_set_pathname(entry, file_name); 112228753Smm archive_entry_set_mode(entry, S_IFREG | 0644); 113228753Smm archive_entry_set_size(entry, sizeof(file_data1) + sizeof(file_data2)); 114228753Smm archive_entry_set_uid(entry, file_uid); 115228753Smm archive_entry_set_gid(entry, file_gid); 116228753Smm archive_entry_set_mtime(entry, t, 0); 117228753Smm archive_entry_set_atime(entry, t, 0); 118228753Smm archive_entry_set_ctime(entry, t, 0); 119228753Smm assertEqualIntA(a, 0, archive_write_header(a, entry)); 120228753Smm assertEqualIntA(a, sizeof(file_data1), archive_write_data(a, file_data1, sizeof(file_data1))); 121228753Smm assertEqualIntA(a, sizeof(file_data2), archive_write_data(a, file_data2, sizeof(file_data2))); 122228753Smm archive_entry_free(entry); 123228753Smm 124228753Smm /* Folder */ 125228753Smm assert((entry = archive_entry_new()) != NULL); 126228753Smm archive_entry_set_pathname(entry, folder_name); 127228753Smm archive_entry_set_mode(entry, S_IFDIR | folder_perm); 128228753Smm archive_entry_set_size(entry, 0); 129228753Smm archive_entry_set_uid(entry, folder_uid); 130228753Smm archive_entry_set_gid(entry, folder_gid); 131228753Smm archive_entry_set_mtime(entry, t, 0); 132228753Smm archive_entry_set_atime(entry, t, 0); 133228753Smm archive_entry_set_ctime(entry, t, 0); 134228753Smm assertEqualIntA(a, 0, archive_write_header(a, entry)); 135228753Smm archive_entry_free(entry); 136228753Smm 137228753Smm /* Close the archive . */ 138228753Smm assertA(0 == archive_write_close(a)); 139228753Smm assertA(0 == archive_write_finish(a)); 140228753Smm 141228753Smm /* Remember the end of the archive in memory. */ 142228753Smm buffend = buff + used; 143228753Smm 144228753Smm /* Verify "End of Central Directory" record. */ 145228753Smm /* Get address of end-of-central-directory record. */ 146228753Smm p = buffend - 22; /* Assumes there is no zip comment field. */ 147228753Smm failure("End-of-central-directory begins with PK\\005\\006 signature"); 148228753Smm assertEqualMem(p, "PK\005\006", 4); 149228753Smm failure("This must be disk 0"); 150228753Smm assertEqualInt(i2(p + 4), 0); 151228753Smm failure("Central dir must start on disk 0"); 152228753Smm assertEqualInt(i2(p + 6), 0); 153228753Smm failure("All central dir entries are on this disk"); 154228753Smm assertEqualInt(i2(p + 8), i2(p + 10)); 155228753Smm failure("CD start (%d) + CD length (%d) should == archive size - 22", 156228753Smm i4(p + 12), i4(p + 16)); 157228753Smm assertEqualInt(i4(p + 12) + i4(p + 16), used - 22); 158228753Smm failure("no zip comment"); 159228753Smm assertEqualInt(i2(p + 20), 0); 160228753Smm 161228753Smm /* Get address of first entry in central directory. */ 162228753Smm p = buff + i4(buffend - 6); 163228753Smm failure("Central file record at offset %d should begin with" 164228753Smm " PK\\001\\002 signature", 165228753Smm i4(buffend - 10)); 166228753Smm 167228753Smm /* Verify file entry in central directory. */ 168228753Smm assertEqualMem(p, "PK\001\002", 4); /* Signature */ 169228753Smm assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */ 170228753Smm assertEqualInt(i2(p + 6), 20); /* Version needed to extract */ 171228753Smm assertEqualInt(i2(p + 8), 8); /* Flags */ 172228753Smm assertEqualInt(i2(p + 10), 0); /* Compression method */ 173228753Smm assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ 174228753Smm assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ 175228753Smm crc = bitcrc32(0, file_data1, sizeof(file_data1)); 176228753Smm crc = bitcrc32(crc, file_data2, sizeof(file_data2)); 177228753Smm assertEqualInt(i4(p + 16), crc); /* CRC-32 */ 178228753Smm assertEqualInt(i4(p + 20), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ 179228753Smm assertEqualInt(i4(p + 24), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ 180228753Smm assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */ 181228753Smm assertEqualInt(i2(p + 30), 13); /* Extra field length */ 182228753Smm assertEqualInt(i2(p + 32), 0); /* File comment length */ 183228753Smm assertEqualInt(i2(p + 34), 0); /* Disk number start */ 184228753Smm assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ 185228753Smm assertEqualInt(i4(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ 186228753Smm assertEqualInt(i4(p + 42), 0); /* Offset of local header */ 187228753Smm assertEqualMem(p + 46, file_name, strlen(file_name)); /* Pathname */ 188228753Smm p = p + 46 + strlen(file_name); 189228753Smm assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ 190228753Smm assertEqualInt(i2(p + 2), 5); /* 'UT' size */ 191228753Smm assertEqualInt(p[4], 7); /* 'UT' flags */ 192228753Smm assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ 193228753Smm p = p + 9; 194228753Smm assertEqualInt(i2(p), 0x7855); /* 'Ux' extension header */ 195228753Smm assertEqualInt(i2(p + 2), 0); /* 'Ux' size */ 196228753Smm p = p + 4; 197228753Smm 198228753Smm /* Verify local header of file entry. */ 199228753Smm q = buff; 200228753Smm assertEqualMem(q, "PK\003\004", 4); /* Signature */ 201228753Smm assertEqualInt(i2(q + 4), 20); /* Version needed to extract */ 202228753Smm assertEqualInt(i2(q + 6), 8); /* Flags */ 203228753Smm assertEqualInt(i2(q + 8), 0); /* Compression method */ 204228753Smm assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ 205228753Smm assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ 206228753Smm assertEqualInt(i4(q + 14), 0); /* CRC-32 */ 207228753Smm assertEqualInt(i4(q + 18), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ 208228753Smm assertEqualInt(i4(q + 22), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ 209228753Smm assertEqualInt(i2(q + 26), strlen(file_name)); /* Pathname length */ 210228753Smm assertEqualInt(i2(q + 28), 25); /* Extra field length */ 211228753Smm assertEqualMem(q + 30, file_name, strlen(file_name)); /* Pathname */ 212228753Smm q = q + 30 + strlen(file_name); 213228753Smm assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */ 214228753Smm assertEqualInt(i2(q + 2), 13); /* 'UT' size */ 215228753Smm assertEqualInt(q[4], 7); /* 'UT' flags */ 216228753Smm assertEqualInt(i4(q + 5), t); /* 'UT' mtime */ 217228753Smm assertEqualInt(i4(q + 9), t); /* 'UT' atime */ 218228753Smm assertEqualInt(i4(q + 13), t); /* 'UT' ctime */ 219228753Smm q = q + 17; 220228753Smm assertEqualInt(i2(q), 0x7855); /* 'Ux' extension header */ 221228753Smm assertEqualInt(i2(q + 2), 4); /* 'Ux' size */ 222228753Smm assertEqualInt(i2(q + 4), file_uid); /* 'Ux' UID */ 223228753Smm assertEqualInt(i2(q + 6), file_gid); /* 'Ux' GID */ 224228753Smm q = q + 8; 225228753Smm 226228753Smm /* Verify data of file entry. */ 227228753Smm assertEqualMem(q, file_data1, sizeof(file_data1)); 228228753Smm assertEqualMem(q + sizeof(file_data1), file_data2, sizeof(file_data2)); 229228753Smm q = q + sizeof(file_data1) + sizeof(file_data2); 230228753Smm 231228753Smm /* Verify data descriptor of file entry. */ 232228753Smm assertEqualMem(q, "PK\007\010", 4); /* Signature */ 233228753Smm assertEqualInt(i4(q + 4), crc); /* CRC-32 */ 234228753Smm assertEqualInt(i4(q + 8), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ 235228753Smm assertEqualInt(i4(q + 12), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ 236228753Smm q = q + 16; 237228753Smm 238228753Smm /* Verify folder entry in central directory. */ 239228753Smm assertEqualMem(p, "PK\001\002", 4); /* Signature */ 240228753Smm assertEqualInt(i2(p + 4), 3 * 256 + 20); /* Version made by */ 241228753Smm assertEqualInt(i2(p + 6), 20); /* Version needed to extract */ 242228753Smm assertEqualInt(i2(p + 8), 8); /* Flags */ 243228753Smm assertEqualInt(i2(p + 10), 0); /* Compression method */ 244228753Smm assertEqualInt(i2(p + 12), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ 245228753Smm assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ 246228753Smm crc = 0; 247228753Smm assertEqualInt(i4(p + 16), crc); /* CRC-32 */ 248228753Smm assertEqualInt(i4(p + 20), 0); /* Compressed size */ 249228753Smm assertEqualInt(i4(p + 24), 0); /* Uncompressed size */ 250228753Smm assertEqualInt(i2(p + 28), strlen(folder_name)); /* Pathname length */ 251228753Smm assertEqualInt(i2(p + 30), 13); /* Extra field length */ 252228753Smm assertEqualInt(i2(p + 32), 0); /* File comment length */ 253228753Smm assertEqualInt(i2(p + 34), 0); /* Disk number start */ 254228753Smm assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ 255228753Smm assertEqualInt(i4(p + 38) >> 16 & 01777, folder_perm); /* External file attrs */ 256228753Smm assertEqualInt(i4(p + 42), q - buff); /* Offset of local header */ 257228753Smm assertEqualMem(p + 46, folder_name, strlen(folder_name)); /* Pathname */ 258228753Smm p = p + 46 + strlen(folder_name); 259228753Smm assertEqualInt(i2(p), 0x5455); /* 'UT' extension header */ 260228753Smm assertEqualInt(i2(p + 2), 5); /* 'UT' size */ 261228753Smm assertEqualInt(p[4], 7); /* 'UT' flags */ 262228753Smm assertEqualInt(i4(p + 5), t); /* 'UT' mtime */ 263228753Smm p = p + 9; 264228753Smm assertEqualInt(i2(p), 0x7855); /* 'Ux' extension header */ 265228753Smm assertEqualInt(i2(p + 2), 0); /* 'Ux' size */ 266228753Smm p = p + 4; 267228753Smm 268228753Smm /* Verify local header of folder entry. */ 269228753Smm assertEqualMem(q, "PK\003\004", 4); /* Signature */ 270228753Smm assertEqualInt(i2(q + 4), 20); /* Version needed to extract */ 271228753Smm assertEqualInt(i2(q + 6), 8); /* Flags */ 272228753Smm assertEqualInt(i2(q + 8), 0); /* Compression method */ 273228753Smm assertEqualInt(i2(q + 10), (tm->tm_hour * 2048) + (tm->tm_min * 32) + (tm->tm_sec / 2)); /* File time */ 274228753Smm assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ 275228753Smm assertEqualInt(i4(q + 14), 0); /* CRC-32 */ 276228753Smm assertEqualInt(i4(q + 18), 0); /* Compressed size */ 277228753Smm assertEqualInt(i4(q + 22), 0); /* Uncompressed size */ 278228753Smm assertEqualInt(i2(q + 26), strlen(folder_name)); /* Pathname length */ 279228753Smm assertEqualInt(i2(q + 28), 25); /* Extra field length */ 280228753Smm assertEqualMem(q + 30, folder_name, strlen(folder_name)); /* Pathname */ 281228753Smm q = q + 30 + strlen(folder_name); 282228753Smm assertEqualInt(i2(q), 0x5455); /* 'UT' extension header */ 283228753Smm assertEqualInt(i2(q + 2), 13); /* 'UT' size */ 284228753Smm assertEqualInt(q[4], 7); /* 'UT' flags */ 285228753Smm assertEqualInt(i4(q + 5), t); /* 'UT' mtime */ 286228753Smm assertEqualInt(i4(q + 9), t); /* 'UT' atime */ 287228753Smm assertEqualInt(i4(q + 13), t); /* 'UT' ctime */ 288228753Smm q = q + 17; 289228753Smm assertEqualInt(i2(q), 0x7855); /* 'Ux' extension header */ 290228753Smm assertEqualInt(i2(q + 2), 4); /* 'Ux' size */ 291228753Smm assertEqualInt(i2(q + 4), folder_uid); /* 'Ux' UID */ 292228753Smm assertEqualInt(i2(q + 6), folder_gid); /* 'Ux' GID */ 293228753Smm q = q + 8; 294228753Smm 295228753Smm /* There should not be any data in the folder entry, 296228753Smm * meaning next is the data descriptor header. */ 297228753Smm 298228753Smm /* Verify data descriptor of folder entry. */ 299228753Smm assertEqualMem(q, "PK\007\010", 4); /* Signature */ 300228753Smm assertEqualInt(i4(q + 4), crc); /* CRC-32 */ 301228753Smm assertEqualInt(i4(q + 8), 0); /* Compressed size */ 302228753Smm assertEqualInt(i4(q + 12), 0); /* Uncompressed size */ 303228753Smm q = q + 16; 304228753Smm} 305