blob: 5ff1f8f2c9285e815a6585af4316d293b27b1893 [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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//
20#define LOG_TAG "zipro"
21//#define LOG_NDEBUG 0
Mathias Agopianf446ba92009-06-04 13:53:57 -070022#include <utils/ZipFileRO.h>
23#include <utils/Log.h>
24#include <utils/misc.h>
Kenny Rootfa989202010-09-24 07:57:37 -070025#include <utils/threads.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080026
27#include <zlib.h>
28
29#include <string.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <assert.h>
Kenny Rootd4066a42010-04-22 18:28:29 -070033#include <unistd.h>
34
Raphael Moll2cf43c62010-10-13 19:13:48 -070035#if HAVE_PRINTF_ZD
36# define ZD "%zd"
37# define ZD_TYPE ssize_t
38#else
39# define ZD "%ld"
40# define ZD_TYPE long
41#endif
42
43/*
44 * We must open binary files using open(path, ... | O_BINARY) under Windows.
45 * Otherwise strange read errors will happen.
46 */
47#ifndef O_BINARY
48# define O_BINARY 0
49#endif
50
Kenny Rootd4066a42010-04-22 18:28:29 -070051/*
52 * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
53 * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
54 * not already defined, then define it here.
55 */
56#ifndef TEMP_FAILURE_RETRY
57/* Used to retry syscalls that can return EINTR. */
58#define TEMP_FAILURE_RETRY(exp) ({ \
59 typeof (exp) _rc; \
60 do { \
61 _rc = (exp); \
62 } while (_rc == -1 && errno == EINTR); \
63 _rc; })
64#endif
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080065
66using namespace android;
67
68/*
69 * Zip file constants.
70 */
71#define kEOCDSignature 0x06054b50
72#define kEOCDLen 22
73#define kEOCDNumEntries 8 // offset to #of entries in file
Kenny Rootd4066a42010-04-22 18:28:29 -070074#define kEOCDSize 12 // size of the central directory
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080075#define kEOCDFileOffset 16 // offset to central directory
76
77#define kMaxCommentLen 65535 // longest possible in ushort
78#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
79
80#define kLFHSignature 0x04034b50
81#define kLFHLen 30 // excluding variable-len fields
82#define kLFHNameLen 26 // offset to filename length
83#define kLFHExtraLen 28 // offset to extra length
84
85#define kCDESignature 0x02014b50
86#define kCDELen 46 // excluding variable-len fields
87#define kCDEMethod 10 // offset to compression method
88#define kCDEModWhen 12 // offset to modification timestamp
89#define kCDECRC 16 // offset to entry CRC
90#define kCDECompLen 20 // offset to compressed length
91#define kCDEUncompLen 24 // offset to uncompressed length
92#define kCDENameLen 28 // offset to filename length
93#define kCDEExtraLen 30 // offset to extra length
94#define kCDECommentLen 32 // offset to comment length
95#define kCDELocalOffset 42 // offset to local hdr
96
97/*
98 * The values we return for ZipEntryRO use 0 as an invalid value, so we
99 * want to adjust the hash table index by a fixed amount. Using a large
100 * value helps insure that people don't mix & match arguments, e.g. to
101 * findEntryByIndex().
102 */
103#define kZipEntryAdj 10000
104
Kenny Rootdbf6f272010-10-01 18:28:28 -0700105ZipFileRO::~ZipFileRO() {
106 free(mHashTable);
107 if (mDirectoryMap)
108 mDirectoryMap->release();
109 if (mFd >= 0)
110 TEMP_FAILURE_RETRY(close(mFd));
111 if (mFileName)
112 free(mFileName);
113}
114
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800115/*
116 * Convert a ZipEntryRO to a hash table index, verifying that it's in a
117 * valid range.
118 */
119int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
120{
121 long ent = ((long) entry) - kZipEntryAdj;
122 if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) {
123 LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent);
124 return -1;
125 }
126 return ent;
127}
128
129
130/*
131 * Open the specified file read-only. We memory-map the entire thing and
132 * close the file before returning.
133 */
134status_t ZipFileRO::open(const char* zipFileName)
135{
136 int fd = -1;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800137
Kenny Rootd4066a42010-04-22 18:28:29 -0700138 assert(mDirectoryMap == NULL);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800139
140 /*
141 * Open and map the specified file.
142 */
Raphael Moll2cf43c62010-10-13 19:13:48 -0700143 fd = ::open(zipFileName, O_RDONLY | O_BINARY);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800144 if (fd < 0) {
145 LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
146 return NAME_NOT_FOUND;
147 }
148
Kenny Rootd4066a42010-04-22 18:28:29 -0700149 mFileLength = lseek(fd, 0, SEEK_END);
150 if (mFileLength < kEOCDLen) {
Kenny Rootdbf6f272010-10-01 18:28:28 -0700151 TEMP_FAILURE_RETRY(close(fd));
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800152 return UNKNOWN_ERROR;
153 }
154
Kenny Rootd4066a42010-04-22 18:28:29 -0700155 if (mFileName != NULL) {
156 free(mFileName);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800157 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700158 mFileName = strdup(zipFileName);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800159
160 mFd = fd;
161
162 /*
Kenny Rootd4066a42010-04-22 18:28:29 -0700163 * Find the Central Directory and store its size and number of entries.
164 */
165 if (!mapCentralDirectory()) {
166 goto bail;
167 }
168
169 /*
170 * Verify Central Directory and create data structures for fast access.
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800171 */
172 if (!parseZipArchive()) {
Kenny Rootd4066a42010-04-22 18:28:29 -0700173 goto bail;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800174 }
175
176 return OK;
Kenny Rootd4066a42010-04-22 18:28:29 -0700177
178bail:
179 free(mFileName);
180 mFileName = NULL;
Kenny Rootdbf6f272010-10-01 18:28:28 -0700181 TEMP_FAILURE_RETRY(close(fd));
Kenny Rootd4066a42010-04-22 18:28:29 -0700182 return UNKNOWN_ERROR;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800183}
184
185/*
186 * Parse the Zip archive, verifying its contents and initializing internal
187 * data structures.
188 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700189bool ZipFileRO::mapCentralDirectory(void)
190{
Raphael Moll2cf43c62010-10-13 19:13:48 -0700191 ssize_t readAmount = kMaxEOCDSearch;
192 if (readAmount > (ssize_t) mFileLength)
Kenny Rootd4066a42010-04-22 18:28:29 -0700193 readAmount = mFileLength;
194
195 unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
196 if (scanBuf == NULL) {
197 LOGW("couldn't allocate scanBuf: %s", strerror(errno));
198 free(scanBuf);
199 return false;
200 }
201
202 /*
203 * Make sure this is a Zip archive.
204 */
205 if (lseek(mFd, 0, SEEK_SET) != 0) {
206 LOGW("seek to start failed: %s", strerror(errno));
207 free(scanBuf);
208 return false;
209 }
210
211 ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t)));
212 if (actual != (ssize_t) sizeof(int32_t)) {
213 LOGI("couldn't read first signature from zip archive: %s", strerror(errno));
214 free(scanBuf);
215 return false;
216 }
217
218 {
219 unsigned int header = get4LE(scanBuf);
220 if (header == kEOCDSignature) {
221 LOGI("Found Zip archive, but it looks empty\n");
222 free(scanBuf);
223 return false;
224 } else if (header != kLFHSignature) {
Kenny Rootfa989202010-09-24 07:57:37 -0700225 LOGV("Not a Zip archive (found 0x%08x)\n", header);
Kenny Rootd4066a42010-04-22 18:28:29 -0700226 free(scanBuf);
227 return false;
228 }
229 }
230
231 /*
232 * Perform the traditional EOCD snipe hunt.
233 *
234 * We're searching for the End of Central Directory magic number,
235 * which appears at the start of the EOCD block. It's followed by
236 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
237 * need to read the last part of the file into a buffer, dig through
238 * it to find the magic number, parse some values out, and use those
239 * to determine the extent of the CD.
240 *
241 * We start by pulling in the last part of the file.
242 */
243 off_t searchStart = mFileLength - readAmount;
244
245 if (lseek(mFd, searchStart, SEEK_SET) != searchStart) {
246 LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno));
247 free(scanBuf);
248 return false;
249 }
250 actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
251 if (actual != (ssize_t) readAmount) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700252 LOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
253 (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
Kenny Rootd4066a42010-04-22 18:28:29 -0700254 free(scanBuf);
255 return false;
256 }
257
258 /*
259 * Scan backward for the EOCD magic. In an archive without a trailing
260 * comment, we'll find it on the first try. (We may want to consider
261 * doing an initial minimal read; if we don't find it, retry with a
262 * second read as above.)
263 */
264 int i;
265 for (i = readAmount - kEOCDLen; i >= 0; i--) {
266 if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
267 LOGV("+++ Found EOCD at buf+%d\n", i);
268 break;
269 }
270 }
271 if (i < 0) {
272 LOGD("Zip: EOCD not found, %s is not zip\n", mFileName);
273 free(scanBuf);
274 return false;
275 }
276
277 off_t eocdOffset = searchStart + i;
278 const unsigned char* eocdPtr = scanBuf + i;
279
280 assert(eocdOffset < mFileLength);
281
282 /*
283 * Grab the CD offset and size, and the number of entries in the
Kenny Root8f20e5e2010-08-04 16:30:40 -0700284 * archive. After that, we can release our EOCD hunt buffer.
Kenny Rootd4066a42010-04-22 18:28:29 -0700285 */
286 unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries);
287 unsigned int dirSize = get4LE(eocdPtr + kEOCDSize);
288 unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
Kenny Root8f20e5e2010-08-04 16:30:40 -0700289 free(scanBuf);
Kenny Rootd4066a42010-04-22 18:28:29 -0700290
Kenny Root8f20e5e2010-08-04 16:30:40 -0700291 // Verify that they look reasonable.
Kenny Rootd4066a42010-04-22 18:28:29 -0700292 if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
293 LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
294 (long) dirOffset, dirSize, (long) eocdOffset);
Kenny Rootd4066a42010-04-22 18:28:29 -0700295 return false;
296 }
297 if (numEntries == 0) {
298 LOGW("empty archive?\n");
Kenny Rootd4066a42010-04-22 18:28:29 -0700299 return false;
300 }
301
302 LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
303 numEntries, dirSize, dirOffset);
304
305 mDirectoryMap = new FileMap();
306 if (mDirectoryMap == NULL) {
307 LOGW("Unable to create directory map: %s", strerror(errno));
Kenny Rootd4066a42010-04-22 18:28:29 -0700308 return false;
309 }
310
311 if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700312 LOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
313 (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno));
Kenny Rootd4066a42010-04-22 18:28:29 -0700314 return false;
315 }
316
317 mNumEntries = numEntries;
318 mDirectoryOffset = dirOffset;
319
320 return true;
321}
322
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800323bool ZipFileRO::parseZipArchive(void)
324{
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800325 bool result = false;
Kenny Rootd4066a42010-04-22 18:28:29 -0700326 const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr();
327 size_t cdLength = mDirectoryMap->getDataLength();
328 int numEntries = mNumEntries;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800329
330 /*
331 * Create hash table. We have a minimum 75% load factor, possibly as
332 * low as 50% after we round off to a power of 2.
333 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700334 mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3);
335 mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry));
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800336
337 /*
338 * Walk through the central directory, adding entries to the hash
339 * table.
340 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700341 const unsigned char* ptr = cdPtr;
342 for (int i = 0; i < numEntries; i++) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800343 if (get4LE(ptr) != kCDESignature) {
344 LOGW("Missed a central dir sig (at %d)\n", i);
345 goto bail;
346 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700347 if (ptr + kCDELen > cdPtr + cdLength) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800348 LOGW("Ran off the end (at %d)\n", i);
349 goto bail;
350 }
351
Kenny Rootd4066a42010-04-22 18:28:29 -0700352 long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
353 if (localHdrOffset >= mDirectoryOffset) {
354 LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i);
355 goto bail;
356 }
357
358 unsigned int fileNameLen, extraLen, commentLen, hash;
359
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800360 fileNameLen = get2LE(ptr + kCDENameLen);
361 extraLen = get2LE(ptr + kCDEExtraLen);
362 commentLen = get2LE(ptr + kCDECommentLen);
363
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800364 /* add the CDE filename to the hash table */
365 hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
366 addToHash((const char*)ptr + kCDELen, fileNameLen, hash);
367
Kenny Rootd4066a42010-04-22 18:28:29 -0700368 ptr += kCDELen + fileNameLen + extraLen + commentLen;
369 if ((size_t)(ptr - cdPtr) > cdLength) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700370 LOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
371 (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800372 goto bail;
373 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800374 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700375 LOGV("+++ zip good scan %d entries\n", numEntries);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800376 result = true;
377
378bail:
379 return result;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800380}
381
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800382/*
383 * Simple string hash function for non-null-terminated strings.
384 */
385/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len)
386{
387 unsigned int hash = 0;
388
389 while (len--)
390 hash = hash * 31 + *str++;
391
392 return hash;
393}
394
395/*
396 * Add a new entry to the hash table.
397 */
398void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash)
399{
400 int ent = hash & (mHashTableSize-1);
401
402 /*
403 * We over-allocate the table, so we're guaranteed to find an empty slot.
404 */
405 while (mHashTable[ent].name != NULL)
406 ent = (ent + 1) & (mHashTableSize-1);
407
408 mHashTable[ent].name = str;
409 mHashTable[ent].nameLen = strLen;
410}
411
412/*
413 * Find a matching entry.
414 *
415 * Returns 0 if not found.
416 */
417ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
418{
419 int nameLen = strlen(fileName);
420 unsigned int hash = computeHash(fileName, nameLen);
421 int ent = hash & (mHashTableSize-1);
422
423 while (mHashTable[ent].name != NULL) {
424 if (mHashTable[ent].nameLen == nameLen &&
425 memcmp(mHashTable[ent].name, fileName, nameLen) == 0)
426 {
427 /* match */
Kenny Rootd4066a42010-04-22 18:28:29 -0700428 return (ZipEntryRO)(long)(ent + kZipEntryAdj);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800429 }
430
431 ent = (ent + 1) & (mHashTableSize-1);
432 }
433
434 return NULL;
435}
436
437/*
438 * Find the Nth entry.
439 *
440 * This currently involves walking through the sparse hash table, counting
441 * non-empty entries. If we need to speed this up we can either allocate
442 * a parallel lookup table or (perhaps better) provide an iterator interface.
443 */
444ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const
445{
446 if (idx < 0 || idx >= mNumEntries) {
447 LOGW("Invalid index %d\n", idx);
448 return NULL;
449 }
450
451 for (int ent = 0; ent < mHashTableSize; ent++) {
452 if (mHashTable[ent].name != NULL) {
453 if (idx-- == 0)
454 return (ZipEntryRO) (ent + kZipEntryAdj);
455 }
456 }
457
458 return NULL;
459}
460
461/*
462 * Get the useful fields from the zip entry.
463 *
464 * Returns "false" if the offsets to the fields or the contents of the fields
465 * appear to be bogus.
466 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700467bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
468 size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800469{
Kenny Rootd4066a42010-04-22 18:28:29 -0700470 bool ret = false;
471
472 const int ent = entryToIndex(entry);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800473 if (ent < 0)
474 return false;
475
Kenny Rootd4066a42010-04-22 18:28:29 -0700476 HashEntry hashEntry = mHashTable[ent];
477
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800478 /*
479 * Recover the start of the central directory entry from the filename
Kenny Rootd4066a42010-04-22 18:28:29 -0700480 * pointer. The filename is the first entry past the fixed-size data,
481 * so we can just subtract back from that.
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800482 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700483 const unsigned char* ptr = (const unsigned char*) hashEntry.name;
484 off_t cdOffset = mDirectoryOffset;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800485
486 ptr -= kCDELen;
487
488 int method = get2LE(ptr + kCDEMethod);
489 if (pMethod != NULL)
490 *pMethod = method;
491
492 if (pModWhen != NULL)
493 *pModWhen = get4LE(ptr + kCDEModWhen);
494 if (pCrc32 != NULL)
495 *pCrc32 = get4LE(ptr + kCDECRC);
496
Kenny Rootd4066a42010-04-22 18:28:29 -0700497 size_t compLen = get4LE(ptr + kCDECompLen);
498 if (pCompLen != NULL)
499 *pCompLen = compLen;
500 size_t uncompLen = get4LE(ptr + kCDEUncompLen);
501 if (pUncompLen != NULL)
502 *pUncompLen = uncompLen;
503
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800504 /*
Kenny Rootd4066a42010-04-22 18:28:29 -0700505 * If requested, determine the offset of the start of the data. All we
506 * have is the offset to the Local File Header, which is variable size,
507 * so we have to read the contents of the struct to figure out where
508 * the actual data starts.
509 *
510 * We also need to make sure that the lengths are not so large that
511 * somebody trying to map the compressed or uncompressed data runs
512 * off the end of the mapped region.
513 *
514 * Note we don't verify compLen/uncompLen if they don't request the
515 * dataOffset, because dataOffset is expensive to determine. However,
516 * if they don't have the file offset, they're not likely to be doing
517 * anything with the contents.
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800518 */
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800519 if (pOffset != NULL) {
Kenny Rootd4066a42010-04-22 18:28:29 -0700520 long localHdrOffset = get4LE(ptr + kCDELocalOffset);
521 if (localHdrOffset + kLFHLen >= cdOffset) {
522 LOGE("ERROR: bad local hdr offset in zip\n");
523 return false;
524 }
525
526 unsigned char lfhBuf[kLFHLen];
Kenny Rootfa989202010-09-24 07:57:37 -0700527
Kenny Root61ef7472010-10-04 14:20:14 -0700528#ifdef HAVE_PREAD
529 /*
530 * This file descriptor might be from zygote's preloaded assets,
531 * so we need to do an pread() instead of a lseek() + read() to
532 * guarantee atomicity across the processes with the shared file
533 * descriptors.
534 */
535 ssize_t actual =
536 TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
537
538 if (actual != sizeof(lfhBuf)) {
539 LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
540 return false;
541 }
542
543 if (get4LE(lfhBuf) != kLFHSignature) {
544 LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
545 "got: data=0x%08lx\n",
546 localHdrOffset, kLFHSignature, get4LE(lfhBuf));
547 return false;
548 }
549#else /* HAVE_PREAD */
550 /*
551 * For hosts don't have pread() we cannot guarantee atomic reads from
552 * an offset in a file. Android should never run on those platforms.
553 * File descriptors inherited from a fork() share file offsets and
554 * there would be nothing to protect from two different processes
555 * calling lseek() concurrently.
556 */
557
Kenny Rootfa989202010-09-24 07:57:37 -0700558 {
559 AutoMutex _l(mFdLock);
560
561 if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
562 LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
563 return false;
564 }
565
566 ssize_t actual =
Kenny Root61ef7472010-10-04 14:20:14 -0700567 TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
Kenny Rootfa989202010-09-24 07:57:37 -0700568 if (actual != sizeof(lfhBuf)) {
569 LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
570 return false;
571 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700572
Kenny Rootdbf6f272010-10-01 18:28:28 -0700573 if (get4LE(lfhBuf) != kLFHSignature) {
574 off_t actualOffset = lseek(mFd, 0, SEEK_CUR);
575 LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
Raphael Moll2cf43c62010-10-13 19:13:48 -0700576 "got: offset=" ZD " data=0x%08lx\n",
577 localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
Kenny Rootdbf6f272010-10-01 18:28:28 -0700578 return false;
579 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700580 }
Kenny Root61ef7472010-10-04 14:20:14 -0700581#endif /* HAVE_PREAD */
Kenny Rootd4066a42010-04-22 18:28:29 -0700582
583 off_t dataOffset = localHdrOffset + kLFHLen
584 + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
585 if (dataOffset >= cdOffset) {
586 LOGW("bad data offset %ld in zip\n", (long) dataOffset);
587 return false;
588 }
589
590 /* check lengths */
591 if ((off_t)(dataOffset + compLen) > cdOffset) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700592 LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
593 (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
Kenny Rootd4066a42010-04-22 18:28:29 -0700594 return false;
595 }
596
597 if (method == kCompressStored &&
598 (off_t)(dataOffset + uncompLen) > cdOffset)
599 {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700600 LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
601 (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
Kenny Rootd4066a42010-04-22 18:28:29 -0700602 return false;
603 }
604
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800605 *pOffset = dataOffset;
606 }
Kenny Rootd4066a42010-04-22 18:28:29 -0700607
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800608 return true;
609}
610
611/*
612 * Copy the entry's filename to the buffer.
613 */
614int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen)
615 const
616{
617 int ent = entryToIndex(entry);
618 if (ent < 0)
619 return -1;
620
621 int nameLen = mHashTable[ent].nameLen;
622 if (bufLen < nameLen+1)
623 return nameLen+1;
624
625 memcpy(buffer, mHashTable[ent].name, nameLen);
626 buffer[nameLen] = '\0';
627 return 0;
628}
629
630/*
631 * Create a new FileMap object that spans the data in "entry".
632 */
633FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
634{
635 /*
636 * TODO: the efficient way to do this is to modify FileMap to allow
637 * sub-regions of a file to be mapped. A reference-counting scheme
638 * can manage the base memory mapping. For now, we just create a brand
639 * new mapping off of the Zip archive file descriptor.
640 */
641
642 FileMap* newMap;
Kenny Rootd4066a42010-04-22 18:28:29 -0700643 size_t compLen;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800644 off_t offset;
645
646 if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL))
647 return NULL;
648
649 newMap = new FileMap();
Kenny Rootd4066a42010-04-22 18:28:29 -0700650 if (!newMap->create(mFileName, mFd, offset, compLen, true)) {
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800651 newMap->release();
652 return NULL;
653 }
654
655 return newMap;
656}
657
658/*
659 * Uncompress an entry, in its entirety, into the provided output buffer.
660 *
661 * This doesn't verify the data's CRC, which might be useful for
662 * uncompressed data. The caller should be able to manage it.
663 */
664bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
665{
Kenny Rootd4066a42010-04-22 18:28:29 -0700666 const size_t kSequentialMin = 32768;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800667 bool result = false;
668 int ent = entryToIndex(entry);
669 if (ent < 0)
670 return -1;
671
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800672 int method;
Kenny Rootd4066a42010-04-22 18:28:29 -0700673 size_t uncompLen, compLen;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800674 off_t offset;
Kenny Rootd4066a42010-04-22 18:28:29 -0700675 const unsigned char* ptr;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800676
677 getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
678
Kenny Rootd4066a42010-04-22 18:28:29 -0700679 FileMap* file = createEntryFileMap(entry);
680 if (file == NULL) {
681 goto bail;
682 }
683
684 ptr = (const unsigned char*) file->getDataPtr();
685
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800686 /*
687 * Experiment with madvise hint. When we want to uncompress a file,
688 * we pull some stuff out of the central dir entry and then hit a
689 * bunch of compressed or uncompressed data sequentially. The CDE
690 * visit will cause a limited amount of read-ahead because it's at
691 * the end of the file. We could end up doing lots of extra disk
692 * access if the file we're prying open is small. Bottom line is we
693 * probably don't want to turn MADV_SEQUENTIAL on and leave it on.
694 *
695 * So, if the compressed size of the file is above a certain minimum
696 * size, temporarily boost the read-ahead in the hope that the extra
697 * pair of system calls are negated by a reduction in page faults.
698 */
699 if (compLen > kSequentialMin)
Kenny Rootd4066a42010-04-22 18:28:29 -0700700 file->advise(FileMap::SEQUENTIAL);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800701
702 if (method == kCompressStored) {
Kenny Rootd4066a42010-04-22 18:28:29 -0700703 memcpy(buffer, ptr, uncompLen);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800704 } else {
Kenny Rootd4066a42010-04-22 18:28:29 -0700705 if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
Kenny Rootb47eafa2010-09-24 09:11:28 -0700706 goto unmap;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800707 }
708
709 if (compLen > kSequentialMin)
Kenny Rootd4066a42010-04-22 18:28:29 -0700710 file->advise(FileMap::NORMAL);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800711
712 result = true;
713
Kenny Rootb47eafa2010-09-24 09:11:28 -0700714unmap:
715 file->release();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800716bail:
717 return result;
718}
719
720/*
721 * Uncompress an entry, in its entirety, to an open file descriptor.
722 *
723 * This doesn't verify the data's CRC, but probably should.
724 */
725bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
726{
727 bool result = false;
728 int ent = entryToIndex(entry);
729 if (ent < 0)
730 return -1;
731
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800732 int method;
Kenny Rootd4066a42010-04-22 18:28:29 -0700733 size_t uncompLen, compLen;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800734 off_t offset;
Kenny Rootd4066a42010-04-22 18:28:29 -0700735 const unsigned char* ptr;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800736
737 getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
738
Kenny Rootb47eafa2010-09-24 09:11:28 -0700739 FileMap* file = createEntryFileMap(entry);
Kenny Rootd4066a42010-04-22 18:28:29 -0700740 if (file == NULL) {
741 goto bail;
742 }
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800743
Kenny Rootd4066a42010-04-22 18:28:29 -0700744 ptr = (const unsigned char*) file->getDataPtr();
745
746 if (method == kCompressStored) {
747 ssize_t actual = write(fd, ptr, uncompLen);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800748 if (actual < 0) {
749 LOGE("Write failed: %s\n", strerror(errno));
Kenny Rootb47eafa2010-09-24 09:11:28 -0700750 goto unmap;
Kenny Rootd4066a42010-04-22 18:28:29 -0700751 } else if ((size_t) actual != uncompLen) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700752 LOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
753 (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
Kenny Rootb47eafa2010-09-24 09:11:28 -0700754 goto unmap;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800755 } else {
756 LOGI("+++ successful write\n");
757 }
758 } else {
Kenny Rootd4066a42010-04-22 18:28:29 -0700759 if (!inflateBuffer(fd, ptr, uncompLen, compLen))
Kenny Rootb47eafa2010-09-24 09:11:28 -0700760 goto unmap;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800761 }
762
763 result = true;
764
Kenny Rootb47eafa2010-09-24 09:11:28 -0700765unmap:
766 file->release();
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800767bail:
768 return result;
769}
770
771/*
772 * Uncompress "deflate" data from one buffer to another.
773 */
774/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf,
Kenny Rootd4066a42010-04-22 18:28:29 -0700775 size_t uncompLen, size_t compLen)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800776{
777 bool result = false;
778 z_stream zstream;
779 int zerr;
780
781 /*
782 * Initialize the zlib stream struct.
783 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700784 memset(&zstream, 0, sizeof(zstream));
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800785 zstream.zalloc = Z_NULL;
786 zstream.zfree = Z_NULL;
787 zstream.opaque = Z_NULL;
788 zstream.next_in = (Bytef*)inBuf;
789 zstream.avail_in = compLen;
790 zstream.next_out = (Bytef*) outBuf;
791 zstream.avail_out = uncompLen;
792 zstream.data_type = Z_UNKNOWN;
793
Kenny Rootd4066a42010-04-22 18:28:29 -0700794 /*
795 * Use the undocumented "negative window bits" feature to tell zlib
796 * that there's no zlib header waiting for it.
797 */
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800798 zerr = inflateInit2(&zstream, -MAX_WBITS);
799 if (zerr != Z_OK) {
800 if (zerr == Z_VERSION_ERROR) {
801 LOGE("Installed zlib is not compatible with linked version (%s)\n",
802 ZLIB_VERSION);
803 } else {
804 LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
805 }
806 goto bail;
807 }
808
809 /*
810 * Expand data.
811 */
812 zerr = inflate(&zstream, Z_FINISH);
813 if (zerr != Z_STREAM_END) {
814 LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
815 zerr, zstream.next_in, zstream.avail_in,
816 zstream.next_out, zstream.avail_out);
817 goto z_bail;
818 }
819
820 /* paranoia */
Kenny Rootd4066a42010-04-22 18:28:29 -0700821 if (zstream.total_out != uncompLen) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700822 LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
823 zstream.total_out, (ZD_TYPE) uncompLen);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800824 goto z_bail;
825 }
826
827 result = true;
828
829z_bail:
830 inflateEnd(&zstream); /* free up any allocated structures */
831
832bail:
833 return result;
834}
835
836/*
837 * Uncompress "deflate" data from one buffer to an open file descriptor.
838 */
839/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf,
Kenny Rootd4066a42010-04-22 18:28:29 -0700840 size_t uncompLen, size_t compLen)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800841{
842 bool result = false;
Kenny Rootd4066a42010-04-22 18:28:29 -0700843 const size_t kWriteBufSize = 32768;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800844 unsigned char writeBuf[kWriteBufSize];
845 z_stream zstream;
846 int zerr;
847
848 /*
849 * Initialize the zlib stream struct.
850 */
Kenny Rootd4066a42010-04-22 18:28:29 -0700851 memset(&zstream, 0, sizeof(zstream));
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800852 zstream.zalloc = Z_NULL;
853 zstream.zfree = Z_NULL;
854 zstream.opaque = Z_NULL;
855 zstream.next_in = (Bytef*)inBuf;
856 zstream.avail_in = compLen;
857 zstream.next_out = (Bytef*) writeBuf;
858 zstream.avail_out = sizeof(writeBuf);
859 zstream.data_type = Z_UNKNOWN;
860
Kenny Rootd4066a42010-04-22 18:28:29 -0700861 /*
862 * Use the undocumented "negative window bits" feature to tell zlib
863 * that there's no zlib header waiting for it.
864 */
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800865 zerr = inflateInit2(&zstream, -MAX_WBITS);
866 if (zerr != Z_OK) {
867 if (zerr == Z_VERSION_ERROR) {
868 LOGE("Installed zlib is not compatible with linked version (%s)\n",
869 ZLIB_VERSION);
870 } else {
871 LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
872 }
873 goto bail;
874 }
875
876 /*
877 * Loop while we have more to do.
878 */
879 do {
880 /*
881 * Expand data.
882 */
883 zerr = inflate(&zstream, Z_NO_FLUSH);
884 if (zerr != Z_OK && zerr != Z_STREAM_END) {
885 LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
886 zerr, zstream.next_in, zstream.avail_in,
887 zstream.next_out, zstream.avail_out);
888 goto z_bail;
889 }
890
891 /* write when we're full or when we're done */
892 if (zstream.avail_out == 0 ||
893 (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf)))
894 {
895 long writeSize = zstream.next_out - writeBuf;
896 int cc = write(fd, writeBuf, writeSize);
897 if (cc != (int) writeSize) {
898 LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize);
899 goto z_bail;
900 }
901
902 zstream.next_out = writeBuf;
903 zstream.avail_out = sizeof(writeBuf);
904 }
905 } while (zerr == Z_OK);
906
907 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
908
909 /* paranoia */
Kenny Rootd4066a42010-04-22 18:28:29 -0700910 if (zstream.total_out != uncompLen) {
Raphael Moll2cf43c62010-10-13 19:13:48 -0700911 LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
912 zstream.total_out, (ZD_TYPE) uncompLen);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800913 goto z_bail;
914 }
915
916 result = true;
917
918z_bail:
919 inflateEnd(&zstream); /* free up any allocated structures */
920
921bail:
922 return result;
923}