blob: 986ee7208fdb8a173eaf2946983cf0cd8f8cca61 [file] [log] [blame]
Narayan Kamath7462f022013-11-21 13:05:04 +00001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Read-only access to Zip archives, with minimal heap allocation.
19 */
Narayan Kamath7462f022013-11-21 13:05:04 +000020
21#include <assert.h>
22#include <errno.h>
Mark Salyzyn99ef9912014-03-14 14:26:22 -070023#include <fcntl.h>
24#include <inttypes.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000025#include <limits.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000026#include <stdlib.h>
27#include <string.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000028#include <unistd.h>
29
Dan Albert1ae07642015-04-09 14:11:18 -070030#include <memory>
31#include <vector>
32
Elliott Hughes4f713192015-12-04 22:00:26 -080033#include "android-base/file.h"
34#include "android-base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
35#include "android-base/memory.h"
Dan Albert1ae07642015-04-09 14:11:18 -070036#include "log/log.h"
37#include "utils/Compat.h"
38#include "utils/FileMap.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080039#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070040#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000041
Narayan Kamath044bc8e2014-12-03 18:22:53 +000042#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070043#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080044#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070045
Dan Albert1ae07642015-04-09 14:11:18 -070046using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000047
Narayan Kamath926973e2014-06-09 14:18:14 +010048// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000049// things will happen.
50#ifndef O_BINARY
51#define O_BINARY 0
52#endif
53
Narayan Kamath926973e2014-06-09 14:18:14 +010054// The maximum number of bytes to scan backwards for the EOCD start.
55static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
56
Narayan Kamath7462f022013-11-21 13:05:04 +000057static const char* kErrorMessages[] = {
58 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000059 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000060 "Zlib error",
61 "Invalid file",
62 "Invalid handle",
63 "Duplicate entries in archive",
64 "Empty archive",
65 "Entry not found",
66 "Invalid offset",
67 "Inconsistent information",
68 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000069 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000070 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000071};
72
73static const int32_t kErrorMessageUpperBound = 0;
74
Narayan Kamatheb41ad22013-12-09 16:26:36 +000075static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000076
77// We encountered a Zlib error when inflating a stream from this file.
78// Usually indicates file corruption.
79static const int32_t kZlibError = -2;
80
81// The input file cannot be processed as a zip archive. Usually because
82// it's too small, too large or does not have a valid signature.
83static const int32_t kInvalidFile = -3;
84
85// An invalid iteration / ziparchive handle was passed in as an input
86// argument.
87static const int32_t kInvalidHandle = -4;
88
89// The zip archive contained two (or possibly more) entries with the same
90// name.
91static const int32_t kDuplicateEntry = -5;
92
93// The zip archive contains no entries.
94static const int32_t kEmptyArchive = -6;
95
96// The specified entry was not found in the archive.
97static const int32_t kEntryNotFound = -7;
98
99// The zip archive contained an invalid local file header pointer.
100static const int32_t kInvalidOffset = -8;
101
102// The zip archive contained inconsistent entry information. This could
103// be because the central directory & local file header did not agree, or
104// if the actual uncompressed length or crc32 do not match their declared
105// values.
106static const int32_t kInconsistentInformation = -9;
107
108// An invalid entry name was encountered.
109static const int32_t kInvalidEntryName = -10;
110
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000111// An I/O related system call (read, lseek, ftruncate, map) failed.
112static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000113
Narayan Kamatheaf98852013-12-11 14:51:51 +0000114// We were not able to mmap the central directory or entry contents.
115static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000116
Narayan Kamatheaf98852013-12-11 14:51:51 +0000117static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000118
Narayan Kamath7462f022013-11-21 13:05:04 +0000119/*
120 * A Read-only Zip archive.
121 *
122 * We want "open" and "find entry by name" to be fast operations, and
123 * we want to use as little memory as possible. We memory-map the zip
124 * central directory, and load a hash table with pointers to the filenames
125 * (which aren't null-terminated). The other fields are at a fixed offset
126 * from the filename, so we don't need to extract those (but we do need
127 * to byte-read and endian-swap them every time we want them).
128 *
129 * It's possible that somebody has handed us a massive (~1GB) zip archive,
130 * so we can't expect to mmap the entire file.
131 *
132 * To speed comparisons when doing a lookup by name, we could make the mapping
133 * "private" (copy-on-write) and null-terminate the filenames after verifying
134 * the record structure. However, this requires a private mapping of
135 * every page that the Central Directory touches. Easier to tuck a copy
136 * of the string length into the hash table entry.
137 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000138
Narayan Kamath7462f022013-11-21 13:05:04 +0000139/*
140 * Round up to the next highest power of 2.
141 *
142 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
143 */
144static uint32_t RoundUpPower2(uint32_t val) {
145 val--;
146 val |= val >> 1;
147 val |= val >> 2;
148 val |= val >> 4;
149 val |= val >> 8;
150 val |= val >> 16;
151 val++;
152
153 return val;
154}
155
Yusuke Sato07447542015-06-25 14:39:19 -0700156static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000157 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100158 uint16_t len = name.name_length;
159 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000160
161 while (len--) {
162 hash = hash * 31 + *str++;
163 }
164
165 return hash;
166}
167
168/*
169 * Convert a ZipEntry to a hash table index, verifying that it's in a
170 * valid range.
171 */
Yusuke Sato07447542015-06-25 14:39:19 -0700172static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000173 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700174 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100175 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000176
177 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
178 uint32_t ent = hash & (hash_table_size - 1);
179 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700180 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000181 return ent;
182 }
183
184 ent = (ent + 1) & (hash_table_size - 1);
185 }
186
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100187 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000188 return kEntryNotFound;
189}
190
191/*
192 * Add a new entry to the hash table.
193 */
Yusuke Sato07447542015-06-25 14:39:19 -0700194static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
195 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100196 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000197 uint32_t ent = hash & (hash_table_size - 1);
198
199 /*
200 * We over-allocated the table, so we're guaranteed to find an empty slot.
201 * Further, we guarantee that the hashtable size is not 0.
202 */
203 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700204 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000205 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100206 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000207 return kDuplicateEntry;
208 }
209 ent = (ent + 1) & (hash_table_size - 1);
210 }
211
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100212 hash_table[ent].name = name.name;
213 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000214 return 0;
215}
216
Narayan Kamath7462f022013-11-21 13:05:04 +0000217static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
218 ZipArchive* archive, off64_t file_length,
Narayan Kamath926973e2014-06-09 14:18:14 +0100219 off64_t read_amount, uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000220 const off64_t search_start = file_length - read_amount;
221
222 if (lseek64(fd, search_start, SEEK_SET) != search_start) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100223 ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
224 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000225 return kIoError;
226 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800227 if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100228 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
229 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000230 return kIoError;
231 }
232
233 /*
234 * Scan backward for the EOCD magic. In an archive without a trailing
235 * comment, we'll find it on the first try. (We may want to consider
236 * doing an initial minimal read; if we don't find it, retry with a
237 * second read as above.)
238 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100239 int i = read_amount - sizeof(EocdRecord);
240 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700241 if (scan_buffer[i] == 0x50) {
242 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
243 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
244 ALOGV("+++ Found EOCD at buf+%d", i);
245 break;
246 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000247 }
248 }
249 if (i < 0) {
250 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
251 return kInvalidFile;
252 }
253
254 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100255 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000256 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100257 * Verify that there's no trailing space at the end of the central directory
258 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000259 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100260 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
261 + eocd->comment_length;
262 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100263 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100264 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100265 return kInvalidFile;
266 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000267
Narayan Kamath926973e2014-06-09 14:18:14 +0100268 /*
269 * Grab the CD offset and size, and the number of entries in the
270 * archive and verify that they look reasonable.
271 */
Tianjie Xu1ee48922016-09-21 14:58:11 -0700272 if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100273 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
274 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Tianjie Xu1ee48922016-09-21 14:58:11 -0700275#if defined(__ANDROID__)
276 if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
277 android_errorWriteLog(0x534e4554, "31251826");
278 }
279#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000280 return kInvalidOffset;
281 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100282 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000283 ALOGW("Zip: empty archive?");
284 return kEmptyArchive;
285 }
286
Elliott Hughese49236b2015-06-04 15:21:59 -0700287 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100288 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000289
290 /*
291 * It all looks good. Create a mapping for the CD, and set the fields
292 * in archive.
293 */
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800294 if (!archive->directory_map.create(debug_file_name, fd,
295 static_cast<off64_t>(eocd->cd_start_offset),
296 static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
Narayan Kamatheaf98852013-12-11 14:51:51 +0000297 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000298 }
299
Narayan Kamath926973e2014-06-09 14:18:14 +0100300 archive->num_entries = eocd->num_records;
301 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000302
303 return 0;
304}
305
306/*
307 * Find the zip Central Directory and memory-map it.
308 *
309 * On success, returns 0 after populating fields from the EOCD area:
310 * directory_offset
311 * directory_map
312 * num_entries
313 */
314static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
315 ZipArchive* archive) {
316
317 // Test file length. We use lseek64 to make sure the file
318 // is small enough to be a zip file (Its size must be less than
319 // 0xffffffff bytes).
320 off64_t file_length = lseek64(fd, 0, SEEK_END);
321 if (file_length == -1) {
322 ALOGV("Zip: lseek on fd %d failed", fd);
323 return kInvalidFile;
324 }
325
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800326 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100327 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000328 return kInvalidFile;
329 }
330
Narayan Kamath926973e2014-06-09 14:18:14 +0100331 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
332 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000333 return kInvalidFile;
334 }
335
336 /*
337 * Perform the traditional EOCD snipe hunt.
338 *
339 * We're searching for the End of Central Directory magic number,
340 * which appears at the start of the EOCD block. It's followed by
341 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
342 * need to read the last part of the file into a buffer, dig through
343 * it to find the magic number, parse some values out, and use those
344 * to determine the extent of the CD.
345 *
346 * We start by pulling in the last part of the file.
347 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100348 off64_t read_amount = kMaxEOCDSearch;
349 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000350 read_amount = file_length;
351 }
352
Narayan Kamath926973e2014-06-09 14:18:14 +0100353 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
Narayan Kamath7462f022013-11-21 13:05:04 +0000354 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
355 file_length, read_amount, scan_buffer);
356
357 free(scan_buffer);
358 return result;
359}
360
361/*
362 * Parses the Zip archive's Central Directory. Allocates and populates the
363 * hash table.
364 *
365 * Returns 0 on success.
366 */
367static int32_t ParseZipArchive(ZipArchive* archive) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800368 const uint8_t* const cd_ptr =
369 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800370 const size_t cd_length = archive->directory_map.getDataLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100371 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000372
373 /*
374 * Create hash table. We have a minimum 75% load factor, possibly as
375 * low as 50% after we round off to a power of 2. There must be at
376 * least one unused entry to avoid an infinite loop during creation.
377 */
378 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700379 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
380 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000381
382 /*
383 * Walk through the central directory, adding entries to the hash
384 * table and verifying values.
385 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100386 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000387 const uint8_t* ptr = cd_ptr;
388 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100389 const CentralDirectoryRecord* cdr =
390 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
391 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700392 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800393 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000394 }
395
Narayan Kamath926973e2014-06-09 14:18:14 +0100396 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700397 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800398 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000399 }
400
Narayan Kamath926973e2014-06-09 14:18:14 +0100401 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000402 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800403 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
404 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800405 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000406 }
407
Narayan Kamath926973e2014-06-09 14:18:14 +0100408 const uint16_t file_name_length = cdr->file_name_length;
409 const uint16_t extra_length = cdr->extra_field_length;
410 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100411 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
412
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000413 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
414 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800415 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100416 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000417
418 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700419 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100420 entry_name.name = file_name;
421 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000422 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100423 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800424 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000425 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800426 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000427 }
428
Narayan Kamath926973e2014-06-09 14:18:14 +0100429 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
430 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700431 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
432 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800433 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000434 }
435 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700436 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000437
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800438 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000439}
440
441static int32_t OpenArchiveInternal(ZipArchive* archive,
442 const char* debug_file_name) {
443 int32_t result = -1;
444 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
445 return result;
446 }
447
448 if ((result = ParseZipArchive(archive))) {
449 return result;
450 }
451
452 return 0;
453}
454
455int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700456 ZipArchiveHandle* handle, bool assume_ownership) {
457 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000458 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000459 return OpenArchiveInternal(archive, debug_file_name);
460}
461
462int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100463 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700464 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000465 *handle = archive;
466
Narayan Kamath7462f022013-11-21 13:05:04 +0000467 if (fd < 0) {
468 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
469 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000470 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700471
Narayan Kamath7462f022013-11-21 13:05:04 +0000472 return OpenArchiveInternal(archive, fileName);
473}
474
475/*
476 * Close a ZipArchive, closing the file and freeing the contents.
477 */
478void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800479 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000480 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100481 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000482}
483
484static int32_t UpdateEntryFromDataDescriptor(int fd,
485 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100486 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800487 if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000488 return kIoError;
489 }
490
Narayan Kamath926973e2014-06-09 14:18:14 +0100491 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
492 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
493 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000494
Narayan Kamath926973e2014-06-09 14:18:14 +0100495 entry->crc32 = descriptor->crc32;
496 entry->compressed_length = descriptor->compressed_size;
497 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000498
499 return 0;
500}
501
502// Attempts to read |len| bytes into |buf| at offset |off|.
Adam Lesinskib1911402016-03-09 17:13:09 -0800503// On non-Windows platforms, callers are guaranteed that the |fd|
504// offset is unchanged and there is no side effect to this call.
505//
506// On Windows platforms this is not thread-safe.
Yabin Cuib2a77002016-02-08 16:26:33 -0800507static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
Adam Lesinskib1911402016-03-09 17:13:09 -0800508#if !defined(_WIN32)
509 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
510#else
Narayan Kamath7462f022013-11-21 13:05:04 +0000511 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700512 ALOGW("Zip: failed seek to offset %" PRId64, off);
Yabin Cuib2a77002016-02-08 16:26:33 -0800513 return false;
Narayan Kamath7462f022013-11-21 13:05:04 +0000514 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800515 return android::base::ReadFully(fd, buf, len);
Adam Lesinskib1911402016-03-09 17:13:09 -0800516#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000517}
518
519static int32_t FindEntry(const ZipArchive* archive, const int ent,
520 ZipEntry* data) {
521 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000522
523 // Recover the start of the central directory entry from the filename
524 // pointer. The filename is the first entry past the fixed-size data,
525 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100526 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100527 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000528
529 // This is the base of our mmapped region, we have to sanity check that
530 // the name that's in the hash table is a pointer to a location within
531 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100532 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800533 archive->directory_map.getDataPtr());
534 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000535 ALOGW("Zip: Invalid entry pointer");
536 return kInvalidOffset;
537 }
538
Narayan Kamath926973e2014-06-09 14:18:14 +0100539 const CentralDirectoryRecord *cdr =
540 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
541
Narayan Kamath7462f022013-11-21 13:05:04 +0000542 // The offset of the start of the central directory in the zipfile.
543 // We keep this lying around so that we can sanity check all our lengths
544 // and our per-file structures.
545 const off64_t cd_offset = archive->directory_offset;
546
547 // Fill out the compression method, modification time, crc32
548 // and other interesting attributes from the central directory. These
549 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100550 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900551 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100552 data->crc32 = cdr->crc32;
553 data->compressed_length = cdr->compressed_size;
554 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000555
556 // Figure out the local header offset from the central directory. The
557 // actual file data will begin after the local header and the name /
558 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100559 const off64_t local_header_offset = cdr->local_file_header_offset;
560 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000561 ALOGW("Zip: bad local hdr offset in zip");
562 return kInvalidOffset;
563 }
564
Narayan Kamath926973e2014-06-09 14:18:14 +0100565 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800566 if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800567 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
568 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000569 return kIoError;
570 }
571
Narayan Kamath926973e2014-06-09 14:18:14 +0100572 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
573
574 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700575 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100576 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000577 return kInvalidOffset;
578 }
579
580 // Paranoia: Match the values specified in the local file header
581 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100582 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000583 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100584 if (data->compressed_length != lfh->compressed_size
585 || data->uncompressed_length != lfh->uncompressed_size
586 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700587 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
588 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000589 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100590 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000591 return kInconsistentInformation;
592 }
593 } else {
594 data->has_data_descriptor = 1;
595 }
596
597 // Check that the local file header name matches the declared
598 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100599 if (lfh->file_name_length == nameLen) {
600 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200601 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000602 ALOGW("Zip: Invalid declared length");
603 return kInvalidOffset;
604 }
605
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800606 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Yabin Cuib2a77002016-02-08 16:26:33 -0800607 if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800608 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000609 free(name_buf);
610 return kIoError;
611 }
612
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100613 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000614 free(name_buf);
615 return kInconsistentInformation;
616 }
617
618 free(name_buf);
619 } else {
620 ALOGW("Zip: lfh name did not match central directory.");
621 return kInconsistentInformation;
622 }
623
Narayan Kamath926973e2014-06-09 14:18:14 +0100624 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
625 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000626 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800627 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000628 return kInvalidOffset;
629 }
630
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800631 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700632 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800633 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000634 return kInvalidOffset;
635 }
636
637 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800638 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700639 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800640 static_cast<int64_t>(data_offset), data->uncompressed_length,
641 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000642 return kInvalidOffset;
643 }
644
645 data->offset = data_offset;
646 return 0;
647}
648
649struct IterationHandle {
650 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100651 // We're not using vector here because this code is used in the Windows SDK
652 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700653 ZipString prefix;
654 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000655 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100656
Yusuke Sato07447542015-06-25 14:39:19 -0700657 IterationHandle(const ZipString* in_prefix,
658 const ZipString* in_suffix) {
659 if (in_prefix) {
660 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
661 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
662 prefix.name = name_copy;
663 prefix.name_length = in_prefix->name_length;
664 } else {
665 prefix.name = NULL;
666 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700667 }
Yusuke Sato07447542015-06-25 14:39:19 -0700668 if (in_suffix) {
669 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
670 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
671 suffix.name = name_copy;
672 suffix.name_length = in_suffix->name_length;
673 } else {
674 suffix.name = NULL;
675 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700676 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100677 }
678
679 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700680 delete[] prefix.name;
681 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100682 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000683};
684
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100685int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700686 const ZipString* optional_prefix,
687 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800688 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000689
690 if (archive == NULL || archive->hash_table == NULL) {
691 ALOGW("Zip: Invalid ZipArchiveHandle");
692 return kInvalidHandle;
693 }
694
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700695 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000696 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000697 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000698
699 *cookie_ptr = cookie ;
700 return 0;
701}
702
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100703void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100704 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100705}
706
Yusuke Sato07447542015-06-25 14:39:19 -0700707int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000708 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800709 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100710 if (entryName.name_length == 0) {
711 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000712 return kInvalidEntryName;
713 }
714
715 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100716 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000717
718 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100719 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000720 return ent;
721 }
722
723 return FindEntry(archive, ent, data);
724}
725
Yusuke Sato07447542015-06-25 14:39:19 -0700726int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800727 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000728 if (handle == NULL) {
729 return kInvalidHandle;
730 }
731
732 ZipArchive* archive = handle->archive;
733 if (archive == NULL || archive->hash_table == NULL) {
734 ALOGW("Zip: Invalid ZipArchiveHandle");
735 return kInvalidHandle;
736 }
737
738 const uint32_t currentOffset = handle->position;
739 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700740 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000741
742 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
743 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700744 (handle->prefix.name_length == 0 ||
745 hash_table[i].StartsWith(handle->prefix)) &&
746 (handle->suffix.name_length == 0 ||
747 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000748 handle->position = (i + 1);
749 const int error = FindEntry(archive, i, data);
750 if (!error) {
751 name->name = hash_table[i].name;
752 name->name_length = hash_table[i].name_length;
753 }
754
755 return error;
756 }
757 }
758
759 handle->position = 0;
760 return kIterationEnd;
761}
762
Narayan Kamathf899bd52015-04-17 11:53:14 +0100763class Writer {
764 public:
765 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
766 virtual ~Writer() {}
767 protected:
768 Writer() = default;
769 private:
770 DISALLOW_COPY_AND_ASSIGN(Writer);
771};
772
773// A Writer that writes data to a fixed size memory region.
774// The size of the memory region must be equal to the total size of
775// the data appended to it.
776class MemoryWriter : public Writer {
777 public:
778 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
779 buf_(buf), size_(size), bytes_written_(0) {
780 }
781
782 virtual bool Append(uint8_t* buf, size_t buf_size) override {
783 if (bytes_written_ + buf_size > size_) {
784 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
785 size_, bytes_written_ + buf_size);
786 return false;
787 }
788
789 memcpy(buf_ + bytes_written_, buf, buf_size);
790 bytes_written_ += buf_size;
791 return true;
792 }
793
794 private:
795 uint8_t* const buf_;
796 const size_t size_;
797 size_t bytes_written_;
798};
799
800// A Writer that appends data to a file |fd| at its current position.
801// The file will be truncated to the end of the written data.
802class FileWriter : public Writer {
803 public:
804
805 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
806 // guaranteeing that the file descriptor is valid and that there's enough
807 // space on the volume to write out the entry completely and that the file
808 // is truncated to the correct length.
809 //
810 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
811 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
812 const uint32_t declared_length = entry->uncompressed_length;
813 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
814 if (current_offset == -1) {
815 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
816 return nullptr;
817 }
818
819 int result = 0;
820#if defined(__linux__)
821 if (declared_length > 0) {
822 // Make sure we have enough space on the volume to extract the compressed
823 // entry. Note that the call to ftruncate below will change the file size but
824 // will not allocate space on disk and this call to fallocate will not
825 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700826 // Note: fallocate is only supported by the following filesystems -
827 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
828 // EOPNOTSUPP error when issued in other filesystems.
829 // Hence, check for the return error code before concluding that the
830 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100831 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700832 if (result == -1 && errno == ENOSPC) {
Narayan Kamathf899bd52015-04-17 11:53:14 +0100833 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
834 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
835 return std::unique_ptr<FileWriter>(nullptr);
836 }
837 }
838#endif // __linux__
839
840 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
841 if (result == -1) {
842 ALOGW("Zip: unable to truncate 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 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
848 }
849
850 virtual bool Append(uint8_t* buf, size_t buf_size) override {
851 if (total_bytes_written_ + buf_size > declared_length_) {
852 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
853 declared_length_, total_bytes_written_ + buf_size);
854 return false;
855 }
856
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100857 const bool result = android::base::WriteFully(fd_, buf, buf_size);
858 if (result) {
859 total_bytes_written_ += buf_size;
860 } else {
861 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100862 }
863
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100864 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100865 }
866 private:
867 FileWriter(const int fd, const size_t declared_length) :
868 Writer(),
869 fd_(fd),
870 declared_length_(declared_length),
871 total_bytes_written_(0) {
872 }
873
874 const int fd_;
875 const size_t declared_length_;
876 size_t total_bytes_written_;
877};
878
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800879// This method is using libz macros with old-style-casts
880#pragma GCC diagnostic push
881#pragma GCC diagnostic ignored "-Wold-style-cast"
882static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
883 return inflateInit2(stream, window_bits);
884}
885#pragma GCC diagnostic pop
886
Narayan Kamathf899bd52015-04-17 11:53:14 +0100887static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
888 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700889 const size_t kBufSize = 32768;
890 std::vector<uint8_t> read_buf(kBufSize);
891 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000892 z_stream zstream;
893 int zerr;
894
895 /*
896 * Initialize the zlib stream struct.
897 */
898 memset(&zstream, 0, sizeof(zstream));
899 zstream.zalloc = Z_NULL;
900 zstream.zfree = Z_NULL;
901 zstream.opaque = Z_NULL;
902 zstream.next_in = NULL;
903 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700904 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000905 zstream.avail_out = kBufSize;
906 zstream.data_type = Z_UNKNOWN;
907
908 /*
909 * Use the undocumented "negative window bits" feature to tell zlib
910 * that there's no zlib header waiting for it.
911 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800912 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000913 if (zerr != Z_OK) {
914 if (zerr == Z_VERSION_ERROR) {
915 ALOGE("Installed zlib is not compatible with linked version (%s)",
916 ZLIB_VERSION);
917 } else {
918 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
919 }
920
921 return kZlibError;
922 }
923
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800924 auto zstream_deleter = [](z_stream* stream) {
925 inflateEnd(stream); /* free up any allocated structures */
926 };
927
928 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
929
Narayan Kamath7462f022013-11-21 13:05:04 +0000930 const uint32_t uncompressed_length = entry->uncompressed_length;
931
932 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000933 do {
934 /* read as much as we can */
935 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800936 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
937 if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
938 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800939 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000940 }
941
942 compressed_length -= getSize;
943
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700944 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000945 zstream.avail_in = getSize;
946 }
947
948 /* uncompress the data */
949 zerr = inflate(&zstream, Z_NO_FLUSH);
950 if (zerr != Z_OK && zerr != Z_STREAM_END) {
951 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
952 zerr, zstream.next_in, zstream.avail_in,
953 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800954 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000955 }
956
957 /* write when we're full or when we're done */
958 if (zstream.avail_out == 0 ||
959 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700960 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100961 if (!writer->Append(&write_buf[0], write_size)) {
962 // The file might have declared a bogus length.
963 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000964 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000965
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700966 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000967 zstream.avail_out = kBufSize;
968 }
969 } while (zerr == Z_OK);
970
971 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
972
973 // stream.adler holds the crc32 value for such streams.
974 *crc_out = zstream.adler;
975
976 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700977 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000978 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800979 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000980 }
981
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800982 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000983}
984
Narayan Kamathf899bd52015-04-17 11:53:14 +0100985static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
986 uint64_t *crc_out) {
987 static const uint32_t kBufSize = 32768;
988 std::vector<uint8_t> buf(kBufSize);
989
990 const uint32_t length = entry->uncompressed_length;
991 uint32_t count = 0;
992 uint64_t crc = 0;
993 while (count < length) {
994 uint32_t remaining = length - count;
995
996 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
997 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -0800998 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
999 if (!android::base::ReadFully(fd, buf.data(), block_size)) {
1000 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001001 return kIoError;
1002 }
1003
1004 if (!writer->Append(&buf[0], block_size)) {
1005 return kIoError;
1006 }
1007 crc = crc32(crc, &buf[0], block_size);
1008 count += block_size;
1009 }
1010
1011 *crc_out = crc;
1012
1013 return 0;
1014}
1015
1016int32_t ExtractToWriter(ZipArchiveHandle handle,
1017 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001018 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001019 const uint16_t method = entry->method;
1020 off64_t data_offset = entry->offset;
1021
1022 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001023 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001024 return kIoError;
1025 }
1026
1027 // this should default to kUnknownCompressionMethod.
1028 int32_t return_value = -1;
1029 uint64_t crc = 0;
1030 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001031 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001032 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001033 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001034 }
1035
1036 if (!return_value && entry->has_data_descriptor) {
1037 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1038 if (return_value) {
1039 return return_value;
1040 }
1041 }
1042
1043 // TODO: Fix this check by passing the right flags to inflate2 so that
1044 // it calculates the CRC for us.
1045 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001046 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001047 return kInconsistentInformation;
1048 }
1049
1050 return return_value;
1051}
1052
Narayan Kamathf899bd52015-04-17 11:53:14 +01001053int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1054 uint8_t* begin, uint32_t size) {
1055 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1056 return ExtractToWriter(handle, entry, writer.get());
1057}
1058
Narayan Kamath7462f022013-11-21 13:05:04 +00001059int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1060 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001061 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1062 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001063 return kIoError;
1064 }
1065
Narayan Kamathf899bd52015-04-17 11:53:14 +01001066 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001067}
1068
1069const char* ErrorCodeString(int32_t error_code) {
1070 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1071 return kErrorMessages[error_code * -1];
1072 }
1073
1074 return kErrorMessages[0];
1075}
1076
1077int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001078 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001079}