blob: b00557cfca27244e2563e8cea26f26a88ff5b37d [file] [log] [blame]
Narayan Kamath7462f022013-11-21 13:05:04 +00001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Read-only access to Zip archives, with minimal heap allocation.
19 */
Narayan Kamath7462f022013-11-21 13:05:04 +000020
21#include <assert.h>
22#include <errno.h>
Mark Salyzyn99ef9912014-03-14 14:26:22 -070023#include <fcntl.h>
24#include <inttypes.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000025#include <limits.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000026#include <stdlib.h>
27#include <string.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000028#include <unistd.h>
29
Dan Albert1ae07642015-04-09 14:11:18 -070030#include <memory>
31#include <vector>
32
Mark Salyzynff2dcd92016-09-28 15:54:45 -070033#include <android/log.h>
34#include <android-base/file.h>
35#include <android-base/logging.h>
36#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
37#include <android-base/memory.h>
38#include <utils/Compat.h>
39#include <utils/FileMap.h>
Christopher Ferrise6884ce2015-11-10 14:55:12 -080040#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070041#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000042
Narayan Kamath044bc8e2014-12-03 18:22:53 +000043#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070044#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080045#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070046
Dan Albert1ae07642015-04-09 14:11:18 -070047using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000048
Narayan Kamath926973e2014-06-09 14:18:14 +010049// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000050// things will happen.
51#ifndef O_BINARY
52#define O_BINARY 0
53#endif
54
Narayan Kamath926973e2014-06-09 14:18:14 +010055// The maximum number of bytes to scan backwards for the EOCD start.
56static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
57
Narayan Kamath7462f022013-11-21 13:05:04 +000058static const char* kErrorMessages[] = {
59 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000060 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000061 "Zlib error",
62 "Invalid file",
63 "Invalid handle",
64 "Duplicate entries in archive",
65 "Empty archive",
66 "Entry not found",
67 "Invalid offset",
68 "Inconsistent information",
69 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000070 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000071 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000072};
73
74static const int32_t kErrorMessageUpperBound = 0;
75
Narayan Kamatheb41ad22013-12-09 16:26:36 +000076static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000077
78// We encountered a Zlib error when inflating a stream from this file.
79// Usually indicates file corruption.
80static const int32_t kZlibError = -2;
81
82// The input file cannot be processed as a zip archive. Usually because
83// it's too small, too large or does not have a valid signature.
84static const int32_t kInvalidFile = -3;
85
86// An invalid iteration / ziparchive handle was passed in as an input
87// argument.
88static const int32_t kInvalidHandle = -4;
89
90// The zip archive contained two (or possibly more) entries with the same
91// name.
92static const int32_t kDuplicateEntry = -5;
93
94// The zip archive contains no entries.
95static const int32_t kEmptyArchive = -6;
96
97// The specified entry was not found in the archive.
98static const int32_t kEntryNotFound = -7;
99
100// The zip archive contained an invalid local file header pointer.
101static const int32_t kInvalidOffset = -8;
102
103// The zip archive contained inconsistent entry information. This could
104// be because the central directory & local file header did not agree, or
105// if the actual uncompressed length or crc32 do not match their declared
106// values.
107static const int32_t kInconsistentInformation = -9;
108
109// An invalid entry name was encountered.
110static const int32_t kInvalidEntryName = -10;
111
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000112// An I/O related system call (read, lseek, ftruncate, map) failed.
113static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000114
Narayan Kamatheaf98852013-12-11 14:51:51 +0000115// We were not able to mmap the central directory or entry contents.
116static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000117
Narayan Kamatheaf98852013-12-11 14:51:51 +0000118static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000119
Narayan Kamath7462f022013-11-21 13:05:04 +0000120/*
121 * A Read-only Zip archive.
122 *
123 * We want "open" and "find entry by name" to be fast operations, and
124 * we want to use as little memory as possible. We memory-map the zip
125 * central directory, and load a hash table with pointers to the filenames
126 * (which aren't null-terminated). The other fields are at a fixed offset
127 * from the filename, so we don't need to extract those (but we do need
128 * to byte-read and endian-swap them every time we want them).
129 *
130 * It's possible that somebody has handed us a massive (~1GB) zip archive,
131 * so we can't expect to mmap the entire file.
132 *
133 * To speed comparisons when doing a lookup by name, we could make the mapping
134 * "private" (copy-on-write) and null-terminate the filenames after verifying
135 * the record structure. However, this requires a private mapping of
136 * every page that the Central Directory touches. Easier to tuck a copy
137 * of the string length into the hash table entry.
138 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000139
Narayan Kamath7462f022013-11-21 13:05:04 +0000140/*
141 * Round up to the next highest power of 2.
142 *
143 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
144 */
145static uint32_t RoundUpPower2(uint32_t val) {
146 val--;
147 val |= val >> 1;
148 val |= val >> 2;
149 val |= val >> 4;
150 val |= val >> 8;
151 val |= val >> 16;
152 val++;
153
154 return val;
155}
156
Yusuke Sato07447542015-06-25 14:39:19 -0700157static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000158 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100159 uint16_t len = name.name_length;
160 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000161
162 while (len--) {
163 hash = hash * 31 + *str++;
164 }
165
166 return hash;
167}
168
169/*
170 * Convert a ZipEntry to a hash table index, verifying that it's in a
171 * valid range.
172 */
Yusuke Sato07447542015-06-25 14:39:19 -0700173static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000174 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700175 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100176 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000177
178 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
179 uint32_t ent = hash & (hash_table_size - 1);
180 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700181 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000182 return ent;
183 }
184
185 ent = (ent + 1) & (hash_table_size - 1);
186 }
187
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100188 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000189 return kEntryNotFound;
190}
191
192/*
193 * Add a new entry to the hash table.
194 */
Yusuke Sato07447542015-06-25 14:39:19 -0700195static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
196 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100197 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000198 uint32_t ent = hash & (hash_table_size - 1);
199
200 /*
201 * We over-allocated the table, so we're guaranteed to find an empty slot.
202 * Further, we guarantee that the hashtable size is not 0.
203 */
204 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700205 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000206 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100207 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000208 return kDuplicateEntry;
209 }
210 ent = (ent + 1) & (hash_table_size - 1);
211 }
212
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100213 hash_table[ent].name = name.name;
214 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000215 return 0;
216}
217
Tianjie Xu18c25922016-09-29 15:27:41 -0700218static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
219 off64_t file_length, off64_t read_amount,
220 uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000221 const off64_t search_start = file_length - read_amount;
222
Tianjie Xu18c25922016-09-29 15:27:41 -0700223 if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
224 ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
225 static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
Narayan Kamath7462f022013-11-21 13:05:04 +0000226 return kIoError;
227 }
228
229 /*
230 * Scan backward for the EOCD magic. In an archive without a trailing
231 * comment, we'll find it on the first try. (We may want to consider
232 * doing an initial minimal read; if we don't find it, retry with a
233 * second read as above.)
234 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100235 int i = read_amount - sizeof(EocdRecord);
236 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700237 if (scan_buffer[i] == 0x50) {
238 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
239 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
240 ALOGV("+++ Found EOCD at buf+%d", i);
241 break;
242 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000243 }
244 }
245 if (i < 0) {
246 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
247 return kInvalidFile;
248 }
249
250 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100251 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000252 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100253 * Verify that there's no trailing space at the end of the central directory
254 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000255 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100256 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
257 + eocd->comment_length;
258 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100259 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100260 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100261 return kInvalidFile;
262 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000263
Narayan Kamath926973e2014-06-09 14:18:14 +0100264 /*
265 * Grab the CD offset and size, and the number of entries in the
266 * archive and verify that they look reasonable.
267 */
268 if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) {
269 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
270 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000271 return kInvalidOffset;
272 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100273 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000274 ALOGW("Zip: empty archive?");
275 return kEmptyArchive;
276 }
277
Elliott Hughese49236b2015-06-04 15:21:59 -0700278 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100279 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000280
281 /*
282 * It all looks good. Create a mapping for the CD, and set the fields
283 * in archive.
284 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700285
286 if (!archive->InitializeCentralDirectory(debug_file_name,
287 static_cast<off64_t>(eocd->cd_start_offset),
288 static_cast<size_t>(eocd->cd_size))) {
289 ALOGE("Zip: failed to intialize central directory.\n");
Narayan Kamatheaf98852013-12-11 14:51:51 +0000290 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000291 }
292
Narayan Kamath926973e2014-06-09 14:18:14 +0100293 archive->num_entries = eocd->num_records;
294 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000295
296 return 0;
297}
298
299/*
300 * Find the zip Central Directory and memory-map it.
301 *
302 * On success, returns 0 after populating fields from the EOCD area:
303 * directory_offset
Tianjie Xu18c25922016-09-29 15:27:41 -0700304 * directory_ptr
Narayan Kamath7462f022013-11-21 13:05:04 +0000305 * num_entries
306 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700307static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000308
309 // Test file length. We use lseek64 to make sure the file
310 // is small enough to be a zip file (Its size must be less than
311 // 0xffffffff bytes).
Tianjie Xu18c25922016-09-29 15:27:41 -0700312 off64_t file_length = archive->mapped_zip.GetFileLength();
Narayan Kamath7462f022013-11-21 13:05:04 +0000313 if (file_length == -1) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000314 return kInvalidFile;
315 }
316
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800317 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100318 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000319 return kInvalidFile;
320 }
321
Narayan Kamath926973e2014-06-09 14:18:14 +0100322 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
323 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000324 return kInvalidFile;
325 }
326
327 /*
328 * Perform the traditional EOCD snipe hunt.
329 *
330 * We're searching for the End of Central Directory magic number,
331 * which appears at the start of the EOCD block. It's followed by
332 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
333 * need to read the last part of the file into a buffer, dig through
334 * it to find the magic number, parse some values out, and use those
335 * to determine the extent of the CD.
336 *
337 * We start by pulling in the last part of the file.
338 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100339 off64_t read_amount = kMaxEOCDSearch;
340 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000341 read_amount = file_length;
342 }
343
Tianjie Xu18c25922016-09-29 15:27:41 -0700344 std::vector<uint8_t> scan_buffer(read_amount);
345 int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
346 scan_buffer.data());
Narayan Kamath7462f022013-11-21 13:05:04 +0000347 return result;
348}
349
350/*
351 * Parses the Zip archive's Central Directory. Allocates and populates the
352 * hash table.
353 *
354 * Returns 0 on success.
355 */
356static int32_t ParseZipArchive(ZipArchive* archive) {
Tianjie Xu18c25922016-09-29 15:27:41 -0700357 const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
358 const size_t cd_length = archive->central_directory.GetMapLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100359 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000360
361 /*
362 * Create hash table. We have a minimum 75% load factor, possibly as
363 * low as 50% after we round off to a power of 2. There must be at
364 * least one unused entry to avoid an infinite loop during creation.
365 */
366 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700367 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
368 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000369
370 /*
371 * Walk through the central directory, adding entries to the hash
372 * table and verifying values.
373 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100374 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000375 const uint8_t* ptr = cd_ptr;
376 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100377 const CentralDirectoryRecord* cdr =
378 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
379 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700380 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800381 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000382 }
383
Narayan Kamath926973e2014-06-09 14:18:14 +0100384 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700385 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800386 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000387 }
388
Narayan Kamath926973e2014-06-09 14:18:14 +0100389 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000390 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800391 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
392 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800393 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000394 }
395
Narayan Kamath926973e2014-06-09 14:18:14 +0100396 const uint16_t file_name_length = cdr->file_name_length;
397 const uint16_t extra_length = cdr->extra_field_length;
398 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100399 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
400
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000401 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
402 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800403 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100404 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000405
406 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700407 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100408 entry_name.name = file_name;
409 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000410 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100411 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800412 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000413 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800414 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000415 }
416
Narayan Kamath926973e2014-06-09 14:18:14 +0100417 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
418 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700419 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
420 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800421 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000422 }
423 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700424 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000425
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800426 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000427}
428
429static int32_t OpenArchiveInternal(ZipArchive* archive,
430 const char* debug_file_name) {
431 int32_t result = -1;
Tianjie Xu18c25922016-09-29 15:27:41 -0700432 if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000433 return result;
434 }
435
436 if ((result = ParseZipArchive(archive))) {
437 return result;
438 }
439
440 return 0;
441}
442
443int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700444 ZipArchiveHandle* handle, bool assume_ownership) {
445 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000446 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000447 return OpenArchiveInternal(archive, debug_file_name);
448}
449
450int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100451 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700452 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000453 *handle = archive;
454
Narayan Kamath7462f022013-11-21 13:05:04 +0000455 if (fd < 0) {
456 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
457 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000458 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700459
Narayan Kamath7462f022013-11-21 13:05:04 +0000460 return OpenArchiveInternal(archive, fileName);
461}
462
Tianjie Xu18c25922016-09-29 15:27:41 -0700463int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
464 ZipArchiveHandle *handle) {
465 ZipArchive* archive = new ZipArchive(address, length);
466 *handle = archive;
467 return OpenArchiveInternal(archive, debug_file_name);
468}
469
Narayan Kamath7462f022013-11-21 13:05:04 +0000470/*
471 * Close a ZipArchive, closing the file and freeing the contents.
472 */
473void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800474 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000475 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100476 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000477}
478
Tianjie Xu18c25922016-09-29 15:27:41 -0700479static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
Narayan Kamath7462f022013-11-21 13:05:04 +0000480 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100481 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700482 if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000483 return kIoError;
484 }
485
Narayan Kamath926973e2014-06-09 14:18:14 +0100486 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
487 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
488 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000489
Narayan Kamath926973e2014-06-09 14:18:14 +0100490 entry->crc32 = descriptor->crc32;
491 entry->compressed_length = descriptor->compressed_size;
492 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000493
494 return 0;
495}
496
Narayan Kamath7462f022013-11-21 13:05:04 +0000497static int32_t FindEntry(const ZipArchive* archive, const int ent,
498 ZipEntry* data) {
499 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000500
501 // Recover the start of the central directory entry from the filename
502 // pointer. The filename is the first entry past the fixed-size data,
503 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100504 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100505 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000506
507 // This is the base of our mmapped region, we have to sanity check that
508 // the name that's in the hash table is a pointer to a location within
509 // this mapped region.
Tianjie Xu18c25922016-09-29 15:27:41 -0700510 const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
511 if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000512 ALOGW("Zip: Invalid entry pointer");
513 return kInvalidOffset;
514 }
515
Narayan Kamath926973e2014-06-09 14:18:14 +0100516 const CentralDirectoryRecord *cdr =
517 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
518
Narayan Kamath7462f022013-11-21 13:05:04 +0000519 // The offset of the start of the central directory in the zipfile.
520 // We keep this lying around so that we can sanity check all our lengths
521 // and our per-file structures.
522 const off64_t cd_offset = archive->directory_offset;
523
524 // Fill out the compression method, modification time, crc32
525 // and other interesting attributes from the central directory. These
526 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100527 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900528 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100529 data->crc32 = cdr->crc32;
530 data->compressed_length = cdr->compressed_size;
531 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000532
533 // Figure out the local header offset from the central directory. The
534 // actual file data will begin after the local header and the name /
535 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100536 const off64_t local_header_offset = cdr->local_file_header_offset;
537 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000538 ALOGW("Zip: bad local hdr offset in zip");
539 return kInvalidOffset;
540 }
541
Narayan Kamath926973e2014-06-09 14:18:14 +0100542 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700543 if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800544 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
545 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000546 return kIoError;
547 }
548
Narayan Kamath926973e2014-06-09 14:18:14 +0100549 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
550
551 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700552 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100553 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000554 return kInvalidOffset;
555 }
556
557 // Paranoia: Match the values specified in the local file header
558 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100559 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000560 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100561 if (data->compressed_length != lfh->compressed_size
562 || data->uncompressed_length != lfh->uncompressed_size
563 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700564 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
565 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000566 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100567 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000568 return kInconsistentInformation;
569 }
570 } else {
571 data->has_data_descriptor = 1;
572 }
573
574 // Check that the local file header name matches the declared
575 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100576 if (lfh->file_name_length == nameLen) {
577 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200578 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000579 ALOGW("Zip: Invalid declared length");
580 return kInvalidOffset;
581 }
582
Tianjie Xu18c25922016-09-29 15:27:41 -0700583 std::vector<uint8_t> name_buf(nameLen);
584 if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800585 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000586 return kIoError;
587 }
588
Tianjie Xu18c25922016-09-29 15:27:41 -0700589 if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000590 return kInconsistentInformation;
591 }
592
Narayan Kamath7462f022013-11-21 13:05:04 +0000593 } else {
594 ALOGW("Zip: lfh name did not match central directory.");
595 return kInconsistentInformation;
596 }
597
Narayan Kamath926973e2014-06-09 14:18:14 +0100598 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
599 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000600 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800601 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000602 return kInvalidOffset;
603 }
604
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800605 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700606 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800607 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000608 return kInvalidOffset;
609 }
610
611 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800612 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700613 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800614 static_cast<int64_t>(data_offset), data->uncompressed_length,
615 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000616 return kInvalidOffset;
617 }
618
619 data->offset = data_offset;
620 return 0;
621}
622
623struct IterationHandle {
624 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100625 // We're not using vector here because this code is used in the Windows SDK
626 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700627 ZipString prefix;
628 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000629 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100630
Yusuke Sato07447542015-06-25 14:39:19 -0700631 IterationHandle(const ZipString* in_prefix,
632 const ZipString* in_suffix) {
633 if (in_prefix) {
634 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
635 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
636 prefix.name = name_copy;
637 prefix.name_length = in_prefix->name_length;
638 } else {
639 prefix.name = NULL;
640 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700641 }
Yusuke Sato07447542015-06-25 14:39:19 -0700642 if (in_suffix) {
643 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
644 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
645 suffix.name = name_copy;
646 suffix.name_length = in_suffix->name_length;
647 } else {
648 suffix.name = NULL;
649 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700650 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100651 }
652
653 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700654 delete[] prefix.name;
655 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100656 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000657};
658
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100659int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700660 const ZipString* optional_prefix,
661 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800662 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000663
664 if (archive == NULL || archive->hash_table == NULL) {
665 ALOGW("Zip: Invalid ZipArchiveHandle");
666 return kInvalidHandle;
667 }
668
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700669 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000670 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000671 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000672
673 *cookie_ptr = cookie ;
674 return 0;
675}
676
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100677void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100678 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100679}
680
Yusuke Sato07447542015-06-25 14:39:19 -0700681int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000682 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800683 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100684 if (entryName.name_length == 0) {
685 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000686 return kInvalidEntryName;
687 }
688
689 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100690 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000691
692 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100693 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000694 return ent;
695 }
696
697 return FindEntry(archive, ent, data);
698}
699
Yusuke Sato07447542015-06-25 14:39:19 -0700700int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800701 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000702 if (handle == NULL) {
703 return kInvalidHandle;
704 }
705
706 ZipArchive* archive = handle->archive;
707 if (archive == NULL || archive->hash_table == NULL) {
708 ALOGW("Zip: Invalid ZipArchiveHandle");
709 return kInvalidHandle;
710 }
711
712 const uint32_t currentOffset = handle->position;
713 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700714 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000715
716 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
717 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700718 (handle->prefix.name_length == 0 ||
719 hash_table[i].StartsWith(handle->prefix)) &&
720 (handle->suffix.name_length == 0 ||
721 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000722 handle->position = (i + 1);
723 const int error = FindEntry(archive, i, data);
724 if (!error) {
725 name->name = hash_table[i].name;
726 name->name_length = hash_table[i].name_length;
727 }
728
729 return error;
730 }
731 }
732
733 handle->position = 0;
734 return kIterationEnd;
735}
736
Narayan Kamathf899bd52015-04-17 11:53:14 +0100737class Writer {
738 public:
739 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
740 virtual ~Writer() {}
741 protected:
742 Writer() = default;
743 private:
744 DISALLOW_COPY_AND_ASSIGN(Writer);
745};
746
747// A Writer that writes data to a fixed size memory region.
748// The size of the memory region must be equal to the total size of
749// the data appended to it.
750class MemoryWriter : public Writer {
751 public:
752 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
753 buf_(buf), size_(size), bytes_written_(0) {
754 }
755
756 virtual bool Append(uint8_t* buf, size_t buf_size) override {
757 if (bytes_written_ + buf_size > size_) {
758 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
759 size_, bytes_written_ + buf_size);
760 return false;
761 }
762
763 memcpy(buf_ + bytes_written_, buf, buf_size);
764 bytes_written_ += buf_size;
765 return true;
766 }
767
768 private:
769 uint8_t* const buf_;
770 const size_t size_;
771 size_t bytes_written_;
772};
773
774// A Writer that appends data to a file |fd| at its current position.
775// The file will be truncated to the end of the written data.
776class FileWriter : public Writer {
777 public:
778
779 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
780 // guaranteeing that the file descriptor is valid and that there's enough
781 // space on the volume to write out the entry completely and that the file
782 // is truncated to the correct length.
783 //
784 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
785 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
786 const uint32_t declared_length = entry->uncompressed_length;
787 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
788 if (current_offset == -1) {
789 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
790 return nullptr;
791 }
792
793 int result = 0;
794#if defined(__linux__)
795 if (declared_length > 0) {
796 // Make sure we have enough space on the volume to extract the compressed
797 // entry. Note that the call to ftruncate below will change the file size but
798 // will not allocate space on disk and this call to fallocate will not
799 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700800 // Note: fallocate is only supported by the following filesystems -
801 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
802 // EOPNOTSUPP error when issued in other filesystems.
803 // Hence, check for the return error code before concluding that the
804 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100805 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700806 if (result == -1 && errno == ENOSPC) {
Narayan Kamathd5d7abe2016-08-10 12:24:05 +0100807 ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 " : %s",
808 static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
809 strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100810 return std::unique_ptr<FileWriter>(nullptr);
811 }
812 }
813#endif // __linux__
814
815 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
816 if (result == -1) {
817 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
818 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
819 return std::unique_ptr<FileWriter>(nullptr);
820 }
821
822 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
823 }
824
825 virtual bool Append(uint8_t* buf, size_t buf_size) override {
826 if (total_bytes_written_ + buf_size > declared_length_) {
827 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
828 declared_length_, total_bytes_written_ + buf_size);
829 return false;
830 }
831
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100832 const bool result = android::base::WriteFully(fd_, buf, buf_size);
833 if (result) {
834 total_bytes_written_ += buf_size;
835 } else {
836 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100837 }
838
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100839 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100840 }
841 private:
842 FileWriter(const int fd, const size_t declared_length) :
843 Writer(),
844 fd_(fd),
845 declared_length_(declared_length),
846 total_bytes_written_(0) {
847 }
848
849 const int fd_;
850 const size_t declared_length_;
851 size_t total_bytes_written_;
852};
853
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800854// This method is using libz macros with old-style-casts
855#pragma GCC diagnostic push
856#pragma GCC diagnostic ignored "-Wold-style-cast"
857static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
858 return inflateInit2(stream, window_bits);
859}
860#pragma GCC diagnostic pop
861
Tianjie Xu18c25922016-09-29 15:27:41 -0700862static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100863 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700864 const size_t kBufSize = 32768;
865 std::vector<uint8_t> read_buf(kBufSize);
866 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000867 z_stream zstream;
868 int zerr;
869
870 /*
871 * Initialize the zlib stream struct.
872 */
873 memset(&zstream, 0, sizeof(zstream));
874 zstream.zalloc = Z_NULL;
875 zstream.zfree = Z_NULL;
876 zstream.opaque = Z_NULL;
877 zstream.next_in = NULL;
878 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700879 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000880 zstream.avail_out = kBufSize;
881 zstream.data_type = Z_UNKNOWN;
882
883 /*
884 * Use the undocumented "negative window bits" feature to tell zlib
885 * that there's no zlib header waiting for it.
886 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800887 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000888 if (zerr != Z_OK) {
889 if (zerr == Z_VERSION_ERROR) {
890 ALOGE("Installed zlib is not compatible with linked version (%s)",
891 ZLIB_VERSION);
892 } else {
893 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
894 }
895
896 return kZlibError;
897 }
898
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800899 auto zstream_deleter = [](z_stream* stream) {
900 inflateEnd(stream); /* free up any allocated structures */
901 };
902
903 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
904
Narayan Kamath7462f022013-11-21 13:05:04 +0000905 const uint32_t uncompressed_length = entry->uncompressed_length;
906
907 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000908 do {
909 /* read as much as we can */
910 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800911 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
Tianjie Xu18c25922016-09-29 15:27:41 -0700912 if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800913 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800914 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000915 }
916
917 compressed_length -= getSize;
918
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700919 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000920 zstream.avail_in = getSize;
921 }
922
923 /* uncompress the data */
924 zerr = inflate(&zstream, Z_NO_FLUSH);
925 if (zerr != Z_OK && zerr != Z_STREAM_END) {
926 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
927 zerr, zstream.next_in, zstream.avail_in,
928 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800929 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000930 }
931
932 /* write when we're full or when we're done */
933 if (zstream.avail_out == 0 ||
934 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700935 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100936 if (!writer->Append(&write_buf[0], write_size)) {
937 // The file might have declared a bogus length.
938 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000939 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000940
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700941 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000942 zstream.avail_out = kBufSize;
943 }
944 } while (zerr == Z_OK);
945
946 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
947
948 // stream.adler holds the crc32 value for such streams.
949 *crc_out = zstream.adler;
950
951 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700952 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000953 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800954 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000955 }
956
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800957 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000958}
959
Tianjie Xu18c25922016-09-29 15:27:41 -0700960static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100961 uint64_t *crc_out) {
962 static const uint32_t kBufSize = 32768;
963 std::vector<uint8_t> buf(kBufSize);
964
965 const uint32_t length = entry->uncompressed_length;
966 uint32_t count = 0;
967 uint64_t crc = 0;
968 while (count < length) {
969 uint32_t remaining = length - count;
970
971 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
972 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -0800973 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
Tianjie Xu18c25922016-09-29 15:27:41 -0700974 if (!mapped_zip.ReadData(buf.data(), block_size)) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800975 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100976 return kIoError;
977 }
978
979 if (!writer->Append(&buf[0], block_size)) {
980 return kIoError;
981 }
982 crc = crc32(crc, &buf[0], block_size);
983 count += block_size;
984 }
985
986 *crc_out = crc;
987
988 return 0;
989}
990
991int32_t ExtractToWriter(ZipArchiveHandle handle,
992 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800993 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000994 const uint16_t method = entry->method;
995 off64_t data_offset = entry->offset;
996
Tianjie Xu18c25922016-09-29 15:27:41 -0700997 if (!archive->mapped_zip.SeekToOffset(data_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800998 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000999 return kIoError;
1000 }
1001
1002 // this should default to kUnknownCompressionMethod.
1003 int32_t return_value = -1;
1004 uint64_t crc = 0;
1005 if (method == kCompressStored) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001006 return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001007 } else if (method == kCompressDeflated) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001008 return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001009 }
1010
1011 if (!return_value && entry->has_data_descriptor) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001012 return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
Narayan Kamath7462f022013-11-21 13:05:04 +00001013 if (return_value) {
1014 return return_value;
1015 }
1016 }
1017
1018 // TODO: Fix this check by passing the right flags to inflate2 so that
1019 // it calculates the CRC for us.
1020 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001021 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001022 return kInconsistentInformation;
1023 }
1024
1025 return return_value;
1026}
1027
Narayan Kamathf899bd52015-04-17 11:53:14 +01001028int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1029 uint8_t* begin, uint32_t size) {
1030 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1031 return ExtractToWriter(handle, entry, writer.get());
1032}
1033
Narayan Kamath7462f022013-11-21 13:05:04 +00001034int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1035 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001036 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1037 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001038 return kIoError;
1039 }
1040
Narayan Kamathf899bd52015-04-17 11:53:14 +01001041 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001042}
1043
1044const char* ErrorCodeString(int32_t error_code) {
1045 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1046 return kErrorMessages[error_code * -1];
1047 }
1048
1049 return kErrorMessages[0];
1050}
1051
1052int GetFileDescriptor(const ZipArchiveHandle handle) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001053 return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
Narayan Kamath7462f022013-11-21 13:05:04 +00001054}
Colin Cross7c6c7f02016-09-16 10:15:51 -07001055
1056ZipString::ZipString(const char* entry_name)
1057 : name(reinterpret_cast<const uint8_t*>(entry_name)) {
1058 size_t len = strlen(entry_name);
1059 CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
1060 name_length = static_cast<uint16_t>(len);
1061}
Tianjie Xu18c25922016-09-29 15:27:41 -07001062
1063#if !defined(_WIN32)
1064class ProcessWriter : public Writer {
1065 public:
1066 ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
1067 proc_function_(func),
1068 cookie_(cookie) {
1069 }
1070
1071 virtual bool Append(uint8_t* buf, size_t buf_size) override {
1072 return proc_function_(buf, buf_size, cookie_);
1073 }
1074
1075 private:
1076 ProcessZipEntryFunction proc_function_;
1077 void* cookie_;
1078};
1079
1080int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
1081 ProcessZipEntryFunction func, void* cookie) {
1082 ProcessWriter writer(func, cookie);
1083 return ExtractToWriter(handle, entry, &writer);
1084}
1085
1086#endif //!defined(_WIN32)
1087
1088int MappedZipFile::GetFileDescriptor() const {
1089 if (!has_fd_) {
1090 ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
1091 return -1;
1092 }
1093 return fd_;
1094}
1095
1096void* MappedZipFile::GetBasePtr() const {
1097 if (has_fd_) {
1098 ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
1099 return nullptr;
1100 }
1101 return base_ptr_;
1102}
1103
1104off64_t MappedZipFile::GetFileLength() const {
1105 if (has_fd_) {
1106 off64_t result = lseek64(fd_, 0, SEEK_END);
1107 if (result == -1) {
1108 ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
1109 }
1110 return result;
1111 } else {
1112 if (base_ptr_ == nullptr) {
1113 ALOGE("Zip: invalid file map\n");
1114 return -1;
1115 }
1116 return static_cast<off64_t>(data_length_);
1117 }
1118}
1119
1120bool MappedZipFile::SeekToOffset(off64_t offset) {
1121 if (has_fd_) {
1122 if (lseek64(fd_, offset, SEEK_SET) != offset) {
1123 ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
1124 return false;
1125 }
1126 return true;
1127 } else {
1128 if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
1129 ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
1130 data_length_);
1131 return false;
1132 }
1133
1134 read_pos_ = offset;
1135 return true;
1136 }
1137}
1138
1139bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
1140 if (has_fd_) {
1141 if(!android::base::ReadFully(fd_, buffer, read_amount)) {
1142 ALOGE("Zip: read from %d failed\n", fd_);
1143 return false;
1144 }
1145 } else {
1146 memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
1147 read_pos_ += read_amount;
1148 }
1149 return true;
1150}
1151
1152// Attempts to read |len| bytes into |buf| at offset |off|.
1153bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
1154#if !defined(_WIN32)
1155 if (has_fd_) {
1156 if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
1157 ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
1158 return false;
1159 }
1160 return true;
1161 }
1162#endif
1163 if (!SeekToOffset(off)) {
1164 return false;
1165 }
1166 return ReadData(buf, len);
1167
1168}
1169
1170void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
1171 base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
1172 length_ = cd_size;
1173}
1174
1175bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
1176 size_t cd_size) {
1177 if (mapped_zip.HasFd()) {
1178 if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
1179 cd_start_offset, cd_size, true /* read only */)) {
1180 return false;
1181 }
1182
1183 CHECK_EQ(directory_map->getDataLength(), cd_size);
1184 central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
1185 } else {
1186 if (mapped_zip.GetBasePtr() == nullptr) {
1187 ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
1188 return false;
1189 }
1190 if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
1191 mapped_zip.GetFileLength()) {
1192 ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
1193 "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
1194 static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
1195 return false;
1196 }
1197
1198 central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
1199 }
1200 return true;
1201}