static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const struct zip_file_header *p; const void *h; if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } zip->version = p->version[0]; zip->system = p->version[1]; zip->flags = tk_archive_le16dec(p->flags); zip->compression = tk_archive_le16dec(p->compression); if (zip->compression < sizeof(compression_names)/sizeof(compression_names[0])) zip->compression_name = compression_names[zip->compression]; else zip->compression_name = "??"; zip->mtime = zip_time(p->timedate); zip->ctime = 0; zip->atime = 0; zip->mode = 0; zip->uid = 0; zip->gid = 0; zip->crc32 = tk_archive_le32dec(p->crc32); zip->filename_length = tk_archive_le16dec(p->filename_length); zip->extra_length = tk_archive_le16dec(p->extra_length); zip->uncompressed_size = tk_archive_le32dec(p->uncompressed_size); zip->compressed_size = tk_archive_le32dec(p->compressed_size); __archive_read_consume(a, sizeof(struct zip_file_header)); /* Read the filename. */ if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (tk_archive_string_ensure(&zip->pathname, zip->filename_length) == NULL) __archive_errx(1, "Out of memory"); tk_archive_strncpy(&zip->pathname, h, zip->filename_length); __archive_read_consume(a, zip->filename_length); tk_archive_entry_set_pathname(entry, zip->pathname.s); if (zip->pathname.s[tk_archive_strlen(&zip->pathname) - 1] == '/') zip->mode = AE_IFDIR | 0777; else zip->mode = AE_IFREG | 0777; /* Read the extra data. */ if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) { tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, zip); __archive_read_consume(a, zip->extra_length); /* Populate some additional entry fields: */ tk_archive_entry_set_mode(entry, zip->mode); tk_archive_entry_set_uid(entry, zip->uid); tk_archive_entry_set_gid(entry, zip->gid); tk_archive_entry_set_mtime(entry, zip->mtime, 0); tk_archive_entry_set_ctime(entry, zip->ctime, 0); tk_archive_entry_set_atime(entry, zip->atime, 0); /* Set the size only if it's meaningful. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END)) tk_archive_entry_set_size(entry, zip->uncompressed_size); zip->entry_bytes_remaining = zip->compressed_size; zip->entry_offset = 0; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip->flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(zip->format_name, "ZIP %d.%d (%s)", zip->version / 10, zip->version % 10, zip->compression_name); a->archive.archive_format_name = zip->format_name; return (ARCHIVE_OK); }
static int slurp_central_directory(struct archive_read *a, struct zip *zip) { unsigned i; int64_t correction; 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 }; /* * Consider the archive file we are reading may be SFX. * So we have to calculate a SFX header size to revise * ZIP header offsets. */ correction = zip->end_of_central_directory_offset - (zip->central_directory_offset + zip->central_directory_size); /* The central directory offset is relative value, and so * we revise this offset for SFX. */ zip->central_directory_offset += correction; __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)); for (i = 0; i < zip->central_directory_entries; ++i) { struct zip_entry *zip_entry = &zip->zip_entries[i]; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; const char *name, *p, *r; if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; if (memcmp(p, "PK\001\002", 4) != 0) { archive_set_error(&a->archive, -1, "Invalid central directory signature"); return ARCHIVE_FATAL; } zip->have_central_directory = 1; /* version = p[4]; */ zip_entry->system = p[5]; /* version_required = archive_le16dec(p + 6); */ zip_entry->flags = archive_le16dec(p + 8); zip_entry->compression = (char)archive_le16dec(p + 10); zip_entry->mtime = zip_time(p + 12); zip_entry->crc32 = archive_le32dec(p + 16); zip_entry->compressed_size = archive_le32dec(p + 20); zip_entry->uncompressed_size = archive_le32dec(p + 24); filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; /* If we can't guess the mode, leave it zero here; when we read the local file header we might get more information. */ zip_entry->mode = 0; if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } /* * 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 = rsrc_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 table-of-contents operations (removing the need to find and read local file header to get the filename) at the cost of requiring a lot of extra space. */ /* We don't read the extra block here. We assume it will be duplicated at the local file header. */ __archive_read_consume(a, 46 + filename_length + extra_length + comment_length); }