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