blob: 4649a75b2dfbae1038f6bccf0de22455b7f54f61 [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"
Colin Cross7c6c7f02016-09-16 10:15:51 -070034#include "android-base/logging.h"
Elliott Hughes4f713192015-12-04 22:00:26 -080035#include "android-base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
36#include "android-base/memory.h"
Dan Albert1ae07642015-04-09 14:11:18 -070037#include "log/log.h"
38#include "utils/Compat.h"
39#include "utils/FileMap.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080040#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070041#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000042
Narayan Kamath044bc8e2014-12-03 18:22:53 +000043#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070044#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080045#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070046
Dan Albert1ae07642015-04-09 14:11:18 -070047using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000048
Narayan Kamath926973e2014-06-09 14:18:14 +010049// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000050// things will happen.
51#ifndef O_BINARY
52#define O_BINARY 0
53#endif
54
Narayan Kamath926973e2014-06-09 14:18:14 +010055// The maximum number of bytes to scan backwards for the EOCD start.
56static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
57
Narayan Kamath7462f022013-11-21 13:05:04 +000058static const char* kErrorMessages[] = {
59 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000060 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000061 "Zlib error",
62 "Invalid file",
63 "Invalid handle",
64 "Duplicate entries in archive",
65 "Empty archive",
66 "Entry not found",
67 "Invalid offset",
68 "Inconsistent information",
69 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000070 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000071 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000072};
73
74static const int32_t kErrorMessageUpperBound = 0;
75
Narayan Kamatheb41ad22013-12-09 16:26:36 +000076static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000077
78// We encountered a Zlib error when inflating a stream from this file.
79// Usually indicates file corruption.
80static const int32_t kZlibError = -2;
81
82// The input file cannot be processed as a zip archive. Usually because
83// it's too small, too large or does not have a valid signature.
84static const int32_t kInvalidFile = -3;
85
86// An invalid iteration / ziparchive handle was passed in as an input
87// argument.
88static const int32_t kInvalidHandle = -4;
89
90// The zip archive contained two (or possibly more) entries with the same
91// name.
92static const int32_t kDuplicateEntry = -5;
93
94// The zip archive contains no entries.
95static const int32_t kEmptyArchive = -6;
96
97// The specified entry was not found in the archive.
98static const int32_t kEntryNotFound = -7;
99
100// The zip archive contained an invalid local file header pointer.
101static const int32_t kInvalidOffset = -8;
102
103// The zip archive contained inconsistent entry information. This could
104// be because the central directory & local file header did not agree, or
105// if the actual uncompressed length or crc32 do not match their declared
106// values.
107static const int32_t kInconsistentInformation = -9;
108
109// An invalid entry name was encountered.
110static const int32_t kInvalidEntryName = -10;
111
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000112// An I/O related system call (read, lseek, ftruncate, map) failed.
113static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000114
Narayan Kamatheaf98852013-12-11 14:51:51 +0000115// We were not able to mmap the central directory or entry contents.
116static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000117
Narayan Kamatheaf98852013-12-11 14:51:51 +0000118static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000119
Narayan Kamath7462f022013-11-21 13:05:04 +0000120/*
121 * A Read-only Zip archive.
122 *
123 * We want "open" and "find entry by name" to be fast operations, and
124 * we want to use as little memory as possible. We memory-map the zip
125 * central directory, and load a hash table with pointers to the filenames
126 * (which aren't null-terminated). The other fields are at a fixed offset
127 * from the filename, so we don't need to extract those (but we do need
128 * to byte-read and endian-swap them every time we want them).
129 *
130 * It's possible that somebody has handed us a massive (~1GB) zip archive,
131 * so we can't expect to mmap the entire file.
132 *
133 * To speed comparisons when doing a lookup by name, we could make the mapping
134 * "private" (copy-on-write) and null-terminate the filenames after verifying
135 * the record structure. However, this requires a private mapping of
136 * every page that the Central Directory touches. Easier to tuck a copy
137 * of the string length into the hash table entry.
138 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000139
Narayan Kamath7462f022013-11-21 13:05:04 +0000140/*
141 * Round up to the next highest power of 2.
142 *
143 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
144 */
145static uint32_t RoundUpPower2(uint32_t val) {
146 val--;
147 val |= val >> 1;
148 val |= val >> 2;
149 val |= val >> 4;
150 val |= val >> 8;
151 val |= val >> 16;
152 val++;
153
154 return val;
155}
156
Yusuke Sato07447542015-06-25 14:39:19 -0700157static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000158 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100159 uint16_t len = name.name_length;
160 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000161
162 while (len--) {
163 hash = hash * 31 + *str++;
164 }
165
166 return hash;
167}
168
169/*
170 * Convert a ZipEntry to a hash table index, verifying that it's in a
171 * valid range.
172 */
Yusuke Sato07447542015-06-25 14:39:19 -0700173static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000174 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700175 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100176 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000177
178 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
179 uint32_t ent = hash & (hash_table_size - 1);
180 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700181 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000182 return ent;
183 }
184
185 ent = (ent + 1) & (hash_table_size - 1);
186 }
187
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100188 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000189 return kEntryNotFound;
190}
191
192/*
193 * Add a new entry to the hash table.
194 */
Yusuke Sato07447542015-06-25 14:39:19 -0700195static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
196 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100197 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000198 uint32_t ent = hash & (hash_table_size - 1);
199
200 /*
201 * We over-allocated the table, so we're guaranteed to find an empty slot.
202 * Further, we guarantee that the hashtable size is not 0.
203 */
204 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700205 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000206 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100207 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000208 return kDuplicateEntry;
209 }
210 ent = (ent + 1) & (hash_table_size - 1);
211 }
212
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100213 hash_table[ent].name = name.name;
214 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000215 return 0;
216}
217
Narayan Kamath7462f022013-11-21 13:05:04 +0000218static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
219 ZipArchive* archive, off64_t file_length,
Narayan Kamath926973e2014-06-09 14:18:14 +0100220 off64_t read_amount, uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000221 const off64_t search_start = file_length - read_amount;
222
223 if (lseek64(fd, search_start, SEEK_SET) != search_start) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100224 ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
225 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000226 return kIoError;
227 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800228 if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100229 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
230 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000231 return kIoError;
232 }
233
234 /*
235 * Scan backward for the EOCD magic. In an archive without a trailing
236 * comment, we'll find it on the first try. (We may want to consider
237 * doing an initial minimal read; if we don't find it, retry with a
238 * second read as above.)
239 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100240 int i = read_amount - sizeof(EocdRecord);
241 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700242 if (scan_buffer[i] == 0x50) {
243 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
244 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
245 ALOGV("+++ Found EOCD at buf+%d", i);
246 break;
247 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000248 }
249 }
250 if (i < 0) {
251 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
252 return kInvalidFile;
253 }
254
255 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100256 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000257 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100258 * Verify that there's no trailing space at the end of the central directory
259 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000260 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100261 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
262 + eocd->comment_length;
263 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100264 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100265 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100266 return kInvalidFile;
267 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000268
Narayan Kamath926973e2014-06-09 14:18:14 +0100269 /*
270 * Grab the CD offset and size, and the number of entries in the
271 * archive and verify that they look reasonable.
272 */
273 if (eocd->cd_start_offset + eocd->cd_size > eocd_offset) {
274 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
275 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000276 return kInvalidOffset;
277 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100278 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000279 ALOGW("Zip: empty archive?");
280 return kEmptyArchive;
281 }
282
Elliott Hughese49236b2015-06-04 15:21:59 -0700283 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100284 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000285
286 /*
287 * It all looks good. Create a mapping for the CD, and set the fields
288 * in archive.
289 */
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800290 if (!archive->directory_map.create(debug_file_name, fd,
291 static_cast<off64_t>(eocd->cd_start_offset),
292 static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
Narayan Kamatheaf98852013-12-11 14:51:51 +0000293 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000294 }
295
Narayan Kamath926973e2014-06-09 14:18:14 +0100296 archive->num_entries = eocd->num_records;
297 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000298
299 return 0;
300}
301
302/*
303 * Find the zip Central Directory and memory-map it.
304 *
305 * On success, returns 0 after populating fields from the EOCD area:
306 * directory_offset
307 * directory_map
308 * num_entries
309 */
310static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
311 ZipArchive* archive) {
312
313 // Test file length. We use lseek64 to make sure the file
314 // is small enough to be a zip file (Its size must be less than
315 // 0xffffffff bytes).
316 off64_t file_length = lseek64(fd, 0, SEEK_END);
317 if (file_length == -1) {
318 ALOGV("Zip: lseek on fd %d failed", fd);
319 return kInvalidFile;
320 }
321
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800322 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100323 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000324 return kInvalidFile;
325 }
326
Narayan Kamath926973e2014-06-09 14:18:14 +0100327 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
328 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000329 return kInvalidFile;
330 }
331
332 /*
333 * Perform the traditional EOCD snipe hunt.
334 *
335 * We're searching for the End of Central Directory magic number,
336 * which appears at the start of the EOCD block. It's followed by
337 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
338 * need to read the last part of the file into a buffer, dig through
339 * it to find the magic number, parse some values out, and use those
340 * to determine the extent of the CD.
341 *
342 * We start by pulling in the last part of the file.
343 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100344 off64_t read_amount = kMaxEOCDSearch;
345 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000346 read_amount = file_length;
347 }
348
Narayan Kamath926973e2014-06-09 14:18:14 +0100349 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
Narayan Kamath7462f022013-11-21 13:05:04 +0000350 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
351 file_length, read_amount, scan_buffer);
352
353 free(scan_buffer);
354 return result;
355}
356
357/*
358 * Parses the Zip archive's Central Directory. Allocates and populates the
359 * hash table.
360 *
361 * Returns 0 on success.
362 */
363static int32_t ParseZipArchive(ZipArchive* archive) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800364 const uint8_t* const cd_ptr =
365 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800366 const size_t cd_length = archive->directory_map.getDataLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100367 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000368
369 /*
370 * Create hash table. We have a minimum 75% load factor, possibly as
371 * low as 50% after we round off to a power of 2. There must be at
372 * least one unused entry to avoid an infinite loop during creation.
373 */
374 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700375 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
376 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000377
378 /*
379 * Walk through the central directory, adding entries to the hash
380 * table and verifying values.
381 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100382 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000383 const uint8_t* ptr = cd_ptr;
384 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100385 const CentralDirectoryRecord* cdr =
386 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
387 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700388 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800389 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000390 }
391
Narayan Kamath926973e2014-06-09 14:18:14 +0100392 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700393 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800394 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000395 }
396
Narayan Kamath926973e2014-06-09 14:18:14 +0100397 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000398 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800399 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
400 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800401 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000402 }
403
Narayan Kamath926973e2014-06-09 14:18:14 +0100404 const uint16_t file_name_length = cdr->file_name_length;
405 const uint16_t extra_length = cdr->extra_field_length;
406 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100407 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
408
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000409 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
410 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800411 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100412 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000413
414 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700415 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100416 entry_name.name = file_name;
417 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000418 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100419 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800420 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000421 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800422 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000423 }
424
Narayan Kamath926973e2014-06-09 14:18:14 +0100425 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
426 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700427 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
428 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800429 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000430 }
431 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700432 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000433
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800434 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000435}
436
437static int32_t OpenArchiveInternal(ZipArchive* archive,
438 const char* debug_file_name) {
439 int32_t result = -1;
440 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
441 return result;
442 }
443
444 if ((result = ParseZipArchive(archive))) {
445 return result;
446 }
447
448 return 0;
449}
450
451int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700452 ZipArchiveHandle* handle, bool assume_ownership) {
453 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000454 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000455 return OpenArchiveInternal(archive, debug_file_name);
456}
457
458int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100459 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700460 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000461 *handle = archive;
462
Narayan Kamath7462f022013-11-21 13:05:04 +0000463 if (fd < 0) {
464 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
465 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000466 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700467
Narayan Kamath7462f022013-11-21 13:05:04 +0000468 return OpenArchiveInternal(archive, fileName);
469}
470
471/*
472 * Close a ZipArchive, closing the file and freeing the contents.
473 */
474void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800475 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000476 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100477 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000478}
479
480static int32_t UpdateEntryFromDataDescriptor(int fd,
481 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100482 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800483 if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000484 return kIoError;
485 }
486
Narayan Kamath926973e2014-06-09 14:18:14 +0100487 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
488 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
489 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000490
Narayan Kamath926973e2014-06-09 14:18:14 +0100491 entry->crc32 = descriptor->crc32;
492 entry->compressed_length = descriptor->compressed_size;
493 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000494
495 return 0;
496}
497
498// Attempts to read |len| bytes into |buf| at offset |off|.
Adam Lesinskib1911402016-03-09 17:13:09 -0800499// On non-Windows platforms, callers are guaranteed that the |fd|
500// offset is unchanged and there is no side effect to this call.
501//
502// On Windows platforms this is not thread-safe.
Yabin Cuib2a77002016-02-08 16:26:33 -0800503static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
Adam Lesinskib1911402016-03-09 17:13:09 -0800504#if !defined(_WIN32)
505 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
506#else
Narayan Kamath7462f022013-11-21 13:05:04 +0000507 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700508 ALOGW("Zip: failed seek to offset %" PRId64, off);
Yabin Cuib2a77002016-02-08 16:26:33 -0800509 return false;
Narayan Kamath7462f022013-11-21 13:05:04 +0000510 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800511 return android::base::ReadFully(fd, buf, len);
Adam Lesinskib1911402016-03-09 17:13:09 -0800512#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000513}
514
515static int32_t FindEntry(const ZipArchive* archive, const int ent,
516 ZipEntry* data) {
517 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000518
519 // Recover the start of the central directory entry from the filename
520 // pointer. The filename is the first entry past the fixed-size data,
521 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100522 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100523 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000524
525 // This is the base of our mmapped region, we have to sanity check that
526 // the name that's in the hash table is a pointer to a location within
527 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100528 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800529 archive->directory_map.getDataPtr());
530 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000531 ALOGW("Zip: Invalid entry pointer");
532 return kInvalidOffset;
533 }
534
Narayan Kamath926973e2014-06-09 14:18:14 +0100535 const CentralDirectoryRecord *cdr =
536 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
537
Narayan Kamath7462f022013-11-21 13:05:04 +0000538 // The offset of the start of the central directory in the zipfile.
539 // We keep this lying around so that we can sanity check all our lengths
540 // and our per-file structures.
541 const off64_t cd_offset = archive->directory_offset;
542
543 // Fill out the compression method, modification time, crc32
544 // and other interesting attributes from the central directory. These
545 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100546 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900547 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100548 data->crc32 = cdr->crc32;
549 data->compressed_length = cdr->compressed_size;
550 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000551
552 // Figure out the local header offset from the central directory. The
553 // actual file data will begin after the local header and the name /
554 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100555 const off64_t local_header_offset = cdr->local_file_header_offset;
556 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000557 ALOGW("Zip: bad local hdr offset in zip");
558 return kInvalidOffset;
559 }
560
Narayan Kamath926973e2014-06-09 14:18:14 +0100561 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800562 if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800563 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
564 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000565 return kIoError;
566 }
567
Narayan Kamath926973e2014-06-09 14:18:14 +0100568 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
569
570 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700571 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100572 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000573 return kInvalidOffset;
574 }
575
576 // Paranoia: Match the values specified in the local file header
577 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100578 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000579 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100580 if (data->compressed_length != lfh->compressed_size
581 || data->uncompressed_length != lfh->uncompressed_size
582 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700583 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
584 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000585 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100586 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000587 return kInconsistentInformation;
588 }
589 } else {
590 data->has_data_descriptor = 1;
591 }
592
593 // Check that the local file header name matches the declared
594 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100595 if (lfh->file_name_length == nameLen) {
596 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200597 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000598 ALOGW("Zip: Invalid declared length");
599 return kInvalidOffset;
600 }
601
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800602 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Yabin Cuib2a77002016-02-08 16:26:33 -0800603 if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800604 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000605 free(name_buf);
606 return kIoError;
607 }
608
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100609 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000610 free(name_buf);
611 return kInconsistentInformation;
612 }
613
614 free(name_buf);
615 } else {
616 ALOGW("Zip: lfh name did not match central directory.");
617 return kInconsistentInformation;
618 }
619
Narayan Kamath926973e2014-06-09 14:18:14 +0100620 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
621 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000622 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800623 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000624 return kInvalidOffset;
625 }
626
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800627 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700628 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800629 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000630 return kInvalidOffset;
631 }
632
633 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800634 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700635 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800636 static_cast<int64_t>(data_offset), data->uncompressed_length,
637 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000638 return kInvalidOffset;
639 }
640
641 data->offset = data_offset;
642 return 0;
643}
644
645struct IterationHandle {
646 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100647 // We're not using vector here because this code is used in the Windows SDK
648 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700649 ZipString prefix;
650 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000651 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100652
Yusuke Sato07447542015-06-25 14:39:19 -0700653 IterationHandle(const ZipString* in_prefix,
654 const ZipString* in_suffix) {
655 if (in_prefix) {
656 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
657 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
658 prefix.name = name_copy;
659 prefix.name_length = in_prefix->name_length;
660 } else {
661 prefix.name = NULL;
662 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700663 }
Yusuke Sato07447542015-06-25 14:39:19 -0700664 if (in_suffix) {
665 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
666 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
667 suffix.name = name_copy;
668 suffix.name_length = in_suffix->name_length;
669 } else {
670 suffix.name = NULL;
671 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700672 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100673 }
674
675 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700676 delete[] prefix.name;
677 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100678 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000679};
680
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100681int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700682 const ZipString* optional_prefix,
683 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800684 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000685
686 if (archive == NULL || archive->hash_table == NULL) {
687 ALOGW("Zip: Invalid ZipArchiveHandle");
688 return kInvalidHandle;
689 }
690
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700691 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000692 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000693 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000694
695 *cookie_ptr = cookie ;
696 return 0;
697}
698
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100699void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100700 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100701}
702
Yusuke Sato07447542015-06-25 14:39:19 -0700703int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000704 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800705 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100706 if (entryName.name_length == 0) {
707 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000708 return kInvalidEntryName;
709 }
710
711 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100712 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000713
714 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100715 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000716 return ent;
717 }
718
719 return FindEntry(archive, ent, data);
720}
721
Yusuke Sato07447542015-06-25 14:39:19 -0700722int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800723 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000724 if (handle == NULL) {
725 return kInvalidHandle;
726 }
727
728 ZipArchive* archive = handle->archive;
729 if (archive == NULL || archive->hash_table == NULL) {
730 ALOGW("Zip: Invalid ZipArchiveHandle");
731 return kInvalidHandle;
732 }
733
734 const uint32_t currentOffset = handle->position;
735 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700736 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000737
738 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
739 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700740 (handle->prefix.name_length == 0 ||
741 hash_table[i].StartsWith(handle->prefix)) &&
742 (handle->suffix.name_length == 0 ||
743 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000744 handle->position = (i + 1);
745 const int error = FindEntry(archive, i, data);
746 if (!error) {
747 name->name = hash_table[i].name;
748 name->name_length = hash_table[i].name_length;
749 }
750
751 return error;
752 }
753 }
754
755 handle->position = 0;
756 return kIterationEnd;
757}
758
Narayan Kamathf899bd52015-04-17 11:53:14 +0100759class Writer {
760 public:
761 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
762 virtual ~Writer() {}
763 protected:
764 Writer() = default;
765 private:
766 DISALLOW_COPY_AND_ASSIGN(Writer);
767};
768
769// A Writer that writes data to a fixed size memory region.
770// The size of the memory region must be equal to the total size of
771// the data appended to it.
772class MemoryWriter : public Writer {
773 public:
774 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
775 buf_(buf), size_(size), bytes_written_(0) {
776 }
777
778 virtual bool Append(uint8_t* buf, size_t buf_size) override {
779 if (bytes_written_ + buf_size > size_) {
780 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
781 size_, bytes_written_ + buf_size);
782 return false;
783 }
784
785 memcpy(buf_ + bytes_written_, buf, buf_size);
786 bytes_written_ += buf_size;
787 return true;
788 }
789
790 private:
791 uint8_t* const buf_;
792 const size_t size_;
793 size_t bytes_written_;
794};
795
796// A Writer that appends data to a file |fd| at its current position.
797// The file will be truncated to the end of the written data.
798class FileWriter : public Writer {
799 public:
800
801 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
802 // guaranteeing that the file descriptor is valid and that there's enough
803 // space on the volume to write out the entry completely and that the file
804 // is truncated to the correct length.
805 //
806 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
807 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
808 const uint32_t declared_length = entry->uncompressed_length;
809 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
810 if (current_offset == -1) {
811 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
812 return nullptr;
813 }
814
815 int result = 0;
816#if defined(__linux__)
817 if (declared_length > 0) {
818 // Make sure we have enough space on the volume to extract the compressed
819 // entry. Note that the call to ftruncate below will change the file size but
820 // will not allocate space on disk and this call to fallocate will not
821 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700822 // Note: fallocate is only supported by the following filesystems -
823 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
824 // EOPNOTSUPP error when issued in other filesystems.
825 // Hence, check for the return error code before concluding that the
826 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100827 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700828 if (result == -1 && errno == ENOSPC) {
Narayan Kamathd5d7abe2016-08-10 12:24:05 +0100829 ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 " : %s",
830 static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
831 strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100832 return std::unique_ptr<FileWriter>(nullptr);
833 }
834 }
835#endif // __linux__
836
837 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
838 if (result == -1) {
839 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
840 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
841 return std::unique_ptr<FileWriter>(nullptr);
842 }
843
844 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
845 }
846
847 virtual bool Append(uint8_t* buf, size_t buf_size) override {
848 if (total_bytes_written_ + buf_size > declared_length_) {
849 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
850 declared_length_, total_bytes_written_ + buf_size);
851 return false;
852 }
853
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100854 const bool result = android::base::WriteFully(fd_, buf, buf_size);
855 if (result) {
856 total_bytes_written_ += buf_size;
857 } else {
858 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100859 }
860
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100861 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100862 }
863 private:
864 FileWriter(const int fd, const size_t declared_length) :
865 Writer(),
866 fd_(fd),
867 declared_length_(declared_length),
868 total_bytes_written_(0) {
869 }
870
871 const int fd_;
872 const size_t declared_length_;
873 size_t total_bytes_written_;
874};
875
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800876// This method is using libz macros with old-style-casts
877#pragma GCC diagnostic push
878#pragma GCC diagnostic ignored "-Wold-style-cast"
879static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
880 return inflateInit2(stream, window_bits);
881}
882#pragma GCC diagnostic pop
883
Narayan Kamathf899bd52015-04-17 11:53:14 +0100884static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
885 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700886 const size_t kBufSize = 32768;
887 std::vector<uint8_t> read_buf(kBufSize);
888 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000889 z_stream zstream;
890 int zerr;
891
892 /*
893 * Initialize the zlib stream struct.
894 */
895 memset(&zstream, 0, sizeof(zstream));
896 zstream.zalloc = Z_NULL;
897 zstream.zfree = Z_NULL;
898 zstream.opaque = Z_NULL;
899 zstream.next_in = NULL;
900 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700901 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000902 zstream.avail_out = kBufSize;
903 zstream.data_type = Z_UNKNOWN;
904
905 /*
906 * Use the undocumented "negative window bits" feature to tell zlib
907 * that there's no zlib header waiting for it.
908 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800909 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000910 if (zerr != Z_OK) {
911 if (zerr == Z_VERSION_ERROR) {
912 ALOGE("Installed zlib is not compatible with linked version (%s)",
913 ZLIB_VERSION);
914 } else {
915 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
916 }
917
918 return kZlibError;
919 }
920
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800921 auto zstream_deleter = [](z_stream* stream) {
922 inflateEnd(stream); /* free up any allocated structures */
923 };
924
925 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
926
Narayan Kamath7462f022013-11-21 13:05:04 +0000927 const uint32_t uncompressed_length = entry->uncompressed_length;
928
929 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000930 do {
931 /* read as much as we can */
932 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800933 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
934 if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
935 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800936 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000937 }
938
939 compressed_length -= getSize;
940
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700941 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000942 zstream.avail_in = getSize;
943 }
944
945 /* uncompress the data */
946 zerr = inflate(&zstream, Z_NO_FLUSH);
947 if (zerr != Z_OK && zerr != Z_STREAM_END) {
948 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
949 zerr, zstream.next_in, zstream.avail_in,
950 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800951 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000952 }
953
954 /* write when we're full or when we're done */
955 if (zstream.avail_out == 0 ||
956 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700957 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100958 if (!writer->Append(&write_buf[0], write_size)) {
959 // The file might have declared a bogus length.
960 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000961 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000962
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700963 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000964 zstream.avail_out = kBufSize;
965 }
966 } while (zerr == Z_OK);
967
968 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
969
970 // stream.adler holds the crc32 value for such streams.
971 *crc_out = zstream.adler;
972
973 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700974 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000975 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800976 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000977 }
978
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800979 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000980}
981
Narayan Kamathf899bd52015-04-17 11:53:14 +0100982static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
983 uint64_t *crc_out) {
984 static const uint32_t kBufSize = 32768;
985 std::vector<uint8_t> buf(kBufSize);
986
987 const uint32_t length = entry->uncompressed_length;
988 uint32_t count = 0;
989 uint64_t crc = 0;
990 while (count < length) {
991 uint32_t remaining = length - count;
992
993 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
994 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -0800995 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
996 if (!android::base::ReadFully(fd, buf.data(), block_size)) {
997 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100998 return kIoError;
999 }
1000
1001 if (!writer->Append(&buf[0], block_size)) {
1002 return kIoError;
1003 }
1004 crc = crc32(crc, &buf[0], block_size);
1005 count += block_size;
1006 }
1007
1008 *crc_out = crc;
1009
1010 return 0;
1011}
1012
1013int32_t ExtractToWriter(ZipArchiveHandle handle,
1014 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001015 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001016 const uint16_t method = entry->method;
1017 off64_t data_offset = entry->offset;
1018
1019 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001020 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001021 return kIoError;
1022 }
1023
1024 // this should default to kUnknownCompressionMethod.
1025 int32_t return_value = -1;
1026 uint64_t crc = 0;
1027 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001028 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001029 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001030 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001031 }
1032
1033 if (!return_value && entry->has_data_descriptor) {
1034 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1035 if (return_value) {
1036 return return_value;
1037 }
1038 }
1039
1040 // TODO: Fix this check by passing the right flags to inflate2 so that
1041 // it calculates the CRC for us.
1042 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001043 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001044 return kInconsistentInformation;
1045 }
1046
1047 return return_value;
1048}
1049
Narayan Kamathf899bd52015-04-17 11:53:14 +01001050int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1051 uint8_t* begin, uint32_t size) {
1052 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1053 return ExtractToWriter(handle, entry, writer.get());
1054}
1055
Narayan Kamath7462f022013-11-21 13:05:04 +00001056int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1057 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001058 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1059 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001060 return kIoError;
1061 }
1062
Narayan Kamathf899bd52015-04-17 11:53:14 +01001063 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001064}
1065
1066const char* ErrorCodeString(int32_t error_code) {
1067 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1068 return kErrorMessages[error_code * -1];
1069 }
1070
1071 return kErrorMessages[0];
1072}
1073
1074int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001075 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001076}
Colin Cross7c6c7f02016-09-16 10:15:51 -07001077
1078ZipString::ZipString(const char* entry_name)
1079 : name(reinterpret_cast<const uint8_t*>(entry_name)) {
1080 size_t len = strlen(entry_name);
1081 CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
1082 name_length = static_cast<uint16_t>(len);
1083}