• 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 
get4LE(const unsigned char * buf)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  */
inflateToBuffer(T & reader,void * buf,long uncompressedLen,long compressedLen)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:
FileReader(FILE * fp)153    FileReader(FILE* fp) :
154        mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
155    {
156    }
157 
~FileReader()158    ~FileReader() {
159        delete[] mReadBuf;
160    }
161 
read(unsigned char ** nextBuffer,long readSize) const162    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:
FdReader(int fd)173    FdReader(int fd) :
174        mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
175    {
176    }
177 
~FdReader()178    ~FdReader() {
179        delete[] mReadBuf;
180    }
181 
read(unsigned char ** nextBuffer,long readSize) const182    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:
BufferReader(void * input,size_t inputSize)193     BufferReader(void* input, size_t inputSize) :
194         mInput(reinterpret_cast<unsigned char*>(input)),
195         mInputSize(inputSize),
196         mBufferReturned(false)
197     {
198     }
199 
read(unsigned char ** nextBuffer,long)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 
inflateToBuffer(FILE * fp,void * buf,long uncompressedLen,long compressedLen)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 
inflateToBuffer(int fd,void * buf,long uncompressedLen,long compressedLen)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 
inflateToBuffer(void * in,void * buf,long uncompressedLen,long compressedLen)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  */
examineGzip(FILE * fp,int * pCompressionMethod,long * pUncompressedLen,long * pCompressedLen,unsigned long * pCRC32)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