1 /* 2 * Copyright (C) 2008 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 * Read-only access to Zip archives, with minimal heap allocation. 19 */ 20 #include "ZipArchive.h" 21 22 #include <zlib.h> 23 24 #include <stdlib.h> 25 #include <unistd.h> 26 #include <string.h> 27 #include <fcntl.h> 28 #include <errno.h> 29 30 #include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd 31 32 33 /* 34 * Zip file constants. 35 */ 36 #define kEOCDSignature 0x06054b50 37 #define kEOCDLen 22 38 #define kEOCDNumEntries 8 // offset to #of entries in file 39 #define kEOCDSize 12 // size of the central directory 40 #define kEOCDFileOffset 16 // offset to central directory 41 42 #define kMaxCommentLen 65535 // longest possible in ushort 43 #define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) 44 45 #define kLFHSignature 0x04034b50 46 #define kLFHLen 30 // excluding variable-len fields 47 #define kLFHNameLen 26 // offset to filename length 48 #define kLFHExtraLen 28 // offset to extra length 49 50 #define kCDESignature 0x02014b50 51 #define kCDELen 46 // excluding variable-len fields 52 #define kCDEMethod 10 // offset to compression method 53 #define kCDEModWhen 12 // offset to modification timestamp 54 #define kCDECRC 16 // offset to entry CRC 55 #define kCDECompLen 20 // offset to compressed length 56 #define kCDEUncompLen 24 // offset to uncompressed length 57 #define kCDENameLen 28 // offset to filename length 58 #define kCDEExtraLen 30 // offset to extra length 59 #define kCDECommentLen 32 // offset to comment length 60 #define kCDELocalOffset 42 // offset to local hdr 61 62 /* 63 * The values we return for ZipEntry use 0 as an invalid value, so we 64 * want to adjust the hash table index by a fixed amount. Using a large 65 * value helps insure that people don't mix & match arguments, e.g. with 66 * entry indices. 67 */ 68 #define kZipEntryAdj 10000 69 70 /* 71 * Convert a ZipEntry to a hash table index, verifying that it's in a 72 * valid range. 73 */ 74 static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry) 75 { 76 long ent = ((long) entry) - kZipEntryAdj; 77 if (ent < 0 || ent >= pArchive->mHashTableSize || 78 pArchive->mHashTable[ent].name == NULL) 79 { 80 LOGW("Zip: invalid ZipEntry %p (%ld)\n", entry, ent); 81 return -1; 82 } 83 return ent; 84 } 85 86 /* 87 * Simple string hash function for non-null-terminated strings. 88 */ 89 static unsigned int computeHash(const char* str, int len) 90 { 91 unsigned int hash = 0; 92 93 while (len--) 94 hash = hash * 31 + *str++; 95 96 return hash; 97 } 98 99 /* 100 * Add a new entry to the hash table. 101 */ 102 static void addToHash(ZipArchive* pArchive, const char* str, int strLen, 103 unsigned int hash) 104 { 105 const int hashTableSize = pArchive->mHashTableSize; 106 int ent = hash & (hashTableSize - 1); 107 108 /* 109 * We over-allocated the table, so we're guaranteed to find an empty slot. 110 */ 111 while (pArchive->mHashTable[ent].name != NULL) 112 ent = (ent + 1) & (hashTableSize-1); 113 114 pArchive->mHashTable[ent].name = str; 115 pArchive->mHashTable[ent].nameLen = strLen; 116 } 117 118 /* 119 * Get 2 little-endian bytes. 120 */ 121 static u2 get2LE(unsigned char const* pSrc) 122 { 123 return pSrc[0] | (pSrc[1] << 8); 124 } 125 126 /* 127 * Get 4 little-endian bytes. 128 */ 129 static u4 get4LE(unsigned char const* pSrc) 130 { 131 u4 result; 132 133 result = pSrc[0]; 134 result |= pSrc[1] << 8; 135 result |= pSrc[2] << 16; 136 result |= pSrc[3] << 24; 137 138 return result; 139 } 140 141 /* 142 * Find the zip Central Directory and memory-map it. 143 * 144 * On success, returns 0 after populating fields from the EOCD area: 145 * mDirectoryOffset 146 * mDirectoryMap 147 * mNumEntries 148 */ 149 static int mapCentralDirectory(int fd, const char* debugFileName, 150 ZipArchive* pArchive) 151 { 152 u1* scanBuf = NULL; 153 int result = -1; 154 155 /* 156 * Get and test file length. 157 */ 158 off_t fileLength = lseek(fd, 0, SEEK_END); 159 if (fileLength < kEOCDLen) { 160 LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength); 161 goto bail; 162 } 163 164 /* 165 * Perform the traditional EOCD snipe hunt. 166 * 167 * We're searching for the End of Central Directory magic number, 168 * which appears at the start of the EOCD block. It's followed by 169 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We 170 * need to read the last part of the file into a buffer, dig through 171 * it to find the magic number, parse some values out, and use those 172 * to determine the extent of the CD. 173 * 174 * We start by pulling in the last part of the file. 175 */ 176 size_t readAmount = kMaxEOCDSearch; 177 if (readAmount > (size_t) fileLength) 178 readAmount = fileLength; 179 off_t searchStart = fileLength - readAmount; 180 181 scanBuf = (u1*) malloc(readAmount); 182 if (lseek(fd, searchStart, SEEK_SET) != searchStart) { 183 LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno)); 184 goto bail; 185 } 186 ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); 187 if (actual != (ssize_t) readAmount) { 188 LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); 189 goto bail; 190 } 191 192 /* 193 * Scan backward for the EOCD magic. In an archive without a trailing 194 * comment, we'll find it on the first try. (We may want to consider 195 * doing an initial minimal read; if we don't find it, retry with a 196 * second read as above.) 197 */ 198 int i; 199 for (i = readAmount - kEOCDLen; i >= 0; i--) { 200 if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { 201 LOGV("+++ Found EOCD at buf+%d\n", i); 202 break; 203 } 204 } 205 if (i < 0) { 206 LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName); 207 goto bail; 208 } 209 210 off_t eocdOffset = searchStart + i; 211 const u1* eocdPtr = scanBuf + i; 212 213 assert(eocdOffset < fileLength); 214 215 /* 216 * Grab the CD offset and size, and the number of entries in the 217 * archive. Verify that they look reasonable. 218 */ 219 u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); 220 u4 dirSize = get4LE(eocdPtr + kEOCDSize); 221 u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset); 222 223 if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { 224 LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n", 225 (long) dirOffset, dirSize, (long) eocdOffset); 226 goto bail; 227 } 228 if (numEntries == 0) { 229 LOGW("Zip: empty archive?\n"); 230 goto bail; 231 } 232 233 LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", 234 numEntries, dirSize, dirOffset); 235 236 /* 237 * It all looks good. Create a mapping for the CD, and set the fields 238 * in pArchive. 239 */ 240 if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize, 241 &pArchive->mDirectoryMap) != 0) 242 { 243 LOGW("Zip: cd map failed\n"); 244 goto bail; 245 } 246 247 pArchive->mNumEntries = numEntries; 248 pArchive->mDirectoryOffset = dirOffset; 249 250 result = 0; 251 252 bail: 253 free(scanBuf); 254 return result; 255 } 256 257 /* 258 * Parses the Zip archive's Central Directory. Allocates and populates the 259 * hash table. 260 * 261 * Returns 0 on success. 262 */ 263 static int parseZipArchive(ZipArchive* pArchive) 264 { 265 int result = -1; 266 const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; 267 size_t cdLength = pArchive->mDirectoryMap.length; 268 int numEntries = pArchive->mNumEntries; 269 270 /* 271 * Create hash table. We have a minimum 75% load factor, possibly as 272 * low as 50% after we round off to a power of 2. There must be at 273 * least one unused entry to avoid an infinite loop during creation. 274 */ 275 pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3); 276 pArchive->mHashTable = (ZipHashEntry*) 277 calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry)); 278 279 /* 280 * Walk through the central directory, adding entries to the hash 281 * table and verifying values. 282 */ 283 const u1* ptr = cdPtr; 284 int i; 285 for (i = 0; i < numEntries; i++) { 286 if (get4LE(ptr) != kCDESignature) { 287 LOGW("Zip: missed a central dir sig (at %d)\n", i); 288 goto bail; 289 } 290 if (ptr + kCDELen > cdPtr + cdLength) { 291 LOGW("Zip: ran off the end (at %d)\n", i); 292 goto bail; 293 } 294 295 long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); 296 if (localHdrOffset >= pArchive->mDirectoryOffset) { 297 LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i); 298 goto bail; 299 } 300 301 unsigned int fileNameLen, extraLen, commentLen, hash; 302 fileNameLen = get2LE(ptr + kCDENameLen); 303 extraLen = get2LE(ptr + kCDEExtraLen); 304 commentLen = get2LE(ptr + kCDECommentLen); 305 306 /* add the CDE filename to the hash table */ 307 hash = computeHash((const char*)ptr + kCDELen, fileNameLen); 308 addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash); 309 310 ptr += kCDELen + fileNameLen + extraLen + commentLen; 311 if ((size_t)(ptr - cdPtr) > cdLength) { 312 LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n", 313 (int) (ptr - cdPtr), cdLength, i); 314 goto bail; 315 } 316 } 317 LOGV("+++ zip good scan %d entries\n", numEntries); 318 319 result = 0; 320 321 bail: 322 return result; 323 } 324 325 /* 326 * Open the specified file read-only. We examine the contents and verify 327 * that it appears to be a valid zip file. 328 * 329 * This will be called on non-Zip files, especially during VM startup, so 330 * we don't want to be too noisy about certain types of failure. (Do 331 * we want a "quiet" flag?) 332 * 333 * On success, we fill out the contents of "pArchive" and return 0. On 334 * failure we return the errno value. 335 */ 336 int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive) 337 { 338 int fd, err; 339 340 LOGV("Opening as zip '%s' %p\n", fileName, pArchive); 341 342 memset(pArchive, 0, sizeof(ZipArchive)); 343 344 fd = open(fileName, O_RDONLY, 0); 345 if (fd < 0) { 346 err = errno ? errno : -1; 347 LOGV("Unable to open '%s': %s\n", fileName, strerror(err)); 348 return err; 349 } 350 351 return dexZipPrepArchive(fd, fileName, pArchive); 352 } 353 354 /* 355 * Prepare to access a ZipArchive through an open file descriptor. 356 * 357 * On success, we fill out the contents of "pArchive" and return 0. 358 */ 359 int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive) 360 { 361 int result = -1; 362 363 memset(pArchive, 0, sizeof(*pArchive)); 364 pArchive->mFd = fd; 365 366 if (mapCentralDirectory(fd, debugFileName, pArchive) != 0) 367 goto bail; 368 369 if (parseZipArchive(pArchive) != 0) { 370 LOGV("Zip: parsing '%s' failed\n", debugFileName); 371 goto bail; 372 } 373 374 /* success */ 375 result = 0; 376 377 bail: 378 if (result != 0) 379 dexZipCloseArchive(pArchive); 380 return result; 381 } 382 383 384 /* 385 * Close a ZipArchive, closing the file and freeing the contents. 386 * 387 * NOTE: the ZipArchive may not have been fully created. 388 */ 389 void dexZipCloseArchive(ZipArchive* pArchive) 390 { 391 LOGV("Closing archive %p\n", pArchive); 392 393 if (pArchive->mFd >= 0) 394 close(pArchive->mFd); 395 396 sysReleaseShmem(&pArchive->mDirectoryMap); 397 398 free(pArchive->mHashTable); 399 400 /* ensure nobody tries to use the ZipArchive after it's closed */ 401 pArchive->mDirectoryOffset = -1; 402 pArchive->mFd = -1; 403 pArchive->mNumEntries = -1; 404 pArchive->mHashTableSize = -1; 405 pArchive->mHashTable = NULL; 406 } 407 408 409 /* 410 * Find a matching entry. 411 * 412 * Returns 0 if not found. 413 */ 414 ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName) 415 { 416 int nameLen = strlen(entryName); 417 unsigned int hash = computeHash(entryName, nameLen); 418 const int hashTableSize = pArchive->mHashTableSize; 419 int ent = hash & (hashTableSize-1); 420 421 while (pArchive->mHashTable[ent].name != NULL) { 422 if (pArchive->mHashTable[ent].nameLen == nameLen && 423 memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0) 424 { 425 /* match */ 426 return (ZipEntry)(long)(ent + kZipEntryAdj); 427 } 428 429 ent = (ent + 1) & (hashTableSize-1); 430 } 431 432 return NULL; 433 } 434 435 #if 0 436 /* 437 * Find the Nth entry. 438 * 439 * This currently involves walking through the sparse hash table, counting 440 * non-empty entries. If we need to speed this up we can either allocate 441 * a parallel lookup table or (perhaps better) provide an iterator interface. 442 */ 443 ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx) 444 { 445 if (idx < 0 || idx >= pArchive->mNumEntries) { 446 LOGW("Invalid index %d\n", idx); 447 return NULL; 448 } 449 450 int ent; 451 for (ent = 0; ent < pArchive->mHashTableSize; ent++) { 452 if (pArchive->mHashTable[ent].name != NULL) { 453 if (idx-- == 0) 454 return (ZipEntry) (ent + kZipEntryAdj); 455 } 456 } 457 458 return NULL; 459 } 460 #endif 461 462 /* 463 * Get the useful fields from the zip entry. 464 * 465 * Returns non-zero if the contents of the fields (particularly the data 466 * offset) appear to be bogus. 467 */ 468 int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry, 469 int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset, 470 long* pModWhen, long* pCrc32) 471 { 472 int ent = entryToIndex(pArchive, entry); 473 if (ent < 0) 474 return -1; 475 476 /* 477 * Recover the start of the central directory entry from the filename 478 * pointer. The filename is the first entry past the fixed-size data, 479 * so we can just subtract back from that. 480 */ 481 const unsigned char* basePtr = (const unsigned char*) 482 pArchive->mDirectoryMap.addr; 483 const unsigned char* ptr = (const unsigned char*) 484 pArchive->mHashTable[ent].name; 485 off_t cdOffset = pArchive->mDirectoryOffset; 486 487 ptr -= kCDELen; 488 489 int method = get2LE(ptr + kCDEMethod); 490 if (pMethod != NULL) 491 *pMethod = method; 492 493 if (pModWhen != NULL) 494 *pModWhen = get4LE(ptr + kCDEModWhen); 495 if (pCrc32 != NULL) 496 *pCrc32 = get4LE(ptr + kCDECRC); 497 498 size_t compLen = get4LE(ptr + kCDECompLen); 499 if (pCompLen != NULL) 500 *pCompLen = compLen; 501 size_t uncompLen = get4LE(ptr + kCDEUncompLen); 502 if (pUncompLen != NULL) 503 *pUncompLen = uncompLen; 504 505 /* 506 * If requested, determine the offset of the start of the data. All we 507 * have is the offset to the Local File Header, which is variable size, 508 * so we have to read the contents of the struct to figure out where 509 * the actual data starts. 510 * 511 * We also need to make sure that the lengths are not so large that 512 * somebody trying to map the compressed or uncompressed data runs 513 * off the end of the mapped region. 514 * 515 * Note we don't verify compLen/uncompLen if they don't request the 516 * dataOffset, because dataOffset is expensive to determine. However, 517 * if they don't have the file offset, they're not likely to be doing 518 * anything with the contents. 519 */ 520 if (pOffset != NULL) { 521 long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); 522 if (localHdrOffset + kLFHLen >= cdOffset) { 523 LOGW("Zip: bad local hdr offset in zip\n"); 524 return -1; 525 } 526 527 u1 lfhBuf[kLFHLen]; 528 if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { 529 LOGW("Zip: failed seeking to lfh at offset %ld\n", localHdrOffset); 530 return -1; 531 } 532 ssize_t actual = 533 TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf))); 534 if (actual != sizeof(lfhBuf)) { 535 LOGW("Zip: failed reading lfh from offset %ld\n", localHdrOffset); 536 return -1; 537 } 538 539 if (get4LE(lfhBuf) != kLFHSignature) { 540 LOGW("Zip: didn't find signature at start of lfh, offset=%ld\n", 541 localHdrOffset); 542 return -1; 543 } 544 545 off_t dataOffset = localHdrOffset + kLFHLen 546 + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); 547 if (dataOffset >= cdOffset) { 548 LOGW("Zip: bad data offset %ld in zip\n", (long) dataOffset); 549 return -1; 550 } 551 552 /* check lengths */ 553 if ((off_t)(dataOffset + compLen) > cdOffset) { 554 LOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)\n", 555 (long) dataOffset, compLen, (long) cdOffset); 556 return -1; 557 } 558 559 if (method == kCompressStored && 560 (off_t)(dataOffset + uncompLen) > cdOffset) 561 { 562 LOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)\n", 563 (long) dataOffset, uncompLen, (long) cdOffset); 564 return -1; 565 } 566 567 *pOffset = dataOffset; 568 } 569 return 0; 570 } 571 572 /* 573 * Uncompress "deflate" data from the archive's file to an open file 574 * descriptor. 575 */ 576 static int inflateToFile(int inFd, int outFd, size_t uncompLen, size_t compLen) 577 { 578 int result = -1; 579 const size_t kBufSize = 32768; 580 unsigned char* readBuf = (unsigned char*) malloc(kBufSize); 581 unsigned char* writeBuf = (unsigned char*) malloc(kBufSize); 582 z_stream zstream; 583 int zerr; 584 585 if (readBuf == NULL || writeBuf == NULL) 586 goto bail; 587 588 /* 589 * Initialize the zlib stream struct. 590 */ 591 memset(&zstream, 0, sizeof(zstream)); 592 zstream.zalloc = Z_NULL; 593 zstream.zfree = Z_NULL; 594 zstream.opaque = Z_NULL; 595 zstream.next_in = NULL; 596 zstream.avail_in = 0; 597 zstream.next_out = (Bytef*) writeBuf; 598 zstream.avail_out = kBufSize; 599 zstream.data_type = Z_UNKNOWN; 600 601 /* 602 * Use the undocumented "negative window bits" feature to tell zlib 603 * that there's no zlib header waiting for it. 604 */ 605 zerr = inflateInit2(&zstream, -MAX_WBITS); 606 if (zerr != Z_OK) { 607 if (zerr == Z_VERSION_ERROR) { 608 LOGE("Installed zlib is not compatible with linked version (%s)\n", 609 ZLIB_VERSION); 610 } else { 611 LOGW("Call to inflateInit2 failed (zerr=%d)\n", zerr); 612 } 613 goto bail; 614 } 615 616 /* 617 * Loop while we have more to do. 618 */ 619 do { 620 /* read as much as we can */ 621 if (zstream.avail_in == 0) { 622 size_t getSize = (compLen > kBufSize) ? kBufSize : compLen; 623 624 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize)); 625 if (actual != (ssize_t) getSize) { 626 LOGW("Zip: inflate read failed (%d vs %zd)\n", 627 (int)actual, getSize); 628 goto z_bail; 629 } 630 631 compLen -= getSize; 632 633 zstream.next_in = readBuf; 634 zstream.avail_in = getSize; 635 } 636 637 /* uncompress the data */ 638 zerr = inflate(&zstream, Z_NO_FLUSH); 639 if (zerr != Z_OK && zerr != Z_STREAM_END) { 640 LOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", 641 zerr, zstream.next_in, zstream.avail_in, 642 zstream.next_out, zstream.avail_out); 643 goto z_bail; 644 } 645 646 /* write when we're full or when we're done */ 647 if (zstream.avail_out == 0 || 648 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) 649 { 650 size_t writeSize = zstream.next_out - writeBuf; 651 if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0) 652 goto z_bail; 653 654 zstream.next_out = writeBuf; 655 zstream.avail_out = kBufSize; 656 } 657 } while (zerr == Z_OK); 658 659 assert(zerr == Z_STREAM_END); /* other errors should've been caught */ 660 661 /* paranoia */ 662 if (zstream.total_out != uncompLen) { 663 LOGW("Zip: size mismatch on inflated file (%ld vs %zd)\n", 664 zstream.total_out, uncompLen); 665 goto z_bail; 666 } 667 668 result = 0; 669 670 z_bail: 671 inflateEnd(&zstream); /* free up any allocated structures */ 672 673 bail: 674 free(readBuf); 675 free(writeBuf); 676 return result; 677 } 678 679 /* 680 * Copy bytes from input to output. 681 */ 682 static int copyFileToFile(int inFd, int outFd, size_t uncompLen) 683 { 684 const size_t kBufSize = 32768; 685 unsigned char buf[kBufSize]; 686 687 while (uncompLen != 0) { 688 size_t getSize = (uncompLen > kBufSize) ? kBufSize : uncompLen; 689 690 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); 691 if (actual != (ssize_t) getSize) { 692 LOGW("Zip: copy read failed (%d vs %zd)\n", (int)actual, getSize); 693 return -1; 694 } 695 696 if (sysWriteFully(outFd, buf, getSize, "Zip copy") != 0) 697 return -1; 698 699 uncompLen -= getSize; 700 } 701 702 return 0; 703 } 704 705 /* 706 * Uncompress an entry, in its entirety, to an open file descriptor. 707 * 708 * TODO: this doesn't verify the data's CRC, but probably should (especially 709 * for uncompressed data). 710 */ 711 int dexZipExtractEntryToFile(const ZipArchive* pArchive, 712 const ZipEntry entry, int fd) 713 { 714 int result = -1; 715 int ent = entryToIndex(pArchive, entry); 716 if (ent < 0) { 717 LOGW("Zip: extract can't find entry %p\n", entry); 718 goto bail; 719 } 720 721 int method; 722 size_t uncompLen, compLen; 723 off_t dataOffset; 724 725 if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen, 726 &dataOffset, NULL, NULL) != 0) 727 { 728 goto bail; 729 } 730 if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) { 731 LOGW("Zip: lseek to data at %ld failed\n", (long) dataOffset); 732 goto bail; 733 } 734 735 if (method == kCompressStored) { 736 if (copyFileToFile(pArchive->mFd, fd, uncompLen) != 0) 737 goto bail; 738 } else { 739 if (inflateToFile(pArchive->mFd, fd, uncompLen, compLen) != 0) 740 goto bail; 741 } 742 743 result = 0; 744 745 bail: 746 return result; 747 } 748