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 */ 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 */ 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 */ 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