1 /* 2 * Copyright (C) 2006 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 // Access to Zip archives. 19 // 20 21 #define LOG_TAG "zip" 22 23 #include <androidfw/ZipUtils.h> 24 #include <utils/Log.h> 25 26 #include "ZipFile.h" 27 28 #include <zlib.h> 29 #define DEF_MEM_LEVEL 8 // normally in zutil.h? 30 31 #include "zopfli/deflate.h" 32 33 #include <memory.h> 34 #include <sys/stat.h> 35 #include <errno.h> 36 #include <assert.h> 37 #include <inttypes.h> 38 39 using namespace android; 40 41 /* 42 * Some environments require the "b", some choke on it. 43 */ 44 #define FILE_OPEN_RO "rb" 45 #define FILE_OPEN_RW "r+b" 46 #define FILE_OPEN_RW_CREATE "w+b" 47 48 /* should live somewhere else? */ 49 static status_t errnoToStatus(int err) 50 { 51 if (err == ENOENT) 52 return NAME_NOT_FOUND; 53 else if (err == EACCES) 54 return PERMISSION_DENIED; 55 else 56 return UNKNOWN_ERROR; 57 } 58 59 /* 60 * Open a file and parse its guts. 61 */ 62 status_t ZipFile::open(const char* zipFileName, int flags) 63 { 64 bool newArchive = false; 65 66 assert(mZipFp == NULL); // no reopen 67 68 if ((flags & kOpenTruncate)) 69 flags |= kOpenCreate; // trunc implies create 70 71 if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) 72 return INVALID_OPERATION; // not both 73 if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) 74 return INVALID_OPERATION; // not neither 75 if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) 76 return INVALID_OPERATION; // create requires write 77 78 if (flags & kOpenTruncate) { 79 newArchive = true; 80 } else { 81 newArchive = (access(zipFileName, F_OK) != 0); 82 if (!(flags & kOpenCreate) && newArchive) { 83 /* not creating, must already exist */ 84 ALOGD("File %s does not exist", zipFileName); 85 return NAME_NOT_FOUND; 86 } 87 } 88 89 /* open the file */ 90 const char* openflags; 91 if (flags & kOpenReadWrite) { 92 if (newArchive) 93 openflags = FILE_OPEN_RW_CREATE; 94 else 95 openflags = FILE_OPEN_RW; 96 } else { 97 openflags = FILE_OPEN_RO; 98 } 99 mZipFp = fopen(zipFileName, openflags); 100 if (mZipFp == NULL) { 101 int err = errno; 102 ALOGD("fopen failed: %d\n", err); 103 return errnoToStatus(err); 104 } 105 106 status_t result; 107 if (!newArchive) { 108 /* 109 * Load the central directory. If that fails, then this probably 110 * isn't a Zip archive. 111 */ 112 result = readCentralDir(); 113 } else { 114 /* 115 * Newly-created. The EndOfCentralDir constructor actually 116 * sets everything to be the way we want it (all zeroes). We 117 * set mNeedCDRewrite so that we create *something* if the 118 * caller doesn't add any files. (We could also just unlink 119 * the file if it's brand new and nothing was added, but that's 120 * probably doing more than we really should -- the user might 121 * have a need for empty zip files.) 122 */ 123 mNeedCDRewrite = true; 124 result = NO_ERROR; 125 } 126 127 if (flags & kOpenReadOnly) 128 mReadOnly = true; 129 else 130 assert(!mReadOnly); 131 132 return result; 133 } 134 135 /* 136 * Return the Nth entry in the archive. 137 */ 138 ZipEntry* ZipFile::getEntryByIndex(int idx) const 139 { 140 if (idx < 0 || idx >= (int) mEntries.size()) 141 return NULL; 142 143 return mEntries[idx]; 144 } 145 146 /* 147 * Find an entry by name. 148 */ 149 ZipEntry* ZipFile::getEntryByName(const char* fileName) const 150 { 151 /* 152 * Do a stupid linear string-compare search. 153 * 154 * There are various ways to speed this up, especially since it's rare 155 * to intermingle changes to the archive with "get by name" calls. We 156 * don't want to sort the mEntries vector itself, however, because 157 * it's used to recreate the Central Directory. 158 * 159 * (Hash table works, parallel list of pointers in sorted order is good.) 160 */ 161 int idx; 162 163 for (idx = mEntries.size()-1; idx >= 0; idx--) { 164 ZipEntry* pEntry = mEntries[idx]; 165 if (!pEntry->getDeleted() && 166 strcmp(fileName, pEntry->getFileName()) == 0) 167 { 168 return pEntry; 169 } 170 } 171 172 return NULL; 173 } 174 175 /* 176 * Empty the mEntries vector. 177 */ 178 void ZipFile::discardEntries(void) 179 { 180 int count = mEntries.size(); 181 182 while (--count >= 0) 183 delete mEntries[count]; 184 185 mEntries.clear(); 186 } 187 188 189 /* 190 * Find the central directory and read the contents. 191 * 192 * The fun thing about ZIP archives is that they may or may not be 193 * readable from start to end. In some cases, notably for archives 194 * that were written to stdout, the only length information is in the 195 * central directory at the end of the file. 196 * 197 * Of course, the central directory can be followed by a variable-length 198 * comment field, so we have to scan through it backwards. The comment 199 * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff 200 * itself, plus apparently sometimes people throw random junk on the end 201 * just for the fun of it. 202 * 203 * This is all a little wobbly. If the wrong value ends up in the EOCD 204 * area, we're hosed. This appears to be the way that everbody handles 205 * it though, so we're in pretty good company if this fails. 206 */ 207 status_t ZipFile::readCentralDir(void) 208 { 209 status_t result = NO_ERROR; 210 uint8_t* buf = NULL; 211 off_t fileLength, seekStart; 212 long readAmount; 213 int i; 214 215 fseek(mZipFp, 0, SEEK_END); 216 fileLength = ftell(mZipFp); 217 rewind(mZipFp); 218 219 /* too small to be a ZIP archive? */ 220 if (fileLength < EndOfCentralDir::kEOCDLen) { 221 ALOGD("Length is %ld -- too small\n", (long)fileLength); 222 result = INVALID_OPERATION; 223 goto bail; 224 } 225 226 buf = new uint8_t[EndOfCentralDir::kMaxEOCDSearch]; 227 if (buf == NULL) { 228 ALOGD("Failure allocating %d bytes for EOCD search", 229 EndOfCentralDir::kMaxEOCDSearch); 230 result = NO_MEMORY; 231 goto bail; 232 } 233 234 if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { 235 seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; 236 readAmount = EndOfCentralDir::kMaxEOCDSearch; 237 } else { 238 seekStart = 0; 239 readAmount = (long) fileLength; 240 } 241 if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { 242 ALOGD("Failure seeking to end of zip at %ld", (long) seekStart); 243 result = UNKNOWN_ERROR; 244 goto bail; 245 } 246 247 /* read the last part of the file into the buffer */ 248 if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { 249 ALOGD("short file? wanted %ld\n", readAmount); 250 result = UNKNOWN_ERROR; 251 goto bail; 252 } 253 254 /* find the end-of-central-dir magic */ 255 for (i = readAmount - 4; i >= 0; i--) { 256 if (buf[i] == 0x50 && 257 ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) 258 { 259 ALOGV("+++ Found EOCD at buf+%d\n", i); 260 break; 261 } 262 } 263 if (i < 0) { 264 ALOGD("EOCD not found, not Zip\n"); 265 result = INVALID_OPERATION; 266 goto bail; 267 } 268 269 /* extract eocd values */ 270 result = mEOCD.readBuf(buf + i, readAmount - i); 271 if (result != NO_ERROR) { 272 ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i); 273 goto bail; 274 } 275 //mEOCD.dump(); 276 277 if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || 278 mEOCD.mNumEntries != mEOCD.mTotalNumEntries) 279 { 280 ALOGD("Archive spanning not supported\n"); 281 result = INVALID_OPERATION; 282 goto bail; 283 } 284 285 /* 286 * So far so good. "mCentralDirSize" is the size in bytes of the 287 * central directory, so we can just seek back that far to find it. 288 * We can also seek forward mCentralDirOffset bytes from the 289 * start of the file. 290 * 291 * We're not guaranteed to have the rest of the central dir in the 292 * buffer, nor are we guaranteed that the central dir will have any 293 * sort of convenient size. We need to skip to the start of it and 294 * read the header, then the other goodies. 295 * 296 * The only thing we really need right now is the file comment, which 297 * we're hoping to preserve. 298 */ 299 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 300 ALOGD("Failure seeking to central dir offset %" PRIu32 "\n", 301 mEOCD.mCentralDirOffset); 302 result = UNKNOWN_ERROR; 303 goto bail; 304 } 305 306 /* 307 * Loop through and read the central dir entries. 308 */ 309 ALOGV("Scanning %" PRIu16 " entries...\n", mEOCD.mTotalNumEntries); 310 int entry; 311 for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { 312 ZipEntry* pEntry = new ZipEntry; 313 314 result = pEntry->initFromCDE(mZipFp); 315 if (result != NO_ERROR) { 316 ALOGD("initFromCDE failed\n"); 317 delete pEntry; 318 goto bail; 319 } 320 321 mEntries.add(pEntry); 322 } 323 324 325 /* 326 * If all went well, we should now be back at the EOCD. 327 */ 328 { 329 uint8_t checkBuf[4]; 330 if (fread(checkBuf, 1, 4, mZipFp) != 4) { 331 ALOGD("EOCD check read failed\n"); 332 result = INVALID_OPERATION; 333 goto bail; 334 } 335 if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { 336 ALOGD("EOCD read check failed\n"); 337 result = UNKNOWN_ERROR; 338 goto bail; 339 } 340 ALOGV("+++ EOCD read check passed\n"); 341 } 342 343 bail: 344 delete[] buf; 345 return result; 346 } 347 348 349 /* 350 * Add a new file to the archive. 351 * 352 * This requires creating and populating a ZipEntry structure, and copying 353 * the data into the file at the appropriate position. The "appropriate 354 * position" is the current location of the central directory, which we 355 * casually overwrite (we can put it back later). 356 * 357 * If we were concerned about safety, we would want to make all changes 358 * in a temp file and then overwrite the original after everything was 359 * safely written. Not really a concern for us. 360 */ 361 status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, 362 const char* storageName, int compressionMethod, ZipEntry** ppEntry) 363 { 364 ZipEntry* pEntry = NULL; 365 status_t result = NO_ERROR; 366 long lfhPosn, startPosn, endPosn, uncompressedLen; 367 FILE* inputFp = NULL; 368 uint32_t crc; 369 time_t modWhen; 370 371 if (mReadOnly) 372 return INVALID_OPERATION; 373 374 assert(compressionMethod == ZipEntry::kCompressDeflated || 375 compressionMethod == ZipEntry::kCompressStored); 376 377 /* make sure we're in a reasonable state */ 378 assert(mZipFp != NULL); 379 assert(mEntries.size() == mEOCD.mTotalNumEntries); 380 381 /* make sure it doesn't already exist */ 382 if (getEntryByName(storageName) != NULL) 383 return ALREADY_EXISTS; 384 385 if (!data) { 386 inputFp = fopen(fileName, FILE_OPEN_RO); 387 if (inputFp == NULL) 388 return errnoToStatus(errno); 389 } 390 391 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 392 result = UNKNOWN_ERROR; 393 goto bail; 394 } 395 396 pEntry = new ZipEntry; 397 pEntry->initNew(storageName, NULL); 398 399 /* 400 * From here on out, failures are more interesting. 401 */ 402 mNeedCDRewrite = true; 403 404 /* 405 * Write the LFH, even though it's still mostly blank. We need it 406 * as a place-holder. In theory the LFH isn't necessary, but in 407 * practice some utilities demand it. 408 */ 409 lfhPosn = ftell(mZipFp); 410 pEntry->mLFH.write(mZipFp); 411 startPosn = ftell(mZipFp); 412 413 /* 414 * Copy the data in, possibly compressing it as we go. 415 */ 416 if (compressionMethod == ZipEntry::kCompressDeflated) { 417 bool failed = false; 418 result = compressFpToFp(mZipFp, inputFp, data, size, &crc); 419 if (result != NO_ERROR) { 420 ALOGD("compression failed, storing\n"); 421 failed = true; 422 } else { 423 /* 424 * Make sure it has compressed "enough". This probably ought 425 * to be set through an API call, but I don't expect our 426 * criteria to change over time. 427 */ 428 long src = inputFp ? ftell(inputFp) : size; 429 long dst = ftell(mZipFp) - startPosn; 430 if (dst + (dst / 10) > src) { 431 ALOGD("insufficient compression (src=%ld dst=%ld), storing\n", 432 src, dst); 433 failed = true; 434 } 435 } 436 437 if (failed) { 438 compressionMethod = ZipEntry::kCompressStored; 439 if (inputFp) rewind(inputFp); 440 fseek(mZipFp, startPosn, SEEK_SET); 441 /* fall through to kCompressStored case */ 442 } 443 } 444 /* handle "no compression" request, or failed compression from above */ 445 if (compressionMethod == ZipEntry::kCompressStored) { 446 if (inputFp) { 447 result = copyFpToFp(mZipFp, inputFp, &crc); 448 } else { 449 result = copyDataToFp(mZipFp, data, size, &crc); 450 } 451 if (result != NO_ERROR) { 452 // don't need to truncate; happens in CDE rewrite 453 ALOGD("failed copying data in\n"); 454 goto bail; 455 } 456 } 457 458 // currently seeked to end of file 459 uncompressedLen = inputFp ? ftell(inputFp) : size; 460 461 /* 462 * We could write the "Data Descriptor", but there doesn't seem to 463 * be any point since we're going to go back and write the LFH. 464 * 465 * Update file offsets. 466 */ 467 endPosn = ftell(mZipFp); // seeked to end of compressed data 468 469 /* 470 * Success! Fill out new values. 471 */ 472 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, 473 compressionMethod); 474 modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); 475 pEntry->setModWhen(modWhen); 476 pEntry->setLFHOffset(lfhPosn); 477 mEOCD.mNumEntries++; 478 mEOCD.mTotalNumEntries++; 479 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 480 mEOCD.mCentralDirOffset = endPosn; 481 482 /* 483 * Go back and write the LFH. 484 */ 485 if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { 486 result = UNKNOWN_ERROR; 487 goto bail; 488 } 489 pEntry->mLFH.write(mZipFp); 490 491 /* 492 * Add pEntry to the list. 493 */ 494 mEntries.add(pEntry); 495 if (ppEntry != NULL) 496 *ppEntry = pEntry; 497 pEntry = NULL; 498 499 bail: 500 if (inputFp != NULL) 501 fclose(inputFp); 502 delete pEntry; 503 return result; 504 } 505 506 /* 507 * Add an entry by copying it from another zip file. If "padding" is 508 * nonzero, the specified number of bytes will be added to the "extra" 509 * field in the header. 510 * 511 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 512 */ 513 status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, 514 int padding, ZipEntry** ppEntry) 515 { 516 ZipEntry* pEntry = NULL; 517 status_t result; 518 long lfhPosn, endPosn; 519 520 if (mReadOnly) 521 return INVALID_OPERATION; 522 523 /* make sure we're in a reasonable state */ 524 assert(mZipFp != NULL); 525 assert(mEntries.size() == mEOCD.mTotalNumEntries); 526 527 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 528 result = UNKNOWN_ERROR; 529 goto bail; 530 } 531 532 pEntry = new ZipEntry; 533 if (pEntry == NULL) { 534 result = NO_MEMORY; 535 goto bail; 536 } 537 538 result = pEntry->initFromExternal(pSourceEntry); 539 if (result != NO_ERROR) 540 goto bail; 541 if (padding != 0) { 542 result = pEntry->addPadding(padding); 543 if (result != NO_ERROR) 544 goto bail; 545 } 546 547 /* 548 * From here on out, failures are more interesting. 549 */ 550 mNeedCDRewrite = true; 551 552 /* 553 * Write the LFH. Since we're not recompressing the data, we already 554 * have all of the fields filled out. 555 */ 556 lfhPosn = ftell(mZipFp); 557 pEntry->mLFH.write(mZipFp); 558 559 /* 560 * Copy the data over. 561 * 562 * If the "has data descriptor" flag is set, we want to copy the DD 563 * fields as well. This is a fixed-size area immediately following 564 * the data. 565 */ 566 if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) 567 { 568 result = UNKNOWN_ERROR; 569 goto bail; 570 } 571 572 off_t copyLen; 573 copyLen = pSourceEntry->getCompressedLen(); 574 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) 575 copyLen += ZipEntry::kDataDescriptorLen; 576 577 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) 578 != NO_ERROR) 579 { 580 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); 581 result = UNKNOWN_ERROR; 582 goto bail; 583 } 584 585 /* 586 * Update file offsets. 587 */ 588 endPosn = ftell(mZipFp); 589 590 /* 591 * Success! Fill out new values. 592 */ 593 pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset 594 mEOCD.mNumEntries++; 595 mEOCD.mTotalNumEntries++; 596 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 597 mEOCD.mCentralDirOffset = endPosn; 598 599 /* 600 * Add pEntry to the list. 601 */ 602 mEntries.add(pEntry); 603 if (ppEntry != NULL) 604 *ppEntry = pEntry; 605 pEntry = NULL; 606 607 result = NO_ERROR; 608 609 bail: 610 delete pEntry; 611 return result; 612 } 613 614 /* 615 * Add an entry by copying it from another zip file, recompressing with 616 * Zopfli if already compressed. 617 * 618 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 619 */ 620 status_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, 621 ZipEntry** ppEntry) 622 { 623 ZipEntry* pEntry = NULL; 624 status_t result; 625 long lfhPosn, startPosn, endPosn, uncompressedLen; 626 627 if (mReadOnly) 628 return INVALID_OPERATION; 629 630 /* make sure we're in a reasonable state */ 631 assert(mZipFp != NULL); 632 assert(mEntries.size() == mEOCD.mTotalNumEntries); 633 634 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 635 result = UNKNOWN_ERROR; 636 goto bail; 637 } 638 639 pEntry = new ZipEntry; 640 if (pEntry == NULL) { 641 result = NO_MEMORY; 642 goto bail; 643 } 644 645 result = pEntry->initFromExternal(pSourceEntry); 646 if (result != NO_ERROR) 647 goto bail; 648 649 /* 650 * From here on out, failures are more interesting. 651 */ 652 mNeedCDRewrite = true; 653 654 /* 655 * Write the LFH, even though it's still mostly blank. We need it 656 * as a place-holder. In theory the LFH isn't necessary, but in 657 * practice some utilities demand it. 658 */ 659 lfhPosn = ftell(mZipFp); 660 pEntry->mLFH.write(mZipFp); 661 startPosn = ftell(mZipFp); 662 663 /* 664 * Copy the data over. 665 * 666 * If the "has data descriptor" flag is set, we want to copy the DD 667 * fields as well. This is a fixed-size area immediately following 668 * the data. 669 */ 670 if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) 671 { 672 result = UNKNOWN_ERROR; 673 goto bail; 674 } 675 676 uncompressedLen = pSourceEntry->getUncompressedLen(); 677 678 if (pSourceEntry->isCompressed()) { 679 void *buf = pSourceZip->uncompress(pSourceEntry); 680 if (buf == NULL) { 681 result = NO_MEMORY; 682 goto bail; 683 } 684 long startPosn = ftell(mZipFp); 685 uint32_t crc; 686 if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != NO_ERROR) { 687 ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName); 688 result = UNKNOWN_ERROR; 689 free(buf); 690 goto bail; 691 } 692 long endPosn = ftell(mZipFp); 693 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, 694 pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated); 695 free(buf); 696 } else { 697 off_t copyLen; 698 copyLen = pSourceEntry->getCompressedLen(); 699 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) 700 copyLen += ZipEntry::kDataDescriptorLen; 701 702 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) 703 != NO_ERROR) 704 { 705 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); 706 result = UNKNOWN_ERROR; 707 goto bail; 708 } 709 } 710 711 /* 712 * Update file offsets. 713 */ 714 endPosn = ftell(mZipFp); 715 716 /* 717 * Success! Fill out new values. 718 */ 719 pEntry->setLFHOffset(lfhPosn); 720 mEOCD.mNumEntries++; 721 mEOCD.mTotalNumEntries++; 722 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 723 mEOCD.mCentralDirOffset = endPosn; 724 725 /* 726 * Go back and write the LFH. 727 */ 728 if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { 729 result = UNKNOWN_ERROR; 730 goto bail; 731 } 732 pEntry->mLFH.write(mZipFp); 733 734 /* 735 * Add pEntry to the list. 736 */ 737 mEntries.add(pEntry); 738 if (ppEntry != NULL) 739 *ppEntry = pEntry; 740 pEntry = NULL; 741 742 result = NO_ERROR; 743 744 bail: 745 delete pEntry; 746 return result; 747 } 748 749 /* 750 * Copy all of the bytes in "src" to "dst". 751 * 752 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 753 * will be seeked immediately past the data. 754 */ 755 status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32) 756 { 757 uint8_t tmpBuf[32768]; 758 size_t count; 759 760 *pCRC32 = crc32(0L, Z_NULL, 0); 761 762 while (1) { 763 count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); 764 if (ferror(srcFp) || ferror(dstFp)) 765 return errnoToStatus(errno); 766 if (count == 0) 767 break; 768 769 *pCRC32 = crc32(*pCRC32, tmpBuf, count); 770 771 if (fwrite(tmpBuf, 1, count, dstFp) != count) { 772 ALOGD("fwrite %d bytes failed\n", (int) count); 773 return UNKNOWN_ERROR; 774 } 775 } 776 777 return NO_ERROR; 778 } 779 780 /* 781 * Copy all of the bytes in "src" to "dst". 782 * 783 * On exit, "dstFp" will be seeked immediately past the data. 784 */ 785 status_t ZipFile::copyDataToFp(FILE* dstFp, 786 const void* data, size_t size, uint32_t* pCRC32) 787 { 788 size_t count; 789 790 *pCRC32 = crc32(0L, Z_NULL, 0); 791 if (size > 0) { 792 *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); 793 if (fwrite(data, 1, size, dstFp) != size) { 794 ALOGD("fwrite %d bytes failed\n", (int) size); 795 return UNKNOWN_ERROR; 796 } 797 } 798 799 return NO_ERROR; 800 } 801 802 /* 803 * Copy some of the bytes in "src" to "dst". 804 * 805 * If "pCRC32" is NULL, the CRC will not be computed. 806 * 807 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 808 * will be seeked immediately past the data just written. 809 */ 810 status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, 811 uint32_t* pCRC32) 812 { 813 uint8_t tmpBuf[32768]; 814 size_t count; 815 816 if (pCRC32 != NULL) 817 *pCRC32 = crc32(0L, Z_NULL, 0); 818 819 while (length) { 820 long readSize; 821 822 readSize = sizeof(tmpBuf); 823 if (readSize > length) 824 readSize = length; 825 826 count = fread(tmpBuf, 1, readSize, srcFp); 827 if ((long) count != readSize) { // error or unexpected EOF 828 ALOGD("fread %d bytes failed\n", (int) readSize); 829 return UNKNOWN_ERROR; 830 } 831 832 if (pCRC32 != NULL) 833 *pCRC32 = crc32(*pCRC32, tmpBuf, count); 834 835 if (fwrite(tmpBuf, 1, count, dstFp) != count) { 836 ALOGD("fwrite %d bytes failed\n", (int) count); 837 return UNKNOWN_ERROR; 838 } 839 840 length -= readSize; 841 } 842 843 return NO_ERROR; 844 } 845 846 /* 847 * Compress all of the data in "srcFp" and write it to "dstFp". 848 * 849 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 850 * will be seeked immediately past the compressed data. 851 */ 852 status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, 853 const void* data, size_t size, uint32_t* pCRC32) 854 { 855 status_t result = NO_ERROR; 856 const size_t kBufSize = 1024 * 1024; 857 uint8_t* inBuf = NULL; 858 uint8_t* outBuf = NULL; 859 size_t outSize = 0; 860 bool atEof = false; // no feof() aviailable yet 861 uint32_t crc; 862 ZopfliOptions options; 863 unsigned char bp = 0; 864 865 ZopfliInitOptions(&options); 866 867 crc = crc32(0L, Z_NULL, 0); 868 869 if (data) { 870 crc = crc32(crc, (const unsigned char*)data, size); 871 ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp, 872 &outBuf, &outSize); 873 } else { 874 /* 875 * Create an input buffer and an output buffer. 876 */ 877 inBuf = new uint8_t[kBufSize]; 878 if (inBuf == NULL) { 879 result = NO_MEMORY; 880 goto bail; 881 } 882 883 /* 884 * Loop while we have data. 885 */ 886 do { 887 size_t getSize; 888 getSize = fread(inBuf, 1, kBufSize, srcFp); 889 if (ferror(srcFp)) { 890 ALOGD("deflate read failed (errno=%d)\n", errno); 891 result = UNKNOWN_ERROR; 892 delete[] inBuf; 893 goto bail; 894 } 895 if (getSize < kBufSize) { 896 ALOGV("+++ got %d bytes, EOF reached\n", 897 (int)getSize); 898 atEof = true; 899 } 900 901 crc = crc32(crc, inBuf, getSize); 902 ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize); 903 } while (!atEof); 904 delete[] inBuf; 905 } 906 907 ALOGV("+++ writing %d bytes\n", (int)outSize); 908 if (fwrite(outBuf, 1, outSize, dstFp) != outSize) { 909 ALOGD("write %d failed in deflate\n", (int)outSize); 910 result = UNKNOWN_ERROR; 911 goto bail; 912 } 913 914 *pCRC32 = crc; 915 916 bail: 917 free(outBuf); 918 919 return result; 920 } 921 922 /* 923 * Mark an entry as deleted. 924 * 925 * We will eventually need to crunch the file down, but if several files 926 * are being removed (perhaps as part of an "update" process) we can make 927 * things considerably faster by deferring the removal to "flush" time. 928 */ 929 status_t ZipFile::remove(ZipEntry* pEntry) 930 { 931 /* 932 * Should verify that pEntry is actually part of this archive, and 933 * not some stray ZipEntry from a different file. 934 */ 935 936 /* mark entry as deleted, and mark archive as dirty */ 937 pEntry->setDeleted(); 938 mNeedCDRewrite = true; 939 return NO_ERROR; 940 } 941 942 /* 943 * Flush any pending writes. 944 * 945 * In particular, this will crunch out deleted entries, and write the 946 * Central Directory and EOCD if we have stomped on them. 947 */ 948 status_t ZipFile::flush(void) 949 { 950 status_t result = NO_ERROR; 951 long eocdPosn; 952 int i, count; 953 954 if (mReadOnly) 955 return INVALID_OPERATION; 956 if (!mNeedCDRewrite) 957 return NO_ERROR; 958 959 assert(mZipFp != NULL); 960 961 result = crunchArchive(); 962 if (result != NO_ERROR) 963 return result; 964 965 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) 966 return UNKNOWN_ERROR; 967 968 count = mEntries.size(); 969 for (i = 0; i < count; i++) { 970 ZipEntry* pEntry = mEntries[i]; 971 pEntry->mCDE.write(mZipFp); 972 } 973 974 eocdPosn = ftell(mZipFp); 975 mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; 976 977 mEOCD.write(mZipFp); 978 979 /* 980 * If we had some stuff bloat up during compression and get replaced 981 * with plain files, or if we deleted some entries, there's a lot 982 * of wasted space at the end of the file. Remove it now. 983 */ 984 if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { 985 ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); 986 // not fatal 987 } 988 989 /* should we clear the "newly added" flag in all entries now? */ 990 991 mNeedCDRewrite = false; 992 return NO_ERROR; 993 } 994 995 /* 996 * Crunch deleted files out of an archive by shifting the later files down. 997 * 998 * Because we're not using a temp file, we do the operation inside the 999 * current file. 1000 */ 1001 status_t ZipFile::crunchArchive(void) 1002 { 1003 status_t result = NO_ERROR; 1004 int i, count; 1005 long delCount, adjust; 1006 1007 #if 0 1008 printf("CONTENTS:\n"); 1009 for (i = 0; i < (int) mEntries.size(); i++) { 1010 printf(" %d: lfhOff=%ld del=%d\n", 1011 i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); 1012 } 1013 printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); 1014 #endif 1015 1016 /* 1017 * Roll through the set of files, shifting them as appropriate. We 1018 * could probably get a slight performance improvement by sliding 1019 * multiple files down at once (because we could use larger reads 1020 * when operating on batches of small files), but it's not that useful. 1021 */ 1022 count = mEntries.size(); 1023 delCount = adjust = 0; 1024 for (i = 0; i < count; i++) { 1025 ZipEntry* pEntry = mEntries[i]; 1026 long span; 1027 1028 if (pEntry->getLFHOffset() != 0) { 1029 long nextOffset; 1030 1031 /* Get the length of this entry by finding the offset 1032 * of the next entry. Directory entries don't have 1033 * file offsets, so we need to find the next non-directory 1034 * entry. 1035 */ 1036 nextOffset = 0; 1037 for (int ii = i+1; nextOffset == 0 && ii < count; ii++) 1038 nextOffset = mEntries[ii]->getLFHOffset(); 1039 if (nextOffset == 0) 1040 nextOffset = mEOCD.mCentralDirOffset; 1041 span = nextOffset - pEntry->getLFHOffset(); 1042 1043 assert(span >= ZipEntry::LocalFileHeader::kLFHLen); 1044 } else { 1045 /* This is a directory entry. It doesn't have 1046 * any actual file contents, so there's no need to 1047 * move anything. 1048 */ 1049 span = 0; 1050 } 1051 1052 //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", 1053 // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); 1054 1055 if (pEntry->getDeleted()) { 1056 adjust += span; 1057 delCount++; 1058 1059 delete pEntry; 1060 mEntries.removeAt(i); 1061 1062 /* adjust loop control */ 1063 count--; 1064 i--; 1065 } else if (span != 0 && adjust > 0) { 1066 /* shuffle this entry back */ 1067 //printf("+++ Shuffling '%s' back %ld\n", 1068 // pEntry->getFileName(), adjust); 1069 result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, 1070 pEntry->getLFHOffset(), span); 1071 if (result != NO_ERROR) { 1072 /* this is why you use a temp file */ 1073 ALOGE("error during crunch - archive is toast\n"); 1074 return result; 1075 } 1076 1077 pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); 1078 } 1079 } 1080 1081 /* 1082 * Fix EOCD info. We have to wait until the end to do some of this 1083 * because we use mCentralDirOffset to determine "span" for the 1084 * last entry. 1085 */ 1086 mEOCD.mCentralDirOffset -= adjust; 1087 mEOCD.mNumEntries -= delCount; 1088 mEOCD.mTotalNumEntries -= delCount; 1089 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 1090 1091 assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); 1092 assert(mEOCD.mNumEntries == count); 1093 1094 return result; 1095 } 1096 1097 /* 1098 * Works like memmove(), but on pieces of a file. 1099 */ 1100 status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) 1101 { 1102 if (dst == src || n <= 0) 1103 return NO_ERROR; 1104 1105 uint8_t readBuf[32768]; 1106 1107 if (dst < src) { 1108 /* shift stuff toward start of file; must read from start */ 1109 while (n != 0) { 1110 size_t getSize = sizeof(readBuf); 1111 if (getSize > n) 1112 getSize = n; 1113 1114 if (fseek(fp, (long) src, SEEK_SET) != 0) { 1115 ALOGD("filemove src seek %ld failed\n", (long) src); 1116 return UNKNOWN_ERROR; 1117 } 1118 1119 if (fread(readBuf, 1, getSize, fp) != getSize) { 1120 ALOGD("filemove read %ld off=%ld failed\n", 1121 (long) getSize, (long) src); 1122 return UNKNOWN_ERROR; 1123 } 1124 1125 if (fseek(fp, (long) dst, SEEK_SET) != 0) { 1126 ALOGD("filemove dst seek %ld failed\n", (long) dst); 1127 return UNKNOWN_ERROR; 1128 } 1129 1130 if (fwrite(readBuf, 1, getSize, fp) != getSize) { 1131 ALOGD("filemove write %ld off=%ld failed\n", 1132 (long) getSize, (long) dst); 1133 return UNKNOWN_ERROR; 1134 } 1135 1136 src += getSize; 1137 dst += getSize; 1138 n -= getSize; 1139 } 1140 } else { 1141 /* shift stuff toward end of file; must read from end */ 1142 assert(false); // write this someday, maybe 1143 return UNKNOWN_ERROR; 1144 } 1145 1146 return NO_ERROR; 1147 } 1148 1149 1150 /* 1151 * Get the modification time from a file descriptor. 1152 */ 1153 time_t ZipFile::getModTime(int fd) 1154 { 1155 struct stat sb; 1156 1157 if (fstat(fd, &sb) < 0) { 1158 ALOGD("HEY: fstat on fd %d failed\n", fd); 1159 return (time_t) -1; 1160 } 1161 1162 return sb.st_mtime; 1163 } 1164 1165 1166 #if 0 /* this is a bad idea */ 1167 /* 1168 * Get a copy of the Zip file descriptor. 1169 * 1170 * We don't allow this if the file was opened read-write because we tend 1171 * to leave the file contents in an uncertain state between calls to 1172 * flush(). The duplicated file descriptor should only be valid for reads. 1173 */ 1174 int ZipFile::getZipFd(void) const 1175 { 1176 if (!mReadOnly) 1177 return INVALID_OPERATION; 1178 assert(mZipFp != NULL); 1179 1180 int fd; 1181 fd = dup(fileno(mZipFp)); 1182 if (fd < 0) { 1183 ALOGD("didn't work, errno=%d\n", errno); 1184 } 1185 1186 return fd; 1187 } 1188 #endif 1189 1190 1191 #if 0 1192 /* 1193 * Expand data. 1194 */ 1195 bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const 1196 { 1197 return false; 1198 } 1199 #endif 1200 1201 // free the memory when you're done 1202 void* ZipFile::uncompress(const ZipEntry* entry) const 1203 { 1204 size_t unlen = entry->getUncompressedLen(); 1205 size_t clen = entry->getCompressedLen(); 1206 1207 void* buf = malloc(unlen); 1208 if (buf == NULL) { 1209 return NULL; 1210 } 1211 1212 fseek(mZipFp, 0, SEEK_SET); 1213 1214 off_t offset = entry->getFileOffset(); 1215 if (fseek(mZipFp, offset, SEEK_SET) != 0) { 1216 goto bail; 1217 } 1218 1219 switch (entry->getCompressionMethod()) 1220 { 1221 case ZipEntry::kCompressStored: { 1222 ssize_t amt = fread(buf, 1, unlen, mZipFp); 1223 if (amt != (ssize_t)unlen) { 1224 goto bail; 1225 } 1226 #if 0 1227 printf("data...\n"); 1228 const unsigned char* p = (unsigned char*)buf; 1229 const unsigned char* end = p+unlen; 1230 for (int i=0; i<32 && p < end; i++) { 1231 printf("0x%08x ", (int)(offset+(i*0x10))); 1232 for (int j=0; j<0x10 && p < end; j++) { 1233 printf(" %02x", *p); 1234 p++; 1235 } 1236 printf("\n"); 1237 } 1238 #endif 1239 1240 } 1241 break; 1242 case ZipEntry::kCompressDeflated: { 1243 if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { 1244 goto bail; 1245 } 1246 } 1247 break; 1248 default: 1249 goto bail; 1250 } 1251 return buf; 1252 1253 bail: 1254 free(buf); 1255 return NULL; 1256 } 1257 1258 1259 /* 1260 * =========================================================================== 1261 * ZipFile::EndOfCentralDir 1262 * =========================================================================== 1263 */ 1264 1265 /* 1266 * Read the end-of-central-dir fields. 1267 * 1268 * "buf" should be positioned at the EOCD signature, and should contain 1269 * the entire EOCD area including the comment. 1270 */ 1271 status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len) 1272 { 1273 /* don't allow re-use */ 1274 assert(mComment == NULL); 1275 1276 if (len < kEOCDLen) { 1277 /* looks like ZIP file got truncated */ 1278 ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n", 1279 kEOCDLen, len); 1280 return INVALID_OPERATION; 1281 } 1282 1283 /* this should probably be an assert() */ 1284 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) 1285 return UNKNOWN_ERROR; 1286 1287 mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); 1288 mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); 1289 mNumEntries = ZipEntry::getShortLE(&buf[0x08]); 1290 mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); 1291 mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); 1292 mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); 1293 mCommentLen = ZipEntry::getShortLE(&buf[0x14]); 1294 1295 // TODO: validate mCentralDirOffset 1296 1297 if (mCommentLen > 0) { 1298 if (kEOCDLen + mCommentLen > len) { 1299 ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n", 1300 kEOCDLen, mCommentLen, len); 1301 return UNKNOWN_ERROR; 1302 } 1303 mComment = new uint8_t[mCommentLen]; 1304 memcpy(mComment, buf + kEOCDLen, mCommentLen); 1305 } 1306 1307 return NO_ERROR; 1308 } 1309 1310 /* 1311 * Write an end-of-central-directory section. 1312 */ 1313 status_t ZipFile::EndOfCentralDir::write(FILE* fp) 1314 { 1315 uint8_t buf[kEOCDLen]; 1316 1317 ZipEntry::putLongLE(&buf[0x00], kSignature); 1318 ZipEntry::putShortLE(&buf[0x04], mDiskNumber); 1319 ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); 1320 ZipEntry::putShortLE(&buf[0x08], mNumEntries); 1321 ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); 1322 ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); 1323 ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); 1324 ZipEntry::putShortLE(&buf[0x14], mCommentLen); 1325 1326 if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) 1327 return UNKNOWN_ERROR; 1328 if (mCommentLen > 0) { 1329 assert(mComment != NULL); 1330 if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) 1331 return UNKNOWN_ERROR; 1332 } 1333 1334 return NO_ERROR; 1335 } 1336 1337 /* 1338 * Dump the contents of an EndOfCentralDir object. 1339 */ 1340 void ZipFile::EndOfCentralDir::dump(void) const 1341 { 1342 ALOGD(" EndOfCentralDir contents:\n"); 1343 ALOGD(" diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n", 1344 mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); 1345 ALOGD(" centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n", 1346 mCentralDirSize, mCentralDirOffset, mCommentLen); 1347 } 1348 1349