From 1399a59680fa2dfca68764468ed0bcaa0331fde7 Mon Sep 17 00:00:00 2001 From: Michihiro NAKAJIMA Date: Thu, 8 Nov 2012 15:29:29 +0900 Subject: [PATCH] Add support for __MACOSX directory in Zip archives, which resource forks are stored in. --- Makefile.am | 2 + libarchive/archive_read_support_format_zip.c | 339 ++++++++++++++++-- libarchive/test/CMakeLists.txt | 1 + .../test/test_read_format_zip_mac_metadata.c | 98 +++++ .../test_read_format_zip_mac_metadata.zip.uu | 17 + 5 files changed, 436 insertions(+), 21 deletions(-) create mode 100644 libarchive/test/test_read_format_zip_mac_metadata.c create mode 100644 libarchive/test/test_read_format_zip_mac_metadata.zip.uu diff --git a/Makefile.am b/Makefile.am index 3fd14cfa3..2a6fe8a3b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -404,6 +404,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_zip.c \ libarchive/test/test_read_format_zip_comment_stored.c \ libarchive/test/test_read_format_zip_filename.c \ + libarchive/test/test_read_format_zip_mac_metadata.c \ libarchive/test/test_read_large.c \ libarchive/test/test_read_pax_truncated.c \ libarchive/test/test_read_position.c \ @@ -643,6 +644,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \ libarchive/test/test_read_format_zip_filename_utf8_ru.zip.uu \ libarchive/test/test_read_format_zip_length_at_end.zip.uu \ + libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ libarchive/test/test_read_format_zip_symlink.zip.uu \ libarchive/test/test_read_format_zip_ux.zip.uu \ libarchive/test/test_read_large_splitted_rar_aa.uu \ diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index b730a7001..ad3ecf1f0 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -57,6 +57,7 @@ struct zip_entry { int64_t gid; int64_t uid; struct archive_entry *entry; + struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; @@ -80,6 +81,7 @@ struct zip { struct zip_entry *zip_entries; struct zip_entry *entry; struct archive_rb_tree tree; + struct archive_rb_tree tree_rsrc; size_t unconsumed; @@ -134,7 +136,9 @@ static int archive_read_format_zip_seekable_read_header( struct archive_read *, struct archive_entry *); static int archive_read_format_zip_streamable_read_header( struct archive_read *, struct archive_entry *); +static ssize_t zip_get_local_file_header_size(struct archive_read *, size_t); #ifdef HAVE_ZLIB_H +static int zip_deflate_init(struct archive_read *, struct zip *); static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif @@ -341,6 +345,65 @@ cmp_key(const struct archive_rb_node *n, const void *key) return 1; } +static int +rsrc_cmp_node(const struct archive_rb_node *n1, + const struct archive_rb_node *n2) +{ + const struct zip_entry *e1 = (const struct zip_entry *)n1; + const struct zip_entry *e2 = (const struct zip_entry *)n2; + + return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); +} + +static int +rsrc_cmp_key(const struct archive_rb_node *n, const void *key) +{ + const struct zip_entry *e = (const struct zip_entry *)n; + return (strcmp((const char *)key, e->rsrcname.s)); +} + +static const char * +basename(const char *name, size_t name_length) +{ + const char *s, *r; + + r = s = name; + for (;;) { + s = memchr(s, '/', name_length - (s - name)); + if (s == NULL) + break; + r = ++s; + } + return (r); +} + +static void +expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) +{ + struct archive_string str; + struct zip_entry *dir; + char *s; + + archive_string_init(&str); + archive_strncpy(&str, name, name_length); + for (;;) { + s = strrchr(str.s, '/'); + if (s == NULL) + break; + *s = '\0'; + /* Transfer the parent directory from zip->tree_rsrc RB + * tree to zip->tree RB tree to expose. */ + dir = (struct zip_entry *) + __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); + if (dir == NULL) + break; + __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); + archive_string_free(&dir->rsrcname); + __archive_rb_tree_insert_node(&zip->tree, &dir->node); + } + archive_string_free(&str); +} + static int slurp_central_directory(struct archive_read *a, struct zip *zip) { @@ -348,10 +411,14 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) static const struct archive_rb_tree_ops rb_ops = { &cmp_node, &cmp_key }; + static const struct archive_rb_tree_ops rb_rsrc_ops = { + &rsrc_cmp_node, &rsrc_cmp_key + }; __archive_read_seek(a, zip->central_directory_offset, SEEK_SET); zip->offset = zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); + __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); zip->zip_entries = calloc(zip->central_directory_entries, sizeof(struct zip_entry)); @@ -359,7 +426,7 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) struct zip_entry *zip_entry = &zip->zip_entries[i]; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; - const char *p; + const char *name, *p, *r; if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; @@ -393,8 +460,50 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } - /* Register an entry to RB tree to sort it by file offset. */ - __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); + + /* + * Mac resource fork files are stored under the + * "__MACOSX/" directory, so we should check if + * it is. + */ + /* Make sure we have the file name. */ + if ((p = __archive_read_ahead(a, 46 + filename_length, NULL)) + == NULL) + return ARCHIVE_FATAL; + name = p + 46; + r = basename(name, filename_length); + if (filename_length >= 9 && + strncmp("__MACOSX/", name, 9) == 0) { + /* If this file is not a resource fork nor + * a directory. We should treat it as a non + * resource fork file to expose it. */ + if (name[filename_length-1] != '/' && + (r - name < 3 || r[0] != '.' || r[1] != '_')) { + __archive_rb_tree_insert_node(&zip->tree, + &zip_entry->node); + /* Expose its parent directories. */ + expose_parent_dirs(zip, name, filename_length); + } else { + /* This file is a resource fork file or + * a directory. */ + archive_strncpy(&(zip_entry->rsrcname), name, + filename_length); + __archive_rb_tree_insert_node(&zip->tree_rsrc, + &zip_entry->node); + } + } else { + /* Generate resource fork name to find its resource + * file at zip->tree_rsrc. */ + archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); + archive_strncat(&(zip_entry->rsrcname), name, r - name); + archive_strcat(&(zip_entry->rsrcname), "._"); + archive_strncat(&(zip_entry->rsrcname), + name + (r - name), filename_length - (r - name)); + /* Register an entry to RB tree to sort it by + * file offset. */ + __archive_rb_tree_insert_node(&zip->tree, + &zip_entry->node); + } /* We don't read the filename until we get to the local file header. Reading it here would speed up @@ -423,11 +532,143 @@ zip_read_consume(struct archive_read *a, int64_t bytes) return (skip); } +static int +zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, + struct zip_entry *rsrc) +{ + struct zip *zip = (struct zip *)a->format->data; + unsigned char *metadata, *mp; + int64_t offset = zip->offset; + size_t remaining_bytes, metadata_bytes; + ssize_t hsize; + int r, ret = ARCHIVE_OK, eof; + + switch(rsrc->compression) { + case 0: /* No compression. */ +#ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ +#endif + break; + default: /* Unsupported compression. */ + /* Return a warning. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported ZIP compression method (%s)", + compression_name(rsrc->compression)); + /* We can't decompress this entry, but we will + * be able to skip() it and try the next entry. */ + return (ARCHIVE_WARN); + } + + if (rsrc->uncompressed_size > (128 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Mac metadata is too large: %jd > 128K bytes", + (intmax_t)rsrc->uncompressed_size); + return (ARCHIVE_WARN); + } + + metadata = malloc(rsrc->uncompressed_size); + if (metadata == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Mac metadata"); + return (ARCHIVE_FATAL); + } + + if (zip->offset < rsrc->local_header_offset) + zip_read_consume(a, rsrc->local_header_offset - zip->offset); + else if (zip->offset != rsrc->local_header_offset) { + __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); + zip->offset = zip->entry->local_header_offset; + } + + hsize = zip_get_local_file_header_size(a, 0); + zip_read_consume(a, hsize); + + remaining_bytes = rsrc->compressed_size; + metadata_bytes = rsrc->uncompressed_size; + mp = metadata; + eof = 0; + while (!eof && remaining_bytes) { + const unsigned char *p; + ssize_t bytes_avail; + size_t bytes_used; + + p = __archive_read_ahead(a, 1, &bytes_avail); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + ret = ARCHIVE_WARN; + goto exit_mac_metadata; + } + if ((size_t)bytes_avail > remaining_bytes) + bytes_avail = remaining_bytes; + switch(rsrc->compression) { + case 0: /* No compression. */ + memcpy(mp, p, bytes_avail); + bytes_used = (size_t)bytes_avail; + metadata_bytes -= bytes_used; + mp += bytes_used; + if (metadata_bytes == 0) + eof = 1; + break; +#ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ + ret = zip_deflate_init(a, zip); + if (ret != ARCHIVE_OK) + goto exit_mac_metadata; + zip->stream.next_in = + (Bytef *)(uintptr_t)(const void *)p; + zip->stream.avail_in = (uInt)bytes_avail; + zip->stream.total_in = 0; + zip->stream.next_out = mp; + zip->stream.avail_out = (uInt)metadata_bytes; + zip->stream.total_out = 0; + + r = inflate(&zip->stream, 0); + switch (r) { + case Z_OK: + break; + case Z_STREAM_END: + eof = 1; + break; + case Z_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Out of memory for ZIP decompression"); + ret = ARCHIVE_FATAL; + goto exit_mac_metadata; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "ZIP decompression failed (%d)", r); + ret = ARCHIVE_FATAL; + goto exit_mac_metadata; + } + bytes_used = zip->stream.total_in; + metadata_bytes -= zip->stream.total_out; + mp += zip->stream.total_out; + break; +#endif + } + zip_read_consume(a, bytes_used); + remaining_bytes -= bytes_used; + } + archive_entry_copy_mac_metadata(entry, metadata, + rsrc->uncompressed_size - metadata_bytes); + + __archive_read_seek(a, offset, SEEK_SET); + zip->offset = offset; +exit_mac_metadata: + zip->decompress_init = 0; + free(metadata); + return (ret); +} + static int archive_read_format_zip_seekable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip = (struct zip *)a->format->data; + struct zip_entry *rsrc; int r, ret = ARCHIVE_OK; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; @@ -453,7 +694,19 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, return ARCHIVE_EOF; --zip->entries_remaining; - if (zip->offset != zip->entry->local_header_offset) { + if (zip->entry->rsrcname.s) + rsrc = (struct zip_entry *)__archive_rb_tree_find_node( + &zip->tree_rsrc, zip->entry->rsrcname.s); + else + rsrc = NULL; + + /* File entries are sorted by the header offset, we should mostly + * use zip_read_consume to advance a read point to avoid redundant + * data reading. */ + if (zip->offset < zip->entry->local_header_offset) + zip_read_consume(a, + zip->entry->local_header_offset - zip->offset); + else if (zip->offset != zip->entry->local_header_offset) { __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); zip->offset = zip->entry->local_header_offset; @@ -508,6 +761,11 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, } } } + if (rsrc) { + int ret2 = zip_read_mac_metadata(a, entry, rsrc); + if (ret2 < ret) + ret = ret2; + } return (ret); } @@ -640,6 +898,29 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, } } +static ssize_t +zip_get_local_file_header_size(struct archive_read *a, size_t extra) +{ + const char *p; + ssize_t filename_length, extra_length; + + if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_WARN); + } + p += extra; + + if (memcmp(p, "PK\003\004", 4) != 0) { + archive_set_error(&a->archive, -1, "Damaged Zip archive"); + return ARCHIVE_WARN; + } + filename_length = archive_le16dec(p + 26); + extra_length = archive_le16dec(p + 28); + + return (30 + filename_length + extra_length); +} + /* * Assumes file pointer is at beginning of local file header. */ @@ -1066,6 +1347,31 @@ zip_read_data_none(struct archive_read *a, const void **_buff, } #ifdef HAVE_ZLIB_H +static int +zip_deflate_init(struct archive_read *a, struct zip *zip) +{ + int r; + + /* If we haven't yet read any data, initialize the decompressor. */ + if (!zip->decompress_init) { + if (zip->stream_valid) + r = inflateReset(&zip->stream); + else + r = inflateInit2(&zip->stream, + -15 /* Don't check for zlib header */); + if (r != Z_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't initialize ZIP decompression."); + return (ARCHIVE_FATAL); + } + /* Stream structure has been set up. */ + zip->stream_valid = 1; + /* We've initialized decompression for this stream. */ + zip->decompress_init = 1; + } + return (ARCHIVE_OK); +} + static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) @@ -1091,23 +1397,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, } } - /* If we haven't yet read any data, initialize the decompressor. */ - if (!zip->decompress_init) { - if (zip->stream_valid) - r = inflateReset(&zip->stream); - else - r = inflateInit2(&zip->stream, - -15 /* Don't check for zlib header */); - if (r != Z_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize ZIP decompression."); - return (ARCHIVE_FATAL); - } - /* Stream structure has been set up. */ - zip->stream_valid = 1; - /* We've initialized decompression for this stream. */ - zip->decompress_init = 1; - } + r = zip_deflate_init(a, zip); + if (r != ARCHIVE_OK) + return (r); /* * Note: '1' here is a performance optimization. @@ -1268,6 +1560,11 @@ archive_read_format_zip_cleanup(struct archive_read *a) if (zip->stream_valid) inflateEnd(&zip->stream); #endif + if (zip->zip_entries && zip->central_directory_entries) { + unsigned i; + for (i = 0; i < zip->central_directory_entries; i++) + archive_string_free(&(zip->zip_entries[i].rsrcname)); + } free(zip->zip_entries); free(zip->uncompressed_buffer); archive_string_free(&(zip->extra)); diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index e39b8c13c..4a2832245 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -139,6 +139,7 @@ IF(ENABLE_TEST) test_read_format_zip.c test_read_format_zip_comment_stored.c test_read_format_zip_filename.c + test_read_format_zip_mac_metadata.c test_read_large.c test_read_pax_truncated.c test_read_position.c diff --git a/libarchive/test/test_read_format_zip_mac_metadata.c b/libarchive/test/test_read_format_zip_mac_metadata.c new file mode 100644 index 000000000..9783633a2 --- /dev/null +++ b/libarchive/test/test_read_format_zip_mac_metadata.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2012 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Read a zip file that has a zip comment in the end of the central + * directory record. + */ +DEFINE_TEST(test_read_format_zip_mac_metadata) +{ + const char *refname = "test_read_format_zip_mac_metadata.zip"; + char *p; + const void *metadata; + size_t s; + struct archive *a; + struct archive_entry *ae; + const unsigned char appledouble[] = { + 0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, + 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x32, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, + 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x87, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x87, + 0x00, 0x00, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x6c, + 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, + 0x21, 0x23, 0x61, 0x63, 0x6c, 0x20, 0x31, 0x0a, + 0x75, 0x73, 0x65, 0x72, 0x3a, 0x46, 0x46, 0x46, + 0x46, 0x45, 0x45, 0x45, 0x45, 0x2d, 0x44, 0x44, + 0x44, 0x44, 0x2d, 0x43, 0x43, 0x43, 0x43, 0x2d, + 0x42, 0x42, 0x42, 0x42, 0x2d, 0x41, 0x41, 0x41, + 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x43, + 0x39, 0x3a, 0x47, 0x75, 0x65, 0x73, 0x74, 0x3a, + 0x32, 0x30, 0x31, 0x3a, 0x64, 0x65, 0x6e, 0x79, + 0x3a, 0x72, 0x65, 0x61, 0x64, 0x0a, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x3a, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x41, 0x42, 0x2d, 0x43, 0x44, 0x45, + 0x46, 0x2d, 0x41, 0x42, 0x43, 0x44, 0x2d, 0x45, + 0x46, 0x41, 0x42, 0x2d, 0x43, 0x44, 0x45, 0x46, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x35, 0x30, + 0x3a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x3a, 0x38, + 0x30, 0x3a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x3a, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x0a, 0x00 + }; + + extract_reference_file(refname); + p = slurpfile(&s, refname); + + /* Mac metadata can only be extracted with the seeking reader. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("file3", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); + failure("Mac metadata should be set"); + if (assert((metadata = archive_entry_mac_metadata(ae, &s)) != NULL)) { + assertEqualMem(metadata, appledouble, sizeof(appledouble)); + } + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_mac_metadata.zip.uu b/libarchive/test/test_read_format_zip_mac_metadata.zip.uu new file mode 100644 index 000000000..d54c24d98 --- /dev/null +++ b/libarchive/test/test_read_format_zip_mac_metadata.zip.uu @@ -0,0 +1,17 @@ +begin 644 test_read_format_zip_mac_metadata.zip +M4$L#!!0`"``(`"UH8T$````````````````%`!``9FEL93-56`P`U?264!:7 +ME%#U`10`2TQ*3DE-2^<"`%!+!P@D*E,-"@````@```!02P,$"@``````5VAF +M00````````````````D`$`!?7TU!0T]36"]56`P`YHN84.:+F%#U`10`4$L# +M!!0`"``(`"UH8T$````````````````0`!``7U]-04-/4U@O+E]F:6QE,U58 +M#`#5])90%I>44/4!%`!C8!5C9V!B8/!-3%;P#U:(4(`"D!@#)Q`;`?%;(`;R +M&>49B`*.(2%!$!98QPP@;D=3PH@0%T[.S]5++"C(2=5+3,[1*TFM*`%**"H# +M.0J&7*7%J456;D#@"@2Z+D"@ZPP$NDY`H.L(!`9@X&QIY5Z:6EQB961@:)62 +MFE=I592:F,*57I1?6F#EZ.3LXNKFZ*0+HG1!/%TX%Z+=U,`J,24W,\_*`LC( +MR^LP```!\!``!02P$"%0,4``@`"``M:&-! +M)"I3#0H````(````!0`,``````````!`I($`````9FEL93-56`@`U?264!:7 +ME%!02P$"%0,*``````!7:&9!````````````````"0`,``````````!`_4%- +M````7U]-04-/4U@O55@(`.:+F%#FBYA04$L!`A4#%``(``@`+6AC0?@\%[ZS +M````'P$``!``#```````````0*2!A````%]?34%#3U-8+RY?9FEL93-56`@` +>U?264!:7E%!02P4&``````,``P#,````A0$````` +` +end -- 2.47.2