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