blob: 3b1e972ce74deaf56da7f4c770f50a725e07bb21 [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 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100227 ssize_t actual = TEMP_FAILURE_RETRY(
228 read(fd, scan_buffer, static_cast<size_t>(read_amount)));
229 if (actual != static_cast<ssize_t>(read_amount)) {
230 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
231 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000232 return kIoError;
233 }
234
235 /*
236 * Scan backward for the EOCD magic. In an archive without a trailing
237 * comment, we'll find it on the first try. (We may want to consider
238 * doing an initial minimal read; if we don't find it, retry with a
239 * second read as above.)
240 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100241 int i = read_amount - sizeof(EocdRecord);
242 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700243 if (scan_buffer[i] == 0x50) {
244 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
245 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
246 ALOGV("+++ Found EOCD at buf+%d", i);
247 break;
248 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000249 }
250 }
251 if (i < 0) {
252 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
253 return kInvalidFile;
254 }
255
256 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100257 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000258 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100259 * Verify that there's no trailing space at the end of the central directory
260 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000261 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100262 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
263 + eocd->comment_length;
264 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100265 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100266 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100267 return kInvalidFile;
268 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000269
Narayan Kamath926973e2014-06-09 14:18:14 +0100270 /*
271 * Grab the CD offset and size, and the number of entries in the
272 * archive and verify that they look reasonable.
273 */
274 if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) {
275 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
276 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000277 return kInvalidOffset;
278 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100279 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000280 ALOGW("Zip: empty archive?");
281 return kEmptyArchive;
282 }
283
Elliott Hughese49236b2015-06-04 15:21:59 -0700284 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100285 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000286
287 /*
288 * It all looks good. Create a mapping for the CD, and set the fields
289 * in archive.
290 */
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800291 if (!archive->directory_map.create(debug_file_name, fd,
292 static_cast<off64_t>(eocd->cd_start_offset),
293 static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
Narayan Kamatheaf98852013-12-11 14:51:51 +0000294 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000295 }
296
Narayan Kamath926973e2014-06-09 14:18:14 +0100297 archive->num_entries = eocd->num_records;
298 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000299
300 return 0;
301}
302
303/*
304 * Find the zip Central Directory and memory-map it.
305 *
306 * On success, returns 0 after populating fields from the EOCD area:
307 * directory_offset
308 * directory_map
309 * num_entries
310 */
311static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
312 ZipArchive* archive) {
313
314 // Test file length. We use lseek64 to make sure the file
315 // is small enough to be a zip file (Its size must be less than
316 // 0xffffffff bytes).
317 off64_t file_length = lseek64(fd, 0, SEEK_END);
318 if (file_length == -1) {
319 ALOGV("Zip: lseek on fd %d failed", fd);
320 return kInvalidFile;
321 }
322
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800323 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100324 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000325 return kInvalidFile;
326 }
327
Narayan Kamath926973e2014-06-09 14:18:14 +0100328 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
329 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000330 return kInvalidFile;
331 }
332
333 /*
334 * Perform the traditional EOCD snipe hunt.
335 *
336 * We're searching for the End of Central Directory magic number,
337 * which appears at the start of the EOCD block. It's followed by
338 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
339 * need to read the last part of the file into a buffer, dig through
340 * it to find the magic number, parse some values out, and use those
341 * to determine the extent of the CD.
342 *
343 * We start by pulling in the last part of the file.
344 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100345 off64_t read_amount = kMaxEOCDSearch;
346 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000347 read_amount = file_length;
348 }
349
Narayan Kamath926973e2014-06-09 14:18:14 +0100350 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
Narayan Kamath7462f022013-11-21 13:05:04 +0000351 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
352 file_length, read_amount, scan_buffer);
353
354 free(scan_buffer);
355 return result;
356}
357
358/*
359 * Parses the Zip archive's Central Directory. Allocates and populates the
360 * hash table.
361 *
362 * Returns 0 on success.
363 */
364static int32_t ParseZipArchive(ZipArchive* archive) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800365 const uint8_t* const cd_ptr =
366 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800367 const size_t cd_length = archive->directory_map.getDataLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100368 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000369
370 /*
371 * Create hash table. We have a minimum 75% load factor, possibly as
372 * low as 50% after we round off to a power of 2. There must be at
373 * least one unused entry to avoid an infinite loop during creation.
374 */
375 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700376 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
377 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000378
379 /*
380 * Walk through the central directory, adding entries to the hash
381 * table and verifying values.
382 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100383 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000384 const uint8_t* ptr = cd_ptr;
385 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100386 const CentralDirectoryRecord* cdr =
387 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
388 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700389 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800390 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000391 }
392
Narayan Kamath926973e2014-06-09 14:18:14 +0100393 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700394 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800395 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000396 }
397
Narayan Kamath926973e2014-06-09 14:18:14 +0100398 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000399 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800400 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
401 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800402 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000403 }
404
Narayan Kamath926973e2014-06-09 14:18:14 +0100405 const uint16_t file_name_length = cdr->file_name_length;
406 const uint16_t extra_length = cdr->extra_field_length;
407 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100408 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
409
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000410 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
411 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800412 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100413 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000414
415 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700416 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100417 entry_name.name = file_name;
418 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000419 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100420 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800421 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000422 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800423 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000424 }
425
Narayan Kamath926973e2014-06-09 14:18:14 +0100426 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
427 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700428 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
429 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800430 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000431 }
432 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700433 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000434
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800435 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000436}
437
438static int32_t OpenArchiveInternal(ZipArchive* archive,
439 const char* debug_file_name) {
440 int32_t result = -1;
441 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
442 return result;
443 }
444
445 if ((result = ParseZipArchive(archive))) {
446 return result;
447 }
448
449 return 0;
450}
451
452int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700453 ZipArchiveHandle* handle, bool assume_ownership) {
454 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000455 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000456 return OpenArchiveInternal(archive, debug_file_name);
457}
458
459int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100460 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700461 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000462 *handle = archive;
463
Narayan Kamath7462f022013-11-21 13:05:04 +0000464 if (fd < 0) {
465 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
466 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000467 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700468
Narayan Kamath7462f022013-11-21 13:05:04 +0000469 return OpenArchiveInternal(archive, fileName);
470}
471
472/*
473 * Close a ZipArchive, closing the file and freeing the contents.
474 */
475void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800476 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000477 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100478 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000479}
480
481static int32_t UpdateEntryFromDataDescriptor(int fd,
482 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100483 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Narayan Kamath7462f022013-11-21 13:05:04 +0000484 ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
485 if (actual != sizeof(ddBuf)) {
486 return kIoError;
487 }
488
Narayan Kamath926973e2014-06-09 14:18:14 +0100489 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
490 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
491 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000492
Narayan Kamath926973e2014-06-09 14:18:14 +0100493 entry->crc32 = descriptor->crc32;
494 entry->compressed_length = descriptor->compressed_size;
495 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000496
497 return 0;
498}
499
500// Attempts to read |len| bytes into |buf| at offset |off|.
501//
502// This method uses pread64 on platforms that support it and
503// lseek64 + read on platforms that don't. This implies that
504// callers should not rely on the |fd| offset being incremented
505// as a side effect of this call.
506static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
507 off64_t off) {
Yabin Cui70160f42014-11-19 20:47:18 -0800508#if !defined(_WIN32)
Narayan Kamath7462f022013-11-21 13:05:04 +0000509 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
510#else
511 // The only supported platform that doesn't support pread at the moment
512 // is Windows. Only recent versions of windows support unix like forks,
513 // and even there the semantics are quite different.
514 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700515 ALOGW("Zip: failed seek to offset %" PRId64, off);
Narayan Kamath7462f022013-11-21 13:05:04 +0000516 return kIoError;
517 }
518
519 return TEMP_FAILURE_RETRY(read(fd, buf, len));
Yabin Cui70160f42014-11-19 20:47:18 -0800520#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000521}
522
523static int32_t FindEntry(const ZipArchive* archive, const int ent,
524 ZipEntry* data) {
525 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000526
527 // Recover the start of the central directory entry from the filename
528 // pointer. The filename is the first entry past the fixed-size data,
529 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100530 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100531 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000532
533 // This is the base of our mmapped region, we have to sanity check that
534 // the name that's in the hash table is a pointer to a location within
535 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100536 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800537 archive->directory_map.getDataPtr());
538 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000539 ALOGW("Zip: Invalid entry pointer");
540 return kInvalidOffset;
541 }
542
Narayan Kamath926973e2014-06-09 14:18:14 +0100543 const CentralDirectoryRecord *cdr =
544 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
545
Narayan Kamath7462f022013-11-21 13:05:04 +0000546 // The offset of the start of the central directory in the zipfile.
547 // We keep this lying around so that we can sanity check all our lengths
548 // and our per-file structures.
549 const off64_t cd_offset = archive->directory_offset;
550
551 // Fill out the compression method, modification time, crc32
552 // and other interesting attributes from the central directory. These
553 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100554 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900555 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100556 data->crc32 = cdr->crc32;
557 data->compressed_length = cdr->compressed_size;
558 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000559
560 // Figure out the local header offset from the central directory. The
561 // actual file data will begin after the local header and the name /
562 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100563 const off64_t local_header_offset = cdr->local_file_header_offset;
564 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000565 ALOGW("Zip: bad local hdr offset in zip");
566 return kInvalidOffset;
567 }
568
Narayan Kamath926973e2014-06-09 14:18:14 +0100569 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Narayan Kamath7462f022013-11-21 13:05:04 +0000570 ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
571 local_header_offset);
572 if (actual != sizeof(lfh_buf)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800573 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
574 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000575 return kIoError;
576 }
577
Narayan Kamath926973e2014-06-09 14:18:14 +0100578 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
579
580 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700581 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100582 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000583 return kInvalidOffset;
584 }
585
586 // Paranoia: Match the values specified in the local file header
587 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100588 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000589 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100590 if (data->compressed_length != lfh->compressed_size
591 || data->uncompressed_length != lfh->uncompressed_size
592 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700593 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
594 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000595 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100596 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000597 return kInconsistentInformation;
598 }
599 } else {
600 data->has_data_descriptor = 1;
601 }
602
603 // Check that the local file header name matches the declared
604 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100605 if (lfh->file_name_length == nameLen) {
606 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200607 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000608 ALOGW("Zip: Invalid declared length");
609 return kInvalidOffset;
610 }
611
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800612 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Narayan Kamath7462f022013-11-21 13:05:04 +0000613 ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
614 name_offset);
615
616 if (actual != nameLen) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800617 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000618 free(name_buf);
619 return kIoError;
620 }
621
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100622 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000623 free(name_buf);
624 return kInconsistentInformation;
625 }
626
627 free(name_buf);
628 } else {
629 ALOGW("Zip: lfh name did not match central directory.");
630 return kInconsistentInformation;
631 }
632
Narayan Kamath926973e2014-06-09 14:18:14 +0100633 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
634 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000635 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800636 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000637 return kInvalidOffset;
638 }
639
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800640 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700641 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800642 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000643 return kInvalidOffset;
644 }
645
646 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800647 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700648 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800649 static_cast<int64_t>(data_offset), data->uncompressed_length,
650 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000651 return kInvalidOffset;
652 }
653
654 data->offset = data_offset;
655 return 0;
656}
657
658struct IterationHandle {
659 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100660 // We're not using vector here because this code is used in the Windows SDK
661 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700662 ZipString prefix;
663 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000664 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100665
Yusuke Sato07447542015-06-25 14:39:19 -0700666 IterationHandle(const ZipString* in_prefix,
667 const ZipString* in_suffix) {
668 if (in_prefix) {
669 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
670 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
671 prefix.name = name_copy;
672 prefix.name_length = in_prefix->name_length;
673 } else {
674 prefix.name = NULL;
675 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700676 }
Yusuke Sato07447542015-06-25 14:39:19 -0700677 if (in_suffix) {
678 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
679 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
680 suffix.name = name_copy;
681 suffix.name_length = in_suffix->name_length;
682 } else {
683 suffix.name = NULL;
684 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700685 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100686 }
687
688 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700689 delete[] prefix.name;
690 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100691 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000692};
693
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100694int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700695 const ZipString* optional_prefix,
696 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800697 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000698
699 if (archive == NULL || archive->hash_table == NULL) {
700 ALOGW("Zip: Invalid ZipArchiveHandle");
701 return kInvalidHandle;
702 }
703
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700704 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000705 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000706 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000707
708 *cookie_ptr = cookie ;
709 return 0;
710}
711
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100712void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100713 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100714}
715
Yusuke Sato07447542015-06-25 14:39:19 -0700716int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000717 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800718 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100719 if (entryName.name_length == 0) {
720 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000721 return kInvalidEntryName;
722 }
723
724 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100725 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000726
727 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100728 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000729 return ent;
730 }
731
732 return FindEntry(archive, ent, data);
733}
734
Yusuke Sato07447542015-06-25 14:39:19 -0700735int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800736 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000737 if (handle == NULL) {
738 return kInvalidHandle;
739 }
740
741 ZipArchive* archive = handle->archive;
742 if (archive == NULL || archive->hash_table == NULL) {
743 ALOGW("Zip: Invalid ZipArchiveHandle");
744 return kInvalidHandle;
745 }
746
747 const uint32_t currentOffset = handle->position;
748 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700749 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000750
751 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
752 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700753 (handle->prefix.name_length == 0 ||
754 hash_table[i].StartsWith(handle->prefix)) &&
755 (handle->suffix.name_length == 0 ||
756 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000757 handle->position = (i + 1);
758 const int error = FindEntry(archive, i, data);
759 if (!error) {
760 name->name = hash_table[i].name;
761 name->name_length = hash_table[i].name_length;
762 }
763
764 return error;
765 }
766 }
767
768 handle->position = 0;
769 return kIterationEnd;
770}
771
Narayan Kamathf899bd52015-04-17 11:53:14 +0100772class Writer {
773 public:
774 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
775 virtual ~Writer() {}
776 protected:
777 Writer() = default;
778 private:
779 DISALLOW_COPY_AND_ASSIGN(Writer);
780};
781
782// A Writer that writes data to a fixed size memory region.
783// The size of the memory region must be equal to the total size of
784// the data appended to it.
785class MemoryWriter : public Writer {
786 public:
787 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
788 buf_(buf), size_(size), bytes_written_(0) {
789 }
790
791 virtual bool Append(uint8_t* buf, size_t buf_size) override {
792 if (bytes_written_ + buf_size > size_) {
793 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
794 size_, bytes_written_ + buf_size);
795 return false;
796 }
797
798 memcpy(buf_ + bytes_written_, buf, buf_size);
799 bytes_written_ += buf_size;
800 return true;
801 }
802
803 private:
804 uint8_t* const buf_;
805 const size_t size_;
806 size_t bytes_written_;
807};
808
809// A Writer that appends data to a file |fd| at its current position.
810// The file will be truncated to the end of the written data.
811class FileWriter : public Writer {
812 public:
813
814 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
815 // guaranteeing that the file descriptor is valid and that there's enough
816 // space on the volume to write out the entry completely and that the file
817 // is truncated to the correct length.
818 //
819 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
820 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
821 const uint32_t declared_length = entry->uncompressed_length;
822 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
823 if (current_offset == -1) {
824 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
825 return nullptr;
826 }
827
828 int result = 0;
829#if defined(__linux__)
830 if (declared_length > 0) {
831 // Make sure we have enough space on the volume to extract the compressed
832 // entry. Note that the call to ftruncate below will change the file size but
833 // will not allocate space on disk and this call to fallocate will not
834 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700835 // Note: fallocate is only supported by the following filesystems -
836 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
837 // EOPNOTSUPP error when issued in other filesystems.
838 // Hence, check for the return error code before concluding that the
839 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100840 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700841 if (result == -1 && errno == ENOSPC) {
Narayan Kamathf899bd52015-04-17 11:53:14 +0100842 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
843 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
844 return std::unique_ptr<FileWriter>(nullptr);
845 }
846 }
847#endif // __linux__
848
849 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
850 if (result == -1) {
851 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
852 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
853 return std::unique_ptr<FileWriter>(nullptr);
854 }
855
856 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
857 }
858
859 virtual bool Append(uint8_t* buf, size_t buf_size) override {
860 if (total_bytes_written_ + buf_size > declared_length_) {
861 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
862 declared_length_, total_bytes_written_ + buf_size);
863 return false;
864 }
865
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100866 const bool result = android::base::WriteFully(fd_, buf, buf_size);
867 if (result) {
868 total_bytes_written_ += buf_size;
869 } else {
870 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100871 }
872
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100873 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100874 }
875 private:
876 FileWriter(const int fd, const size_t declared_length) :
877 Writer(),
878 fd_(fd),
879 declared_length_(declared_length),
880 total_bytes_written_(0) {
881 }
882
883 const int fd_;
884 const size_t declared_length_;
885 size_t total_bytes_written_;
886};
887
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800888// This method is using libz macros with old-style-casts
889#pragma GCC diagnostic push
890#pragma GCC diagnostic ignored "-Wold-style-cast"
891static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
892 return inflateInit2(stream, window_bits);
893}
894#pragma GCC diagnostic pop
895
Narayan Kamathf899bd52015-04-17 11:53:14 +0100896static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
897 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700898 const size_t kBufSize = 32768;
899 std::vector<uint8_t> read_buf(kBufSize);
900 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000901 z_stream zstream;
902 int zerr;
903
904 /*
905 * Initialize the zlib stream struct.
906 */
907 memset(&zstream, 0, sizeof(zstream));
908 zstream.zalloc = Z_NULL;
909 zstream.zfree = Z_NULL;
910 zstream.opaque = Z_NULL;
911 zstream.next_in = NULL;
912 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700913 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000914 zstream.avail_out = kBufSize;
915 zstream.data_type = Z_UNKNOWN;
916
917 /*
918 * Use the undocumented "negative window bits" feature to tell zlib
919 * that there's no zlib header waiting for it.
920 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800921 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000922 if (zerr != Z_OK) {
923 if (zerr == Z_VERSION_ERROR) {
924 ALOGE("Installed zlib is not compatible with linked version (%s)",
925 ZLIB_VERSION);
926 } else {
927 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
928 }
929
930 return kZlibError;
931 }
932
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800933 auto zstream_deleter = [](z_stream* stream) {
934 inflateEnd(stream); /* free up any allocated structures */
935 };
936
937 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
938
Narayan Kamath7462f022013-11-21 13:05:04 +0000939 const uint32_t uncompressed_length = entry->uncompressed_length;
940
941 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000942 do {
943 /* read as much as we can */
944 if (zstream.avail_in == 0) {
Mark Salyzyn51d562d2014-05-05 14:38:05 -0700945 const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700946 const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
Narayan Kamath7462f022013-11-21 13:05:04 +0000947 if (actual != getSize) {
Mark Salyzyn51d562d2014-05-05 14:38:05 -0700948 ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800949 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000950 }
951
952 compressed_length -= getSize;
953
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700954 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000955 zstream.avail_in = getSize;
956 }
957
958 /* uncompress the data */
959 zerr = inflate(&zstream, Z_NO_FLUSH);
960 if (zerr != Z_OK && zerr != Z_STREAM_END) {
961 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
962 zerr, zstream.next_in, zstream.avail_in,
963 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800964 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000965 }
966
967 /* write when we're full or when we're done */
968 if (zstream.avail_out == 0 ||
969 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700970 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100971 if (!writer->Append(&write_buf[0], write_size)) {
972 // The file might have declared a bogus length.
973 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000974 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000975
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700976 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000977 zstream.avail_out = kBufSize;
978 }
979 } while (zerr == Z_OK);
980
981 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
982
983 // stream.adler holds the crc32 value for such streams.
984 *crc_out = zstream.adler;
985
986 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700987 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000988 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800989 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000990 }
991
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800992 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000993}
994
Narayan Kamathf899bd52015-04-17 11:53:14 +0100995static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
996 uint64_t *crc_out) {
997 static const uint32_t kBufSize = 32768;
998 std::vector<uint8_t> buf(kBufSize);
999
1000 const uint32_t length = entry->uncompressed_length;
1001 uint32_t count = 0;
1002 uint64_t crc = 0;
1003 while (count < length) {
1004 uint32_t remaining = length - count;
1005
1006 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1007 // value.
1008 const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
1009 const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
1010
1011 if (actual != block_size) {
1012 ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
1013 return kIoError;
1014 }
1015
1016 if (!writer->Append(&buf[0], block_size)) {
1017 return kIoError;
1018 }
1019 crc = crc32(crc, &buf[0], block_size);
1020 count += block_size;
1021 }
1022
1023 *crc_out = crc;
1024
1025 return 0;
1026}
1027
1028int32_t ExtractToWriter(ZipArchiveHandle handle,
1029 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001030 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001031 const uint16_t method = entry->method;
1032 off64_t data_offset = entry->offset;
1033
1034 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001035 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001036 return kIoError;
1037 }
1038
1039 // this should default to kUnknownCompressionMethod.
1040 int32_t return_value = -1;
1041 uint64_t crc = 0;
1042 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001043 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001044 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001045 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001046 }
1047
1048 if (!return_value && entry->has_data_descriptor) {
1049 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1050 if (return_value) {
1051 return return_value;
1052 }
1053 }
1054
1055 // TODO: Fix this check by passing the right flags to inflate2 so that
1056 // it calculates the CRC for us.
1057 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001058 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001059 return kInconsistentInformation;
1060 }
1061
1062 return return_value;
1063}
1064
Narayan Kamathf899bd52015-04-17 11:53:14 +01001065int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1066 uint8_t* begin, uint32_t size) {
1067 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1068 return ExtractToWriter(handle, entry, writer.get());
1069}
1070
Narayan Kamath7462f022013-11-21 13:05:04 +00001071int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1072 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001073 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1074 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001075 return kIoError;
1076 }
1077
Narayan Kamathf899bd52015-04-17 11:53:14 +01001078 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001079}
1080
1081const char* ErrorCodeString(int32_t error_code) {
1082 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1083 return kErrorMessages[error_code * -1];
1084 }
1085
1086 return kErrorMessages[0];
1087}
1088
1089int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001090 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001091}