blob: 89cfe77bc4d8b3f38187efa9379b32e6c46592a6 [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
Elliott Hughes4f713192015-12-04 22:00:26 -080033#include "android-base/file.h"
34#include "android-base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
35#include "android-base/memory.h"
Dan Albert1ae07642015-04-09 14:11:18 -070036#include "log/log.h"
37#include "utils/Compat.h"
38#include "utils/FileMap.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080039#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070040#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000041
Narayan Kamath044bc8e2014-12-03 18:22:53 +000042#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070043#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080044#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070045
Dan Albert1ae07642015-04-09 14:11:18 -070046using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000047
Narayan Kamath926973e2014-06-09 14:18:14 +010048// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000049// things will happen.
50#ifndef O_BINARY
51#define O_BINARY 0
52#endif
53
Narayan Kamath926973e2014-06-09 14:18:14 +010054// The maximum number of bytes to scan backwards for the EOCD start.
55static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
56
Narayan Kamath7462f022013-11-21 13:05:04 +000057static const char* kErrorMessages[] = {
58 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000059 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000060 "Zlib error",
61 "Invalid file",
62 "Invalid handle",
63 "Duplicate entries in archive",
64 "Empty archive",
65 "Entry not found",
66 "Invalid offset",
67 "Inconsistent information",
68 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000069 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000070 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000071};
72
73static const int32_t kErrorMessageUpperBound = 0;
74
Narayan Kamatheb41ad22013-12-09 16:26:36 +000075static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000076
77// We encountered a Zlib error when inflating a stream from this file.
78// Usually indicates file corruption.
79static const int32_t kZlibError = -2;
80
81// The input file cannot be processed as a zip archive. Usually because
82// it's too small, too large or does not have a valid signature.
83static const int32_t kInvalidFile = -3;
84
85// An invalid iteration / ziparchive handle was passed in as an input
86// argument.
87static const int32_t kInvalidHandle = -4;
88
89// The zip archive contained two (or possibly more) entries with the same
90// name.
91static const int32_t kDuplicateEntry = -5;
92
93// The zip archive contains no entries.
94static const int32_t kEmptyArchive = -6;
95
96// The specified entry was not found in the archive.
97static const int32_t kEntryNotFound = -7;
98
99// The zip archive contained an invalid local file header pointer.
100static const int32_t kInvalidOffset = -8;
101
102// The zip archive contained inconsistent entry information. This could
103// be because the central directory & local file header did not agree, or
104// if the actual uncompressed length or crc32 do not match their declared
105// values.
106static const int32_t kInconsistentInformation = -9;
107
108// An invalid entry name was encountered.
109static const int32_t kInvalidEntryName = -10;
110
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000111// An I/O related system call (read, lseek, ftruncate, map) failed.
112static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000113
Narayan Kamatheaf98852013-12-11 14:51:51 +0000114// We were not able to mmap the central directory or entry contents.
115static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000116
Narayan Kamatheaf98852013-12-11 14:51:51 +0000117static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000118
Narayan Kamath7462f022013-11-21 13:05:04 +0000119/*
120 * A Read-only Zip archive.
121 *
122 * We want "open" and "find entry by name" to be fast operations, and
123 * we want to use as little memory as possible. We memory-map the zip
124 * central directory, and load a hash table with pointers to the filenames
125 * (which aren't null-terminated). The other fields are at a fixed offset
126 * from the filename, so we don't need to extract those (but we do need
127 * to byte-read and endian-swap them every time we want them).
128 *
129 * It's possible that somebody has handed us a massive (~1GB) zip archive,
130 * so we can't expect to mmap the entire file.
131 *
132 * To speed comparisons when doing a lookup by name, we could make the mapping
133 * "private" (copy-on-write) and null-terminate the filenames after verifying
134 * the record structure. However, this requires a private mapping of
135 * every page that the Central Directory touches. Easier to tuck a copy
136 * of the string length into the hash table entry.
137 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000138
Narayan Kamath7462f022013-11-21 13:05:04 +0000139/*
140 * Round up to the next highest power of 2.
141 *
142 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
143 */
144static uint32_t RoundUpPower2(uint32_t val) {
145 val--;
146 val |= val >> 1;
147 val |= val >> 2;
148 val |= val >> 4;
149 val |= val >> 8;
150 val |= val >> 16;
151 val++;
152
153 return val;
154}
155
Yusuke Sato07447542015-06-25 14:39:19 -0700156static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000157 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100158 uint16_t len = name.name_length;
159 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000160
161 while (len--) {
162 hash = hash * 31 + *str++;
163 }
164
165 return hash;
166}
167
168/*
169 * Convert a ZipEntry to a hash table index, verifying that it's in a
170 * valid range.
171 */
Yusuke Sato07447542015-06-25 14:39:19 -0700172static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000173 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700174 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100175 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000176
177 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
178 uint32_t ent = hash & (hash_table_size - 1);
179 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700180 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000181 return ent;
182 }
183
184 ent = (ent + 1) & (hash_table_size - 1);
185 }
186
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100187 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000188 return kEntryNotFound;
189}
190
191/*
192 * Add a new entry to the hash table.
193 */
Yusuke Sato07447542015-06-25 14:39:19 -0700194static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
195 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100196 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000197 uint32_t ent = hash & (hash_table_size - 1);
198
199 /*
200 * We over-allocated the table, so we're guaranteed to find an empty slot.
201 * Further, we guarantee that the hashtable size is not 0.
202 */
203 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700204 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000205 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100206 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000207 return kDuplicateEntry;
208 }
209 ent = (ent + 1) & (hash_table_size - 1);
210 }
211
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100212 hash_table[ent].name = name.name;
213 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000214 return 0;
215}
216
Narayan Kamath7462f022013-11-21 13:05:04 +0000217static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
218 ZipArchive* archive, off64_t file_length,
Narayan Kamath926973e2014-06-09 14:18:14 +0100219 off64_t read_amount, uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000220 const off64_t search_start = file_length - read_amount;
221
222 if (lseek64(fd, search_start, SEEK_SET) != search_start) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100223 ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
224 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000225 return kIoError;
226 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800227 if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100228 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
229 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000230 return kIoError;
231 }
232
233 /*
234 * Scan backward for the EOCD magic. In an archive without a trailing
235 * comment, we'll find it on the first try. (We may want to consider
236 * doing an initial minimal read; if we don't find it, retry with a
237 * second read as above.)
238 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100239 int i = read_amount - sizeof(EocdRecord);
240 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700241 if (scan_buffer[i] == 0x50) {
242 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
243 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
244 ALOGV("+++ Found EOCD at buf+%d", i);
245 break;
246 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000247 }
248 }
249 if (i < 0) {
250 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
251 return kInvalidFile;
252 }
253
254 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100255 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000256 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100257 * Verify that there's no trailing space at the end of the central directory
258 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000259 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100260 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
261 + eocd->comment_length;
262 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100263 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100264 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100265 return kInvalidFile;
266 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000267
Narayan Kamath926973e2014-06-09 14:18:14 +0100268 /*
269 * Grab the CD offset and size, and the number of entries in the
270 * archive and verify that they look reasonable.
271 */
Tianjie Xu1ee48922016-09-21 14:58:11 -0700272 if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100273 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
274 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Tianjie Xu1ee48922016-09-21 14:58:11 -0700275#if defined(__ANDROID__)
276 if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
277 android_errorWriteLog(0x534e4554, "31251826");
278 }
279#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000280 return kInvalidOffset;
281 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100282 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000283 ALOGW("Zip: empty archive?");
284 return kEmptyArchive;
285 }
286
Elliott Hughese49236b2015-06-04 15:21:59 -0700287 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100288 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000289
290 /*
291 * It all looks good. Create a mapping for the CD, and set the fields
292 * in archive.
293 */
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800294 if (!archive->directory_map.create(debug_file_name, fd,
295 static_cast<off64_t>(eocd->cd_start_offset),
296 static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
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
311 * directory_map
312 * num_entries
313 */
314static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
315 ZipArchive* archive) {
316
317 // Test file length. We use lseek64 to make sure the file
318 // is small enough to be a zip file (Its size must be less than
319 // 0xffffffff bytes).
320 off64_t file_length = lseek64(fd, 0, SEEK_END);
321 if (file_length == -1) {
322 ALOGV("Zip: lseek on fd %d failed", fd);
323 return kInvalidFile;
324 }
325
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800326 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100327 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000328 return kInvalidFile;
329 }
330
Narayan Kamath926973e2014-06-09 14:18:14 +0100331 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
332 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000333 return kInvalidFile;
334 }
335
336 /*
337 * Perform the traditional EOCD snipe hunt.
338 *
339 * We're searching for the End of Central Directory magic number,
340 * which appears at the start of the EOCD block. It's followed by
341 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
342 * need to read the last part of the file into a buffer, dig through
343 * it to find the magic number, parse some values out, and use those
344 * to determine the extent of the CD.
345 *
346 * We start by pulling in the last part of the file.
347 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100348 off64_t read_amount = kMaxEOCDSearch;
349 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000350 read_amount = file_length;
351 }
352
Narayan Kamath926973e2014-06-09 14:18:14 +0100353 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
Narayan Kamath7462f022013-11-21 13:05:04 +0000354 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
355 file_length, read_amount, scan_buffer);
356
357 free(scan_buffer);
358 return result;
359}
360
Narayan Kamath2740bb02017-08-09 18:32:09 +0100361static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off);
362
Narayan Kamath7462f022013-11-21 13:05:04 +0000363/*
364 * Parses the Zip archive's Central Directory. Allocates and populates the
365 * hash table.
366 *
367 * Returns 0 on success.
368 */
369static int32_t ParseZipArchive(ZipArchive* archive) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800370 const uint8_t* const cd_ptr =
371 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800372 const size_t cd_length = archive->directory_map.getDataLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100373 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000374
375 /*
376 * Create hash table. We have a minimum 75% load factor, possibly as
377 * low as 50% after we round off to a power of 2. There must be at
378 * least one unused entry to avoid an infinite loop during creation.
379 */
380 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700381 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
382 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000383
384 /*
385 * Walk through the central directory, adding entries to the hash
386 * table and verifying values.
387 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100388 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000389 const uint8_t* ptr = cd_ptr;
390 for (uint16_t i = 0; i < num_entries; i++) {
Tianjie Xud9fd1862017-04-05 14:46:27 -0700391 if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
392 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
393#if defined(__ANDROID__)
394 android_errorWriteLog(0x534e4554, "36392138");
395#endif
396 return -1;
397 }
398
Narayan Kamath926973e2014-06-09 14:18:14 +0100399 const CentralDirectoryRecord* cdr =
400 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
401 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700402 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800403 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000404 }
405
Narayan Kamath926973e2014-06-09 14:18:14 +0100406 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000407 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800408 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
409 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800410 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000411 }
412
Narayan Kamath926973e2014-06-09 14:18:14 +0100413 const uint16_t file_name_length = cdr->file_name_length;
414 const uint16_t extra_length = cdr->extra_field_length;
415 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100416 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
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 }
Narayan Kamath2740bb02017-08-09 18:32:09 +0100441
442 uint32_t lfh_start_bytes;
443 if (!ReadAtOffset(archive->fd, reinterpret_cast<uint8_t*>(&lfh_start_bytes),
444 sizeof(uint32_t), 0)) {
445 ALOGW("Zip: Unable to read header for entry at offset == 0.");
446 return -1;
447 }
448
449 if (lfh_start_bytes != LocalFileHeader::kSignature) {
450 ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
451#if defined(__ANDROID__)
452 android_errorWriteLog(0x534e4554, "64211847");
453#endif
454 return -1;
455 }
456
Mark Salyzyn088bf902014-05-08 16:02:20 -0700457 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000458
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800459 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000460}
461
462static int32_t OpenArchiveInternal(ZipArchive* archive,
463 const char* debug_file_name) {
464 int32_t result = -1;
465 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
466 return result;
467 }
468
469 if ((result = ParseZipArchive(archive))) {
470 return result;
471 }
472
473 return 0;
474}
475
476int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700477 ZipArchiveHandle* handle, bool assume_ownership) {
478 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000479 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000480 return OpenArchiveInternal(archive, debug_file_name);
481}
482
483int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100484 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700485 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000486 *handle = archive;
487
Narayan Kamath7462f022013-11-21 13:05:04 +0000488 if (fd < 0) {
489 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
490 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000491 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700492
Narayan Kamath7462f022013-11-21 13:05:04 +0000493 return OpenArchiveInternal(archive, fileName);
494}
495
496/*
497 * Close a ZipArchive, closing the file and freeing the contents.
498 */
499void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800500 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000501 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100502 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000503}
504
505static int32_t UpdateEntryFromDataDescriptor(int fd,
506 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100507 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800508 if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000509 return kIoError;
510 }
511
Narayan Kamath926973e2014-06-09 14:18:14 +0100512 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
513 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
514 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000515
Narayan Kamath926973e2014-06-09 14:18:14 +0100516 entry->crc32 = descriptor->crc32;
517 entry->compressed_length = descriptor->compressed_size;
518 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000519
520 return 0;
521}
522
523// Attempts to read |len| bytes into |buf| at offset |off|.
Adam Lesinskib1911402016-03-09 17:13:09 -0800524// On non-Windows platforms, callers are guaranteed that the |fd|
525// offset is unchanged and there is no side effect to this call.
526//
527// On Windows platforms this is not thread-safe.
Yabin Cuib2a77002016-02-08 16:26:33 -0800528static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
Adam Lesinskib1911402016-03-09 17:13:09 -0800529#if !defined(_WIN32)
530 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
531#else
Narayan Kamath7462f022013-11-21 13:05:04 +0000532 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700533 ALOGW("Zip: failed seek to offset %" PRId64, off);
Yabin Cuib2a77002016-02-08 16:26:33 -0800534 return false;
Narayan Kamath7462f022013-11-21 13:05:04 +0000535 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800536 return android::base::ReadFully(fd, buf, len);
Adam Lesinskib1911402016-03-09 17:13:09 -0800537#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000538}
539
540static int32_t FindEntry(const ZipArchive* archive, const int ent,
541 ZipEntry* data) {
542 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000543
544 // Recover the start of the central directory entry from the filename
545 // pointer. The filename is the first entry past the fixed-size data,
546 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100547 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100548 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000549
550 // This is the base of our mmapped region, we have to sanity check that
551 // the name that's in the hash table is a pointer to a location within
552 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100553 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800554 archive->directory_map.getDataPtr());
555 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000556 ALOGW("Zip: Invalid entry pointer");
557 return kInvalidOffset;
558 }
559
Narayan Kamath926973e2014-06-09 14:18:14 +0100560 const CentralDirectoryRecord *cdr =
561 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
562
Narayan Kamath7462f022013-11-21 13:05:04 +0000563 // The offset of the start of the central directory in the zipfile.
564 // We keep this lying around so that we can sanity check all our lengths
565 // and our per-file structures.
566 const off64_t cd_offset = archive->directory_offset;
567
568 // Fill out the compression method, modification time, crc32
569 // and other interesting attributes from the central directory. These
570 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100571 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900572 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100573 data->crc32 = cdr->crc32;
574 data->compressed_length = cdr->compressed_size;
575 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000576
577 // Figure out the local header offset from the central directory. The
578 // actual file data will begin after the local header and the name /
579 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100580 const off64_t local_header_offset = cdr->local_file_header_offset;
581 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000582 ALOGW("Zip: bad local hdr offset in zip");
583 return kInvalidOffset;
584 }
585
Narayan Kamath926973e2014-06-09 14:18:14 +0100586 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800587 if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800588 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
589 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000590 return kIoError;
591 }
592
Narayan Kamath926973e2014-06-09 14:18:14 +0100593 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
594
595 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700596 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100597 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000598 return kInvalidOffset;
599 }
600
601 // Paranoia: Match the values specified in the local file header
602 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100603 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000604 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100605 if (data->compressed_length != lfh->compressed_size
606 || data->uncompressed_length != lfh->uncompressed_size
607 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700608 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
609 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000610 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100611 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000612 return kInconsistentInformation;
613 }
614 } else {
615 data->has_data_descriptor = 1;
616 }
617
618 // Check that the local file header name matches the declared
619 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100620 if (lfh->file_name_length == nameLen) {
621 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200622 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000623 ALOGW("Zip: Invalid declared length");
624 return kInvalidOffset;
625 }
626
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800627 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Yabin Cuib2a77002016-02-08 16:26:33 -0800628 if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800629 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000630 free(name_buf);
631 return kIoError;
632 }
633
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100634 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000635 free(name_buf);
636 return kInconsistentInformation;
637 }
638
639 free(name_buf);
640 } else {
641 ALOGW("Zip: lfh name did not match central directory.");
642 return kInconsistentInformation;
643 }
644
Narayan Kamath926973e2014-06-09 14:18:14 +0100645 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
646 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000647 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800648 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000649 return kInvalidOffset;
650 }
651
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800652 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700653 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800654 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000655 return kInvalidOffset;
656 }
657
658 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800659 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700660 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800661 static_cast<int64_t>(data_offset), data->uncompressed_length,
662 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000663 return kInvalidOffset;
664 }
665
666 data->offset = data_offset;
667 return 0;
668}
669
670struct IterationHandle {
671 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100672 // We're not using vector here because this code is used in the Windows SDK
673 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700674 ZipString prefix;
675 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000676 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100677
Yusuke Sato07447542015-06-25 14:39:19 -0700678 IterationHandle(const ZipString* in_prefix,
679 const ZipString* in_suffix) {
680 if (in_prefix) {
681 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
682 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
683 prefix.name = name_copy;
684 prefix.name_length = in_prefix->name_length;
685 } else {
686 prefix.name = NULL;
687 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700688 }
Yusuke Sato07447542015-06-25 14:39:19 -0700689 if (in_suffix) {
690 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
691 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
692 suffix.name = name_copy;
693 suffix.name_length = in_suffix->name_length;
694 } else {
695 suffix.name = NULL;
696 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700697 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100698 }
699
700 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700701 delete[] prefix.name;
702 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100703 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000704};
705
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100706int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700707 const ZipString* optional_prefix,
708 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800709 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000710
711 if (archive == NULL || archive->hash_table == NULL) {
712 ALOGW("Zip: Invalid ZipArchiveHandle");
713 return kInvalidHandle;
714 }
715
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700716 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000717 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000718 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000719
720 *cookie_ptr = cookie ;
721 return 0;
722}
723
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100724void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100725 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100726}
727
Yusuke Sato07447542015-06-25 14:39:19 -0700728int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000729 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800730 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100731 if (entryName.name_length == 0) {
732 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000733 return kInvalidEntryName;
734 }
735
736 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100737 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000738
739 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100740 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000741 return ent;
742 }
743
744 return FindEntry(archive, ent, data);
745}
746
Yusuke Sato07447542015-06-25 14:39:19 -0700747int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800748 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000749 if (handle == NULL) {
750 return kInvalidHandle;
751 }
752
753 ZipArchive* archive = handle->archive;
754 if (archive == NULL || archive->hash_table == NULL) {
755 ALOGW("Zip: Invalid ZipArchiveHandle");
756 return kInvalidHandle;
757 }
758
759 const uint32_t currentOffset = handle->position;
760 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700761 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000762
763 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
764 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700765 (handle->prefix.name_length == 0 ||
766 hash_table[i].StartsWith(handle->prefix)) &&
767 (handle->suffix.name_length == 0 ||
768 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000769 handle->position = (i + 1);
770 const int error = FindEntry(archive, i, data);
771 if (!error) {
772 name->name = hash_table[i].name;
773 name->name_length = hash_table[i].name_length;
774 }
775
776 return error;
777 }
778 }
779
780 handle->position = 0;
781 return kIterationEnd;
782}
783
Narayan Kamathf899bd52015-04-17 11:53:14 +0100784class Writer {
785 public:
786 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
787 virtual ~Writer() {}
788 protected:
789 Writer() = default;
790 private:
791 DISALLOW_COPY_AND_ASSIGN(Writer);
792};
793
794// A Writer that writes data to a fixed size memory region.
795// The size of the memory region must be equal to the total size of
796// the data appended to it.
797class MemoryWriter : public Writer {
798 public:
799 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
800 buf_(buf), size_(size), bytes_written_(0) {
801 }
802
803 virtual bool Append(uint8_t* buf, size_t buf_size) override {
804 if (bytes_written_ + buf_size > size_) {
805 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
806 size_, bytes_written_ + buf_size);
807 return false;
808 }
809
810 memcpy(buf_ + bytes_written_, buf, buf_size);
811 bytes_written_ += buf_size;
812 return true;
813 }
814
815 private:
816 uint8_t* const buf_;
817 const size_t size_;
818 size_t bytes_written_;
819};
820
821// A Writer that appends data to a file |fd| at its current position.
822// The file will be truncated to the end of the written data.
823class FileWriter : public Writer {
824 public:
825
826 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
827 // guaranteeing that the file descriptor is valid and that there's enough
828 // space on the volume to write out the entry completely and that the file
829 // is truncated to the correct length.
830 //
831 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
832 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
833 const uint32_t declared_length = entry->uncompressed_length;
834 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
835 if (current_offset == -1) {
836 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
837 return nullptr;
838 }
839
840 int result = 0;
841#if defined(__linux__)
842 if (declared_length > 0) {
843 // Make sure we have enough space on the volume to extract the compressed
844 // entry. Note that the call to ftruncate below will change the file size but
845 // will not allocate space on disk and this call to fallocate will not
846 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700847 // Note: fallocate is only supported by the following filesystems -
848 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
849 // EOPNOTSUPP error when issued in other filesystems.
850 // Hence, check for the return error code before concluding that the
851 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100852 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700853 if (result == -1 && errno == ENOSPC) {
Narayan Kamathf899bd52015-04-17 11:53:14 +0100854 ALOGW("Zip: unable to allocate space for 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#endif // __linux__
860
861 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
862 if (result == -1) {
863 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
864 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
865 return std::unique_ptr<FileWriter>(nullptr);
866 }
867
868 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
869 }
870
871 virtual bool Append(uint8_t* buf, size_t buf_size) override {
872 if (total_bytes_written_ + buf_size > declared_length_) {
873 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
874 declared_length_, total_bytes_written_ + buf_size);
875 return false;
876 }
877
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100878 const bool result = android::base::WriteFully(fd_, buf, buf_size);
879 if (result) {
880 total_bytes_written_ += buf_size;
881 } else {
882 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100883 }
884
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100885 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100886 }
887 private:
888 FileWriter(const int fd, const size_t declared_length) :
889 Writer(),
890 fd_(fd),
891 declared_length_(declared_length),
892 total_bytes_written_(0) {
893 }
894
895 const int fd_;
896 const size_t declared_length_;
897 size_t total_bytes_written_;
898};
899
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800900// This method is using libz macros with old-style-casts
901#pragma GCC diagnostic push
902#pragma GCC diagnostic ignored "-Wold-style-cast"
903static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
904 return inflateInit2(stream, window_bits);
905}
906#pragma GCC diagnostic pop
907
Narayan Kamathf899bd52015-04-17 11:53:14 +0100908static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
909 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700910 const size_t kBufSize = 32768;
911 std::vector<uint8_t> read_buf(kBufSize);
912 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000913 z_stream zstream;
914 int zerr;
915
916 /*
917 * Initialize the zlib stream struct.
918 */
919 memset(&zstream, 0, sizeof(zstream));
920 zstream.zalloc = Z_NULL;
921 zstream.zfree = Z_NULL;
922 zstream.opaque = Z_NULL;
923 zstream.next_in = NULL;
924 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700925 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000926 zstream.avail_out = kBufSize;
927 zstream.data_type = Z_UNKNOWN;
928
929 /*
930 * Use the undocumented "negative window bits" feature to tell zlib
931 * that there's no zlib header waiting for it.
932 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800933 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000934 if (zerr != Z_OK) {
935 if (zerr == Z_VERSION_ERROR) {
936 ALOGE("Installed zlib is not compatible with linked version (%s)",
937 ZLIB_VERSION);
938 } else {
939 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
940 }
941
942 return kZlibError;
943 }
944
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800945 auto zstream_deleter = [](z_stream* stream) {
946 inflateEnd(stream); /* free up any allocated structures */
947 };
948
949 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
950
Narayan Kamath7462f022013-11-21 13:05:04 +0000951 const uint32_t uncompressed_length = entry->uncompressed_length;
952
953 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000954 do {
955 /* read as much as we can */
956 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800957 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
958 if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
959 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800960 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000961 }
962
963 compressed_length -= getSize;
964
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700965 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000966 zstream.avail_in = getSize;
967 }
968
969 /* uncompress the data */
970 zerr = inflate(&zstream, Z_NO_FLUSH);
971 if (zerr != Z_OK && zerr != Z_STREAM_END) {
972 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
973 zerr, zstream.next_in, zstream.avail_in,
974 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800975 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000976 }
977
978 /* write when we're full or when we're done */
979 if (zstream.avail_out == 0 ||
980 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700981 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100982 if (!writer->Append(&write_buf[0], write_size)) {
983 // The file might have declared a bogus length.
984 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000985 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000986
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700987 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000988 zstream.avail_out = kBufSize;
989 }
990 } while (zerr == Z_OK);
991
992 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
993
994 // stream.adler holds the crc32 value for such streams.
995 *crc_out = zstream.adler;
996
997 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700998 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000999 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -08001000 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +00001001 }
1002
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -08001003 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +00001004}
1005
Narayan Kamathf899bd52015-04-17 11:53:14 +01001006static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
1007 uint64_t *crc_out) {
1008 static const uint32_t kBufSize = 32768;
1009 std::vector<uint8_t> buf(kBufSize);
1010
1011 const uint32_t length = entry->uncompressed_length;
1012 uint32_t count = 0;
1013 uint64_t crc = 0;
1014 while (count < length) {
1015 uint32_t remaining = length - count;
1016
1017 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1018 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -08001019 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
1020 if (!android::base::ReadFully(fd, buf.data(), block_size)) {
1021 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001022 return kIoError;
1023 }
1024
1025 if (!writer->Append(&buf[0], block_size)) {
1026 return kIoError;
1027 }
1028 crc = crc32(crc, &buf[0], block_size);
1029 count += block_size;
1030 }
1031
1032 *crc_out = crc;
1033
1034 return 0;
1035}
1036
1037int32_t ExtractToWriter(ZipArchiveHandle handle,
1038 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001039 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001040 const uint16_t method = entry->method;
1041 off64_t data_offset = entry->offset;
1042
1043 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001044 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001045 return kIoError;
1046 }
1047
1048 // this should default to kUnknownCompressionMethod.
1049 int32_t return_value = -1;
1050 uint64_t crc = 0;
1051 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001052 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001053 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001054 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001055 }
1056
1057 if (!return_value && entry->has_data_descriptor) {
1058 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1059 if (return_value) {
1060 return return_value;
1061 }
1062 }
1063
1064 // TODO: Fix this check by passing the right flags to inflate2 so that
1065 // it calculates the CRC for us.
1066 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001067 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001068 return kInconsistentInformation;
1069 }
1070
1071 return return_value;
1072}
1073
Narayan Kamathf899bd52015-04-17 11:53:14 +01001074int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1075 uint8_t* begin, uint32_t size) {
1076 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1077 return ExtractToWriter(handle, entry, writer.get());
1078}
1079
Narayan Kamath7462f022013-11-21 13:05:04 +00001080int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1081 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001082 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1083 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001084 return kIoError;
1085 }
1086
Narayan Kamathf899bd52015-04-17 11:53:14 +01001087 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001088}
1089
1090const char* ErrorCodeString(int32_t error_code) {
1091 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1092 return kErrorMessages[error_code * -1];
1093 }
1094
1095 return kErrorMessages[0];
1096}
1097
1098int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001099 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001100}