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);
}
Exemplo n.º 2
0
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);
	}