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
33using 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
135z_bail:
136    inflateEnd(&zstream);        /* free up any allocated structures */
137
138bail:
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
247z_bail:
248    inflateEnd(&zstream);        /* free up any allocated structures */
249
250bail:
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