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 add_entry(struct archive_match *a, int flag, struct archive_entry *entry) { struct match_file *f; const void *pathname; int r; f = calloc(1, sizeof(*f)); if (f == NULL) return (error_nomem(a)); #if defined(_WIN32) && !defined(__CYGWIN__) pathname = archive_entry_pathname_w(entry); if (pathname == NULL) { free(f); archive_set_error(&(a->archive), EINVAL, "pathname is NULL"); return (ARCHIVE_FAILED); } archive_mstring_copy_wcs(&(f->pathname), pathname); a->exclusion_tree.rbt_ops = &rb_ops_wcs; #else (void)rb_ops_wcs; pathname = archive_entry_pathname(entry); if (pathname == NULL) { free(f); archive_set_error(&(a->archive), EINVAL, "pathname is NULL"); return (ARCHIVE_FAILED); } archive_mstring_copy_mbs(&(f->pathname), pathname); a->exclusion_tree.rbt_ops = &rb_ops_mbs; #endif f->flag = flag; f->mtime_sec = archive_entry_mtime(entry); f->mtime_nsec = archive_entry_mtime_nsec(entry); f->ctime_sec = archive_entry_ctime(entry); f->ctime_nsec = archive_entry_ctime_nsec(entry); r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node)); if (!r) { struct match_file *f2; /* Get the duplicated file. */ f2 = (struct match_file *)__archive_rb_tree_find_node( &(a->exclusion_tree), pathname); /* * We always overwrite comparison condition. * If you do not want to overwrite it, you should not * call archive_match_exclude_entry(). We cannot know * what behavior you really expect since overwriting * condition might be different with the flag. */ if (f2 != NULL) { f2->flag = f->flag; f2->mtime_sec = f->mtime_sec; f2->mtime_nsec = f->mtime_nsec; f2->ctime_sec = f->ctime_sec; f2->ctime_nsec = f->ctime_nsec; } /* Release the duplicated file. */ archive_mstring_clean(&(f->pathname)); free(f); return (ARCHIVE_OK); } entry_list_add(&(a->exclusion_entry_list), f); a->setflag |= TIME_IS_SET; 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); }