Home | History | Annotate | Download | only in androidfw
      1 /*
      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 // Misc zip/gzip utility functions.
     19 //
     20 
     21 #define LOG_TAG "ziputil"
     22 
     23 #include <androidfw/ZipUtils.h>
     24 #include <androidfw/ZipFileRO.h>
     25 #include <utils/Log.h>
     26 #include <utils/Compat.h>
     27 
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <assert.h>
     31 
     32 #include <zlib.h>
     33 
     34 using namespace android;
     35 
     36 static inline unsigned long get4LE(const unsigned char* buf) {
     37     return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
     38 }
     39 
     40 
     41 static const unsigned long kReadBufSize = 32768;
     42 
     43 /*
     44  * Utility function that expands zip/gzip "deflate" compressed data
     45  * into a buffer.
     46  *
     47  * (This is a clone of the previous function, but it takes a FILE* instead
     48  * of an fd.  We could pass fileno(fd) to the above, but we can run into
     49  * trouble when "fp" has a different notion of what fd's file position is.)
     50  *
     51  * "fp" is an open file positioned at the start of the "deflate" data
     52  * "buf" must hold at least "uncompressedLen" bytes.
     53  */
     54 /*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
     55     long uncompressedLen, long compressedLen)
     56 {
     57     bool result = false;
     58 
     59     z_stream zstream;
     60     int zerr;
     61     unsigned long compRemaining;
     62 
     63     assert(uncompressedLen >= 0);
     64     assert(compressedLen >= 0);
     65 
     66     compRemaining = compressedLen;
     67 
     68     /*
     69      * Initialize the zlib stream.
     70      */
     71     memset(&zstream, 0, sizeof(zstream));
     72     zstream.zalloc = Z_NULL;
     73     zstream.zfree = Z_NULL;
     74     zstream.opaque = Z_NULL;
     75     zstream.next_in = NULL;
     76     zstream.avail_in = 0;
     77     zstream.next_out = (Bytef*) buf;
     78     zstream.avail_out = uncompressedLen;
     79     zstream.data_type = Z_UNKNOWN;
     80 
     81     /*
     82      * Use the undocumented "negative window bits" feature to tell zlib
     83      * that there's no zlib header waiting for it.
     84      */
     85     zerr = inflateInit2(&zstream, -MAX_WBITS);
     86     if (zerr != Z_OK) {
     87         if (zerr == Z_VERSION_ERROR) {
     88             ALOGE("Installed zlib is not compatible with linked version (%s)\n",
     89                 ZLIB_VERSION);
     90         } else {
     91             ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
     92         }
     93         goto bail;
     94     }
     95 
     96     /*
     97      * Loop while we have data.
     98      */
     99     do {
    100         unsigned long getSize;
    101 
    102         /* read as much as we can */
    103         if (zstream.avail_in == 0) {
    104             getSize = (compRemaining > kReadBufSize) ?
    105                         kReadBufSize : compRemaining;
    106             ALOGV("+++ reading %ld bytes (%ld left)\n",
    107                 getSize, compRemaining);
    108 
    109             unsigned char* nextBuffer = NULL;
    110             const unsigned long nextSize = reader.read(&nextBuffer, getSize);
    111 
    112             if (nextSize < getSize || nextBuffer == NULL) {
    113                 ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
    114                 goto z_bail;
    115             }
    116 
    117             compRemaining -= nextSize;
    118 
    119             zstream.next_in = nextBuffer;
    120             zstream.avail_in = nextSize;
    121         }
    122 
    123         /* uncompress the data */
    124         zerr = inflate(&zstream, Z_NO_FLUSH);
    125         if (zerr != Z_OK && zerr != Z_STREAM_END) {
    126             ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
    127             goto z_bail;
    128         }
    129 
    130         /* output buffer holds all, so no need to write the output */
    131     } while (zerr == Z_OK);
    132 
    133     assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
    134 
    135     if ((long) zstream.total_out != uncompressedLen) {
    136         ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
    137             zstream.total_out, uncompressedLen);
    138         goto z_bail;
    139     }
    140 
    141     // success!
    142     result = true;
    143 
    144 z_bail:
    145     inflateEnd(&zstream);        /* free up any allocated structures */
    146 
    147 bail:
    148     return result;
    149 }
    150 
    151 class FileReader {
    152 public:
    153    FileReader(FILE* fp) :
    154        mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
    155    {
    156    }
    157 
    158    ~FileReader() {
    159        delete[] mReadBuf;
    160    }
    161 
    162    long read(unsigned char** nextBuffer, long readSize) const {
    163        *nextBuffer = mReadBuf;
    164        return fread(mReadBuf, 1, readSize, mFp);
    165    }
    166 
    167    FILE* mFp;
    168    unsigned char* mReadBuf;
    169 };
    170 
    171 class FdReader {
    172 public:
    173    FdReader(int fd) :
    174        mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
    175    {
    176    }
    177 
    178    ~FdReader() {
    179        delete[] mReadBuf;
    180    }
    181 
    182    long read(unsigned char** nextBuffer, long readSize) const {
    183        *nextBuffer = mReadBuf;
    184        return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
    185    }
    186 
    187    int mFd;
    188    unsigned char* mReadBuf;
    189 };
    190 
    191 class BufferReader {
    192 public:
    193     BufferReader(void* input, size_t inputSize) :
    194         mInput(reinterpret_cast<unsigned char*>(input)),
    195         mInputSize(inputSize),
    196         mBufferReturned(false)
    197     {
    198     }
    199 
    200     long read(unsigned char** nextBuffer, long /*readSize*/) {
    201         if (!mBufferReturned) {
    202             mBufferReturned = true;
    203             *nextBuffer = mInput;
    204             return mInputSize;
    205         }
    206 
    207         *nextBuffer = NULL;
    208         return 0;
    209     }
    210 
    211     unsigned char* mInput;
    212     const size_t mInputSize;
    213     bool mBufferReturned;
    214 };
    215 
    216 /*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
    217     long uncompressedLen, long compressedLen)
    218 {
    219     FileReader reader(fp);
    220     return ::inflateToBuffer<FileReader>(reader, buf,
    221         uncompressedLen, compressedLen);
    222 }
    223 
    224 /*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
    225     long uncompressedLen, long compressedLen)
    226 {
    227     FdReader reader(fd);
    228     return ::inflateToBuffer<FdReader>(reader, buf,
    229         uncompressedLen, compressedLen);
    230 }
    231 
    232 /*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
    233     long uncompressedLen, long compressedLen)
    234 {
    235     BufferReader reader(in, compressedLen);
    236     return ::inflateToBuffer<BufferReader>(reader, buf,
    237         uncompressedLen, compressedLen);
    238 }
    239 
    240 
    241 
    242 /*
    243  * Look at the contents of a gzip archive.  We want to know where the
    244  * data starts, and how long it will be after it is uncompressed.
    245  *
    246  * We expect to find the CRC and length as the last 8 bytes on the file.
    247  * This is a pretty reasonable thing to expect for locally-compressed
    248  * files, but there's a small chance that some extra padding got thrown
    249  * on (the man page talks about compressed data written to tape).  We
    250  * don't currently deal with that here.  If "gzip -l" whines, we're going
    251  * to fail too.
    252  *
    253  * On exit, "fp" is pointing at the start of the compressed data.
    254  */
    255 /*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod,
    256     long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32)
    257 {
    258     enum {  // flags
    259         FTEXT       = 0x01,
    260         FHCRC       = 0x02,
    261         FEXTRA      = 0x04,
    262         FNAME       = 0x08,
    263         FCOMMENT    = 0x10,
    264     };
    265     int ic;
    266     int method, flags;
    267     int i;
    268 
    269     ic = getc(fp);
    270     if (ic != 0x1f || getc(fp) != 0x8b)
    271         return false;       // not gzip
    272     method = getc(fp);
    273     flags = getc(fp);
    274 
    275     /* quick sanity checks */
    276     if (method == EOF || flags == EOF)
    277         return false;
    278     if (method != ZipFileRO::kCompressDeflated)
    279         return false;
    280 
    281     /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
    282     for (i = 0; i < 6; i++)
    283         (void) getc(fp);
    284     /* consume "extra" field, if present */
    285     if ((flags & FEXTRA) != 0) {
    286         int len;
    287 
    288         len = getc(fp);
    289         len |= getc(fp) << 8;
    290         while (len-- && getc(fp) != EOF)
    291             ;
    292     }
    293     /* consume filename, if present */
    294     if ((flags & FNAME) != 0) {
    295         do {
    296             ic = getc(fp);
    297         } while (ic != 0 && ic != EOF);
    298     }
    299     /* consume comment, if present */
    300     if ((flags & FCOMMENT) != 0) {
    301         do {
    302             ic = getc(fp);
    303         } while (ic != 0 && ic != EOF);
    304     }
    305     /* consume 16-bit header CRC, if present */
    306     if ((flags & FHCRC) != 0) {
    307         (void) getc(fp);
    308         (void) getc(fp);
    309     }
    310 
    311     if (feof(fp) || ferror(fp))
    312         return false;
    313 
    314     /* seek to the end; CRC and length are in the last 8 bytes */
    315     long curPosn = ftell(fp);
    316     unsigned char buf[8];
    317     fseek(fp, -8, SEEK_END);
    318     *pCompressedLen = ftell(fp) - curPosn;
    319 
    320     if (fread(buf, 1, 8, fp) != 8)
    321         return false;
    322     /* seek back to start of compressed data */
    323     fseek(fp, curPosn, SEEK_SET);
    324 
    325     *pCompressionMethod = method;
    326     *pCRC32 = get4LE(&buf[0]);
    327     *pUncompressedLen = get4LE(&buf[4]);
    328 
    329     return true;
    330 }
    331