blob: 779ef2fb9930d873f8589c453c844254c6290353 [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
Mark Salyzyncfd5b082016-10-17 14:28:00 -070021#define LOG_TAG "ziparchive"
22
Narayan Kamath7462f022013-11-21 13:05:04 +000023#include <assert.h>
24#include <errno.h>
Mark Salyzyn99ef9912014-03-14 14:26:22 -070025#include <fcntl.h>
26#include <inttypes.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000027#include <limits.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000028#include <stdlib.h>
29#include <string.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000030#include <unistd.h>
31
Dan Albert1ae07642015-04-09 14:11:18 -070032#include <memory>
33#include <vector>
34
Mark Salyzynff2dcd92016-09-28 15:54:45 -070035#include <android-base/file.h>
36#include <android-base/logging.h>
37#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
38#include <android-base/memory.h>
Mark Salyzyncfd5b082016-10-17 14:28:00 -070039#include <log/log.h>
Mark Salyzynff2dcd92016-09-28 15:54:45 -070040#include <utils/Compat.h>
41#include <utils/FileMap.h>
Christopher Ferrise6884ce2015-11-10 14:55:12 -080042#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070043#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000044
Narayan Kamath044bc8e2014-12-03 18:22:53 +000045#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070046#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080047#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070048
Dan Albert1ae07642015-04-09 14:11:18 -070049using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000050
Narayan Kamath926973e2014-06-09 14:18:14 +010051// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000052// things will happen.
53#ifndef O_BINARY
54#define O_BINARY 0
55#endif
56
Narayan Kamath926973e2014-06-09 14:18:14 +010057// The maximum number of bytes to scan backwards for the EOCD start.
58static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
59
Narayan Kamath7462f022013-11-21 13:05:04 +000060static const char* kErrorMessages[] = {
61 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000062 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000063 "Zlib error",
64 "Invalid file",
65 "Invalid handle",
66 "Duplicate entries in archive",
67 "Empty archive",
68 "Entry not found",
69 "Invalid offset",
70 "Inconsistent information",
71 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000072 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000073 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000074};
75
76static const int32_t kErrorMessageUpperBound = 0;
77
Narayan Kamatheb41ad22013-12-09 16:26:36 +000078static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000079
80// We encountered a Zlib error when inflating a stream from this file.
81// Usually indicates file corruption.
82static const int32_t kZlibError = -2;
83
84// The input file cannot be processed as a zip archive. Usually because
85// it's too small, too large or does not have a valid signature.
86static const int32_t kInvalidFile = -3;
87
88// An invalid iteration / ziparchive handle was passed in as an input
89// argument.
90static const int32_t kInvalidHandle = -4;
91
92// The zip archive contained two (or possibly more) entries with the same
93// name.
94static const int32_t kDuplicateEntry = -5;
95
96// The zip archive contains no entries.
97static const int32_t kEmptyArchive = -6;
98
99// The specified entry was not found in the archive.
100static const int32_t kEntryNotFound = -7;
101
102// The zip archive contained an invalid local file header pointer.
103static const int32_t kInvalidOffset = -8;
104
105// The zip archive contained inconsistent entry information. This could
106// be because the central directory & local file header did not agree, or
107// if the actual uncompressed length or crc32 do not match their declared
108// values.
109static const int32_t kInconsistentInformation = -9;
110
111// An invalid entry name was encountered.
112static const int32_t kInvalidEntryName = -10;
113
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000114// An I/O related system call (read, lseek, ftruncate, map) failed.
115static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000116
Narayan Kamatheaf98852013-12-11 14:51:51 +0000117// We were not able to mmap the central directory or entry contents.
118static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000119
Narayan Kamatheaf98852013-12-11 14:51:51 +0000120static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000121
Narayan Kamath7462f022013-11-21 13:05:04 +0000122/*
123 * A Read-only Zip archive.
124 *
125 * We want "open" and "find entry by name" to be fast operations, and
126 * we want to use as little memory as possible. We memory-map the zip
127 * central directory, and load a hash table with pointers to the filenames
128 * (which aren't null-terminated). The other fields are at a fixed offset
129 * from the filename, so we don't need to extract those (but we do need
130 * to byte-read and endian-swap them every time we want them).
131 *
132 * It's possible that somebody has handed us a massive (~1GB) zip archive,
133 * so we can't expect to mmap the entire file.
134 *
135 * To speed comparisons when doing a lookup by name, we could make the mapping
136 * "private" (copy-on-write) and null-terminate the filenames after verifying
137 * the record structure. However, this requires a private mapping of
138 * every page that the Central Directory touches. Easier to tuck a copy
139 * of the string length into the hash table entry.
140 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000141
Narayan Kamath7462f022013-11-21 13:05:04 +0000142/*
143 * Round up to the next highest power of 2.
144 *
145 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
146 */
147static uint32_t RoundUpPower2(uint32_t val) {
148 val--;
149 val |= val >> 1;
150 val |= val >> 2;
151 val |= val >> 4;
152 val |= val >> 8;
153 val |= val >> 16;
154 val++;
155
156 return val;
157}
158
Yusuke Sato07447542015-06-25 14:39:19 -0700159static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000160 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100161 uint16_t len = name.name_length;
162 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000163
164 while (len--) {
165 hash = hash * 31 + *str++;
166 }
167
168 return hash;
169}
170
171/*
172 * Convert a ZipEntry to a hash table index, verifying that it's in a
173 * valid range.
174 */
Yusuke Sato07447542015-06-25 14:39:19 -0700175static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000176 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700177 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100178 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000179
180 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
181 uint32_t ent = hash & (hash_table_size - 1);
182 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700183 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000184 return ent;
185 }
186
187 ent = (ent + 1) & (hash_table_size - 1);
188 }
189
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100190 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000191 return kEntryNotFound;
192}
193
194/*
195 * Add a new entry to the hash table.
196 */
Yusuke Sato07447542015-06-25 14:39:19 -0700197static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
198 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100199 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000200 uint32_t ent = hash & (hash_table_size - 1);
201
202 /*
203 * We over-allocated the table, so we're guaranteed to find an empty slot.
204 * Further, we guarantee that the hashtable size is not 0.
205 */
206 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700207 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000208 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100209 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000210 return kDuplicateEntry;
211 }
212 ent = (ent + 1) & (hash_table_size - 1);
213 }
214
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100215 hash_table[ent].name = name.name;
216 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000217 return 0;
218}
219
Tianjie Xu18c25922016-09-29 15:27:41 -0700220static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
221 off64_t file_length, off64_t read_amount,
222 uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000223 const off64_t search_start = file_length - read_amount;
224
Tianjie Xu18c25922016-09-29 15:27:41 -0700225 if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
226 ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
227 static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
Narayan Kamath7462f022013-11-21 13:05:04 +0000228 return kIoError;
229 }
230
231 /*
232 * Scan backward for the EOCD magic. In an archive without a trailing
233 * comment, we'll find it on the first try. (We may want to consider
234 * doing an initial minimal read; if we don't find it, retry with a
235 * second read as above.)
236 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100237 int i = read_amount - sizeof(EocdRecord);
238 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700239 if (scan_buffer[i] == 0x50) {
240 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
241 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
242 ALOGV("+++ Found EOCD at buf+%d", i);
243 break;
244 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000245 }
246 }
247 if (i < 0) {
248 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
249 return kInvalidFile;
250 }
251
252 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100253 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000254 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100255 * Verify that there's no trailing space at the end of the central directory
256 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000257 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100258 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
259 + eocd->comment_length;
260 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100261 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100262 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100263 return kInvalidFile;
264 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000265
Narayan Kamath926973e2014-06-09 14:18:14 +0100266 /*
267 * Grab the CD offset and size, and the number of entries in the
268 * archive and verify that they look reasonable.
269 */
Tianjie Xuae8180c2016-09-21 14:58:11 -0700270 if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100271 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
272 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Tianjie Xuae8180c2016-09-21 14:58:11 -0700273#if defined(__ANDROID__)
274 if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
275 android_errorWriteLog(0x534e4554, "31251826");
276 }
277#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000278 return kInvalidOffset;
279 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100280 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000281 ALOGW("Zip: empty archive?");
282 return kEmptyArchive;
283 }
284
Elliott Hughese49236b2015-06-04 15:21:59 -0700285 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100286 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000287
288 /*
289 * It all looks good. Create a mapping for the CD, and set the fields
290 * in archive.
291 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700292
293 if (!archive->InitializeCentralDirectory(debug_file_name,
294 static_cast<off64_t>(eocd->cd_start_offset),
295 static_cast<size_t>(eocd->cd_size))) {
296 ALOGE("Zip: failed to intialize central directory.\n");
Narayan Kamatheaf98852013-12-11 14:51:51 +0000297 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000298 }
299
Narayan Kamath926973e2014-06-09 14:18:14 +0100300 archive->num_entries = eocd->num_records;
301 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000302
303 return 0;
304}
305
306/*
307 * Find the zip Central Directory and memory-map it.
308 *
309 * On success, returns 0 after populating fields from the EOCD area:
310 * directory_offset
Tianjie Xu18c25922016-09-29 15:27:41 -0700311 * directory_ptr
Narayan Kamath7462f022013-11-21 13:05:04 +0000312 * num_entries
313 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700314static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000315
316 // Test file length. We use lseek64 to make sure the file
317 // is small enough to be a zip file (Its size must be less than
318 // 0xffffffff bytes).
Tianjie Xu18c25922016-09-29 15:27:41 -0700319 off64_t file_length = archive->mapped_zip.GetFileLength();
Narayan Kamath7462f022013-11-21 13:05:04 +0000320 if (file_length == -1) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000321 return kInvalidFile;
322 }
323
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800324 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100325 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000326 return kInvalidFile;
327 }
328
Narayan Kamath926973e2014-06-09 14:18:14 +0100329 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
330 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000331 return kInvalidFile;
332 }
333
334 /*
335 * Perform the traditional EOCD snipe hunt.
336 *
337 * We're searching for the End of Central Directory magic number,
338 * which appears at the start of the EOCD block. It's followed by
339 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
340 * need to read the last part of the file into a buffer, dig through
341 * it to find the magic number, parse some values out, and use those
342 * to determine the extent of the CD.
343 *
344 * We start by pulling in the last part of the file.
345 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100346 off64_t read_amount = kMaxEOCDSearch;
347 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000348 read_amount = file_length;
349 }
350
Tianjie Xu18c25922016-09-29 15:27:41 -0700351 std::vector<uint8_t> scan_buffer(read_amount);
352 int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
353 scan_buffer.data());
Narayan Kamath7462f022013-11-21 13:05:04 +0000354 return result;
355}
356
357/*
358 * Parses the Zip archive's Central Directory. Allocates and populates the
359 * hash table.
360 *
361 * Returns 0 on success.
362 */
363static int32_t ParseZipArchive(ZipArchive* archive) {
Tianjie Xu18c25922016-09-29 15:27:41 -0700364 const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
365 const size_t cd_length = archive->central_directory.GetMapLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100366 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000367
368 /*
369 * Create hash table. We have a minimum 75% load factor, possibly as
370 * low as 50% after we round off to a power of 2. There must be at
371 * least one unused entry to avoid an infinite loop during creation.
372 */
373 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700374 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
375 sizeof(ZipString)));
Tianjie Xubcc44312016-10-10 12:11:30 -0700376 if (archive->hash_table == nullptr) {
377 ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
378 archive->hash_table_size, sizeof(ZipString));
379 return -1;
380 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000381
382 /*
383 * Walk through the central directory, adding entries to the hash
384 * table and verifying values.
385 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100386 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000387 const uint8_t* ptr = cd_ptr;
388 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100389 const CentralDirectoryRecord* cdr =
390 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
391 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700392 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", 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 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700397 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800398 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000399 }
400
Narayan Kamath926973e2014-06-09 14:18:14 +0100401 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000402 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800403 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
404 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800405 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000406 }
407
Narayan Kamath926973e2014-06-09 14:18:14 +0100408 const uint16_t file_name_length = cdr->file_name_length;
409 const uint16_t extra_length = cdr->extra_field_length;
410 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100411 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
412
Tianjie Xubcc44312016-10-10 12:11:30 -0700413 if (file_name + file_name_length > cd_end) {
414 ALOGW("Zip: file name boundary exceeds the central directory range, file_name_length: "
415 "%" PRIx16 ", cd_length: %zu", file_name_length, cd_length);
416 return -1;
417 }
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000418 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
419 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800420 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100421 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000422
423 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700424 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100425 entry_name.name = file_name;
426 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000427 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100428 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800429 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000430 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800431 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000432 }
433
Narayan Kamath926973e2014-06-09 14:18:14 +0100434 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
435 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700436 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
437 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800438 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000439 }
440 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700441 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000442
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800443 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000444}
445
446static int32_t OpenArchiveInternal(ZipArchive* archive,
447 const char* debug_file_name) {
448 int32_t result = -1;
Tianjie Xu18c25922016-09-29 15:27:41 -0700449 if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000450 return result;
451 }
452
453 if ((result = ParseZipArchive(archive))) {
454 return result;
455 }
456
457 return 0;
458}
459
460int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700461 ZipArchiveHandle* handle, bool assume_ownership) {
462 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000463 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000464 return OpenArchiveInternal(archive, debug_file_name);
465}
466
467int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100468 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700469 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000470 *handle = archive;
471
Narayan Kamath7462f022013-11-21 13:05:04 +0000472 if (fd < 0) {
473 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
474 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000475 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700476
Narayan Kamath7462f022013-11-21 13:05:04 +0000477 return OpenArchiveInternal(archive, fileName);
478}
479
Tianjie Xu18c25922016-09-29 15:27:41 -0700480int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
481 ZipArchiveHandle *handle) {
482 ZipArchive* archive = new ZipArchive(address, length);
483 *handle = archive;
484 return OpenArchiveInternal(archive, debug_file_name);
485}
486
Narayan Kamath7462f022013-11-21 13:05:04 +0000487/*
488 * Close a ZipArchive, closing the file and freeing the contents.
489 */
490void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800491 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000492 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100493 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000494}
495
Tianjie Xu18c25922016-09-29 15:27:41 -0700496static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
Narayan Kamath7462f022013-11-21 13:05:04 +0000497 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100498 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700499 if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000500 return kIoError;
501 }
502
Narayan Kamath926973e2014-06-09 14:18:14 +0100503 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
504 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
505 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000506
Narayan Kamath926973e2014-06-09 14:18:14 +0100507 entry->crc32 = descriptor->crc32;
508 entry->compressed_length = descriptor->compressed_size;
509 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000510
511 return 0;
512}
513
Narayan Kamath7462f022013-11-21 13:05:04 +0000514static int32_t FindEntry(const ZipArchive* archive, const int ent,
515 ZipEntry* data) {
516 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000517
518 // Recover the start of the central directory entry from the filename
519 // pointer. The filename is the first entry past the fixed-size data,
520 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100521 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100522 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000523
524 // This is the base of our mmapped region, we have to sanity check that
525 // the name that's in the hash table is a pointer to a location within
526 // this mapped region.
Tianjie Xu18c25922016-09-29 15:27:41 -0700527 const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
528 if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000529 ALOGW("Zip: Invalid entry pointer");
530 return kInvalidOffset;
531 }
532
Narayan Kamath926973e2014-06-09 14:18:14 +0100533 const CentralDirectoryRecord *cdr =
534 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
535
Narayan Kamath7462f022013-11-21 13:05:04 +0000536 // The offset of the start of the central directory in the zipfile.
537 // We keep this lying around so that we can sanity check all our lengths
538 // and our per-file structures.
539 const off64_t cd_offset = archive->directory_offset;
540
541 // Fill out the compression method, modification time, crc32
542 // and other interesting attributes from the central directory. These
543 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100544 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900545 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100546 data->crc32 = cdr->crc32;
547 data->compressed_length = cdr->compressed_size;
548 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000549
550 // Figure out the local header offset from the central directory. The
551 // actual file data will begin after the local header and the name /
552 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100553 const off64_t local_header_offset = cdr->local_file_header_offset;
554 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000555 ALOGW("Zip: bad local hdr offset in zip");
556 return kInvalidOffset;
557 }
558
Narayan Kamath926973e2014-06-09 14:18:14 +0100559 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700560 if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800561 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
562 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000563 return kIoError;
564 }
565
Narayan Kamath926973e2014-06-09 14:18:14 +0100566 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
567
568 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700569 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100570 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000571 return kInvalidOffset;
572 }
573
574 // Paranoia: Match the values specified in the local file header
575 // to those specified in the central directory.
Adam Lesinskie0eca552017-04-06 18:55:47 -0700576
577 // Verify that the central directory and local file header agree on the use of a trailing
578 // Data Descriptor.
579 if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
580 ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
581 cdr->gpb_flags, lfh->gpb_flags);
582 return kInconsistentInformation;
583 }
584
585 // If there is no trailing data descriptor, verify that the central directory and local file
586 // header agree on the crc, compressed, and uncompressed sizes of the entry.
Narayan Kamath926973e2014-06-09 14:18:14 +0100587 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000588 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100589 if (data->compressed_length != lfh->compressed_size
590 || data->uncompressed_length != lfh->uncompressed_size
591 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700592 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
593 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000594 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100595 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000596 return kInconsistentInformation;
597 }
598 } else {
599 data->has_data_descriptor = 1;
600 }
601
602 // Check that the local file header name matches the declared
603 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100604 if (lfh->file_name_length == nameLen) {
605 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200606 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000607 ALOGW("Zip: Invalid declared length");
608 return kInvalidOffset;
609 }
610
Tianjie Xu18c25922016-09-29 15:27:41 -0700611 std::vector<uint8_t> name_buf(nameLen);
612 if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800613 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000614 return kIoError;
615 }
616
Tianjie Xu18c25922016-09-29 15:27:41 -0700617 if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000618 return kInconsistentInformation;
619 }
620
Narayan Kamath7462f022013-11-21 13:05:04 +0000621 } else {
622 ALOGW("Zip: lfh name did not match central directory.");
623 return kInconsistentInformation;
624 }
625
Narayan Kamath926973e2014-06-09 14:18:14 +0100626 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
627 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000628 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800629 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000630 return kInvalidOffset;
631 }
632
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800633 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700634 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800635 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000636 return kInvalidOffset;
637 }
638
639 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800640 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700641 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800642 static_cast<int64_t>(data_offset), data->uncompressed_length,
643 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000644 return kInvalidOffset;
645 }
646
647 data->offset = data_offset;
648 return 0;
649}
650
651struct IterationHandle {
652 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100653 // We're not using vector here because this code is used in the Windows SDK
654 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700655 ZipString prefix;
656 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000657 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100658
Yusuke Sato07447542015-06-25 14:39:19 -0700659 IterationHandle(const ZipString* in_prefix,
660 const ZipString* in_suffix) {
661 if (in_prefix) {
662 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
663 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
664 prefix.name = name_copy;
665 prefix.name_length = in_prefix->name_length;
666 } else {
667 prefix.name = NULL;
668 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700669 }
Yusuke Sato07447542015-06-25 14:39:19 -0700670 if (in_suffix) {
671 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
672 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
673 suffix.name = name_copy;
674 suffix.name_length = in_suffix->name_length;
675 } else {
676 suffix.name = NULL;
677 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700678 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100679 }
680
681 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700682 delete[] prefix.name;
683 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100684 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000685};
686
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100687int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700688 const ZipString* optional_prefix,
689 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800690 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000691
692 if (archive == NULL || archive->hash_table == NULL) {
693 ALOGW("Zip: Invalid ZipArchiveHandle");
694 return kInvalidHandle;
695 }
696
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700697 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000698 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000699 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000700
701 *cookie_ptr = cookie ;
702 return 0;
703}
704
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100705void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100706 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100707}
708
Yusuke Sato07447542015-06-25 14:39:19 -0700709int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000710 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800711 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100712 if (entryName.name_length == 0) {
713 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000714 return kInvalidEntryName;
715 }
716
717 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100718 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000719
720 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100721 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000722 return ent;
723 }
724
725 return FindEntry(archive, ent, data);
726}
727
Yusuke Sato07447542015-06-25 14:39:19 -0700728int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800729 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000730 if (handle == NULL) {
731 return kInvalidHandle;
732 }
733
734 ZipArchive* archive = handle->archive;
735 if (archive == NULL || archive->hash_table == NULL) {
736 ALOGW("Zip: Invalid ZipArchiveHandle");
737 return kInvalidHandle;
738 }
739
740 const uint32_t currentOffset = handle->position;
741 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700742 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000743
744 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
745 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700746 (handle->prefix.name_length == 0 ||
747 hash_table[i].StartsWith(handle->prefix)) &&
748 (handle->suffix.name_length == 0 ||
749 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000750 handle->position = (i + 1);
751 const int error = FindEntry(archive, i, data);
752 if (!error) {
753 name->name = hash_table[i].name;
754 name->name_length = hash_table[i].name_length;
755 }
756
757 return error;
758 }
759 }
760
761 handle->position = 0;
762 return kIterationEnd;
763}
764
Narayan Kamathf899bd52015-04-17 11:53:14 +0100765class Writer {
766 public:
767 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
768 virtual ~Writer() {}
769 protected:
770 Writer() = default;
771 private:
772 DISALLOW_COPY_AND_ASSIGN(Writer);
773};
774
775// A Writer that writes data to a fixed size memory region.
776// The size of the memory region must be equal to the total size of
777// the data appended to it.
778class MemoryWriter : public Writer {
779 public:
780 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
781 buf_(buf), size_(size), bytes_written_(0) {
782 }
783
784 virtual bool Append(uint8_t* buf, size_t buf_size) override {
785 if (bytes_written_ + buf_size > size_) {
786 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
787 size_, bytes_written_ + buf_size);
788 return false;
789 }
790
791 memcpy(buf_ + bytes_written_, buf, buf_size);
792 bytes_written_ += buf_size;
793 return true;
794 }
795
796 private:
797 uint8_t* const buf_;
798 const size_t size_;
799 size_t bytes_written_;
800};
801
802// A Writer that appends data to a file |fd| at its current position.
803// The file will be truncated to the end of the written data.
804class FileWriter : public Writer {
805 public:
806
807 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
808 // guaranteeing that the file descriptor is valid and that there's enough
809 // space on the volume to write out the entry completely and that the file
Tao Baoa456c212016-11-15 10:08:07 -0800810 // is truncated to the correct length (no truncation if |fd| references a
811 // block device).
Narayan Kamathf899bd52015-04-17 11:53:14 +0100812 //
813 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
814 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
815 const uint32_t declared_length = entry->uncompressed_length;
816 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
817 if (current_offset == -1) {
818 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
819 return nullptr;
820 }
821
822 int result = 0;
823#if defined(__linux__)
824 if (declared_length > 0) {
825 // Make sure we have enough space on the volume to extract the compressed
826 // entry. Note that the call to ftruncate below will change the file size but
827 // will not allocate space on disk and this call to fallocate will not
828 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700829 // Note: fallocate is only supported by the following filesystems -
830 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
831 // EOPNOTSUPP error when issued in other filesystems.
832 // Hence, check for the return error code before concluding that the
833 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100834 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700835 if (result == -1 && errno == ENOSPC) {
Narayan Kamathd5d7abe2016-08-10 12:24:05 +0100836 ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 " : %s",
837 static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
838 strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100839 return std::unique_ptr<FileWriter>(nullptr);
840 }
841 }
842#endif // __linux__
843
Tao Baoa456c212016-11-15 10:08:07 -0800844 struct stat sb;
845 if (fstat(fd, &sb) == -1) {
846 ALOGW("Zip: unable to fstat file: %s", strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100847 return std::unique_ptr<FileWriter>(nullptr);
848 }
849
Tao Baoa456c212016-11-15 10:08:07 -0800850 // Block device doesn't support ftruncate(2).
851 if (!S_ISBLK(sb.st_mode)) {
852 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
853 if (result == -1) {
854 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
855 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
856 return std::unique_ptr<FileWriter>(nullptr);
857 }
858 }
859
Narayan Kamathf899bd52015-04-17 11:53:14 +0100860 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
861 }
862
863 virtual bool Append(uint8_t* buf, size_t buf_size) override {
864 if (total_bytes_written_ + buf_size > declared_length_) {
865 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
866 declared_length_, total_bytes_written_ + buf_size);
867 return false;
868 }
869
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100870 const bool result = android::base::WriteFully(fd_, buf, buf_size);
871 if (result) {
872 total_bytes_written_ += buf_size;
873 } else {
874 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100875 }
876
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100877 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100878 }
879 private:
880 FileWriter(const int fd, const size_t declared_length) :
881 Writer(),
882 fd_(fd),
883 declared_length_(declared_length),
884 total_bytes_written_(0) {
885 }
886
887 const int fd_;
888 const size_t declared_length_;
889 size_t total_bytes_written_;
890};
891
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800892// This method is using libz macros with old-style-casts
893#pragma GCC diagnostic push
894#pragma GCC diagnostic ignored "-Wold-style-cast"
895static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
896 return inflateInit2(stream, window_bits);
897}
898#pragma GCC diagnostic pop
899
Tianjie Xu18c25922016-09-29 15:27:41 -0700900static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100901 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700902 const size_t kBufSize = 32768;
903 std::vector<uint8_t> read_buf(kBufSize);
904 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000905 z_stream zstream;
906 int zerr;
907
908 /*
909 * Initialize the zlib stream struct.
910 */
911 memset(&zstream, 0, sizeof(zstream));
912 zstream.zalloc = Z_NULL;
913 zstream.zfree = Z_NULL;
914 zstream.opaque = Z_NULL;
915 zstream.next_in = NULL;
916 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700917 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000918 zstream.avail_out = kBufSize;
919 zstream.data_type = Z_UNKNOWN;
920
921 /*
922 * Use the undocumented "negative window bits" feature to tell zlib
923 * that there's no zlib header waiting for it.
924 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800925 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000926 if (zerr != Z_OK) {
927 if (zerr == Z_VERSION_ERROR) {
928 ALOGE("Installed zlib is not compatible with linked version (%s)",
929 ZLIB_VERSION);
930 } else {
931 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
932 }
933
934 return kZlibError;
935 }
936
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800937 auto zstream_deleter = [](z_stream* stream) {
938 inflateEnd(stream); /* free up any allocated structures */
939 };
940
941 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
942
Narayan Kamath7462f022013-11-21 13:05:04 +0000943 const uint32_t uncompressed_length = entry->uncompressed_length;
944
945 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000946 do {
947 /* read as much as we can */
948 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800949 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
Tianjie Xu18c25922016-09-29 15:27:41 -0700950 if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800951 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800952 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000953 }
954
955 compressed_length -= getSize;
956
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700957 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000958 zstream.avail_in = getSize;
959 }
960
961 /* uncompress the data */
962 zerr = inflate(&zstream, Z_NO_FLUSH);
963 if (zerr != Z_OK && zerr != Z_STREAM_END) {
964 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
965 zerr, zstream.next_in, zstream.avail_in,
966 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800967 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000968 }
969
970 /* write when we're full or when we're done */
971 if (zstream.avail_out == 0 ||
972 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700973 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100974 if (!writer->Append(&write_buf[0], write_size)) {
975 // The file might have declared a bogus length.
976 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000977 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000978
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700979 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000980 zstream.avail_out = kBufSize;
981 }
982 } while (zerr == Z_OK);
983
984 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
985
986 // stream.adler holds the crc32 value for such streams.
987 *crc_out = zstream.adler;
988
989 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700990 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000991 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800992 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000993 }
994
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800995 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000996}
997
Tianjie Xu18c25922016-09-29 15:27:41 -0700998static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100999 uint64_t *crc_out) {
1000 static const uint32_t kBufSize = 32768;
1001 std::vector<uint8_t> buf(kBufSize);
1002
1003 const uint32_t length = entry->uncompressed_length;
1004 uint32_t count = 0;
1005 uint64_t crc = 0;
1006 while (count < length) {
1007 uint32_t remaining = length - count;
1008
1009 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1010 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -08001011 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
Tianjie Xu18c25922016-09-29 15:27:41 -07001012 if (!mapped_zip.ReadData(buf.data(), block_size)) {
Yabin Cuib2a77002016-02-08 16:26:33 -08001013 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001014 return kIoError;
1015 }
1016
1017 if (!writer->Append(&buf[0], block_size)) {
1018 return kIoError;
1019 }
1020 crc = crc32(crc, &buf[0], block_size);
1021 count += block_size;
1022 }
1023
1024 *crc_out = crc;
1025
1026 return 0;
1027}
1028
1029int32_t ExtractToWriter(ZipArchiveHandle handle,
1030 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001031 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001032 const uint16_t method = entry->method;
1033 off64_t data_offset = entry->offset;
1034
Tianjie Xu18c25922016-09-29 15:27:41 -07001035 if (!archive->mapped_zip.SeekToOffset(data_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001036 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001037 return kIoError;
1038 }
1039
1040 // this should default to kUnknownCompressionMethod.
1041 int32_t return_value = -1;
1042 uint64_t crc = 0;
1043 if (method == kCompressStored) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001044 return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001045 } else if (method == kCompressDeflated) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001046 return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001047 }
1048
1049 if (!return_value && entry->has_data_descriptor) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001050 return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
Narayan Kamath7462f022013-11-21 13:05:04 +00001051 if (return_value) {
1052 return return_value;
1053 }
1054 }
1055
1056 // TODO: Fix this check by passing the right flags to inflate2 so that
1057 // it calculates the CRC for us.
1058 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001059 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001060 return kInconsistentInformation;
1061 }
1062
1063 return return_value;
1064}
1065
Narayan Kamathf899bd52015-04-17 11:53:14 +01001066int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1067 uint8_t* begin, uint32_t size) {
1068 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1069 return ExtractToWriter(handle, entry, writer.get());
1070}
1071
Narayan Kamath7462f022013-11-21 13:05:04 +00001072int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1073 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001074 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1075 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001076 return kIoError;
1077 }
1078
Narayan Kamathf899bd52015-04-17 11:53:14 +01001079 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001080}
1081
1082const char* ErrorCodeString(int32_t error_code) {
1083 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1084 return kErrorMessages[error_code * -1];
1085 }
1086
1087 return kErrorMessages[0];
1088}
1089
1090int GetFileDescriptor(const ZipArchiveHandle handle) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001091 return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
Narayan Kamath7462f022013-11-21 13:05:04 +00001092}
Colin Cross7c6c7f02016-09-16 10:15:51 -07001093
1094ZipString::ZipString(const char* entry_name)
1095 : name(reinterpret_cast<const uint8_t*>(entry_name)) {
1096 size_t len = strlen(entry_name);
1097 CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
1098 name_length = static_cast<uint16_t>(len);
1099}
Tianjie Xu18c25922016-09-29 15:27:41 -07001100
1101#if !defined(_WIN32)
1102class ProcessWriter : public Writer {
1103 public:
1104 ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
1105 proc_function_(func),
1106 cookie_(cookie) {
1107 }
1108
1109 virtual bool Append(uint8_t* buf, size_t buf_size) override {
1110 return proc_function_(buf, buf_size, cookie_);
1111 }
1112
1113 private:
1114 ProcessZipEntryFunction proc_function_;
1115 void* cookie_;
1116};
1117
1118int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
1119 ProcessZipEntryFunction func, void* cookie) {
1120 ProcessWriter writer(func, cookie);
1121 return ExtractToWriter(handle, entry, &writer);
1122}
1123
1124#endif //!defined(_WIN32)
1125
1126int MappedZipFile::GetFileDescriptor() const {
1127 if (!has_fd_) {
1128 ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
1129 return -1;
1130 }
1131 return fd_;
1132}
1133
1134void* MappedZipFile::GetBasePtr() const {
1135 if (has_fd_) {
1136 ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
1137 return nullptr;
1138 }
1139 return base_ptr_;
1140}
1141
1142off64_t MappedZipFile::GetFileLength() const {
1143 if (has_fd_) {
1144 off64_t result = lseek64(fd_, 0, SEEK_END);
1145 if (result == -1) {
1146 ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
1147 }
1148 return result;
1149 } else {
1150 if (base_ptr_ == nullptr) {
1151 ALOGE("Zip: invalid file map\n");
1152 return -1;
1153 }
1154 return static_cast<off64_t>(data_length_);
1155 }
1156}
1157
1158bool MappedZipFile::SeekToOffset(off64_t offset) {
1159 if (has_fd_) {
1160 if (lseek64(fd_, offset, SEEK_SET) != offset) {
1161 ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
1162 return false;
1163 }
1164 return true;
1165 } else {
1166 if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
1167 ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
1168 data_length_);
1169 return false;
1170 }
1171
1172 read_pos_ = offset;
1173 return true;
1174 }
1175}
1176
1177bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
1178 if (has_fd_) {
1179 if(!android::base::ReadFully(fd_, buffer, read_amount)) {
1180 ALOGE("Zip: read from %d failed\n", fd_);
1181 return false;
1182 }
1183 } else {
1184 memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
1185 read_pos_ += read_amount;
1186 }
1187 return true;
1188}
1189
1190// Attempts to read |len| bytes into |buf| at offset |off|.
1191bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
1192#if !defined(_WIN32)
1193 if (has_fd_) {
1194 if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
1195 ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
1196 return false;
1197 }
1198 return true;
1199 }
1200#endif
1201 if (!SeekToOffset(off)) {
1202 return false;
1203 }
1204 return ReadData(buf, len);
1205
1206}
1207
1208void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
1209 base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
1210 length_ = cd_size;
1211}
1212
1213bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
1214 size_t cd_size) {
1215 if (mapped_zip.HasFd()) {
1216 if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
1217 cd_start_offset, cd_size, true /* read only */)) {
1218 return false;
1219 }
1220
1221 CHECK_EQ(directory_map->getDataLength(), cd_size);
1222 central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
1223 } else {
1224 if (mapped_zip.GetBasePtr() == nullptr) {
1225 ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
1226 return false;
1227 }
1228 if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
1229 mapped_zip.GetFileLength()) {
1230 ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
1231 "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
1232 static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
1233 return false;
1234 }
1235
1236 central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
1237 }
1238 return true;
1239}