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 #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  */
     88 static 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  */
    103 static 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  */
    116 static 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  */
    135 static u2 get2LE(unsigned char const* pSrc)
    136 {
    137     return pSrc[0] | (pSrc[1] << 8);
    138 }
    139 
    140 /*
    141  * Get 4 little-endian bytes.
    142  */
    143 static 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 
    155 static 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  */
    288 static 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  */
    334 static 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 
    407 bail:
    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  */
    422 int 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  */
    445 int 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 
    463 bail:
    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  */
    475 void 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  */
    500 ZipEntry 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  */
    529 ZipEntry 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  */
    554 int 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  */
    668 static 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 
    762 z_bail:
    763     inflateEnd(&zstream);        /* free up any allocated structures */
    764 
    765 bail:
    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  */
    777 int 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 
    811 bail:
    812     return result;
    813 }
    814