blob: 6860c4e2dd4d041864efbfb5701fa233ab50b2de [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
Mark Salyzyncfd5b082016-10-17 14:28:00 -070021#define LOG_TAG "ziparchive"
22
Narayan Kamath7462f022013-11-21 13:05:04 +000023#include <assert.h>
24#include <errno.h>
Mark Salyzyn99ef9912014-03-14 14:26:22 -070025#include <fcntl.h>
26#include <inttypes.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000027#include <limits.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000028#include <stdlib.h>
29#include <string.h>
Elliott Hughes55fd2932017-05-28 22:59:04 -070030#include <time.h>
Narayan Kamath7462f022013-11-21 13:05:04 +000031#include <unistd.h>
32
Dan Albert1ae07642015-04-09 14:11:18 -070033#include <memory>
34#include <vector>
35
Mark Salyzynff2dcd92016-09-28 15:54:45 -070036#include <android-base/file.h>
37#include <android-base/logging.h>
38#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
39#include <android-base/memory.h>
Mark Salyzyncfd5b082016-10-17 14:28:00 -070040#include <log/log.h>
Mark Salyzynff2dcd92016-09-28 15:54:45 -070041#include <utils/Compat.h>
42#include <utils/FileMap.h>
Christopher Ferrise6884ce2015-11-10 14:55:12 -080043#include "ziparchive/zip_archive.h"
Dan Albert1ae07642015-04-09 14:11:18 -070044#include "zlib.h"
Narayan Kamath7462f022013-11-21 13:05:04 +000045
Narayan Kamath044bc8e2014-12-03 18:22:53 +000046#include "entry_name_utils-inl.h"
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070047#include "zip_archive_common.h"
Christopher Ferrise6884ce2015-11-10 14:55:12 -080048#include "zip_archive_private.h"
Mark Salyzyn99ef9912014-03-14 14:26:22 -070049
Dan Albert1ae07642015-04-09 14:11:18 -070050using android::base::get_unaligned;
Narayan Kamath044bc8e2014-12-03 18:22:53 +000051
Narayan Kamath926973e2014-06-09 14:18:14 +010052// This is for windows. If we don't open a file in binary mode, weird
Narayan Kamath7462f022013-11-21 13:05:04 +000053// things will happen.
54#ifndef O_BINARY
55#define O_BINARY 0
56#endif
57
Narayan Kamath926973e2014-06-09 14:18:14 +010058// The maximum number of bytes to scan backwards for the EOCD start.
59static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
60
Narayan Kamath7462f022013-11-21 13:05:04 +000061static const char* kErrorMessages[] = {
62 "Unknown return code.",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000063 "Iteration ended",
Narayan Kamath7462f022013-11-21 13:05:04 +000064 "Zlib error",
65 "Invalid file",
66 "Invalid handle",
67 "Duplicate entries in archive",
68 "Empty archive",
69 "Entry not found",
70 "Invalid offset",
71 "Inconsistent information",
72 "Invalid entry name",
Narayan Kamatheb41ad22013-12-09 16:26:36 +000073 "I/O Error",
Narayan Kamatheaf98852013-12-11 14:51:51 +000074 "File mapping failed"
Narayan Kamath7462f022013-11-21 13:05:04 +000075};
76
77static const int32_t kErrorMessageUpperBound = 0;
78
Narayan Kamatheb41ad22013-12-09 16:26:36 +000079static const int32_t kIterationEnd = -1;
Narayan Kamath7462f022013-11-21 13:05:04 +000080
81// We encountered a Zlib error when inflating a stream from this file.
82// Usually indicates file corruption.
83static const int32_t kZlibError = -2;
84
85// The input file cannot be processed as a zip archive. Usually because
86// it's too small, too large or does not have a valid signature.
87static const int32_t kInvalidFile = -3;
88
89// An invalid iteration / ziparchive handle was passed in as an input
90// argument.
91static const int32_t kInvalidHandle = -4;
92
93// The zip archive contained two (or possibly more) entries with the same
94// name.
95static const int32_t kDuplicateEntry = -5;
96
97// The zip archive contains no entries.
98static const int32_t kEmptyArchive = -6;
99
100// The specified entry was not found in the archive.
101static const int32_t kEntryNotFound = -7;
102
103// The zip archive contained an invalid local file header pointer.
104static const int32_t kInvalidOffset = -8;
105
106// The zip archive contained inconsistent entry information. This could
107// be because the central directory & local file header did not agree, or
108// if the actual uncompressed length or crc32 do not match their declared
109// values.
110static const int32_t kInconsistentInformation = -9;
111
112// An invalid entry name was encountered.
113static const int32_t kInvalidEntryName = -10;
114
Narayan Kamatheb41ad22013-12-09 16:26:36 +0000115// An I/O related system call (read, lseek, ftruncate, map) failed.
116static const int32_t kIoError = -11;
Narayan Kamath7462f022013-11-21 13:05:04 +0000117
Narayan Kamatheaf98852013-12-11 14:51:51 +0000118// We were not able to mmap the central directory or entry contents.
119static const int32_t kMmapFailed = -12;
Narayan Kamath7462f022013-11-21 13:05:04 +0000120
Narayan Kamatheaf98852013-12-11 14:51:51 +0000121static const int32_t kErrorMessageLowerBound = -13;
Narayan Kamath7462f022013-11-21 13:05:04 +0000122
Narayan Kamath7462f022013-11-21 13:05:04 +0000123/*
124 * A Read-only Zip archive.
125 *
126 * We want "open" and "find entry by name" to be fast operations, and
127 * we want to use as little memory as possible. We memory-map the zip
128 * central directory, and load a hash table with pointers to the filenames
129 * (which aren't null-terminated). The other fields are at a fixed offset
130 * from the filename, so we don't need to extract those (but we do need
131 * to byte-read and endian-swap them every time we want them).
132 *
133 * It's possible that somebody has handed us a massive (~1GB) zip archive,
134 * so we can't expect to mmap the entire file.
135 *
136 * To speed comparisons when doing a lookup by name, we could make the mapping
137 * "private" (copy-on-write) and null-terminate the filenames after verifying
138 * the record structure. However, this requires a private mapping of
139 * every page that the Central Directory touches. Easier to tuck a copy
140 * of the string length into the hash table entry.
141 */
Narayan Kamath7462f022013-11-21 13:05:04 +0000142
Narayan Kamath7462f022013-11-21 13:05:04 +0000143/*
144 * Round up to the next highest power of 2.
145 *
146 * Found on http://graphics.stanford.edu/~seander/bithacks.html.
147 */
148static uint32_t RoundUpPower2(uint32_t val) {
149 val--;
150 val |= val >> 1;
151 val |= val >> 2;
152 val |= val >> 4;
153 val |= val >> 8;
154 val |= val >> 16;
155 val++;
156
157 return val;
158}
159
Yusuke Sato07447542015-06-25 14:39:19 -0700160static uint32_t ComputeHash(const ZipString& name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000161 uint32_t hash = 0;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100162 uint16_t len = name.name_length;
163 const uint8_t* str = name.name;
Narayan Kamath7462f022013-11-21 13:05:04 +0000164
165 while (len--) {
166 hash = hash * 31 + *str++;
167 }
168
169 return hash;
170}
171
172/*
173 * Convert a ZipEntry to a hash table index, verifying that it's in a
174 * valid range.
175 */
Yusuke Sato07447542015-06-25 14:39:19 -0700176static int64_t EntryToIndex(const ZipString* hash_table,
Narayan Kamath7462f022013-11-21 13:05:04 +0000177 const uint32_t hash_table_size,
Yusuke Sato07447542015-06-25 14:39:19 -0700178 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100179 const uint32_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000180
181 // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
182 uint32_t ent = hash & (hash_table_size - 1);
183 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700184 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000185 return ent;
186 }
187
188 ent = (ent + 1) & (hash_table_size - 1);
189 }
190
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100191 ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000192 return kEntryNotFound;
193}
194
195/*
196 * Add a new entry to the hash table.
197 */
Yusuke Sato07447542015-06-25 14:39:19 -0700198static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
199 const ZipString& name) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100200 const uint64_t hash = ComputeHash(name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000201 uint32_t ent = hash & (hash_table_size - 1);
202
203 /*
204 * We over-allocated the table, so we're guaranteed to find an empty slot.
205 * Further, we guarantee that the hashtable size is not 0.
206 */
207 while (hash_table[ent].name != NULL) {
Yusuke Sato07447542015-06-25 14:39:19 -0700208 if (hash_table[ent] == name) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000209 // We've found a duplicate entry. We don't accept it
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100210 ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000211 return kDuplicateEntry;
212 }
213 ent = (ent + 1) & (hash_table_size - 1);
214 }
215
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100216 hash_table[ent].name = name.name;
217 hash_table[ent].name_length = name.name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000218 return 0;
219}
220
Tianjie Xu18c25922016-09-29 15:27:41 -0700221static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
222 off64_t file_length, off64_t read_amount,
223 uint8_t* scan_buffer) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000224 const off64_t search_start = file_length - read_amount;
225
Tianjie Xu18c25922016-09-29 15:27:41 -0700226 if(!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
227 ALOGE("Zip: read %" PRId64 " from offset %" PRId64 " failed",
228 static_cast<int64_t>(read_amount), static_cast<int64_t>(search_start));
Narayan Kamath7462f022013-11-21 13:05:04 +0000229 return kIoError;
230 }
231
232 /*
233 * Scan backward for the EOCD magic. In an archive without a trailing
234 * comment, we'll find it on the first try. (We may want to consider
235 * doing an initial minimal read; if we don't find it, retry with a
236 * second read as above.)
237 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100238 int i = read_amount - sizeof(EocdRecord);
239 for (; i >= 0; i--) {
Dan Albert1ae07642015-04-09 14:11:18 -0700240 if (scan_buffer[i] == 0x50) {
241 uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
242 if (get_unaligned<uint32_t>(sig_addr) == EocdRecord::kSignature) {
243 ALOGV("+++ Found EOCD at buf+%d", i);
244 break;
245 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000246 }
247 }
248 if (i < 0) {
249 ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
250 return kInvalidFile;
251 }
252
253 const off64_t eocd_offset = search_start + i;
Narayan Kamath926973e2014-06-09 14:18:14 +0100254 const EocdRecord* eocd = reinterpret_cast<const EocdRecord*>(scan_buffer + i);
Narayan Kamath7462f022013-11-21 13:05:04 +0000255 /*
Narayan Kamath926973e2014-06-09 14:18:14 +0100256 * Verify that there's no trailing space at the end of the central directory
257 * and its comment.
Narayan Kamath7462f022013-11-21 13:05:04 +0000258 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100259 const off64_t calculated_length = eocd_offset + sizeof(EocdRecord)
260 + eocd->comment_length;
261 if (calculated_length != file_length) {
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100262 ALOGW("Zip: %" PRId64 " extraneous bytes at the end of the central directory",
Narayan Kamath926973e2014-06-09 14:18:14 +0100263 static_cast<int64_t>(file_length - calculated_length));
Narayan Kamath4f6b4992014-06-03 13:59:23 +0100264 return kInvalidFile;
265 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000266
Narayan Kamath926973e2014-06-09 14:18:14 +0100267 /*
268 * Grab the CD offset and size, and the number of entries in the
269 * archive and verify that they look reasonable.
270 */
Tianjie Xu1ee48922016-09-21 14:58:11 -0700271 if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100272 ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
273 eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
Tianjie Xu1ee48922016-09-21 14:58:11 -0700274#if defined(__ANDROID__)
275 if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
276 android_errorWriteLog(0x534e4554, "31251826");
277 }
278#endif
Narayan Kamath7462f022013-11-21 13:05:04 +0000279 return kInvalidOffset;
280 }
Narayan Kamath926973e2014-06-09 14:18:14 +0100281 if (eocd->num_records == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000282 ALOGW("Zip: empty archive?");
283 return kEmptyArchive;
284 }
285
Elliott Hughese49236b2015-06-04 15:21:59 -0700286 ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100287 eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
Narayan Kamath7462f022013-11-21 13:05:04 +0000288
289 /*
290 * It all looks good. Create a mapping for the CD, and set the fields
291 * in archive.
292 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700293
294 if (!archive->InitializeCentralDirectory(debug_file_name,
295 static_cast<off64_t>(eocd->cd_start_offset),
296 static_cast<size_t>(eocd->cd_size))) {
297 ALOGE("Zip: failed to intialize central directory.\n");
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
Tianjie Xu18c25922016-09-29 15:27:41 -0700312 * directory_ptr
Narayan Kamath7462f022013-11-21 13:05:04 +0000313 * num_entries
314 */
Tianjie Xu18c25922016-09-29 15:27:41 -0700315static int32_t MapCentralDirectory(const char* debug_file_name, ZipArchive* archive) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000316
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).
Tianjie Xu18c25922016-09-29 15:27:41 -0700320 off64_t file_length = archive->mapped_zip.GetFileLength();
Narayan Kamath7462f022013-11-21 13:05:04 +0000321 if (file_length == -1) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000322 return kInvalidFile;
323 }
324
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800325 if (file_length > static_cast<off64_t>(0xffffffff)) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100326 ALOGV("Zip: zip file too long %" PRId64, static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000327 return kInvalidFile;
328 }
329
Narayan Kamath926973e2014-06-09 14:18:14 +0100330 if (file_length < static_cast<off64_t>(sizeof(EocdRecord))) {
331 ALOGV("Zip: length %" PRId64 " is too small to be zip", static_cast<int64_t>(file_length));
Narayan Kamath7462f022013-11-21 13:05:04 +0000332 return kInvalidFile;
333 }
334
335 /*
336 * Perform the traditional EOCD snipe hunt.
337 *
338 * We're searching for the End of Central Directory magic number,
339 * which appears at the start of the EOCD block. It's followed by
340 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
341 * need to read the last part of the file into a buffer, dig through
342 * it to find the magic number, parse some values out, and use those
343 * to determine the extent of the CD.
344 *
345 * We start by pulling in the last part of the file.
346 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100347 off64_t read_amount = kMaxEOCDSearch;
348 if (file_length < read_amount) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000349 read_amount = file_length;
350 }
351
Tianjie Xu18c25922016-09-29 15:27:41 -0700352 std::vector<uint8_t> scan_buffer(read_amount);
353 int32_t result = MapCentralDirectory0(debug_file_name, archive, file_length, read_amount,
354 scan_buffer.data());
Narayan Kamath7462f022013-11-21 13:05:04 +0000355 return result;
356}
357
358/*
359 * Parses the Zip archive's Central Directory. Allocates and populates the
360 * hash table.
361 *
362 * Returns 0 on success.
363 */
364static int32_t ParseZipArchive(ZipArchive* archive) {
Tianjie Xu18c25922016-09-29 15:27:41 -0700365 const uint8_t* const cd_ptr = archive->central_directory.GetBasePtr();
366 const size_t cd_length = archive->central_directory.GetMapLength();
Narayan Kamath926973e2014-06-09 14:18:14 +0100367 const uint16_t num_entries = archive->num_entries;
Narayan Kamath7462f022013-11-21 13:05:04 +0000368
369 /*
370 * Create hash table. We have a minimum 75% load factor, possibly as
371 * low as 50% after we round off to a power of 2. There must be at
372 * least one unused entry to avoid an infinite loop during creation.
373 */
374 archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
Yusuke Sato07447542015-06-25 14:39:19 -0700375 archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
376 sizeof(ZipString)));
Narayan Kamath7462f022013-11-21 13:05:04 +0000377
378 /*
379 * Walk through the central directory, adding entries to the hash
380 * table and verifying values.
381 */
Narayan Kamath926973e2014-06-09 14:18:14 +0100382 const uint8_t* const cd_end = cd_ptr + cd_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000383 const uint8_t* ptr = cd_ptr;
384 for (uint16_t i = 0; i < num_entries; i++) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100385 const CentralDirectoryRecord* cdr =
386 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
387 if (cdr->record_signature != CentralDirectoryRecord::kSignature) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700388 ALOGW("Zip: missed a central dir sig (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800389 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000390 }
391
Narayan Kamath926973e2014-06-09 14:18:14 +0100392 if (ptr + sizeof(CentralDirectoryRecord) > cd_end) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700393 ALOGW("Zip: ran off the end (at %" PRIu16 ")", i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800394 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000395 }
396
Narayan Kamath926973e2014-06-09 14:18:14 +0100397 const off64_t local_header_offset = cdr->local_file_header_offset;
Narayan Kamath7462f022013-11-21 13:05:04 +0000398 if (local_header_offset >= archive->directory_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800399 ALOGW("Zip: bad LFH offset %" PRId64 " at entry %" PRIu16,
400 static_cast<int64_t>(local_header_offset), i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800401 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000402 }
403
Narayan Kamath926973e2014-06-09 14:18:14 +0100404 const uint16_t file_name_length = cdr->file_name_length;
405 const uint16_t extra_length = cdr->extra_field_length;
406 const uint16_t comment_length = cdr->comment_length;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100407 const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
408
Narayan Kamath044bc8e2014-12-03 18:22:53 +0000409 /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
410 if (!IsValidEntryName(file_name, file_name_length)) {
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800411 return -1;
Piotr Jastrzebski78271ba2014-08-15 12:53:00 +0100412 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000413
414 /* add the CDE filename to the hash table */
Yusuke Sato07447542015-06-25 14:39:19 -0700415 ZipString entry_name;
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100416 entry_name.name = file_name;
417 entry_name.name_length = file_name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000418 const int add_result = AddToHash(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100419 archive->hash_table_size, entry_name);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800420 if (add_result != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000421 ALOGW("Zip: Error adding entry to hash table %d", add_result);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800422 return add_result;
Narayan Kamath7462f022013-11-21 13:05:04 +0000423 }
424
Narayan Kamath926973e2014-06-09 14:18:14 +0100425 ptr += sizeof(CentralDirectoryRecord) + file_name_length + extra_length + comment_length;
426 if ((ptr - cd_ptr) > static_cast<int64_t>(cd_length)) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700427 ALOGW("Zip: bad CD advance (%tu vs %zu) at entry %" PRIu16,
428 ptr - cd_ptr, cd_length, i);
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800429 return -1;
Narayan Kamath7462f022013-11-21 13:05:04 +0000430 }
431 }
Mark Salyzyn088bf902014-05-08 16:02:20 -0700432 ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
Narayan Kamath7462f022013-11-21 13:05:04 +0000433
Dmitriy Ivanov3ea93da2015-03-06 11:48:47 -0800434 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000435}
436
437static int32_t OpenArchiveInternal(ZipArchive* archive,
438 const char* debug_file_name) {
439 int32_t result = -1;
Tianjie Xu18c25922016-09-29 15:27:41 -0700440 if ((result = MapCentralDirectory(debug_file_name, archive)) != 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000441 return result;
442 }
443
444 if ((result = ParseZipArchive(archive))) {
445 return result;
446 }
447
448 return 0;
449}
450
451int32_t OpenArchiveFd(int fd, const char* debug_file_name,
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700452 ZipArchiveHandle* handle, bool assume_ownership) {
453 ZipArchive* archive = new ZipArchive(fd, assume_ownership);
Narayan Kamath7462f022013-11-21 13:05:04 +0000454 *handle = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000455 return OpenArchiveInternal(archive, debug_file_name);
456}
457
458int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
Neil Fullerb1a113f2014-07-25 14:43:04 +0100459 const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700460 ZipArchive* archive = new ZipArchive(fd, true);
Narayan Kamath7462f022013-11-21 13:05:04 +0000461 *handle = archive;
462
Narayan Kamath7462f022013-11-21 13:05:04 +0000463 if (fd < 0) {
464 ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
465 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000466 }
Dmitriy Ivanov40b52b22014-07-15 19:33:00 -0700467
Narayan Kamath7462f022013-11-21 13:05:04 +0000468 return OpenArchiveInternal(archive, fileName);
469}
470
Tianjie Xu18c25922016-09-29 15:27:41 -0700471int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
472 ZipArchiveHandle *handle) {
473 ZipArchive* archive = new ZipArchive(address, length);
474 *handle = archive;
475 return OpenArchiveInternal(archive, debug_file_name);
476}
477
Narayan Kamath7462f022013-11-21 13:05:04 +0000478/*
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
Tianjie Xu18c25922016-09-29 15:27:41 -0700487static int32_t UpdateEntryFromDataDescriptor(MappedZipFile& mapped_zip,
Narayan Kamath7462f022013-11-21 13:05:04 +0000488 ZipEntry *entry) {
Narayan Kamath926973e2014-06-09 14:18:14 +0100489 uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700490 if (!mapped_zip.ReadData(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
Narayan Kamath7462f022013-11-21 13:05:04 +0000505static int32_t FindEntry(const ZipArchive* archive, const int ent,
506 ZipEntry* data) {
507 const uint16_t nameLen = archive->hash_table[ent].name_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000508
509 // Recover the start of the central directory entry from the filename
510 // pointer. The filename is the first entry past the fixed-size data,
511 // so we can just subtract back from that.
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100512 const uint8_t* ptr = archive->hash_table[ent].name;
Narayan Kamath926973e2014-06-09 14:18:14 +0100513 ptr -= sizeof(CentralDirectoryRecord);
Narayan Kamath7462f022013-11-21 13:05:04 +0000514
515 // This is the base of our mmapped region, we have to sanity check that
516 // the name that's in the hash table is a pointer to a location within
517 // this mapped region.
Tianjie Xu18c25922016-09-29 15:27:41 -0700518 const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
519 if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000520 ALOGW("Zip: Invalid entry pointer");
521 return kInvalidOffset;
522 }
523
Narayan Kamath926973e2014-06-09 14:18:14 +0100524 const CentralDirectoryRecord *cdr =
525 reinterpret_cast<const CentralDirectoryRecord*>(ptr);
526
Narayan Kamath7462f022013-11-21 13:05:04 +0000527 // The offset of the start of the central directory in the zipfile.
528 // We keep this lying around so that we can sanity check all our lengths
529 // and our per-file structures.
530 const off64_t cd_offset = archive->directory_offset;
531
532 // Fill out the compression method, modification time, crc32
533 // and other interesting attributes from the central directory. These
534 // will later be compared against values from the local file header.
Narayan Kamath926973e2014-06-09 14:18:14 +0100535 data->method = cdr->compression_method;
beonit0e99a2f2015-07-18 02:08:16 +0900536 data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
Narayan Kamath926973e2014-06-09 14:18:14 +0100537 data->crc32 = cdr->crc32;
538 data->compressed_length = cdr->compressed_size;
539 data->uncompressed_length = cdr->uncompressed_size;
Narayan Kamath7462f022013-11-21 13:05:04 +0000540
541 // Figure out the local header offset from the central directory. The
542 // actual file data will begin after the local header and the name /
543 // extra comments.
Narayan Kamath926973e2014-06-09 14:18:14 +0100544 const off64_t local_header_offset = cdr->local_file_header_offset;
545 if (local_header_offset + static_cast<off64_t>(sizeof(LocalFileHeader)) >= cd_offset) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000546 ALOGW("Zip: bad local hdr offset in zip");
547 return kInvalidOffset;
548 }
549
Narayan Kamath926973e2014-06-09 14:18:14 +0100550 uint8_t lfh_buf[sizeof(LocalFileHeader)];
Tianjie Xu18c25922016-09-29 15:27:41 -0700551 if (!archive->mapped_zip.ReadAtOffset(lfh_buf, sizeof(lfh_buf), local_header_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800552 ALOGW("Zip: failed reading lfh name from offset %" PRId64,
553 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000554 return kIoError;
555 }
556
Narayan Kamath926973e2014-06-09 14:18:14 +0100557 const LocalFileHeader *lfh = reinterpret_cast<const LocalFileHeader*>(lfh_buf);
558
559 if (lfh->lfh_signature != LocalFileHeader::kSignature) {
Mark Salyzyn99ef9912014-03-14 14:26:22 -0700560 ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
Narayan Kamath926973e2014-06-09 14:18:14 +0100561 static_cast<int64_t>(local_header_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000562 return kInvalidOffset;
563 }
564
565 // Paranoia: Match the values specified in the local file header
566 // to those specified in the central directory.
Adam Lesinskid987c9d2017-04-06 18:55:47 -0700567
568 // Verify that the central directory and local file header agree on the use of a trailing
569 // Data Descriptor.
570 if ((lfh->gpb_flags & kGPBDDFlagMask) != (cdr->gpb_flags & kGPBDDFlagMask)) {
571 ALOGW("Zip: gpb flag mismatch. expected {%04" PRIx16 "}, was {%04" PRIx16 "}",
572 cdr->gpb_flags, lfh->gpb_flags);
573 return kInconsistentInformation;
574 }
575
576 // If there is no trailing data descriptor, verify that the central directory and local file
577 // header agree on the crc, compressed, and uncompressed sizes of the entry.
Narayan Kamath926973e2014-06-09 14:18:14 +0100578 if ((lfh->gpb_flags & kGPBDDFlagMask) == 0) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000579 data->has_data_descriptor = 0;
Narayan Kamath926973e2014-06-09 14:18:14 +0100580 if (data->compressed_length != lfh->compressed_size
581 || data->uncompressed_length != lfh->uncompressed_size
582 || data->crc32 != lfh->crc32) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700583 ALOGW("Zip: size/crc32 mismatch. expected {%" PRIu32 ", %" PRIu32
584 ", %" PRIx32 "}, was {%" PRIu32 ", %" PRIu32 ", %" PRIx32 "}",
Narayan Kamath7462f022013-11-21 13:05:04 +0000585 data->compressed_length, data->uncompressed_length, data->crc32,
Narayan Kamath926973e2014-06-09 14:18:14 +0100586 lfh->compressed_size, lfh->uncompressed_size, lfh->crc32);
Narayan Kamath7462f022013-11-21 13:05:04 +0000587 return kInconsistentInformation;
588 }
589 } else {
590 data->has_data_descriptor = 1;
591 }
592
Elliott Hughes55fd2932017-05-28 22:59:04 -0700593 // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
594 if ((cdr->version_made_by >> 8) == 3) {
595 data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
596 } else {
597 data->unix_mode = 0777;
598 }
599
Narayan Kamath7462f022013-11-21 13:05:04 +0000600 // 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
Tianjie Xu18c25922016-09-29 15:27:41 -0700609 std::vector<uint8_t> name_buf(nameLen);
610 if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), 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 return kIoError;
613 }
614
Tianjie Xu18c25922016-09-29 15:27:41 -0700615 if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000616 return kInconsistentInformation;
617 }
618
Narayan Kamath7462f022013-11-21 13:05:04 +0000619 } else {
620 ALOGW("Zip: lfh name did not match central directory.");
621 return kInconsistentInformation;
622 }
623
Narayan Kamath926973e2014-06-09 14:18:14 +0100624 const off64_t data_offset = local_header_offset + sizeof(LocalFileHeader)
625 + lfh->file_name_length + lfh->extra_field_length;
Narayan Kamath48953a12014-01-24 12:32:39 +0000626 if (data_offset > cd_offset) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800627 ALOGW("Zip: bad data offset %" PRId64 " in zip", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000628 return kInvalidOffset;
629 }
630
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800631 if (static_cast<off64_t>(data_offset + data->compressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700632 ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800633 static_cast<int64_t>(data_offset), data->compressed_length, static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000634 return kInvalidOffset;
635 }
636
637 if (data->method == kCompressStored &&
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800638 static_cast<off64_t>(data_offset + data->uncompressed_length) > cd_offset) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700639 ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %" PRIu32 " > %" PRId64 ")",
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800640 static_cast<int64_t>(data_offset), data->uncompressed_length,
641 static_cast<int64_t>(cd_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +0000642 return kInvalidOffset;
643 }
644
645 data->offset = data_offset;
646 return 0;
647}
648
649struct IterationHandle {
650 uint32_t position;
Piotr Jastrzebski10aa9a02014-08-19 09:01:20 +0100651 // We're not using vector here because this code is used in the Windows SDK
652 // where the STL is not available.
Yusuke Sato07447542015-06-25 14:39:19 -0700653 ZipString prefix;
654 ZipString suffix;
Narayan Kamath7462f022013-11-21 13:05:04 +0000655 ZipArchive* archive;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100656
Yusuke Sato07447542015-06-25 14:39:19 -0700657 IterationHandle(const ZipString* in_prefix,
658 const ZipString* in_suffix) {
659 if (in_prefix) {
660 uint8_t* name_copy = new uint8_t[in_prefix->name_length];
661 memcpy(name_copy, in_prefix->name, in_prefix->name_length);
662 prefix.name = name_copy;
663 prefix.name_length = in_prefix->name_length;
664 } else {
665 prefix.name = NULL;
666 prefix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700667 }
Yusuke Sato07447542015-06-25 14:39:19 -0700668 if (in_suffix) {
669 uint8_t* name_copy = new uint8_t[in_suffix->name_length];
670 memcpy(name_copy, in_suffix->name, in_suffix->name_length);
671 suffix.name = name_copy;
672 suffix.name_length = in_suffix->name_length;
673 } else {
674 suffix.name = NULL;
675 suffix.name_length = 0;
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700676 }
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100677 }
678
679 ~IterationHandle() {
Yusuke Sato07447542015-06-25 14:39:19 -0700680 delete[] prefix.name;
681 delete[] suffix.name;
Piotr Jastrzebski8e085362014-08-18 11:37:45 +0100682 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000683};
684
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100685int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
Yusuke Sato07447542015-06-25 14:39:19 -0700686 const ZipString* optional_prefix,
687 const ZipString* optional_suffix) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800688 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +0000689
690 if (archive == NULL || archive->hash_table == NULL) {
691 ALOGW("Zip: Invalid ZipArchiveHandle");
692 return kInvalidHandle;
693 }
694
Yusuke Satof1d3d3b2015-06-25 14:09:00 -0700695 IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
Narayan Kamath7462f022013-11-21 13:05:04 +0000696 cookie->position = 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000697 cookie->archive = archive;
Narayan Kamath7462f022013-11-21 13:05:04 +0000698
699 *cookie_ptr = cookie ;
700 return 0;
701}
702
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100703void EndIteration(void* cookie) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100704 delete reinterpret_cast<IterationHandle*>(cookie);
Piotr Jastrzebski79c8b342014-08-08 14:02:17 +0100705}
706
Yusuke Sato07447542015-06-25 14:39:19 -0700707int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
Narayan Kamath7462f022013-11-21 13:05:04 +0000708 ZipEntry* data) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800709 const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100710 if (entryName.name_length == 0) {
711 ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000712 return kInvalidEntryName;
713 }
714
715 const int64_t ent = EntryToIndex(archive->hash_table,
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100716 archive->hash_table_size, entryName);
Narayan Kamath7462f022013-11-21 13:05:04 +0000717
718 if (ent < 0) {
Piotr Jastrzebskiecccc5a2014-08-11 16:35:11 +0100719 ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
Narayan Kamath7462f022013-11-21 13:05:04 +0000720 return ent;
721 }
722
723 return FindEntry(archive, ent, data);
724}
725
Yusuke Sato07447542015-06-25 14:39:19 -0700726int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -0800727 IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
Narayan Kamath7462f022013-11-21 13:05:04 +0000728 if (handle == NULL) {
729 return kInvalidHandle;
730 }
731
732 ZipArchive* archive = handle->archive;
733 if (archive == NULL || archive->hash_table == NULL) {
734 ALOGW("Zip: Invalid ZipArchiveHandle");
735 return kInvalidHandle;
736 }
737
738 const uint32_t currentOffset = handle->position;
739 const uint32_t hash_table_length = archive->hash_table_size;
Yusuke Sato07447542015-06-25 14:39:19 -0700740 const ZipString* hash_table = archive->hash_table;
Narayan Kamath7462f022013-11-21 13:05:04 +0000741
742 for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
743 if (hash_table[i].name != NULL &&
Yusuke Sato07447542015-06-25 14:39:19 -0700744 (handle->prefix.name_length == 0 ||
745 hash_table[i].StartsWith(handle->prefix)) &&
746 (handle->suffix.name_length == 0 ||
747 hash_table[i].EndsWith(handle->suffix))) {
Narayan Kamath7462f022013-11-21 13:05:04 +0000748 handle->position = (i + 1);
749 const int error = FindEntry(archive, i, data);
750 if (!error) {
751 name->name = hash_table[i].name;
752 name->name_length = hash_table[i].name_length;
753 }
754
755 return error;
756 }
757 }
758
759 handle->position = 0;
760 return kIterationEnd;
761}
762
Narayan Kamathf899bd52015-04-17 11:53:14 +0100763class Writer {
764 public:
765 virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
766 virtual ~Writer() {}
767 protected:
768 Writer() = default;
769 private:
770 DISALLOW_COPY_AND_ASSIGN(Writer);
771};
772
773// A Writer that writes data to a fixed size memory region.
774// The size of the memory region must be equal to the total size of
775// the data appended to it.
776class MemoryWriter : public Writer {
777 public:
778 MemoryWriter(uint8_t* buf, size_t size) : Writer(),
779 buf_(buf), size_(size), bytes_written_(0) {
780 }
781
782 virtual bool Append(uint8_t* buf, size_t buf_size) override {
783 if (bytes_written_ + buf_size > size_) {
784 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
785 size_, bytes_written_ + buf_size);
786 return false;
787 }
788
789 memcpy(buf_ + bytes_written_, buf, buf_size);
790 bytes_written_ += buf_size;
791 return true;
792 }
793
794 private:
795 uint8_t* const buf_;
796 const size_t size_;
797 size_t bytes_written_;
798};
799
800// A Writer that appends data to a file |fd| at its current position.
801// The file will be truncated to the end of the written data.
802class FileWriter : public Writer {
803 public:
804
805 // Creates a FileWriter for |fd| and prepare to write |entry| to it,
806 // guaranteeing that the file descriptor is valid and that there's enough
807 // space on the volume to write out the entry completely and that the file
Tao Baoa456c212016-11-15 10:08:07 -0800808 // is truncated to the correct length (no truncation if |fd| references a
809 // block device).
Narayan Kamathf899bd52015-04-17 11:53:14 +0100810 //
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
Tao Baoa456c212016-11-15 10:08:07 -0800842 struct stat sb;
843 if (fstat(fd, &sb) == -1) {
844 ALOGW("Zip: unable to fstat file: %s", strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100845 return std::unique_ptr<FileWriter>(nullptr);
846 }
847
Tao Baoa456c212016-11-15 10:08:07 -0800848 // Block device doesn't support ftruncate(2).
849 if (!S_ISBLK(sb.st_mode)) {
850 result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
851 if (result == -1) {
852 ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
853 static_cast<int64_t>(declared_length + current_offset), strerror(errno));
854 return std::unique_ptr<FileWriter>(nullptr);
855 }
856 }
857
Narayan Kamathf899bd52015-04-17 11:53:14 +0100858 return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
859 }
860
861 virtual bool Append(uint8_t* buf, size_t buf_size) override {
862 if (total_bytes_written_ + buf_size > declared_length_) {
863 ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)",
864 declared_length_, total_bytes_written_ + buf_size);
865 return false;
866 }
867
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100868 const bool result = android::base::WriteFully(fd_, buf, buf_size);
869 if (result) {
870 total_bytes_written_ += buf_size;
871 } else {
872 ALOGW("Zip: unable to write " ZD " bytes to file; %s", buf_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +0100873 }
874
Narayan Kamathe97e66e2015-04-27 16:25:53 +0100875 return result;
Narayan Kamathf899bd52015-04-17 11:53:14 +0100876 }
877 private:
878 FileWriter(const int fd, const size_t declared_length) :
879 Writer(),
880 fd_(fd),
881 declared_length_(declared_length),
882 total_bytes_written_(0) {
883 }
884
885 const int fd_;
886 const size_t declared_length_;
887 size_t total_bytes_written_;
888};
889
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800890// This method is using libz macros with old-style-casts
891#pragma GCC diagnostic push
892#pragma GCC diagnostic ignored "-Wold-style-cast"
893static inline int zlib_inflateInit2(z_stream* stream, int window_bits) {
894 return inflateInit2(stream, window_bits);
895}
896#pragma GCC diagnostic pop
897
Tianjie Xu18c25922016-09-29 15:27:41 -0700898static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100899 Writer* writer, uint64_t* crc_out) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700900 const size_t kBufSize = 32768;
901 std::vector<uint8_t> read_buf(kBufSize);
902 std::vector<uint8_t> write_buf(kBufSize);
Narayan Kamath7462f022013-11-21 13:05:04 +0000903 z_stream zstream;
904 int zerr;
905
906 /*
907 * Initialize the zlib stream struct.
908 */
909 memset(&zstream, 0, sizeof(zstream));
910 zstream.zalloc = Z_NULL;
911 zstream.zfree = Z_NULL;
912 zstream.opaque = Z_NULL;
913 zstream.next_in = NULL;
914 zstream.avail_in = 0;
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700915 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000916 zstream.avail_out = kBufSize;
917 zstream.data_type = Z_UNKNOWN;
918
919 /*
920 * Use the undocumented "negative window bits" feature to tell zlib
921 * that there's no zlib header waiting for it.
922 */
Dmitriy Ivanovf94e1592015-03-06 13:27:59 -0800923 zerr = zlib_inflateInit2(&zstream, -MAX_WBITS);
Narayan Kamath7462f022013-11-21 13:05:04 +0000924 if (zerr != Z_OK) {
925 if (zerr == Z_VERSION_ERROR) {
926 ALOGE("Installed zlib is not compatible with linked version (%s)",
927 ZLIB_VERSION);
928 } else {
929 ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
930 }
931
932 return kZlibError;
933 }
934
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800935 auto zstream_deleter = [](z_stream* stream) {
936 inflateEnd(stream); /* free up any allocated structures */
937 };
938
939 std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
940
Narayan Kamath7462f022013-11-21 13:05:04 +0000941 const uint32_t uncompressed_length = entry->uncompressed_length;
942
943 uint32_t compressed_length = entry->compressed_length;
Narayan Kamath7462f022013-11-21 13:05:04 +0000944 do {
945 /* read as much as we can */
946 if (zstream.avail_in == 0) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800947 const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
Tianjie Xu18c25922016-09-29 15:27:41 -0700948 if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
Yabin Cuib2a77002016-02-08 16:26:33 -0800949 ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800950 return kIoError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000951 }
952
953 compressed_length -= getSize;
954
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700955 zstream.next_in = &read_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000956 zstream.avail_in = getSize;
957 }
958
959 /* uncompress the data */
960 zerr = inflate(&zstream, Z_NO_FLUSH);
961 if (zerr != Z_OK && zerr != Z_STREAM_END) {
962 ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
963 zerr, zstream.next_in, zstream.avail_in,
964 zstream.next_out, zstream.avail_out);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800965 return kZlibError;
Narayan Kamath7462f022013-11-21 13:05:04 +0000966 }
967
968 /* write when we're full or when we're done */
969 if (zstream.avail_out == 0 ||
970 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700971 const size_t write_size = zstream.next_out - &write_buf[0];
Narayan Kamathf899bd52015-04-17 11:53:14 +0100972 if (!writer->Append(&write_buf[0], write_size)) {
973 // The file might have declared a bogus length.
974 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000975 }
Narayan Kamath7462f022013-11-21 13:05:04 +0000976
Dmitriy Ivanovedbabfe2015-03-12 09:58:15 -0700977 zstream.next_out = &write_buf[0];
Narayan Kamath7462f022013-11-21 13:05:04 +0000978 zstream.avail_out = kBufSize;
979 }
980 } while (zerr == Z_OK);
981
982 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
983
984 // stream.adler holds the crc32 value for such streams.
985 *crc_out = zstream.adler;
986
987 if (zstream.total_out != uncompressed_length || compressed_length != 0) {
Mark Salyzyn088bf902014-05-08 16:02:20 -0700988 ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")",
Narayan Kamath7462f022013-11-21 13:05:04 +0000989 zstream.total_out, uncompressed_length);
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800990 return kInconsistentInformation;
Narayan Kamath7462f022013-11-21 13:05:04 +0000991 }
992
Dmitriy Ivanov1f741e52015-03-06 14:26:37 -0800993 return 0;
Narayan Kamath7462f022013-11-21 13:05:04 +0000994}
995
Tianjie Xu18c25922016-09-29 15:27:41 -0700996static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
Narayan Kamathf899bd52015-04-17 11:53:14 +0100997 uint64_t *crc_out) {
998 static const uint32_t kBufSize = 32768;
999 std::vector<uint8_t> buf(kBufSize);
1000
1001 const uint32_t length = entry->uncompressed_length;
1002 uint32_t count = 0;
1003 uint64_t crc = 0;
1004 while (count < length) {
1005 uint32_t remaining = length - count;
1006
1007 // Safe conversion because kBufSize is narrow enough for a 32 bit signed
1008 // value.
Yabin Cuib2a77002016-02-08 16:26:33 -08001009 const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
Tianjie Xu18c25922016-09-29 15:27:41 -07001010 if (!mapped_zip.ReadData(buf.data(), block_size)) {
Yabin Cuib2a77002016-02-08 16:26:33 -08001011 ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
Narayan Kamathf899bd52015-04-17 11:53:14 +01001012 return kIoError;
1013 }
1014
1015 if (!writer->Append(&buf[0], block_size)) {
1016 return kIoError;
1017 }
1018 crc = crc32(crc, &buf[0], block_size);
1019 count += block_size;
1020 }
1021
1022 *crc_out = crc;
1023
1024 return 0;
1025}
1026
1027int32_t ExtractToWriter(ZipArchiveHandle handle,
1028 ZipEntry* entry, Writer* writer) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001029 ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
Narayan Kamath7462f022013-11-21 13:05:04 +00001030 const uint16_t method = entry->method;
1031 off64_t data_offset = entry->offset;
1032
Tianjie Xu18c25922016-09-29 15:27:41 -07001033 if (!archive->mapped_zip.SeekToOffset(data_offset)) {
Dmitriy Ivanovf4cb8e22015-03-06 10:50:56 -08001034 ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
Narayan Kamath7462f022013-11-21 13:05:04 +00001035 return kIoError;
1036 }
1037
1038 // this should default to kUnknownCompressionMethod.
1039 int32_t return_value = -1;
1040 uint64_t crc = 0;
1041 if (method == kCompressStored) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001042 return_value = CopyEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001043 } else if (method == kCompressDeflated) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001044 return_value = InflateEntryToWriter(archive->mapped_zip, entry, writer, &crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001045 }
1046
1047 if (!return_value && entry->has_data_descriptor) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001048 return_value = UpdateEntryFromDataDescriptor(archive->mapped_zip, entry);
Narayan Kamath7462f022013-11-21 13:05:04 +00001049 if (return_value) {
1050 return return_value;
1051 }
1052 }
1053
1054 // TODO: Fix this check by passing the right flags to inflate2 so that
1055 // it calculates the CRC for us.
1056 if (entry->crc32 != crc && false) {
Mark Salyzyn088bf902014-05-08 16:02:20 -07001057 ALOGW("Zip: crc mismatch: expected %" PRIu32 ", was %" PRIu64, entry->crc32, crc);
Narayan Kamath7462f022013-11-21 13:05:04 +00001058 return kInconsistentInformation;
1059 }
1060
1061 return return_value;
1062}
1063
Narayan Kamathf899bd52015-04-17 11:53:14 +01001064int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
1065 uint8_t* begin, uint32_t size) {
1066 std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
1067 return ExtractToWriter(handle, entry, writer.get());
1068}
1069
Narayan Kamath7462f022013-11-21 13:05:04 +00001070int32_t ExtractEntryToFile(ZipArchiveHandle handle,
1071 ZipEntry* entry, int fd) {
Narayan Kamathf899bd52015-04-17 11:53:14 +01001072 std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
1073 if (writer.get() == nullptr) {
Narayan Kamath7462f022013-11-21 13:05:04 +00001074 return kIoError;
1075 }
1076
Narayan Kamathf899bd52015-04-17 11:53:14 +01001077 return ExtractToWriter(handle, entry, writer.get());
Narayan Kamath7462f022013-11-21 13:05:04 +00001078}
1079
1080const char* ErrorCodeString(int32_t error_code) {
1081 if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
1082 return kErrorMessages[error_code * -1];
1083 }
1084
1085 return kErrorMessages[0];
1086}
1087
1088int GetFileDescriptor(const ZipArchiveHandle handle) {
Tianjie Xu18c25922016-09-29 15:27:41 -07001089 return reinterpret_cast<ZipArchive*>(handle)->mapped_zip.GetFileDescriptor();
Narayan Kamath7462f022013-11-21 13:05:04 +00001090}
Colin Cross7c6c7f02016-09-16 10:15:51 -07001091
1092ZipString::ZipString(const char* entry_name)
1093 : name(reinterpret_cast<const uint8_t*>(entry_name)) {
1094 size_t len = strlen(entry_name);
1095 CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
1096 name_length = static_cast<uint16_t>(len);
1097}
Tianjie Xu18c25922016-09-29 15:27:41 -07001098
1099#if !defined(_WIN32)
1100class ProcessWriter : public Writer {
1101 public:
1102 ProcessWriter(ProcessZipEntryFunction func, void* cookie) : Writer(),
1103 proc_function_(func),
1104 cookie_(cookie) {
1105 }
1106
1107 virtual bool Append(uint8_t* buf, size_t buf_size) override {
1108 return proc_function_(buf, buf_size, cookie_);
1109 }
1110
1111 private:
1112 ProcessZipEntryFunction proc_function_;
1113 void* cookie_;
1114};
1115
1116int32_t ProcessZipEntryContents(ZipArchiveHandle handle, ZipEntry* entry,
1117 ProcessZipEntryFunction func, void* cookie) {
1118 ProcessWriter writer(func, cookie);
1119 return ExtractToWriter(handle, entry, &writer);
1120}
1121
1122#endif //!defined(_WIN32)
1123
1124int MappedZipFile::GetFileDescriptor() const {
1125 if (!has_fd_) {
1126 ALOGW("Zip: MappedZipFile doesn't have a file descriptor.");
1127 return -1;
1128 }
1129 return fd_;
1130}
1131
1132void* MappedZipFile::GetBasePtr() const {
1133 if (has_fd_) {
1134 ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
1135 return nullptr;
1136 }
1137 return base_ptr_;
1138}
1139
1140off64_t MappedZipFile::GetFileLength() const {
1141 if (has_fd_) {
1142 off64_t result = lseek64(fd_, 0, SEEK_END);
1143 if (result == -1) {
1144 ALOGE("Zip: lseek on fd %d failed: %s", fd_, strerror(errno));
1145 }
1146 return result;
1147 } else {
1148 if (base_ptr_ == nullptr) {
1149 ALOGE("Zip: invalid file map\n");
1150 return -1;
1151 }
1152 return static_cast<off64_t>(data_length_);
1153 }
1154}
1155
1156bool MappedZipFile::SeekToOffset(off64_t offset) {
1157 if (has_fd_) {
1158 if (lseek64(fd_, offset, SEEK_SET) != offset) {
1159 ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
1160 return false;
1161 }
1162 return true;
1163 } else {
1164 if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
1165 ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n" , offset,
1166 data_length_);
1167 return false;
1168 }
1169
1170 read_pos_ = offset;
1171 return true;
1172 }
1173}
1174
1175bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
1176 if (has_fd_) {
1177 if(!android::base::ReadFully(fd_, buffer, read_amount)) {
1178 ALOGE("Zip: read from %d failed\n", fd_);
1179 return false;
1180 }
1181 } else {
1182 memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
1183 read_pos_ += read_amount;
1184 }
1185 return true;
1186}
1187
1188// Attempts to read |len| bytes into |buf| at offset |off|.
1189bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
1190#if !defined(_WIN32)
1191 if (has_fd_) {
1192 if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
1193 ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
1194 return false;
1195 }
1196 return true;
1197 }
1198#endif
1199 if (!SeekToOffset(off)) {
1200 return false;
1201 }
1202 return ReadData(buf, len);
1203
1204}
1205
1206void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
1207 base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
1208 length_ = cd_size;
1209}
1210
1211bool ZipArchive::InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
1212 size_t cd_size) {
1213 if (mapped_zip.HasFd()) {
1214 if (!directory_map->create(debug_file_name, mapped_zip.GetFileDescriptor(),
1215 cd_start_offset, cd_size, true /* read only */)) {
1216 return false;
1217 }
1218
1219 CHECK_EQ(directory_map->getDataLength(), cd_size);
1220 central_directory.Initialize(directory_map->getDataPtr(), 0/*offset*/, cd_size);
1221 } else {
1222 if (mapped_zip.GetBasePtr() == nullptr) {
1223 ALOGE("Zip: Failed to map central directory, bad mapped_zip base pointer\n");
1224 return false;
1225 }
1226 if (static_cast<off64_t>(cd_start_offset) + static_cast<off64_t>(cd_size) >
1227 mapped_zip.GetFileLength()) {
1228 ALOGE("Zip: Failed to map central directory, offset exceeds mapped memory region ("
1229 "start_offset %" PRId64 ", cd_size %zu, mapped_region_size %" PRId64 ")",
1230 static_cast<int64_t>(cd_start_offset), cd_size, mapped_zip.GetFileLength());
1231 return false;
1232 }
1233
1234 central_directory.Initialize(mapped_zip.GetBasePtr(), cd_start_offset, cd_size);
1235 }
1236 return true;
1237}
Elliott Hughes55fd2932017-05-28 22:59:04 -07001238
1239tm ZipEntry::GetModificationTime() const {
1240 tm t = {};
1241
1242 t.tm_hour = (mod_time >> 11) & 0x1f;
1243 t.tm_min = (mod_time >> 5) & 0x3f;
1244 t.tm_sec = (mod_time & 0x1f) << 1;
1245
1246 t.tm_year = ((mod_time >> 25) & 0x7f) + 80;
1247 t.tm_mon = ((mod_time >> 21) & 0xf) - 1;
1248 t.tm_mday = (mod_time >> 16) & 0x1f;
1249
1250 return t;
1251}