blob: 49097ce9b00415e045b5b4b78b53fac1564f04f6 [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++) {
Tianjie Xud9fd1862017-04-05 14:46:27 -0700389 if (ptr > cd_end - sizeof(CentralDirectoryRecord)) {
390 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
391#if defined(__ANDROID__)
392 android_errorWriteLog(0x534e4554, "36392138");
393#endif
394 return -1;
395 }
396
Narayan Kamath926973e2014-06-09 14:18:14 +0100397 const CentralDirectoryRecord* cdr =
398 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
399 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700400 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", 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 off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000405 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800406 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
407 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800408 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000409 }
410
Narayan Kamath926973e2014-06-09 14:18:14 +0100411 const uint16_t file_name_length = cdr->file_name_length;
412 const uint16_t extra_length = cdr->extra_field_length;
413 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100414 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
415
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000416 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
417 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800418 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100419 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000420
421 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700422 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100423 entry_name.name = file_name;
424 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000425 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100426 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800427 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000428 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800429 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000430 }
431
Narayan Kamath926973e2014-06-09 14:18:14 +0100432 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
433 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700434 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
435 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800436 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000437 }
438 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700439 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000440
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800441 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000442}
443
444static int32_t OpenArchiveInternal(ZipArchive* archive,
445 const char* debug_file_name) {
446 int32_t result = -1;
447 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
448 return result;
449 }
450
451 if ((result = ParseZipArchive(archive))) {
452 return result;
453 }
454
455 return 0;
456}
457
458int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700459 ZipArchiveHandle* handle, bool assume_ownership) {
460 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000461 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000462 return OpenArchiveInternal(archive, debug_file_name);
463}
464
465int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100466 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700467 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000468 *handle = archive;
469
Narayan Kamath7462f022013-11-21 13:05:04 +0000470 if (fd < 0) {
471 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
472 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000473 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700474
Narayan Kamath7462f022013-11-21 13:05:04 +0000475 return OpenArchiveInternal(archive, fileName);
476}
477
478/*
479 * Close a ZipArchive, closing the file and freeing the contents.
480 */
481void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800482 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000483 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100484 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000485}
486
487static int32_t UpdateEntryFromDataDescriptor(int fd,
488 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100489 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800490 if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000491 return kIoError;
492 }
493
Narayan Kamath926973e2014-06-09 14:18:14 +0100494 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
495 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
496 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000497
Narayan Kamath926973e2014-06-09 14:18:14 +0100498 entry->crc32 = descriptor->crc32;
499 entry->compressed_length = descriptor->compressed_size;
500 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000501
502 return 0;
503}
504
505// Attempts to read |len| bytes into |buf| at offset |off|.
Adam Lesinskib1911402016-03-09 17:13:09 -0800506// On non-Windows platforms, callers are guaranteed that the |fd|
507// offset is unchanged and there is no side effect to this call.
508//
509// On Windows platforms this is not thread-safe.
Yabin Cuib2a77002016-02-08 16:26:33 -0800510static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
Adam Lesinskib1911402016-03-09 17:13:09 -0800511#if !defined(_WIN32)
512 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
513#else
Narayan Kamath7462f022013-11-21 13:05:04 +0000514 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700515 ALOGW("Zip: failed seek to offset %" PRId64, off);
Yabin Cuib2a77002016-02-08 16:26:33 -0800516 return false;
Narayan Kamath7462f022013-11-21 13:05:04 +0000517 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800518 return android::base::ReadFully(fd, buf, len);
Adam Lesinskib1911402016-03-09 17:13:09 -0800519#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000520}
521
522static int32_t FindEntry(const ZipArchive* archive, const int ent,
523 ZipEntry* data) {
524 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000525
526 // Recover the start of the central directory entry from the filename
527 // pointer. The filename is the first entry past the fixed-size data,
528 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100529 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100530 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000531
532 // This is the base of our mmapped region, we have to sanity check that
533 // the name that's in the hash table is a pointer to a location within
534 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100535 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800536 archive->directory_map.getDataPtr());
537 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000538 ALOGW("Zip: Invalid entry pointer");
539 return kInvalidOffset;
540 }
541
Narayan Kamath926973e2014-06-09 14:18:14 +0100542 const CentralDirectoryRecord *cdr =
543 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
544
Narayan Kamath7462f022013-11-21 13:05:04 +0000545 // The offset of the start of the central directory in the zipfile.
546 // We keep this lying around so that we can sanity check all our lengths
547 // and our per-file structures.
548 const off64_t cd_offset = archive->directory_offset;
549
550 // Fill out the compression method, modification time, crc32
551 // and other interesting attributes from the central directory. These
552 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100553 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900554 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100555 data->crc32 = cdr->crc32;
556 data->compressed_length = cdr->compressed_size;
557 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000558
559 // Figure out the local header offset from the central directory. The
560 // actual file data will begin after the local header and the name /
561 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100562 const off64_t local_header_offset = cdr->local_file_header_offset;
563 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000564 ALOGW("Zip: bad local hdr offset in zip");
565 return kInvalidOffset;
566 }
567
Narayan Kamath926973e2014-06-09 14:18:14 +0100568 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800569 if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800570 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
571 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000572 return kIoError;
573 }
574
Narayan Kamath926973e2014-06-09 14:18:14 +0100575 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
576
577 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700578 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100579 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000580 return kInvalidOffset;
581 }
582
583 // Paranoia: Match the values specified in the local file header
584 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100585 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000586 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100587 if (data->compressed_length != lfh->compressed_size
588 || data->uncompressed_length != lfh->uncompressed_size
589 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700590 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
591 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000592 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100593 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000594 return kInconsistentInformation;
595 }
596 } else {
597 data->has_data_descriptor = 1;
598 }
599
600 // Check that the local file header name matches the declared
601 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100602 if (lfh->file_name_length == nameLen) {
603 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200604 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000605 ALOGW("Zip: Invalid declared length");
606 return kInvalidOffset;
607 }
608
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800609 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Yabin Cuib2a77002016-02-08 16:26:33 -0800610 if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800611 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000612 free(name_buf);
613 return kIoError;
614 }
615
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100616 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000617 free(name_buf);
618 return kInconsistentInformation;
619 }
620
621 free(name_buf);
622 } else {
623 ALOGW("Zip: lfh name did not match central directory.");
624 return kInconsistentInformation;
625 }
626
Narayan Kamath926973e2014-06-09 14:18:14 +0100627 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
628 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000629 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800630 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000631 return kInvalidOffset;
632 }
633
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800634 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700635 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800636 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000637 return kInvalidOffset;
638 }
639
640 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800641 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700642 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800643 static_cast<int64_t>(data_offset), data->uncompressed_length,
644 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000645 return kInvalidOffset;
646 }
647
648 data->offset = data_offset;
649 return 0;
650}
651
652struct IterationHandle {
653 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100654 // We're not using vector here because this code is used in the Windows SDK
655 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700656 ZipString prefix;
657 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000658 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100659
Yusuke Sato07447542015-06-25 14:39:19 -0700660 IterationHandle(const ZipString* in_prefix,
661 const ZipString* in_suffix) {
662 if (in_prefix) {
663 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
664 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
665 prefix.name = name_copy;
666 prefix.name_length = in_prefix->name_length;
667 } else {
668 prefix.name = NULL;
669 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700670 }
Yusuke Sato07447542015-06-25 14:39:19 -0700671 if (in_suffix) {
672 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
673 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
674 suffix.name = name_copy;
675 suffix.name_length = in_suffix->name_length;
676 } else {
677 suffix.name = NULL;
678 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700679 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100680 }
681
682 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700683 delete[] prefix.name;
684 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100685 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000686};
687
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100688int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700689 const ZipString* optional_prefix,
690 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800691 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000692
693 if (archive == NULL || archive->hash_table == NULL) {
694 ALOGW("Zip: Invalid ZipArchiveHandle");
695 return kInvalidHandle;
696 }
697
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700698 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000699 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000700 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000701
702 *cookie_ptr = cookie ;
703 return 0;
704}
705
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100706void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100707 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100708}
709
Yusuke Sato07447542015-06-25 14:39:19 -0700710int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000711 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800712 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100713 if (entryName.name_length == 0) {
714 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000715 return kInvalidEntryName;
716 }
717
718 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100719 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000720
721 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100722 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000723 return ent;
724 }
725
726 return FindEntry(archive, ent, data);
727}
728
Yusuke Sato07447542015-06-25 14:39:19 -0700729int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800730 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000731 if (handle == NULL) {
732 return kInvalidHandle;
733 }
734
735 ZipArchive* archive = handle->archive;
736 if (archive == NULL || archive->hash_table == NULL) {
737 ALOGW("Zip: Invalid ZipArchiveHandle");
738 return kInvalidHandle;
739 }
740
741 const uint32_t currentOffset = handle->position;
742 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700743 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000744
745 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
746 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700747 (handle->prefix.name_length == 0 ||
748 hash_table[i].StartsWith(handle->prefix)) &&
749 (handle->suffix.name_length == 0 ||
750 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000751 handle->position = (i + 1);
752 const int error = FindEntry(archive, i, data);
753 if (!error) {
754 name->name = hash_table[i].name;
755 name->name_length = hash_table[i].name_length;
756 }
757
758 return error;
759 }
760 }
761
762 handle->position = 0;
763 return kIterationEnd;
764}
765
Narayan Kamathf899bd52015-04-17 11:53:14 +0100766class Writer {
767 public:
768 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
769 virtual ~Writer() {}
770 protected:
771 Writer() = default;
772 private:
773 DISALLOW_COPY_AND_ASSIGN(Writer);
774};
775
776// A Writer that writes data to a fixed size memory region.
777// The size of the memory region must be equal to the total size of
778// the data appended to it.
779class MemoryWriter : public Writer {
780 public:
781 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
782 buf_(buf), size_(size), bytes_written_(0) {
783 }
784
785 virtual bool Append(uint8_t* buf, size_t buf_size) override {
786 if (bytes_written_ + buf_size > size_) {
787 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
788 size_, bytes_written_ + buf_size);
789 return false;
790 }
791
792 memcpy(buf_ + bytes_written_, buf, buf_size);
793 bytes_written_ += buf_size;
794 return true;
795 }
796
797 private:
798 uint8_t* const buf_;
799 const size_t size_;
800 size_t bytes_written_;
801};
802
803// A Writer that appends data to a file |fd| at its current position.
804// The file will be truncated to the end of the written data.
805class FileWriter : public Writer {
806 public:
807
808 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
809 // guaranteeing that the file descriptor is valid and that there's enough
810 // space on the volume to write out the entry completely and that the file
811 // is truncated to the correct length.
812 //
813 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
814 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
815 const uint32_t declared_length = entry->uncompressed_length;
816 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
817 if (current_offset == -1) {
818 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
819 return nullptr;
820 }
821
822 int result = 0;
823#if defined(__linux__)
824 if (declared_length > 0) {
825 // Make sure we have enough space on the volume to extract the compressed
826 // entry. Note that the call to ftruncate below will change the file size but
827 // will not allocate space on disk and this call to fallocate will not
828 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700829 // Note: fallocate is only supported by the following filesystems -
830 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
831 // EOPNOTSUPP error when issued in other filesystems.
832 // Hence, check for the return error code before concluding that the
833 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100834 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700835 if (result == -1 && errno == ENOSPC) {
Narayan Kamathf899bd52015-04-17 11:53:14 +0100836 ALOGW("Zip: unable to allocate space for file to %" PRId64 ": %s",
837 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
838 return std::unique_ptr<FileWriter>(nullptr);
839 }
840 }
841#endif // __linux__
842
843 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
844 if (result == -1) {
845 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
846 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
847 return std::unique_ptr<FileWriter>(nullptr);
848 }
849
850 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
851 }
852
853 virtual bool Append(uint8_t* buf, size_t buf_size) override {
854 if (total_bytes_written_ + buf_size > declared_length_) {
855 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
856 declared_length_, total_bytes_written_ + buf_size);
857 return false;
858 }
859
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100860 const bool result = android::base::WriteFully(fd_, buf, buf_size);
861 if (result) {
862 total_bytes_written_ += buf_size;
863 } else {
864 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100865 }
866
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100867 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100868 }
869 private:
870 FileWriter(const int fd, const size_t declared_length) :
871 Writer(),
872 fd_(fd),
873 declared_length_(declared_length),
874 total_bytes_written_(0) {
875 }
876
877 const int fd_;
878 const size_t declared_length_;
879 size_t total_bytes_written_;
880};
881
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800882// This method is using libz macros with old-style-casts
883#pragma GCC diagnostic push
884#pragma GCC diagnostic ignored "-Wold-style-cast"
885static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
886 return inflateInit2(stream, window_bits);
887}
888#pragma GCC diagnostic pop
889
Narayan Kamathf899bd52015-04-17 11:53:14 +0100890static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
891 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700892 const size_t kBufSize = 32768;
893 std::vector<uint8_t> read_buf(kBufSize);
894 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000895 z_stream zstream;
896 int zerr;
897
898 /*
899 * Initialize the zlib stream struct.
900 */
901 memset(&zstream, 0, sizeof(zstream));
902 zstream.zalloc = Z_NULL;
903 zstream.zfree = Z_NULL;
904 zstream.opaque = Z_NULL;
905 zstream.next_in = NULL;
906 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700907 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000908 zstream.avail_out = kBufSize;
909 zstream.data_type = Z_UNKNOWN;
910
911 /*
912 * Use the undocumented "negative window bits" feature to tell zlib
913 * that there's no zlib header waiting for it.
914 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800915 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000916 if (zerr != Z_OK) {
917 if (zerr == Z_VERSION_ERROR) {
918 ALOGE("Installed zlib is not compatible with linked version (%s)",
919 ZLIB_VERSION);
920 } else {
921 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
922 }
923
924 return kZlibError;
925 }
926
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800927 auto zstream_deleter = [](z_stream* stream) {
928 inflateEnd(stream); /* free up any allocated structures */
929 };
930
931 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
932
Narayan Kamath7462f022013-11-21 13:05:04 +0000933 const uint32_t uncompressed_length = entry->uncompressed_length;
934
935 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000936 do {
937 /* read as much as we can */
938 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800939 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
940 if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
941 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800942 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000943 }
944
945 compressed_length -= getSize;
946
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700947 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000948 zstream.avail_in = getSize;
949 }
950
951 /* uncompress the data */
952 zerr = inflate(&zstream, Z_NO_FLUSH);
953 if (zerr != Z_OK && zerr != Z_STREAM_END) {
954 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
955 zerr, zstream.next_in, zstream.avail_in,
956 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800957 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000958 }
959
960 /* write when we're full or when we're done */
961 if (zstream.avail_out == 0 ||
962 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700963 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100964 if (!writer->Append(&write_buf[0], write_size)) {
965 // The file might have declared a bogus length.
966 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000967 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000968
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700969 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000970 zstream.avail_out = kBufSize;
971 }
972 } while (zerr == Z_OK);
973
974 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
975
976 // stream.adler holds the crc32 value for such streams.
977 *crc_out = zstream.adler;
978
979 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700980 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000981 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800982 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000983 }
984
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800985 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000986}
987
Narayan Kamathf899bd52015-04-17 11:53:14 +0100988static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
989 uint64_t *crc_out) {
990 static const uint32_t kBufSize = 32768;
991 std::vector<uint8_t> buf(kBufSize);
992
993 const uint32_t length = entry->uncompressed_length;
994 uint32_t count = 0;
995 uint64_t crc = 0;
996 while (count < length) {
997 uint32_t remaining = length - count;
998
999 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1000 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -08001001 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
1002 if (!android::base::ReadFully(fd, buf.data(), block_size)) {
1003 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001004 return kIoError;
1005 }
1006
1007 if (!writer->Append(&buf[0], block_size)) {
1008 return kIoError;
1009 }
1010 crc = crc32(crc, &buf[0], block_size);
1011 count += block_size;
1012 }
1013
1014 *crc_out = crc;
1015
1016 return 0;
1017}
1018
1019int32_t ExtractToWriter(ZipArchiveHandle handle,
1020 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001021 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001022 const uint16_t method = entry->method;
1023 off64_t data_offset = entry->offset;
1024
1025 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001026 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001027 return kIoError;
1028 }
1029
1030 // this should default to kUnknownCompressionMethod.
1031 int32_t return_value = -1;
1032 uint64_t crc = 0;
1033 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001034 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001035 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001036 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001037 }
1038
1039 if (!return_value && entry->has_data_descriptor) {
1040 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1041 if (return_value) {
1042 return return_value;
1043 }
1044 }
1045
1046 // TODO: Fix this check by passing the right flags to inflate2 so that
1047 // it calculates the CRC for us.
1048 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001049 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001050 return kInconsistentInformation;
1051 }
1052
1053 return return_value;
1054}
1055
Narayan Kamathf899bd52015-04-17 11:53:14 +01001056int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1057 uint8_t* begin, uint32_t size) {
1058 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1059 return ExtractToWriter(handle, entry, writer.get());
1060}
1061
Narayan Kamath7462f022013-11-21 13:05:04 +00001062int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1063 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001064 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1065 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001066 return kIoError;
1067 }
1068
Narayan Kamathf899bd52015-04-17 11:53:14 +01001069 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001070}
1071
1072const char* ErrorCodeString(int32_t error_code) {
1073 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1074 return kErrorMessages[error_code * -1];
1075 }
1076
1077 return kErrorMessages[0];
1078}
1079
1080int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001081 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001082}