From owner-p4-projects@FreeBSD.ORG Thu Aug 14 12:53:08 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 2C24A1065678; Thu, 14 Aug 2008 12:53:08 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E3C721065673 for ; Thu, 14 Aug 2008 12:53:07 +0000 (UTC) (envelope-from strauss@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id CE4AA8FC08 for ; Thu, 14 Aug 2008 12:53:07 +0000 (UTC) (envelope-from strauss@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.2/8.14.2) with ESMTP id m7ECr7BV080554 for ; Thu, 14 Aug 2008 12:53:07 GMT (envelope-from strauss@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.2/8.14.1/Submit) id m7ECr7J5080552 for perforce@freebsd.org; Thu, 14 Aug 2008 12:53:07 GMT (envelope-from strauss@FreeBSD.org) Date: Thu, 14 Aug 2008 12:53:07 GMT Message-Id: <200808141253.m7ECr7J5080552@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to strauss@FreeBSD.org using -f From: Anselm Strauss To: Perforce Change Reviews Cc: Subject: PERFORCE change 147374 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Aug 2008 12:53:08 -0000 http://perforce.freebsd.org/chv.cgi?CH=147374 Change 147374 by strauss@strauss_marvelman on 2008/08/14 12:52:47 - Fix: Finishing an entry did write the CRC-32 in the wrong descriptor (only occured on archives with multiple entries). - Change: The order of entries in the central directory is no longer reversed (this is allowed according to the specification but can cause confusion). - A lot more tests, added test for folder entry. Affected files ... .. //depot/projects/soc2008/strauss_libarchive/TODO#15 edit .. //depot/projects/soc2008/strauss_libarchive/libarchive/archive_write_set_format_zip.c#36 edit .. //depot/projects/soc2008/strauss_libarchive/libarchive/test/test_write_format_zip_no_compression.c#12 edit Differences ... ==== //depot/projects/soc2008/strauss_libarchive/TODO#15 (text+ko) ==== @@ -1,6 +1,8 @@ To Be Done ========== +- Fix compiler warnings (also in checks) +- Consider switching to 0x000d extension - Consider portability of code to other operating systems - Test for memory leaks again (ask Tim) - Update ZIP writer in docs ==== //depot/projects/soc2008/strauss_libarchive/libarchive/archive_write_set_format_zip.c#36 (text+ko) ==== @@ -153,6 +153,7 @@ struct zip { struct zip_data_descriptor data_descriptor; struct zip_file_header_link *central_directory; + struct zip_file_header_link *central_directory_end; off_t offset; size_t written_bytes; }; @@ -184,6 +185,7 @@ return (ARCHIVE_FATAL); } zip->central_directory = NULL; + zip->central_directory_end = NULL; zip->offset = 0; zip->written_bytes = 0; a->format_data = zip; @@ -229,9 +231,7 @@ d = &zip->data_descriptor; size = archive_entry_size(entry); - /* Append archive entry to the central directory data. - * Storing in reverse order, for ease of coding. - * According to specification order should not matter, right? */ + /* Append archive entry to the central directory data. */ l = (struct zip_file_header_link *) malloc(sizeof(*l)); if (l == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); @@ -239,8 +239,13 @@ } l->entry = archive_entry_clone(entry); l->crc32 = crc32(0, NULL, 0); - l->next = zip->central_directory; - zip->central_directory = l; + l->next = NULL; + if (zip->central_directory == NULL) { + zip->central_directory = l; + } else { + zip->central_directory_end->next = l; + } + zip->central_directory_end = l; /* Store the offset of this header for later use in central directory. */ l->offset = zip->written_bytes; @@ -303,7 +308,7 @@ { int ret; struct zip *zip = a->format_data; - struct zip_file_header_link *l = zip->central_directory; + struct zip_file_header_link *l = zip->central_directory_end; ret = (a->compressor.write)(a, buff, s); if (ret >= 0) { @@ -323,7 +328,7 @@ int ret; struct zip *zip = a->format_data; struct zip_data_descriptor *d = &zip->data_descriptor; - struct zip_file_header_link *l = zip->central_directory; + struct zip_file_header_link *l = zip->central_directory_end; zip_encode(l->crc32, &d->crc32, sizeof(d->crc32)); @@ -369,7 +374,8 @@ entries = 0; offset_start = zip->written_bytes; - /* Formatting individual header fields per entry. */ + /* Formatting individual header fields per entry and + * writing each entry. */ while (l != NULL) { size = archive_entry_size(l->entry); ==== //depot/projects/soc2008/strauss_libarchive/libarchive/test/test_write_format_zip_no_compression.c#12 (text+ko) ==== @@ -33,28 +33,38 @@ DEFINE_TEST(test_write_format_zip_no_compression) { + /* Buffer data */ struct archive *a; struct archive_entry *entry; char buff[100000]; - const char *p, *q, *buffend; + const char *buffend; + /* p is the pointer to walk over the central directory, + * q walks over the local headers, the data and the data descriptors. */ + const char *p, *q; size_t used; - int crc; /* File data */ char file_name[] = "file"; char file_data1[] = {'1', '2', '3', '4', '5'}; char file_data2[] = {'6', '7', '8', '9', '0'}; int file_perm = 00644; + short file_uid = 10; + short file_gid = 20; /* Folder data */ - char folder_name[] = "folder"; + char folder_name[] = "folder/"; int folder_perm = 00755; + short folder_uid = 30; + short folder_gid = 40; - /* Time fields */ + /* Time data */ time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); + + /* Misc variables */ + int crc; /* Create new ZIP archive in memory without padding. */ assert((a = archive_write_new()) != NULL); @@ -70,37 +80,35 @@ assert((entry = archive_entry_new()) != NULL); archive_entry_set_pathname(entry, file_name); archive_entry_set_mode(entry, S_IFREG | 0644); - archive_entry_set_size(entry, 10); - archive_entry_set_uid(entry, 80); - archive_entry_set_gid(entry, 90); - archive_entry_set_dev(entry, 12); - archive_entry_set_ino(entry, 89); - archive_entry_set_nlink(entry, 1); + archive_entry_set_size(entry, sizeof(file_data1) + sizeof(file_data2)); + 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); 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); /* Folder */ - /*assert((entry = archive_entry_new()) != NULL); + assert((entry = archive_entry_new()) != NULL); archive_entry_set_pathname(entry, folder_name); archive_entry_set_mode(entry, S_IFDIR | folder_perm); archive_entry_set_size(entry, 0); - archive_entry_set_uid(entry, 80); - archive_entry_set_gid(entry, 90); - archive_entry_set_dev(entry, 12); - archive_entry_set_ino(entry, 89); - archive_entry_set_nlink(entry, 1); + 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); assertEqualIntA(a, 0, archive_write_header(a, entry)); - archive_entry_free(entry);*/ + archive_entry_free(entry); - /* Close out the archive . */ + /* Close the archive . */ assertA(0 == archive_write_close(a)); assertA(0 == archive_write_finish(a)); - /* Verify the format of the Zip file. */ + /* Remember the end of the archive in memory. */ buffend = buff + used; /* Verify "End of Central Directory" record. */ @@ -125,7 +133,9 @@ failure("Central file record at offset %d should begin with" " PK\\001\\002 signature", i4(buffend - 10)); - assertEqualMem(p, "PK\001\002", 4); + + /* 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 + 8), 8); /* Flags */ @@ -137,20 +147,128 @@ assertEqualInt(i4(p + 16), crc); /* CRC-32 */ 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)); /* Filename length */ + assertEqualInt(i2(p + 28), strlen(file_name)); /* Pathname length */ assertEqualInt(i2(p + 30), 13); /* 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 */ assertEqualInt(i4(p + 38) >> 16 & 01777, file_perm); /* External file attrs */ + assertEqualInt(i4(p + 42), 0); /* Offset of local header */ + 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(i4(p + 5), t); /* 'UT' mtime */ + p = p + 9; + assertEqualInt(i2(p), 0x7855); /* 'Ux' extension header */ + assertEqualInt(i2(p + 2), 0); /* 'Ux' size */ + p = p + 4; + + /* 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 + 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 */ + assertEqualInt(i2(q + 12), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ + assertEqualInt(i4(q + 14), 0); /* CRC-32 */ + assertEqualInt(i4(q + 18), 0); /* Compressed size */ + assertEqualInt(i4(q + 22), 0); /* Uncompressed size */ + assertEqualInt(i2(q + 26), strlen(file_name)); /* Pathname length */ + assertEqualInt(i2(q + 28), 25); /* 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(i4(q + 5), t); /* 'UT' mtime */ + assertEqualInt(i4(q + 9), t); /* 'UT' atime */ + assertEqualInt(i4(q + 13), t); /* 'UT' ctime */ + q = q + 17; + assertEqualInt(i2(q), 0x7855); /* 'Ux' extension header */ + assertEqualInt(i2(q + 2), 4); /* 'Ux' size */ + assertEqualInt(i2(q + 4), file_uid); /* 'Ux' UID */ + assertEqualInt(i2(q + 6), file_gid); /* 'Ux' GID */ + q = q + 8; + + /* Verify data of file entry. */ + assertEqualMem(q, file_data1, sizeof(file_data1)); + assertEqualMem(q + sizeof(file_data1), file_data2, sizeof(file_data2)); + q = q + sizeof(file_data1) + sizeof(file_data2); + + /* Verify data descriptor of file entry. */ + assertEqualMem(q, "PK\007\010", 4); /* Signature */ + assertEqualInt(i4(q + 4), crc); /* CRC-32 */ + assertEqualInt(i4(q + 8), sizeof(file_data1) + sizeof(file_data2)); /* Compressed size */ + assertEqualInt(i4(q + 12), sizeof(file_data1) + sizeof(file_data2)); /* Uncompressed size */ + q = q + 16; + /* Verify folder 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 + 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 */ + assertEqualInt(i2(p + 14), ((tm->tm_year - 80) * 512) + ((tm->tm_mon + 1) * 32) + tm->tm_mday); /* File date */ + crc = 0; + assertEqualInt(i4(p + 16), crc); /* CRC-32 */ + 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 + 32), 0); /* File comment length */ + assertEqualInt(i2(p + 34), 0); /* Disk number start */ + assertEqualInt(i2(p + 36), 0); /* Internal file attrs */ + assertEqualInt(i4(p + 38) >> 16 & 01777, folder_perm); /* External file attrs */ + assertEqualInt(i4(p + 42), q - buff); /* Offset of local header */ + 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(i4(p + 5), t); /* 'UT' mtime */ + p = p + 9; + assertEqualInt(i2(p), 0x7855); /* 'Ux' extension header */ + assertEqualInt(i2(p + 2), 0); /* 'Ux' size */ + p = p + 4; - /* Get address of local header for this file. */ - q = buff + i4(p + 42); - failure("Local file header at offset %d should begin with" - " PK\\003\\004 signature", - i4(p + 42)); - assertEqualMem(q, "PK\003\004", 4); - /* TODO: Verify local header */ + /* 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 + 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 */ + assertEqualInt(i4(q + 14), 0); /* CRC-32 */ + 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), 25); /* 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(i4(q + 5), t); /* 'UT' mtime */ + assertEqualInt(i4(q + 9), t); /* 'UT' atime */ + assertEqualInt(i4(q + 13), t); /* 'UT' ctime */ + q = q + 17; + assertEqualInt(i2(q), 0x7855); /* 'Ux' extension header */ + assertEqualInt(i2(q + 2), 4); /* 'Ux' size */ + assertEqualInt(i2(q + 4), folder_uid); /* 'Ux' UID */ + assertEqualInt(i2(q + 6), folder_gid); /* 'Ux' GID */ + q = q + 8; + + /* 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; +}