1/*
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 */
20#include "ZipArchive.h"
21
22#include <zlib.h>
23
24#include <stdlib.h>
25#include <unistd.h>
26#include <string.h>
27#include <fcntl.h>
28#include <errno.h>
29
30#include <JNIHelp.h>        // TEMP_FAILURE_RETRY may or may not be in unistd
31#include <utils/Compat.h>   // For off64_t and lseek64 on Mac
32
33#ifndef O_BINARY
34#define O_BINARY 0
35#endif
36
37/*
38 * Zip file constants.
39 */
40#define kEOCDSignature       0x06054b50
41#define kEOCDLen             22
42#define kEOCDDiskNumber      4               // number of the current disk
43#define kEOCDDiskNumberForCD 6               // disk number with the Central Directory
44#define kEOCDNumEntries      8               // offset to #of entries in file
45#define kEOCDTotalNumEntries 10              // offset to total #of entries in spanned archives
46#define kEOCDSize            12              // size of the central directory
47#define kEOCDFileOffset      16              // offset to central directory
48#define kEOCDCommentSize     20              // offset to the length of the file comment
49
50#define kMaxCommentLen       65535           // longest possible in ushort
51#define kMaxEOCDSearch       (kMaxCommentLen + kEOCDLen)
52
53#define kLFHSignature        0x04034b50
54#define kLFHLen              30              // excluding variable-len fields
55#define kLFHGPBFlags          6              // offset to GPB flags
56#define kLFHNameLen          26              // offset to filename length
57#define kLFHExtraLen         28              // offset to extra length
58
59#define kCDESignature        0x02014b50
60#define kCDELen              46              // excluding variable-len fields
61#define kCDEGPBFlags          8              // offset to GPB flags
62#define kCDEMethod           10              // offset to compression method
63#define kCDEModWhen          12              // offset to modification timestamp
64#define kCDECRC              16              // offset to entry CRC
65#define kCDECompLen          20              // offset to compressed length
66#define kCDEUncompLen        24              // offset to uncompressed length
67#define kCDENameLen          28              // offset to filename length
68#define kCDEExtraLen         30              // offset to extra length
69#define kCDECommentLen       32              // offset to comment length
70#define kCDELocalOffset      42              // offset to local hdr
71
72/* General Purpose Bit Flag */
73#define kGPFEncryptedFlag    (1 << 0)
74#define kGPFUnsupportedMask  (kGPFEncryptedFlag)
75
76/*
77 * The values we return for ZipEntryRO use 0 as an invalid value, so we
78 * want to adjust the hash table index by a fixed amount.  Using a large
79 * value helps insure that people don't mix & match arguments, e.g. to
80 * findEntryByIndex().
81 */
82#define kZipEntryAdj        10000
83
84/*
85 * Convert a ZipEntry to a hash table index, verifying that it's in a
86 * valid range.
87 */
88static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry)
89{
90    long ent = ((long) entry) - kZipEntryAdj;
91    if (ent < 0 || ent >= pArchive->mHashTableSize ||
92        pArchive->mHashTable[ent].name == NULL)
93    {
94        ALOGW("Zip: invalid ZipEntry %p (%ld)", entry, ent);
95        return -1;
96    }
97    return ent;
98}
99
100/*
101 * Simple string hash function for non-null-terminated strings.
102 */
103static unsigned int computeHash(const char* str, int len)
104{
105    unsigned int hash = 0;
106
107    while (len--)
108        hash = hash * 31 + *str++;
109
110    return hash;
111}
112
113/*
114 * Add a new entry to the hash table.
115 */
116static void addToHash(ZipArchive* pArchive, const char* str, int strLen,
117    unsigned int hash)
118{
119    const int hashTableSize = pArchive->mHashTableSize;
120    int ent = hash & (hashTableSize - 1);
121
122    /*
123     * We over-allocated the table, so we're guaranteed to find an empty slot.
124     */
125    while (pArchive->mHashTable[ent].name != NULL)
126        ent = (ent + 1) & (hashTableSize-1);
127
128    pArchive->mHashTable[ent].name = str;
129    pArchive->mHashTable[ent].nameLen = strLen;
130}
131
132/*
133 * Get 2 little-endian bytes.
134 */
135static u2 get2LE(unsigned char const* pSrc)
136{
137    return pSrc[0] | (pSrc[1] << 8);
138}
139
140/*
141 * Get 4 little-endian bytes.
142 */
143static u4 get4LE(unsigned char const* pSrc)
144{
145    u4 result;
146
147    result = pSrc[0];
148    result |= pSrc[1] << 8;
149    result |= pSrc[2] << 16;
150    result |= pSrc[3] << 24;
151
152    return result;
153}
154
155static int mapCentralDirectory0(int fd, const char* debugFileName,
156        ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf)
157{
158    /*
159     * Make sure this is a Zip archive.
160     */
161    if (lseek64(pArchive->mFd, 0, SEEK_SET) != 0) {
162        ALOGW("seek to start failed: %s", strerror(errno));
163        return false;
164    }
165
166    ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, sizeof(int32_t)));
167    if (actual != (ssize_t) sizeof(int32_t)) {
168        ALOGI("couldn't read first signature from zip archive: %s", strerror(errno));
169        return false;
170    }
171
172    unsigned int header = get4LE(scanBuf);
173    if (header != kLFHSignature) {
174        ALOGV("Not a Zip archive (found 0x%08x)\n", header);
175        return false;
176    }
177
178    /*
179     * Perform the traditional EOCD snipe hunt.
180     *
181     * We're searching for the End of Central Directory magic number,
182     * which appears at the start of the EOCD block.  It's followed by
183     * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
184     * need to read the last part of the file into a buffer, dig through
185     * it to find the magic number, parse some values out, and use those
186     * to determine the extent of the CD.
187     *
188     * We start by pulling in the last part of the file.
189     */
190    off64_t searchStart = fileLength - readAmount;
191
192    if (lseek64(pArchive->mFd, searchStart, SEEK_SET) != searchStart) {
193        ALOGW("seek %ld failed: %s\n",  (long) searchStart, strerror(errno));
194        return false;
195    }
196    actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, readAmount));
197    if (actual != (ssize_t) readAmount) {
198        ALOGW("Zip: read %zd, expected %zd. Failed: %s\n",
199            actual, readAmount, strerror(errno));
200        return false;
201    }
202
203
204    /*
205     * Scan backward for the EOCD magic.  In an archive without a trailing
206     * comment, we'll find it on the first try.  (We may want to consider
207     * doing an initial minimal read; if we don't find it, retry with a
208     * second read as above.)
209     */
210    int i;
211    for (i = readAmount - kEOCDLen; i >= 0; i--) {
212        if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
213            ALOGV("+++ Found EOCD at buf+%d", i);
214            break;
215        }
216    }
217    if (i < 0) {
218        ALOGD("Zip: EOCD not found, %s is not zip", debugFileName);
219        return -1;
220    }
221
222    off64_t eocdOffset = searchStart + i;
223    const u1* eocdPtr = scanBuf + i;
224
225    assert(eocdOffset < fileLength);
226
227    /*
228     * Grab the CD offset and size, and the number of entries in the
229     * archive.  Verify that they look reasonable.
230     */
231    u4 diskNumber = get2LE(eocdPtr + kEOCDDiskNumber);
232    u4 diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD);
233    u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries);
234    u4 totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries);
235    u4 centralDirSize = get4LE(eocdPtr + kEOCDSize);
236    u4 centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset);
237    u4 commentSize = get2LE(eocdPtr + kEOCDCommentSize);
238
239    // Verify that they look reasonable.
240    if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) {
241        ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
242            (long) centralDirOffset, centralDirSize, (long) eocdOffset);
243        return false;
244    }
245    if (numEntries == 0) {
246        ALOGW("empty archive?\n");
247        return false;
248    } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
249        ALOGW("spanned archives not supported");
250        return false;
251    }
252
253    // Check to see if comment is a sane size
254    if (((size_t) commentSize > (fileLength - kEOCDLen))
255            || (eocdOffset > (fileLength - kEOCDLen) - commentSize)) {
256        ALOGW("comment size runs off end of file");
257        return false;
258    }
259
260    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
261        numEntries, centralDirSize, centralDirOffset);
262
263    /*
264     * It all looks good.  Create a mapping for the CD, and set the fields
265     * in pArchive.
266     */
267    if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize,
268            &pArchive->mDirectoryMap) != 0)
269    {
270        ALOGW("Zip: cd map failed");
271        return -1;
272    }
273
274    pArchive->mNumEntries = numEntries;
275    pArchive->mDirectoryOffset = centralDirOffset;
276
277    return 0;
278}
279
280/*
281 * Find the zip Central Directory and memory-map it.
282 *
283 * On success, returns 0 after populating fields from the EOCD area:
284 *   mDirectoryOffset
285 *   mDirectoryMap
286 *   mNumEntries
287 */
288static int mapCentralDirectory(int fd, const char* debugFileName,
289    ZipArchive* pArchive)
290{
291    /*
292     * Get and test file length.
293     */
294    off64_t fileLength = lseek64(fd, 0, SEEK_END);
295    if (fileLength < kEOCDLen) {
296        ALOGV("Zip: length %ld is too small to be zip", (long) fileLength);
297        return -1;
298    }
299
300    /*
301     * Perform the traditional EOCD snipe hunt.
302     *
303     * We're searching for the End of Central Directory magic number,
304     * which appears at the start of the EOCD block.  It's followed by
305     * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
306     * need to read the last part of the file into a buffer, dig through
307     * it to find the magic number, parse some values out, and use those
308     * to determine the extent of the CD.
309     *
310     * We start by pulling in the last part of the file.
311     */
312    size_t readAmount = kMaxEOCDSearch;
313    if (fileLength < off_t(readAmount))
314        readAmount = fileLength;
315
316    u1* scanBuf = (u1*) malloc(readAmount);
317    if (scanBuf == NULL) {
318        return -1;
319    }
320
321    int result = mapCentralDirectory0(fd, debugFileName, pArchive,
322            fileLength, readAmount, scanBuf);
323
324    free(scanBuf);
325    return result;
326}
327
328/*
329 * Parses the Zip archive's Central Directory.  Allocates and populates the
330 * hash table.
331 *
332 * Returns 0 on success.
333 */
334static int parseZipArchive(ZipArchive* pArchive)
335{
336    int result = -1;
337    const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr;
338    size_t cdLength = pArchive->mDirectoryMap.length;
339    int numEntries = pArchive->mNumEntries;
340
341    /*
342     * Create hash table.  We have a minimum 75% load factor, possibly as
343     * low as 50% after we round off to a power of 2.  There must be at
344     * least one unused entry to avoid an infinite loop during creation.
345     */
346    pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3);
347    pArchive->mHashTable = (ZipHashEntry*)
348            calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry));
349
350    /*
351     * Walk through the central directory, adding entries to the hash
352     * table and verifying values.
353     */
354    const u1* ptr = cdPtr;
355    int i;
356    for (i = 0; i < numEntries; i++) {
357        if (get4LE(ptr) != kCDESignature) {
358            ALOGW("Zip: missed a central dir sig (at %d)", i);
359            goto bail;
360        }
361        if (ptr + kCDELen > cdPtr + cdLength) {
362            ALOGW("Zip: ran off the end (at %d)", i);
363            goto bail;
364        }
365
366        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
367        if (localHdrOffset >= pArchive->mDirectoryOffset) {
368            ALOGW("Zip: bad LFH offset %ld at entry %d", localHdrOffset, i);
369            goto bail;
370        }
371
372        unsigned int gpbf = get2LE(ptr + kCDEGPBFlags);
373        if ((gpbf & kGPFUnsupportedMask) != 0) {
374            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
375            goto bail;
376        }
377
378        unsigned int nameLen, extraLen, commentLen, hash;
379        nameLen = get2LE(ptr + kCDENameLen);
380        extraLen = get2LE(ptr + kCDEExtraLen);
381        commentLen = get2LE(ptr + kCDECommentLen);
382
383        const char *name = (const char *) ptr + kCDELen;
384
385        /* Check name for NULL characters */
386        if (memchr(name, 0, nameLen) != NULL) {
387            ALOGW("Filename contains NUL byte");
388            goto bail;
389        }
390
391        /* add the CDE filename to the hash table */
392        hash = computeHash(name, nameLen);
393        addToHash(pArchive, name, nameLen, hash);
394
395        /* We don't care about the comment or extra data. */
396        ptr += kCDELen + nameLen + extraLen + commentLen;
397        if ((size_t)(ptr - cdPtr) > cdLength) {
398            ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
399                (int) (ptr - cdPtr), cdLength, i);
400            goto bail;
401        }
402    }
403    ALOGV("+++ zip good scan %d entries", numEntries);
404
405    result = 0;
406
407bail:
408    return result;
409}
410
411/*
412 * Open the specified file read-only.  We examine the contents and verify
413 * that it appears to be a valid zip file.
414 *
415 * This will be called on non-Zip files, especially during VM startup, so
416 * we don't want to be too noisy about certain types of failure.  (Do
417 * we want a "quiet" flag?)
418 *
419 * On success, we fill out the contents of "pArchive" and return 0.  On
420 * failure we return the errno value.
421 */
422int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
423{
424    int fd, err;
425
426    ALOGV("Opening as zip '%s' %p", fileName, pArchive);
427
428    memset(pArchive, 0, sizeof(ZipArchive));
429
430    fd = open(fileName, O_RDONLY | O_BINARY, 0);
431    if (fd < 0) {
432        err = errno ? errno : -1;
433        ALOGV("Unable to open '%s': %s", fileName, strerror(err));
434        return err;
435    }
436
437    return dexZipPrepArchive(fd, fileName, pArchive);
438}
439
440/*
441 * Prepare to access a ZipArchive through an open file descriptor.
442 *
443 * On success, we fill out the contents of "pArchive" and return 0.
444 */
445int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
446{
447    int result = -1;
448
449    memset(pArchive, 0, sizeof(*pArchive));
450    pArchive->mFd = fd;
451
452    if (mapCentralDirectory(fd, debugFileName, pArchive) != 0)
453        goto bail;
454
455    if (parseZipArchive(pArchive) != 0) {
456        ALOGV("Zip: parsing '%s' failed", debugFileName);
457        goto bail;
458    }
459
460    /* success */
461    result = 0;
462
463bail:
464    if (result != 0)
465        dexZipCloseArchive(pArchive);
466    return result;
467}
468
469
470/*
471 * Close a ZipArchive, closing the file and freeing the contents.
472 *
473 * NOTE: the ZipArchive may not have been fully created.
474 */
475void dexZipCloseArchive(ZipArchive* pArchive)
476{
477    ALOGV("Closing archive %p", pArchive);
478
479    if (pArchive->mFd >= 0)
480        close(pArchive->mFd);
481
482    sysReleaseShmem(&pArchive->mDirectoryMap);
483
484    free(pArchive->mHashTable);
485
486    /* ensure nobody tries to use the ZipArchive after it's closed */
487    pArchive->mDirectoryOffset = -1;
488    pArchive->mFd = -1;
489    pArchive->mNumEntries = -1;
490    pArchive->mHashTableSize = -1;
491    pArchive->mHashTable = NULL;
492}
493
494
495/*
496 * Find a matching entry.
497 *
498 * Returns 0 if not found.
499 */
500ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName)
501{
502    int nameLen = strlen(entryName);
503    unsigned int hash = computeHash(entryName, nameLen);
504    const int hashTableSize = pArchive->mHashTableSize;
505    int ent = hash & (hashTableSize-1);
506
507    while (pArchive->mHashTable[ent].name != NULL) {
508        if (pArchive->mHashTable[ent].nameLen == nameLen &&
509            memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0)
510        {
511            /* match */
512            return (ZipEntry)(long)(ent + kZipEntryAdj);
513        }
514
515        ent = (ent + 1) & (hashTableSize-1);
516    }
517
518    return NULL;
519}
520
521#if 0
522/*
523 * Find the Nth entry.
524 *
525 * This currently involves walking through the sparse hash table, counting
526 * non-empty entries.  If we need to speed this up we can either allocate
527 * a parallel lookup table or (perhaps better) provide an iterator interface.
528 */
529ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx)
530{
531    if (idx < 0 || idx >= pArchive->mNumEntries) {
532        ALOGW("Invalid index %d", idx);
533        return NULL;
534    }
535
536    int ent;
537    for (ent = 0; ent < pArchive->mHashTableSize; ent++) {
538        if (pArchive->mHashTable[ent].name != NULL) {
539            if (idx-- == 0)
540                return (ZipEntry) (ent + kZipEntryAdj);
541        }
542    }
543
544    return NULL;
545}
546#endif
547
548/*
549 * Get the useful fields from the zip entry.
550 *
551 * Returns non-zero if the contents of the fields (particularly the data
552 * offset) appear to be bogus.
553 */
554int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
555    int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
556    long* pModWhen, long* pCrc32)
557{
558    int ent = entryToIndex(pArchive, entry);
559    if (ent < 0)
560        return -1;
561
562    /*
563     * Recover the start of the central directory entry from the filename
564     * pointer.  The filename is the first entry past the fixed-size data,
565     * so we can just subtract back from that.
566     */
567    const unsigned char* basePtr = (const unsigned char*)
568        pArchive->mDirectoryMap.addr;
569    const unsigned char* ptr = (const unsigned char*)
570        pArchive->mHashTable[ent].name;
571    off_t cdOffset = pArchive->mDirectoryOffset;
572
573    ptr -= kCDELen;
574
575    int method = get2LE(ptr + kCDEMethod);
576    if (pMethod != NULL)
577        *pMethod = method;
578
579    if (pModWhen != NULL)
580        *pModWhen = get4LE(ptr + kCDEModWhen);
581    if (pCrc32 != NULL)
582        *pCrc32 = get4LE(ptr + kCDECRC);
583
584    size_t compLen = get4LE(ptr + kCDECompLen);
585    if (pCompLen != NULL)
586        *pCompLen = compLen;
587    size_t uncompLen = get4LE(ptr + kCDEUncompLen);
588    if (pUncompLen != NULL)
589        *pUncompLen = uncompLen;
590
591    /*
592     * If requested, determine the offset of the start of the data.  All we
593     * have is the offset to the Local File Header, which is variable size,
594     * so we have to read the contents of the struct to figure out where
595     * the actual data starts.
596     *
597     * We also need to make sure that the lengths are not so large that
598     * somebody trying to map the compressed or uncompressed data runs
599     * off the end of the mapped region.
600     *
601     * Note we don't verify compLen/uncompLen if they don't request the
602     * dataOffset, because dataOffset is expensive to determine.  However,
603     * if they don't have the file offset, they're not likely to be doing
604     * anything with the contents.
605     */
606    if (pOffset != NULL) {
607        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
608        if (localHdrOffset + kLFHLen >= cdOffset) {
609            ALOGW("Zip: bad local hdr offset in zip");
610            return -1;
611        }
612
613        u1 lfhBuf[kLFHLen];
614        if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
615            ALOGW("Zip: failed seeking to lfh at offset %ld", localHdrOffset);
616            return -1;
617        }
618        ssize_t actual =
619            TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf)));
620        if (actual != sizeof(lfhBuf)) {
621            ALOGW("Zip: failed reading lfh from offset %ld", localHdrOffset);
622            return -1;
623        }
624
625        if (get4LE(lfhBuf) != kLFHSignature) {
626            ALOGW("Zip: didn't find signature at start of lfh, offset=%ld",
627                localHdrOffset);
628            return -1;
629        }
630
631        u4 gpbf = get2LE(lfhBuf + kLFHGPBFlags);
632        if ((gpbf & kGPFUnsupportedMask) != 0) {
633            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
634            return -1;
635        }
636
637        off64_t dataOffset = localHdrOffset + kLFHLen
638            + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
639        if (dataOffset >= cdOffset) {
640            ALOGW("Zip: bad data offset %ld in zip", (long) dataOffset);
641            return -1;
642        }
643
644        /* check lengths */
645        if ((off_t)(dataOffset + compLen) > cdOffset) {
646            ALOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)",
647                (long) dataOffset, compLen, (long) cdOffset);
648            return -1;
649        }
650
651        if (method == kCompressStored &&
652            (off_t)(dataOffset + uncompLen) > cdOffset)
653        {
654            ALOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)",
655                (long) dataOffset, uncompLen, (long) cdOffset);
656            return -1;
657        }
658
659        *pOffset = dataOffset;
660    }
661    return 0;
662}
663
664/*
665 * Uncompress "deflate" data from the archive's file to an open file
666 * descriptor.
667 */
668static int inflateToFile(int outFd, int inFd, size_t uncompLen, size_t compLen)
669{
670    int result = -1;
671    const size_t kBufSize = 32768;
672    unsigned char* readBuf = (unsigned char*) malloc(kBufSize);
673    unsigned char* writeBuf = (unsigned char*) malloc(kBufSize);
674    z_stream zstream;
675    int zerr;
676
677    if (readBuf == NULL || writeBuf == NULL)
678        goto bail;
679
680    /*
681     * Initialize the zlib stream struct.
682     */
683    memset(&zstream, 0, sizeof(zstream));
684    zstream.zalloc = Z_NULL;
685    zstream.zfree = Z_NULL;
686    zstream.opaque = Z_NULL;
687    zstream.next_in = NULL;
688    zstream.avail_in = 0;
689    zstream.next_out = (Bytef*) writeBuf;
690    zstream.avail_out = kBufSize;
691    zstream.data_type = Z_UNKNOWN;
692
693    /*
694     * Use the undocumented "negative window bits" feature to tell zlib
695     * that there's no zlib header waiting for it.
696     */
697    zerr = inflateInit2(&zstream, -MAX_WBITS);
698    if (zerr != Z_OK) {
699        if (zerr == Z_VERSION_ERROR) {
700            ALOGE("Installed zlib is not compatible with linked version (%s)",
701                ZLIB_VERSION);
702        } else {
703            ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
704        }
705        goto bail;
706    }
707
708    /*
709     * Loop while we have more to do.
710     */
711    do {
712        /* read as much as we can */
713        if (zstream.avail_in == 0) {
714            size_t getSize = (compLen > kBufSize) ? kBufSize : compLen;
715
716            ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize));
717            if (actual != (ssize_t) getSize) {
718                ALOGW("Zip: inflate read failed (%d vs %zd)",
719                    (int)actual, getSize);
720                goto z_bail;
721            }
722
723            compLen -= getSize;
724
725            zstream.next_in = readBuf;
726            zstream.avail_in = getSize;
727        }
728
729        /* uncompress the data */
730        zerr = inflate(&zstream, Z_NO_FLUSH);
731        if (zerr != Z_OK && zerr != Z_STREAM_END) {
732            ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
733                zerr, zstream.next_in, zstream.avail_in,
734                zstream.next_out, zstream.avail_out);
735            goto z_bail;
736        }
737
738        /* write when we're full or when we're done */
739        if (zstream.avail_out == 0 ||
740            (zerr == Z_STREAM_END && zstream.avail_out != kBufSize))
741        {
742            size_t writeSize = zstream.next_out - writeBuf;
743            if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0)
744                goto z_bail;
745
746            zstream.next_out = writeBuf;
747            zstream.avail_out = kBufSize;
748        }
749    } while (zerr == Z_OK);
750
751    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
752
753    /* paranoia */
754    if (zstream.total_out != uncompLen) {
755        ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)",
756            zstream.total_out, uncompLen);
757        goto z_bail;
758    }
759
760    result = 0;
761
762z_bail:
763    inflateEnd(&zstream);        /* free up any allocated structures */
764
765bail:
766    free(readBuf);
767    free(writeBuf);
768    return result;
769}
770
771/*
772 * Uncompress an entry, in its entirety, to an open file descriptor.
773 *
774 * TODO: this doesn't verify the data's CRC, but probably should (especially
775 * for uncompressed data).
776 */
777int dexZipExtractEntryToFile(const ZipArchive* pArchive,
778    const ZipEntry entry, int fd)
779{
780    int result = -1;
781    int ent = entryToIndex(pArchive, entry);
782    if (ent < 0) {
783        ALOGW("Zip: extract can't find entry %p", entry);
784        goto bail;
785    }
786
787    int method;
788    size_t uncompLen, compLen;
789    off_t dataOffset;
790
791    if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen,
792            &dataOffset, NULL, NULL) != 0)
793    {
794        goto bail;
795    }
796    if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) {
797        ALOGW("Zip: lseek to data at %ld failed", (long) dataOffset);
798        goto bail;
799    }
800
801    if (method == kCompressStored) {
802        if (sysCopyFileToFile(fd, pArchive->mFd, uncompLen) != 0)
803            goto bail;
804    } else {
805        if (inflateToFile(fd, pArchive->mFd, uncompLen, compLen) != 0)
806            goto bail;
807    }
808
809    result = 0;
810
811bail:
812    return result;
813}
814