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