blob: 45f6f0f650a8856b544b89e63f2ff5f34b91a418 [file] [log] [blame]
Narayan Kamath7462f022013-11-21 13:05:04 +00001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Read-only access to Zip archives, with minimal heap allocation.
19 */
Narayan Kamath7462f022013-11-21 13:05:04 +000020
21#include <assert.h>
22#include <errno.h>
Mark Salyzyn99ef9912014-03-14 14:26:22 -070023#include <fcntl.h>
24#include <inttypes.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000025#include <limits.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000026#include <stdlib.h>
27#include <string.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000028#include <unistd.h>
29
Dan Albert1ae07642015-04-09 14:11:18 -070030#include <memory>
31#include <vector>
32
Elliott Hughes4f713192015-12-04 22:00:26 -080033#include "android-base/file.h"
Colin Cross7c6c7f02016-09-16 10:15:51 -070034#include "android-base/logging.h"
Elliott Hughes4f713192015-12-04 22:00:26 -080035#include "android-base/macros.h" // TEMP_FAILURE_RETRY may or may not be in unistd
36#include "android-base/memory.h"
Dan Albert1ae07642015-04-09 14:11:18 -070037#include "log/log.h"
38#include "utils/Compat.h"
39#include "utils/FileMap.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080040#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070041#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000042
Narayan Kamath044bc8e2014-12-03 18:22:53 +000043#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070044#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080045#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070046
Dan Albert1ae07642015-04-09 14:11:18 -070047using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000048
Narayan Kamath926973e2014-06-09 14:18:14 +010049// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000050// things will happen.
51#ifndef O_BINARY
52#define O_BINARY 0
53#endif
54
Narayan Kamath926973e2014-06-09 14:18:14 +010055// The maximum number of bytes to scan backwards for the EOCD start.
56static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
57
Narayan Kamath7462f022013-11-21 13:05:04 +000058static const char* kErrorMessages[] = {
59 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000060 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000061 "Zlib error",
62 "Invalid file",
63 "Invalid handle",
64 "Duplicate entries in archive",
65 "Empty archive",
66 "Entry not found",
67 "Invalid offset",
68 "Inconsistent information",
69 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000070 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000071 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000072};
73
74static const int32_t kErrorMessageUpperBound = 0;
75
Narayan Kamatheb41ad22013-12-09 16:26:36 +000076static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000077
78// We encountered a Zlib error when inflating a stream from this file.
79// Usually indicates file corruption.
80static const int32_t kZlibError = -2;
81
82// The input file cannot be processed as a zip archive. Usually because
83// it's too small, too large or does not have a valid signature.
84static const int32_t kInvalidFile = -3;
85
86// An invalid iteration / ziparchive handle was passed in as an input
87// argument.
88static const int32_t kInvalidHandle = -4;
89
90// The zip archive contained two (or possibly more) entries with the same
91// name.
92static const int32_t kDuplicateEntry = -5;
93
94// The zip archive contains no entries.
95static const int32_t kEmptyArchive = -6;
96
97// The specified entry was not found in the archive.
98static const int32_t kEntryNotFound = -7;
99
100// The zip archive contained an invalid local file header pointer.
101static const int32_t kInvalidOffset = -8;
102
103// The zip archive contained inconsistent entry information. This could
104// be because the central directory & local file header did not agree, or
105// if the actual uncompressed length or crc32 do not match their declared
106// values.
107static const int32_t kInconsistentInformation = -9;
108
109// An invalid entry name was encountered.
110static const int32_t kInvalidEntryName = -10;
111
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000112// An I/O related system call (read, lseek, ftruncate, map) failed.
113static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000114
Narayan Kamatheaf98852013-12-11 14:51:51 +0000115// We were not able to mmap the central directory or entry contents.
116static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000117
Narayan Kamatheaf98852013-12-11 14:51:51 +0000118static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000119
Narayan Kamath7462f022013-11-21 13:05:04 +0000120/*
121 * A Read-only Zip archive.
122 *
123 * We want "open" and "find entry by name" to be fast operations, and
124 * we want to use as little memory as possible. We memory-map the zip
125 * central directory, and load a hash table with pointers to the filenames
126 * (which aren't null-terminated). The other fields are at a fixed offset
127 * from the filename, so we don't need to extract those (but we do need
128 * to byte-read and endian-swap them every time we want them).
129 *
130 * It's possible that somebody has handed us a massive (~1GB) zip archive,
131 * so we can't expect to mmap the entire file.
132 *
133 * To speed comparisons when doing a lookup by name, we could make the mapping
134 * "private" (copy-on-write) and null-terminate the filenames after verifying
135 * the record structure. However, this requires a private mapping of
136 * every page that the Central Directory touches. Easier to tuck a copy
137 * of the string length into the hash table entry.
138 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000139
Narayan Kamath7462f022013-11-21 13:05:04 +0000140/*
141 * Round up to the next highest power of 2.
142 *
143 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
144 */
145static uint32_t RoundUpPower2(uint32_t val) {
146 val--;
147 val |= val >> 1;
148 val |= val >> 2;
149 val |= val >> 4;
150 val |= val >> 8;
151 val |= val >> 16;
152 val++;
153
154 return val;
155}
156
Yusuke Sato07447542015-06-25 14:39:19 -0700157static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000158 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100159 uint16_t len = name.name_length;
160 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000161
162 while (len--) {
163 hash = hash * 31 + *str++;
164 }
165
166 return hash;
167}
168
169/*
170 * Convert a ZipEntry to a hash table index, verifying that it's in a
171 * valid range.
172 */
Yusuke Sato07447542015-06-25 14:39:19 -0700173static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000174 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700175 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100176 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000177
178 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
179 uint32_t ent = hash & (hash_table_size - 1);
180 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700181 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000182 return ent;
183 }
184
185 ent = (ent + 1) & (hash_table_size - 1);
186 }
187
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100188 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000189 return kEntryNotFound;
190}
191
192/*
193 * Add a new entry to the hash table.
194 */
Yusuke Sato07447542015-06-25 14:39:19 -0700195static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
196 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100197 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000198 uint32_t ent = hash & (hash_table_size - 1);
199
200 /*
201 * We over-allocated the table, so we're guaranteed to find an empty slot.
202 * Further, we guarantee that the hashtable size is not 0.
203 */
204 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700205 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000206 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100207 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000208 return kDuplicateEntry;
209 }
210 ent = (ent + 1) & (hash_table_size - 1);
211 }
212
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100213 hash_table[ent].name = name.name;
214 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000215 return 0;
216}
217
Narayan Kamath7462f022013-11-21 13:05:04 +0000218static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
219 ZipArchive* archive, off64_t file_length,
Narayan Kamath926973e2014-06-09 14:18:14 +0100220 off64_t read_amount, uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000221 const off64_t search_start = file_length - read_amount;
222
223 if (lseek64(fd, search_start, SEEK_SET) != search_start) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100224 ALOGW("Zip: seek %" PRId64 " failed: %s", static_cast<int64_t>(search_start),
225 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000226 return kIoError;
227 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800228 if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100229 ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
230 strerror(errno));
Narayan Kamath7462f022013-11-21 13:05:04 +0000231 return kIoError;
232 }
233
234 /*
235 * Scan backward for the EOCD magic. In an archive without a trailing
236 * comment, we'll find it on the first try. (We may want to consider
237 * doing an initial minimal read; if we don't find it, retry with a
238 * second read as above.)
239 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100240 int i = read_amount - sizeof(EocdRecord);
241 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700242 if (scan_buffer[i] == 0x50) {
243 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
244 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
245 ALOGV("+++ Found EOCD at buf+%d", i);
246 break;
247 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000248 }
249 }
250 if (i < 0) {
251 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
252 return kInvalidFile;
253 }
254
255 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100256 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000257 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100258 * Verify that there's no trailing space at the end of the central directory
259 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000260 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100261 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
262 + eocd->comment_length;
263 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100264 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100265 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100266 return kInvalidFile;
267 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000268
Narayan Kamath926973e2014-06-09 14:18:14 +0100269 /*
270 * Grab the CD offset and size, and the number of entries in the
271 * archive and verify that they look reasonable.
272 */
Tianjie Xuae8180c2016-09-21 14:58:11 -0700273 if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100274 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
275 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Tianjie Xuae8180c2016-09-21 14:58:11 -0700276#if defined(__ANDROID__)
277 if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
278 android_errorWriteLog(0x534e4554, "31251826");
279 }
280#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000281 return kInvalidOffset;
282 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100283 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000284 ALOGW("Zip: empty archive?");
285 return kEmptyArchive;
286 }
287
Elliott Hughese49236b2015-06-04 15:21:59 -0700288 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100289 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000290
291 /*
292 * It all looks good. Create a mapping for the CD, and set the fields
293 * in archive.
294 */
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800295 if (!archive->directory_map.create(debug_file_name, fd,
296 static_cast<off64_t>(eocd->cd_start_offset),
297 static_cast<size_t>(eocd->cd_size), true /* read only */) ) {
Narayan Kamatheaf98852013-12-11 14:51:51 +0000298 return kMmapFailed;
Narayan Kamath7462f022013-11-21 13:05:04 +0000299 }
300
Narayan Kamath926973e2014-06-09 14:18:14 +0100301 archive->num_entries = eocd->num_records;
302 archive->directory_offset = eocd->cd_start_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000303
304 return 0;
305}
306
307/*
308 * Find the zip Central Directory and memory-map it.
309 *
310 * On success, returns 0 after populating fields from the EOCD area:
311 * directory_offset
312 * directory_map
313 * num_entries
314 */
315static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
316 ZipArchive* archive) {
317
318 // Test file length. We use lseek64 to make sure the file
319 // is small enough to be a zip file (Its size must be less than
320 // 0xffffffff bytes).
321 off64_t file_length = lseek64(fd, 0, SEEK_END);
322 if (file_length == -1) {
323 ALOGV("Zip: lseek on fd %d failed", fd);
324 return kInvalidFile;
325 }
326
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800327 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100328 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000329 return kInvalidFile;
330 }
331
Narayan Kamath926973e2014-06-09 14:18:14 +0100332 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
333 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000334 return kInvalidFile;
335 }
336
337 /*
338 * Perform the traditional EOCD snipe hunt.
339 *
340 * We're searching for the End of Central Directory magic number,
341 * which appears at the start of the EOCD block. It's followed by
342 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
343 * need to read the last part of the file into a buffer, dig through
344 * it to find the magic number, parse some values out, and use those
345 * to determine the extent of the CD.
346 *
347 * We start by pulling in the last part of the file.
348 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100349 off64_t read_amount = kMaxEOCDSearch;
350 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000351 read_amount = file_length;
352 }
353
Narayan Kamath926973e2014-06-09 14:18:14 +0100354 uint8_t* scan_buffer = reinterpret_cast<uint8_t*>(malloc(read_amount));
Narayan Kamath7462f022013-11-21 13:05:04 +0000355 int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
356 file_length, read_amount, scan_buffer);
357
358 free(scan_buffer);
359 return result;
360}
361
362/*
363 * Parses the Zip archive's Central Directory. Allocates and populates the
364 * hash table.
365 *
366 * Returns 0 on success.
367 */
368static int32_t ParseZipArchive(ZipArchive* archive) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800369 const uint8_t* const cd_ptr =
370 reinterpret_cast<const uint8_t*>(archive->directory_map.getDataPtr());
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800371 const size_t cd_length = archive->directory_map.getDataLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100372 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000373
374 /*
375 * Create hash table. We have a minimum 75% load factor, possibly as
376 * low as 50% after we round off to a power of 2. There must be at
377 * least one unused entry to avoid an infinite loop during creation.
378 */
379 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700380 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
381 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000382
383 /*
384 * Walk through the central directory, adding entries to the hash
385 * table and verifying values.
386 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100387 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000388 const uint8_t* ptr = cd_ptr;
389 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100390 const CentralDirectoryRecord* cdr =
391 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
392 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700393 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800394 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000395 }
396
Narayan Kamath926973e2014-06-09 14:18:14 +0100397 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700398 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800399 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000400 }
401
Narayan Kamath926973e2014-06-09 14:18:14 +0100402 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000403 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800404 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
405 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800406 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000407 }
408
Narayan Kamath926973e2014-06-09 14:18:14 +0100409 const uint16_t file_name_length = cdr->file_name_length;
410 const uint16_t extra_length = cdr->extra_field_length;
411 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100412 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
413
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000414 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
415 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800416 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100417 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000418
419 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700420 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100421 entry_name.name = file_name;
422 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000423 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100424 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800425 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000426 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800427 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000428 }
429
Narayan Kamath926973e2014-06-09 14:18:14 +0100430 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
431 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700432 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
433 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800434 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000435 }
436 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700437 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000438
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800439 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000440}
441
442static int32_t OpenArchiveInternal(ZipArchive* archive,
443 const char* debug_file_name) {
444 int32_t result = -1;
445 if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
446 return result;
447 }
448
449 if ((result = ParseZipArchive(archive))) {
450 return result;
451 }
452
453 return 0;
454}
455
456int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700457 ZipArchiveHandle* handle, bool assume_ownership) {
458 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000459 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000460 return OpenArchiveInternal(archive, debug_file_name);
461}
462
463int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100464 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700465 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000466 *handle = archive;
467
Narayan Kamath7462f022013-11-21 13:05:04 +0000468 if (fd < 0) {
469 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
470 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000471 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700472
Narayan Kamath7462f022013-11-21 13:05:04 +0000473 return OpenArchiveInternal(archive, fileName);
474}
475
476/*
477 * Close a ZipArchive, closing the file and freeing the contents.
478 */
479void CloseArchive(ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800480 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000481 ALOGV("Closing archive %p", archive);
Neil Fullerb1a113f2014-07-25 14:43:04 +0100482 delete archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000483}
484
485static int32_t UpdateEntryFromDataDescriptor(int fd,
486 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100487 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800488 if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000489 return kIoError;
490 }
491
Narayan Kamath926973e2014-06-09 14:18:14 +0100492 const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
493 const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
494 const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000495
Narayan Kamath926973e2014-06-09 14:18:14 +0100496 entry->crc32 = descriptor->crc32;
497 entry->compressed_length = descriptor->compressed_size;
498 entry->uncompressed_length = descriptor->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000499
500 return 0;
501}
502
503// Attempts to read |len| bytes into |buf| at offset |off|.
Adam Lesinskib1911402016-03-09 17:13:09 -0800504// On non-Windows platforms, callers are guaranteed that the |fd|
505// offset is unchanged and there is no side effect to this call.
506//
507// On Windows platforms this is not thread-safe.
Yabin Cuib2a77002016-02-08 16:26:33 -0800508static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
Adam Lesinskib1911402016-03-09 17:13:09 -0800509#if !defined(_WIN32)
510 return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
511#else
Narayan Kamath7462f022013-11-21 13:05:04 +0000512 if (lseek64(fd, off, SEEK_SET) != off) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700513 ALOGW("Zip: failed seek to offset %" PRId64, off);
Yabin Cuib2a77002016-02-08 16:26:33 -0800514 return false;
Narayan Kamath7462f022013-11-21 13:05:04 +0000515 }
Yabin Cuib2a77002016-02-08 16:26:33 -0800516 return android::base::ReadFully(fd, buf, len);
Adam Lesinskib1911402016-03-09 17:13:09 -0800517#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000518}
519
520static int32_t FindEntry(const ZipArchive* archive, const int ent,
521 ZipEntry* data) {
522 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000523
524 // Recover the start of the central directory entry from the filename
525 // pointer. The filename is the first entry past the fixed-size data,
526 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100527 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100528 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000529
530 // This is the base of our mmapped region, we have to sanity check that
531 // the name that's in the hash table is a pointer to a location within
532 // this mapped region.
Narayan Kamath926973e2014-06-09 14:18:14 +0100533 const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(
Dmitriy Ivanov4b67f832015-03-06 10:22:34 -0800534 archive->directory_map.getDataPtr());
535 if (ptr < base_ptr || ptr > base_ptr + archive->directory_map.getDataLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000536 ALOGW("Zip: Invalid entry pointer");
537 return kInvalidOffset;
538 }
539
Narayan Kamath926973e2014-06-09 14:18:14 +0100540 const CentralDirectoryRecord *cdr =
541 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
542
Narayan Kamath7462f022013-11-21 13:05:04 +0000543 // The offset of the start of the central directory in the zipfile.
544 // We keep this lying around so that we can sanity check all our lengths
545 // and our per-file structures.
546 const off64_t cd_offset = archive->directory_offset;
547
548 // Fill out the compression method, modification time, crc32
549 // and other interesting attributes from the central directory. These
550 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100551 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900552 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100553 data->crc32 = cdr->crc32;
554 data->compressed_length = cdr->compressed_size;
555 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000556
557 // Figure out the local header offset from the central directory. The
558 // actual file data will begin after the local header and the name /
559 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100560 const off64_t local_header_offset = cdr->local_file_header_offset;
561 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000562 ALOGW("Zip: bad local hdr offset in zip");
563 return kInvalidOffset;
564 }
565
Narayan Kamath926973e2014-06-09 14:18:14 +0100566 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Yabin Cuib2a77002016-02-08 16:26:33 -0800567 if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800568 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
569 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000570 return kIoError;
571 }
572
Narayan Kamath926973e2014-06-09 14:18:14 +0100573 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
574
575 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700576 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100577 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000578 return kInvalidOffset;
579 }
580
581 // Paranoia: Match the values specified in the local file header
582 // to those specified in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100583 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000584 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100585 if (data->compressed_length != lfh->compressed_size
586 || data->uncompressed_length != lfh->uncompressed_size
587 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700588 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
589 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000590 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100591 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000592 return kInconsistentInformation;
593 }
594 } else {
595 data->has_data_descriptor = 1;
596 }
597
598 // Check that the local file header name matches the declared
599 // name in the central directory.
Narayan Kamath926973e2014-06-09 14:18:14 +0100600 if (lfh->file_name_length == nameLen) {
601 const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
Mykola Kondratenko50afc152014-09-08 12:46:37 +0200602 if (name_offset + lfh->file_name_length > cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000603 ALOGW("Zip: Invalid declared length");
604 return kInvalidOffset;
605 }
606
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800607 uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
Yabin Cuib2a77002016-02-08 16:26:33 -0800608 if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800609 ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000610 free(name_buf);
611 return kIoError;
612 }
613
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100614 if (memcmp(archive->hash_table[ent].name, name_buf, nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000615 free(name_buf);
616 return kInconsistentInformation;
617 }
618
619 free(name_buf);
620 } else {
621 ALOGW("Zip: lfh name did not match central directory.");
622 return kInconsistentInformation;
623 }
624
Narayan Kamath926973e2014-06-09 14:18:14 +0100625 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
626 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000627 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800628 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000629 return kInvalidOffset;
630 }
631
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800632 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700633 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800634 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000635 return kInvalidOffset;
636 }
637
638 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800639 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700640 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800641 static_cast<int64_t>(data_offset), data->uncompressed_length,
642 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000643 return kInvalidOffset;
644 }
645
646 data->offset = data_offset;
647 return 0;
648}
649
650struct IterationHandle {
651 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100652 // We're not using vector here because this code is used in the Windows SDK
653 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700654 ZipString prefix;
655 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000656 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100657
Yusuke Sato07447542015-06-25 14:39:19 -0700658 IterationHandle(const ZipString* in_prefix,
659 const ZipString* in_suffix) {
660 if (in_prefix) {
661 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
662 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
663 prefix.name = name_copy;
664 prefix.name_length = in_prefix->name_length;
665 } else {
666 prefix.name = NULL;
667 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700668 }
Yusuke Sato07447542015-06-25 14:39:19 -0700669 if (in_suffix) {
670 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
671 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
672 suffix.name = name_copy;
673 suffix.name_length = in_suffix->name_length;
674 } else {
675 suffix.name = NULL;
676 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700677 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100678 }
679
680 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700681 delete[] prefix.name;
682 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100683 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000684};
685
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100686int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700687 const ZipString* optional_prefix,
688 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800689 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000690
691 if (archive == NULL || archive->hash_table == NULL) {
692 ALOGW("Zip: Invalid ZipArchiveHandle");
693 return kInvalidHandle;
694 }
695
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700696 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000697 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000698 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000699
700 *cookie_ptr = cookie ;
701 return 0;
702}
703
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100704void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100705 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100706}
707
Yusuke Sato07447542015-06-25 14:39:19 -0700708int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000709 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800710 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100711 if (entryName.name_length == 0) {
712 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000713 return kInvalidEntryName;
714 }
715
716 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100717 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000718
719 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100720 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000721 return ent;
722 }
723
724 return FindEntry(archive, ent, data);
725}
726
Yusuke Sato07447542015-06-25 14:39:19 -0700727int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800728 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000729 if (handle == NULL) {
730 return kInvalidHandle;
731 }
732
733 ZipArchive* archive = handle->archive;
734 if (archive == NULL || archive->hash_table == NULL) {
735 ALOGW("Zip: Invalid ZipArchiveHandle");
736 return kInvalidHandle;
737 }
738
739 const uint32_t currentOffset = handle->position;
740 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700741 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000742
743 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
744 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700745 (handle->prefix.name_length == 0 ||
746 hash_table[i].StartsWith(handle->prefix)) &&
747 (handle->suffix.name_length == 0 ||
748 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000749 handle->position = (i + 1);
750 const int error = FindEntry(archive, i, data);
751 if (!error) {
752 name->name = hash_table[i].name;
753 name->name_length = hash_table[i].name_length;
754 }
755
756 return error;
757 }
758 }
759
760 handle->position = 0;
761 return kIterationEnd;
762}
763
Narayan Kamathf899bd52015-04-17 11:53:14 +0100764class Writer {
765 public:
766 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
767 virtual ~Writer() {}
768 protected:
769 Writer() = default;
770 private:
771 DISALLOW_COPY_AND_ASSIGN(Writer);
772};
773
774// A Writer that writes data to a fixed size memory region.
775// The size of the memory region must be equal to the total size of
776// the data appended to it.
777class MemoryWriter : public Writer {
778 public:
779 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
780 buf_(buf), size_(size), bytes_written_(0) {
781 }
782
783 virtual bool Append(uint8_t* buf, size_t buf_size) override {
784 if (bytes_written_ + buf_size > size_) {
785 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
786 size_, bytes_written_ + buf_size);
787 return false;
788 }
789
790 memcpy(buf_ + bytes_written_, buf, buf_size);
791 bytes_written_ += buf_size;
792 return true;
793 }
794
795 private:
796 uint8_t* const buf_;
797 const size_t size_;
798 size_t bytes_written_;
799};
800
801// A Writer that appends data to a file |fd| at its current position.
802// The file will be truncated to the end of the written data.
803class FileWriter : public Writer {
804 public:
805
806 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
807 // guaranteeing that the file descriptor is valid and that there's enough
808 // space on the volume to write out the entry completely and that the file
809 // is truncated to the correct length.
810 //
811 // Returns a valid FileWriter on success, |nullptr| if an error occurred.
812 static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
813 const uint32_t declared_length = entry->uncompressed_length;
814 const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
815 if (current_offset == -1) {
816 ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
817 return nullptr;
818 }
819
820 int result = 0;
821#if defined(__linux__)
822 if (declared_length > 0) {
823 // Make sure we have enough space on the volume to extract the compressed
824 // entry. Note that the call to ftruncate below will change the file size but
825 // will not allocate space on disk and this call to fallocate will not
826 // change the file size.
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700827 // Note: fallocate is only supported by the following filesystems -
828 // btrfs, ext4, ocfs2, and xfs. Therefore fallocate might fail with
829 // EOPNOTSUPP error when issued in other filesystems.
830 // Hence, check for the return error code before concluding that the
831 // disk does not have enough space.
Narayan Kamathf899bd52015-04-17 11:53:14 +0100832 result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
Badhri Jagan Sridharana68d0d12015-06-02 14:47:57 -0700833 if (result == -1 && errno == ENOSPC) {
Narayan Kamathd5d7abe2016-08-10 12:24:05 +0100834 ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 " : %s",
835 static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
836 strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100837 return std::unique_ptr<FileWriter>(nullptr);
838 }
839 }
840#endif // __linux__
841
842 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
843 if (result == -1) {
844 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
845 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
846 return std::unique_ptr<FileWriter>(nullptr);
847 }
848
849 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
850 }
851
852 virtual bool Append(uint8_t* buf, size_t buf_size) override {
853 if (total_bytes_written_ + buf_size > declared_length_) {
854 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
855 declared_length_, total_bytes_written_ + buf_size);
856 return false;
857 }
858
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100859 const bool result = android::base::WriteFully(fd_, buf, buf_size);
860 if (result) {
861 total_bytes_written_ += buf_size;
862 } else {
863 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100864 }
865
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100866 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100867 }
868 private:
869 FileWriter(const int fd, const size_t declared_length) :
870 Writer(),
871 fd_(fd),
872 declared_length_(declared_length),
873 total_bytes_written_(0) {
874 }
875
876 const int fd_;
877 const size_t declared_length_;
878 size_t total_bytes_written_;
879};
880
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800881// This method is using libz macros with old-style-casts
882#pragma GCC diagnostic push
883#pragma GCC diagnostic ignored "-Wold-style-cast"
884static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
885 return inflateInit2(stream, window_bits);
886}
887#pragma GCC diagnostic pop
888
Narayan Kamathf899bd52015-04-17 11:53:14 +0100889static int32_t InflateEntryToWriter(int fd, const ZipEntry* entry,
890 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700891 const size_t kBufSize = 32768;
892 std::vector<uint8_t> read_buf(kBufSize);
893 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000894 z_stream zstream;
895 int zerr;
896
897 /*
898 * Initialize the zlib stream struct.
899 */
900 memset(&zstream, 0, sizeof(zstream));
901 zstream.zalloc = Z_NULL;
902 zstream.zfree = Z_NULL;
903 zstream.opaque = Z_NULL;
904 zstream.next_in = NULL;
905 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700906 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000907 zstream.avail_out = kBufSize;
908 zstream.data_type = Z_UNKNOWN;
909
910 /*
911 * Use the undocumented "negative window bits" feature to tell zlib
912 * that there's no zlib header waiting for it.
913 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800914 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000915 if (zerr != Z_OK) {
916 if (zerr == Z_VERSION_ERROR) {
917 ALOGE("Installed zlib is not compatible with linked version (%s)",
918 ZLIB_VERSION);
919 } else {
920 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
921 }
922
923 return kZlibError;
924 }
925
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800926 auto zstream_deleter = [](z_stream* stream) {
927 inflateEnd(stream); /* free up any allocated structures */
928 };
929
930 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
931
Narayan Kamath7462f022013-11-21 13:05:04 +0000932 const uint32_t uncompressed_length = entry->uncompressed_length;
933
934 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000935 do {
936 /* read as much as we can */
937 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800938 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
939 if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
940 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800941 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000942 }
943
944 compressed_length -= getSize;
945
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700946 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000947 zstream.avail_in = getSize;
948 }
949
950 /* uncompress the data */
951 zerr = inflate(&zstream, Z_NO_FLUSH);
952 if (zerr != Z_OK && zerr != Z_STREAM_END) {
953 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
954 zerr, zstream.next_in, zstream.avail_in,
955 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800956 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000957 }
958
959 /* write when we're full or when we're done */
960 if (zstream.avail_out == 0 ||
961 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700962 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100963 if (!writer->Append(&write_buf[0], write_size)) {
964 // The file might have declared a bogus length.
965 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000966 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000967
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700968 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000969 zstream.avail_out = kBufSize;
970 }
971 } while (zerr == Z_OK);
972
973 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
974
975 // stream.adler holds the crc32 value for such streams.
976 *crc_out = zstream.adler;
977
978 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700979 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000980 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800981 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000982 }
983
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800984 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000985}
986
Narayan Kamathf899bd52015-04-17 11:53:14 +0100987static int32_t CopyEntryToWriter(int fd, const ZipEntry* entry, Writer* writer,
988 uint64_t *crc_out) {
989 static const uint32_t kBufSize = 32768;
990 std::vector<uint8_t> buf(kBufSize);
991
992 const uint32_t length = entry->uncompressed_length;
993 uint32_t count = 0;
994 uint64_t crc = 0;
995 while (count < length) {
996 uint32_t remaining = length - count;
997
998 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
999 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -08001000 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
1001 if (!android::base::ReadFully(fd, buf.data(), block_size)) {
1002 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001003 return kIoError;
1004 }
1005
1006 if (!writer->Append(&buf[0], block_size)) {
1007 return kIoError;
1008 }
1009 crc = crc32(crc, &buf[0], block_size);
1010 count += block_size;
1011 }
1012
1013 *crc_out = crc;
1014
1015 return 0;
1016}
1017
1018int32_t ExtractToWriter(ZipArchiveHandle handle,
1019 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001020 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001021 const uint16_t method = entry->method;
1022 off64_t data_offset = entry->offset;
1023
1024 if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001025 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001026 return kIoError;
1027 }
1028
1029 // this should default to kUnknownCompressionMethod.
1030 int32_t return_value = -1;
1031 uint64_t crc = 0;
1032 if (method == kCompressStored) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001033 return_value = CopyEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001034 } else if (method == kCompressDeflated) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001035 return_value = InflateEntryToWriter(archive->fd, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001036 }
1037
1038 if (!return_value && entry->has_data_descriptor) {
1039 return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
1040 if (return_value) {
1041 return return_value;
1042 }
1043 }
1044
1045 // TODO: Fix this check by passing the right flags to inflate2 so that
1046 // it calculates the CRC for us.
1047 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001048 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001049 return kInconsistentInformation;
1050 }
1051
1052 return return_value;
1053}
1054
Narayan Kamathf899bd52015-04-17 11:53:14 +01001055int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1056 uint8_t* begin, uint32_t size) {
1057 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1058 return ExtractToWriter(handle, entry, writer.get());
1059}
1060
Narayan Kamath7462f022013-11-21 13:05:04 +00001061int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1062 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001063 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1064 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001065 return kIoError;
1066 }
1067
Narayan Kamathf899bd52015-04-17 11:53:14 +01001068 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001069}
1070
1071const char* ErrorCodeString(int32_t error_code) {
1072 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1073 return kErrorMessages[error_code * -1];
1074 }
1075
1076 return kErrorMessages[0];
1077}
1078
1079int GetFileDescriptor(const ZipArchiveHandle handle) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001080 return reinterpret_cast<ZipArchive*>(handle)->fd;
Narayan Kamath7462f022013-11-21 13:05:04 +00001081}
Colin Cross7c6c7f02016-09-16 10:15:51 -07001082
1083ZipString::ZipString(const char* entry_name)
1084 : name(reinterpret_cast<const uint8_t*>(entry_name)) {
1085 size_t len = strlen(entry_name);
1086 CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
1087 name_length = static_cast<uint16_t>(len);
1088}