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 sourceType, int compressionMethod, 363 ZipEntry** ppEntry) 364 { 365 ZipEntry* pEntry = NULL; 366 status_t result = NO_ERROR; 367 long lfhPosn, startPosn, endPosn, uncompressedLen; 368 FILE* inputFp = NULL; 369 uint32_t crc; 370 time_t modWhen; 371 372 if (mReadOnly) 373 return INVALID_OPERATION; 374 375 assert(compressionMethod == ZipEntry::kCompressDeflated || 376 compressionMethod == ZipEntry::kCompressStored); 377 378 /* make sure we're in a reasonable state */ 379 assert(mZipFp != NULL); 380 assert(mEntries.size() == mEOCD.mTotalNumEntries); 381 382 /* make sure it doesn't already exist */ 383 if (getEntryByName(storageName) != NULL) 384 return ALREADY_EXISTS; 385 386 if (!data) { 387 inputFp = fopen(fileName, FILE_OPEN_RO); 388 if (inputFp == NULL) 389 return errnoToStatus(errno); 390 } 391 392 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 393 result = UNKNOWN_ERROR; 394 goto bail; 395 } 396 397 pEntry = new ZipEntry; 398 pEntry->initNew(storageName, NULL); 399 400 /* 401 * From here on out, failures are more interesting. 402 */ 403 mNeedCDRewrite = true; 404 405 /* 406 * Write the LFH, even though it's still mostly blank. We need it 407 * as a place-holder. In theory the LFH isn't necessary, but in 408 * practice some utilities demand it. 409 */ 410 lfhPosn = ftell(mZipFp); 411 pEntry->mLFH.write(mZipFp); 412 startPosn = ftell(mZipFp); 413 414 /* 415 * Copy the data in, possibly compressing it as we go. 416 */ 417 if (sourceType == ZipEntry::kCompressStored) { 418 if (compressionMethod == ZipEntry::kCompressDeflated) { 419 bool failed = false; 420 result = compressFpToFp(mZipFp, inputFp, data, size, &crc); 421 if (result != NO_ERROR) { 422 ALOGD("compression failed, storing\n"); 423 failed = true; 424 } else { 425 /* 426 * Make sure it has compressed "enough". This probably ought 427 * to be set through an API call, but I don't expect our 428 * criteria to change over time. 429 */ 430 long src = inputFp ? ftell(inputFp) : size; 431 long dst = ftell(mZipFp) - startPosn; 432 if (dst + (dst / 10) > src) { 433 ALOGD("insufficient compression (src=%ld dst=%ld), storing\n", 434 src, dst); 435 failed = true; 436 } 437 } 438 439 if (failed) { 440 compressionMethod = ZipEntry::kCompressStored; 441 if (inputFp) rewind(inputFp); 442 fseek(mZipFp, startPosn, SEEK_SET); 443 /* fall through to kCompressStored case */ 444 } 445 } 446 /* handle "no compression" request, or failed compression from above */ 447 if (compressionMethod == ZipEntry::kCompressStored) { 448 if (inputFp) { 449 result = copyFpToFp(mZipFp, inputFp, &crc); 450 } else { 451 result = copyDataToFp(mZipFp, data, size, &crc); 452 } 453 if (result != NO_ERROR) { 454 // don't need to truncate; happens in CDE rewrite 455 ALOGD("failed copying data in\n"); 456 goto bail; 457 } 458 } 459 460 // currently seeked to end of file 461 uncompressedLen = inputFp ? ftell(inputFp) : size; 462 } else if (sourceType == ZipEntry::kCompressDeflated) { 463 /* we should support uncompressed-from-compressed, but it's not 464 * important right now */ 465 assert(compressionMethod == ZipEntry::kCompressDeflated); 466 467 bool scanResult; 468 int method; 469 long compressedLen; 470 unsigned long longcrc; 471 472 scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, 473 &compressedLen, &longcrc); 474 if (!scanResult || method != ZipEntry::kCompressDeflated) { 475 ALOGD("this isn't a deflated gzip file?"); 476 result = UNKNOWN_ERROR; 477 goto bail; 478 } 479 crc = longcrc; 480 481 result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); 482 if (result != NO_ERROR) { 483 ALOGD("failed copying gzip data in\n"); 484 goto bail; 485 } 486 } else { 487 assert(false); 488 result = UNKNOWN_ERROR; 489 goto bail; 490 } 491 492 /* 493 * We could write the "Data Descriptor", but there doesn't seem to 494 * be any point since we're going to go back and write the LFH. 495 * 496 * Update file offsets. 497 */ 498 endPosn = ftell(mZipFp); // seeked to end of compressed data 499 500 /* 501 * Success! Fill out new values. 502 */ 503 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, 504 compressionMethod); 505 modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); 506 pEntry->setModWhen(modWhen); 507 pEntry->setLFHOffset(lfhPosn); 508 mEOCD.mNumEntries++; 509 mEOCD.mTotalNumEntries++; 510 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 511 mEOCD.mCentralDirOffset = endPosn; 512 513 /* 514 * Go back and write the LFH. 515 */ 516 if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { 517 result = UNKNOWN_ERROR; 518 goto bail; 519 } 520 pEntry->mLFH.write(mZipFp); 521 522 /* 523 * Add pEntry to the list. 524 */ 525 mEntries.add(pEntry); 526 if (ppEntry != NULL) 527 *ppEntry = pEntry; 528 pEntry = NULL; 529 530 bail: 531 if (inputFp != NULL) 532 fclose(inputFp); 533 delete pEntry; 534 return result; 535 } 536 537 /* 538 * Add an entry by copying it from another zip file. If "padding" is 539 * nonzero, the specified number of bytes will be added to the "extra" 540 * field in the header. 541 * 542 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 543 */ 544 status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, 545 int padding, ZipEntry** ppEntry) 546 { 547 ZipEntry* pEntry = NULL; 548 status_t result; 549 long lfhPosn, endPosn; 550 551 if (mReadOnly) 552 return INVALID_OPERATION; 553 554 /* make sure we're in a reasonable state */ 555 assert(mZipFp != NULL); 556 assert(mEntries.size() == mEOCD.mTotalNumEntries); 557 558 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 559 result = UNKNOWN_ERROR; 560 goto bail; 561 } 562 563 pEntry = new ZipEntry; 564 if (pEntry == NULL) { 565 result = NO_MEMORY; 566 goto bail; 567 } 568 569 result = pEntry->initFromExternal(pSourceEntry); 570 if (result != NO_ERROR) 571 goto bail; 572 if (padding != 0) { 573 result = pEntry->addPadding(padding); 574 if (result != NO_ERROR) 575 goto bail; 576 } 577 578 /* 579 * From here on out, failures are more interesting. 580 */ 581 mNeedCDRewrite = true; 582 583 /* 584 * Write the LFH. Since we're not recompressing the data, we already 585 * have all of the fields filled out. 586 */ 587 lfhPosn = ftell(mZipFp); 588 pEntry->mLFH.write(mZipFp); 589 590 /* 591 * Copy the data over. 592 * 593 * If the "has data descriptor" flag is set, we want to copy the DD 594 * fields as well. This is a fixed-size area immediately following 595 * the data. 596 */ 597 if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) 598 { 599 result = UNKNOWN_ERROR; 600 goto bail; 601 } 602 603 off_t copyLen; 604 copyLen = pSourceEntry->getCompressedLen(); 605 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) 606 copyLen += ZipEntry::kDataDescriptorLen; 607 608 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) 609 != NO_ERROR) 610 { 611 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); 612 result = UNKNOWN_ERROR; 613 goto bail; 614 } 615 616 /* 617 * Update file offsets. 618 */ 619 endPosn = ftell(mZipFp); 620 621 /* 622 * Success! Fill out new values. 623 */ 624 pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset 625 mEOCD.mNumEntries++; 626 mEOCD.mTotalNumEntries++; 627 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 628 mEOCD.mCentralDirOffset = endPosn; 629 630 /* 631 * Add pEntry to the list. 632 */ 633 mEntries.add(pEntry); 634 if (ppEntry != NULL) 635 *ppEntry = pEntry; 636 pEntry = NULL; 637 638 result = NO_ERROR; 639 640 bail: 641 delete pEntry; 642 return result; 643 } 644 645 /* 646 * Add an entry by copying it from another zip file, recompressing with 647 * Zopfli if already compressed. 648 * 649 * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. 650 */ 651 status_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, 652 ZipEntry** ppEntry) 653 { 654 ZipEntry* pEntry = NULL; 655 status_t result; 656 long lfhPosn, startPosn, endPosn, uncompressedLen; 657 658 if (mReadOnly) 659 return INVALID_OPERATION; 660 661 /* make sure we're in a reasonable state */ 662 assert(mZipFp != NULL); 663 assert(mEntries.size() == mEOCD.mTotalNumEntries); 664 665 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 666 result = UNKNOWN_ERROR; 667 goto bail; 668 } 669 670 pEntry = new ZipEntry; 671 if (pEntry == NULL) { 672 result = NO_MEMORY; 673 goto bail; 674 } 675 676 result = pEntry->initFromExternal(pSourceEntry); 677 if (result != NO_ERROR) 678 goto bail; 679 680 /* 681 * From here on out, failures are more interesting. 682 */ 683 mNeedCDRewrite = true; 684 685 /* 686 * Write the LFH, even though it's still mostly blank. We need it 687 * as a place-holder. In theory the LFH isn't necessary, but in 688 * practice some utilities demand it. 689 */ 690 lfhPosn = ftell(mZipFp); 691 pEntry->mLFH.write(mZipFp); 692 startPosn = ftell(mZipFp); 693 694 /* 695 * Copy the data over. 696 * 697 * If the "has data descriptor" flag is set, we want to copy the DD 698 * fields as well. This is a fixed-size area immediately following 699 * the data. 700 */ 701 if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) 702 { 703 result = UNKNOWN_ERROR; 704 goto bail; 705 } 706 707 uncompressedLen = pSourceEntry->getUncompressedLen(); 708 709 if (pSourceEntry->isCompressed()) { 710 void *buf = pSourceZip->uncompress(pSourceEntry); 711 if (buf == NULL) { 712 result = NO_MEMORY; 713 goto bail; 714 } 715 long startPosn = ftell(mZipFp); 716 uint32_t crc; 717 if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != NO_ERROR) { 718 ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName); 719 result = UNKNOWN_ERROR; 720 free(buf); 721 goto bail; 722 } 723 long endPosn = ftell(mZipFp); 724 pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, 725 pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated); 726 free(buf); 727 } else { 728 off_t copyLen; 729 copyLen = pSourceEntry->getCompressedLen(); 730 if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) 731 copyLen += ZipEntry::kDataDescriptorLen; 732 733 if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) 734 != NO_ERROR) 735 { 736 ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); 737 result = UNKNOWN_ERROR; 738 goto bail; 739 } 740 } 741 742 /* 743 * Update file offsets. 744 */ 745 endPosn = ftell(mZipFp); 746 747 /* 748 * Success! Fill out new values. 749 */ 750 pEntry->setLFHOffset(lfhPosn); 751 mEOCD.mNumEntries++; 752 mEOCD.mTotalNumEntries++; 753 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 754 mEOCD.mCentralDirOffset = endPosn; 755 756 /* 757 * Go back and write the LFH. 758 */ 759 if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { 760 result = UNKNOWN_ERROR; 761 goto bail; 762 } 763 pEntry->mLFH.write(mZipFp); 764 765 /* 766 * Add pEntry to the list. 767 */ 768 mEntries.add(pEntry); 769 if (ppEntry != NULL) 770 *ppEntry = pEntry; 771 pEntry = NULL; 772 773 result = NO_ERROR; 774 775 bail: 776 delete pEntry; 777 return result; 778 } 779 780 /* 781 * Copy all of the bytes in "src" to "dst". 782 * 783 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 784 * will be seeked immediately past the data. 785 */ 786 status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32) 787 { 788 uint8_t tmpBuf[32768]; 789 size_t count; 790 791 *pCRC32 = crc32(0L, Z_NULL, 0); 792 793 while (1) { 794 count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); 795 if (ferror(srcFp) || ferror(dstFp)) 796 return errnoToStatus(errno); 797 if (count == 0) 798 break; 799 800 *pCRC32 = crc32(*pCRC32, tmpBuf, count); 801 802 if (fwrite(tmpBuf, 1, count, dstFp) != count) { 803 ALOGD("fwrite %d bytes failed\n", (int) count); 804 return UNKNOWN_ERROR; 805 } 806 } 807 808 return NO_ERROR; 809 } 810 811 /* 812 * Copy all of the bytes in "src" to "dst". 813 * 814 * On exit, "dstFp" will be seeked immediately past the data. 815 */ 816 status_t ZipFile::copyDataToFp(FILE* dstFp, 817 const void* data, size_t size, uint32_t* pCRC32) 818 { 819 size_t count; 820 821 *pCRC32 = crc32(0L, Z_NULL, 0); 822 if (size > 0) { 823 *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); 824 if (fwrite(data, 1, size, dstFp) != size) { 825 ALOGD("fwrite %d bytes failed\n", (int) size); 826 return UNKNOWN_ERROR; 827 } 828 } 829 830 return NO_ERROR; 831 } 832 833 /* 834 * Copy some of the bytes in "src" to "dst". 835 * 836 * If "pCRC32" is NULL, the CRC will not be computed. 837 * 838 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 839 * will be seeked immediately past the data just written. 840 */ 841 status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, 842 uint32_t* pCRC32) 843 { 844 uint8_t tmpBuf[32768]; 845 size_t count; 846 847 if (pCRC32 != NULL) 848 *pCRC32 = crc32(0L, Z_NULL, 0); 849 850 while (length) { 851 long readSize; 852 853 readSize = sizeof(tmpBuf); 854 if (readSize > length) 855 readSize = length; 856 857 count = fread(tmpBuf, 1, readSize, srcFp); 858 if ((long) count != readSize) { // error or unexpected EOF 859 ALOGD("fread %d bytes failed\n", (int) readSize); 860 return UNKNOWN_ERROR; 861 } 862 863 if (pCRC32 != NULL) 864 *pCRC32 = crc32(*pCRC32, tmpBuf, count); 865 866 if (fwrite(tmpBuf, 1, count, dstFp) != count) { 867 ALOGD("fwrite %d bytes failed\n", (int) count); 868 return UNKNOWN_ERROR; 869 } 870 871 length -= readSize; 872 } 873 874 return NO_ERROR; 875 } 876 877 /* 878 * Compress all of the data in "srcFp" and write it to "dstFp". 879 * 880 * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" 881 * will be seeked immediately past the compressed data. 882 */ 883 status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, 884 const void* data, size_t size, uint32_t* pCRC32) 885 { 886 status_t result = NO_ERROR; 887 const size_t kBufSize = 1024 * 1024; 888 uint8_t* inBuf = NULL; 889 uint8_t* outBuf = NULL; 890 size_t outSize = 0; 891 bool atEof = false; // no feof() aviailable yet 892 uint32_t crc; 893 ZopfliOptions options; 894 unsigned char bp = 0; 895 896 ZopfliInitOptions(&options); 897 898 crc = crc32(0L, Z_NULL, 0); 899 900 if (data) { 901 crc = crc32(crc, (const unsigned char*)data, size); 902 ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp, 903 &outBuf, &outSize); 904 } else { 905 /* 906 * Create an input buffer and an output buffer. 907 */ 908 inBuf = new uint8_t[kBufSize]; 909 if (inBuf == NULL) { 910 result = NO_MEMORY; 911 goto bail; 912 } 913 914 /* 915 * Loop while we have data. 916 */ 917 do { 918 size_t getSize; 919 getSize = fread(inBuf, 1, kBufSize, srcFp); 920 if (ferror(srcFp)) { 921 ALOGD("deflate read failed (errno=%d)\n", errno); 922 delete[] inBuf; 923 goto bail; 924 } 925 if (getSize < kBufSize) { 926 ALOGV("+++ got %d bytes, EOF reached\n", 927 (int)getSize); 928 atEof = true; 929 } 930 931 crc = crc32(crc, inBuf, getSize); 932 ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize); 933 } while (!atEof); 934 delete[] inBuf; 935 } 936 937 ALOGV("+++ writing %d bytes\n", (int)outSize); 938 if (fwrite(outBuf, 1, outSize, dstFp) != outSize) { 939 ALOGD("write %d failed in deflate\n", (int)outSize); 940 goto bail; 941 } 942 943 *pCRC32 = crc; 944 945 bail: 946 free(outBuf); 947 948 return result; 949 } 950 951 /* 952 * Mark an entry as deleted. 953 * 954 * We will eventually need to crunch the file down, but if several files 955 * are being removed (perhaps as part of an "update" process) we can make 956 * things considerably faster by deferring the removal to "flush" time. 957 */ 958 status_t ZipFile::remove(ZipEntry* pEntry) 959 { 960 /* 961 * Should verify that pEntry is actually part of this archive, and 962 * not some stray ZipEntry from a different file. 963 */ 964 965 /* mark entry as deleted, and mark archive as dirty */ 966 pEntry->setDeleted(); 967 mNeedCDRewrite = true; 968 return NO_ERROR; 969 } 970 971 /* 972 * Flush any pending writes. 973 * 974 * In particular, this will crunch out deleted entries, and write the 975 * Central Directory and EOCD if we have stomped on them. 976 */ 977 status_t ZipFile::flush(void) 978 { 979 status_t result = NO_ERROR; 980 long eocdPosn; 981 int i, count; 982 983 if (mReadOnly) 984 return INVALID_OPERATION; 985 if (!mNeedCDRewrite) 986 return NO_ERROR; 987 988 assert(mZipFp != NULL); 989 990 result = crunchArchive(); 991 if (result != NO_ERROR) 992 return result; 993 994 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) 995 return UNKNOWN_ERROR; 996 997 count = mEntries.size(); 998 for (i = 0; i < count; i++) { 999 ZipEntry* pEntry = mEntries[i]; 1000 pEntry->mCDE.write(mZipFp); 1001 } 1002 1003 eocdPosn = ftell(mZipFp); 1004 mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; 1005 1006 mEOCD.write(mZipFp); 1007 1008 /* 1009 * If we had some stuff bloat up during compression and get replaced 1010 * with plain files, or if we deleted some entries, there's a lot 1011 * of wasted space at the end of the file. Remove it now. 1012 */ 1013 if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { 1014 ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); 1015 // not fatal 1016 } 1017 1018 /* should we clear the "newly added" flag in all entries now? */ 1019 1020 mNeedCDRewrite = false; 1021 return NO_ERROR; 1022 } 1023 1024 /* 1025 * Crunch deleted files out of an archive by shifting the later files down. 1026 * 1027 * Because we're not using a temp file, we do the operation inside the 1028 * current file. 1029 */ 1030 status_t ZipFile::crunchArchive(void) 1031 { 1032 status_t result = NO_ERROR; 1033 int i, count; 1034 long delCount, adjust; 1035 1036 #if 0 1037 printf("CONTENTS:\n"); 1038 for (i = 0; i < (int) mEntries.size(); i++) { 1039 printf(" %d: lfhOff=%ld del=%d\n", 1040 i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); 1041 } 1042 printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); 1043 #endif 1044 1045 /* 1046 * Roll through the set of files, shifting them as appropriate. We 1047 * could probably get a slight performance improvement by sliding 1048 * multiple files down at once (because we could use larger reads 1049 * when operating on batches of small files), but it's not that useful. 1050 */ 1051 count = mEntries.size(); 1052 delCount = adjust = 0; 1053 for (i = 0; i < count; i++) { 1054 ZipEntry* pEntry = mEntries[i]; 1055 long span; 1056 1057 if (pEntry->getLFHOffset() != 0) { 1058 long nextOffset; 1059 1060 /* Get the length of this entry by finding the offset 1061 * of the next entry. Directory entries don't have 1062 * file offsets, so we need to find the next non-directory 1063 * entry. 1064 */ 1065 nextOffset = 0; 1066 for (int ii = i+1; nextOffset == 0 && ii < count; ii++) 1067 nextOffset = mEntries[ii]->getLFHOffset(); 1068 if (nextOffset == 0) 1069 nextOffset = mEOCD.mCentralDirOffset; 1070 span = nextOffset - pEntry->getLFHOffset(); 1071 1072 assert(span >= ZipEntry::LocalFileHeader::kLFHLen); 1073 } else { 1074 /* This is a directory entry. It doesn't have 1075 * any actual file contents, so there's no need to 1076 * move anything. 1077 */ 1078 span = 0; 1079 } 1080 1081 //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", 1082 // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); 1083 1084 if (pEntry->getDeleted()) { 1085 adjust += span; 1086 delCount++; 1087 1088 delete pEntry; 1089 mEntries.removeAt(i); 1090 1091 /* adjust loop control */ 1092 count--; 1093 i--; 1094 } else if (span != 0 && adjust > 0) { 1095 /* shuffle this entry back */ 1096 //printf("+++ Shuffling '%s' back %ld\n", 1097 // pEntry->getFileName(), adjust); 1098 result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, 1099 pEntry->getLFHOffset(), span); 1100 if (result != NO_ERROR) { 1101 /* this is why you use a temp file */ 1102 ALOGE("error during crunch - archive is toast\n"); 1103 return result; 1104 } 1105 1106 pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); 1107 } 1108 } 1109 1110 /* 1111 * Fix EOCD info. We have to wait until the end to do some of this 1112 * because we use mCentralDirOffset to determine "span" for the 1113 * last entry. 1114 */ 1115 mEOCD.mCentralDirOffset -= adjust; 1116 mEOCD.mNumEntries -= delCount; 1117 mEOCD.mTotalNumEntries -= delCount; 1118 mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() 1119 1120 assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); 1121 assert(mEOCD.mNumEntries == count); 1122 1123 return result; 1124 } 1125 1126 /* 1127 * Works like memmove(), but on pieces of a file. 1128 */ 1129 status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) 1130 { 1131 if (dst == src || n <= 0) 1132 return NO_ERROR; 1133 1134 uint8_t readBuf[32768]; 1135 1136 if (dst < src) { 1137 /* shift stuff toward start of file; must read from start */ 1138 while (n != 0) { 1139 size_t getSize = sizeof(readBuf); 1140 if (getSize > n) 1141 getSize = n; 1142 1143 if (fseek(fp, (long) src, SEEK_SET) != 0) { 1144 ALOGD("filemove src seek %ld failed\n", (long) src); 1145 return UNKNOWN_ERROR; 1146 } 1147 1148 if (fread(readBuf, 1, getSize, fp) != getSize) { 1149 ALOGD("filemove read %ld off=%ld failed\n", 1150 (long) getSize, (long) src); 1151 return UNKNOWN_ERROR; 1152 } 1153 1154 if (fseek(fp, (long) dst, SEEK_SET) != 0) { 1155 ALOGD("filemove dst seek %ld failed\n", (long) dst); 1156 return UNKNOWN_ERROR; 1157 } 1158 1159 if (fwrite(readBuf, 1, getSize, fp) != getSize) { 1160 ALOGD("filemove write %ld off=%ld failed\n", 1161 (long) getSize, (long) dst); 1162 return UNKNOWN_ERROR; 1163 } 1164 1165 src += getSize; 1166 dst += getSize; 1167 n -= getSize; 1168 } 1169 } else { 1170 /* shift stuff toward end of file; must read from end */ 1171 assert(false); // write this someday, maybe 1172 return UNKNOWN_ERROR; 1173 } 1174 1175 return NO_ERROR; 1176 } 1177 1178 1179 /* 1180 * Get the modification time from a file descriptor. 1181 */ 1182 time_t ZipFile::getModTime(int fd) 1183 { 1184 struct stat sb; 1185 1186 if (fstat(fd, &sb) < 0) { 1187 ALOGD("HEY: fstat on fd %d failed\n", fd); 1188 return (time_t) -1; 1189 } 1190 1191 return sb.st_mtime; 1192 } 1193 1194 1195 #if 0 /* this is a bad idea */ 1196 /* 1197 * Get a copy of the Zip file descriptor. 1198 * 1199 * We don't allow this if the file was opened read-write because we tend 1200 * to leave the file contents in an uncertain state between calls to 1201 * flush(). The duplicated file descriptor should only be valid for reads. 1202 */ 1203 int ZipFile::getZipFd(void) const 1204 { 1205 if (!mReadOnly) 1206 return INVALID_OPERATION; 1207 assert(mZipFp != NULL); 1208 1209 int fd; 1210 fd = dup(fileno(mZipFp)); 1211 if (fd < 0) { 1212 ALOGD("didn't work, errno=%d\n", errno); 1213 } 1214 1215 return fd; 1216 } 1217 #endif 1218 1219 1220 #if 0 1221 /* 1222 * Expand data. 1223 */ 1224 bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const 1225 { 1226 return false; 1227 } 1228 #endif 1229 1230 // free the memory when you're done 1231 void* ZipFile::uncompress(const ZipEntry* entry) const 1232 { 1233 size_t unlen = entry->getUncompressedLen(); 1234 size_t clen = entry->getCompressedLen(); 1235 1236 void* buf = malloc(unlen); 1237 if (buf == NULL) { 1238 return NULL; 1239 } 1240 1241 fseek(mZipFp, 0, SEEK_SET); 1242 1243 off_t offset = entry->getFileOffset(); 1244 if (fseek(mZipFp, offset, SEEK_SET) != 0) { 1245 goto bail; 1246 } 1247 1248 switch (entry->getCompressionMethod()) 1249 { 1250 case ZipEntry::kCompressStored: { 1251 ssize_t amt = fread(buf, 1, unlen, mZipFp); 1252 if (amt != (ssize_t)unlen) { 1253 goto bail; 1254 } 1255 #if 0 1256 printf("data...\n"); 1257 const unsigned char* p = (unsigned char*)buf; 1258 const unsigned char* end = p+unlen; 1259 for (int i=0; i<32 && p < end; i++) { 1260 printf("0x%08x ", (int)(offset+(i*0x10))); 1261 for (int j=0; j<0x10 && p < end; j++) { 1262 printf(" %02x", *p); 1263 p++; 1264 } 1265 printf("\n"); 1266 } 1267 #endif 1268 1269 } 1270 break; 1271 case ZipEntry::kCompressDeflated: { 1272 if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { 1273 goto bail; 1274 } 1275 } 1276 break; 1277 default: 1278 goto bail; 1279 } 1280 return buf; 1281 1282 bail: 1283 free(buf); 1284 return NULL; 1285 } 1286 1287 1288 /* 1289 * =========================================================================== 1290 * ZipFile::EndOfCentralDir 1291 * =========================================================================== 1292 */ 1293 1294 /* 1295 * Read the end-of-central-dir fields. 1296 * 1297 * "buf" should be positioned at the EOCD signature, and should contain 1298 * the entire EOCD area including the comment. 1299 */ 1300 status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len) 1301 { 1302 /* don't allow re-use */ 1303 assert(mComment == NULL); 1304 1305 if (len < kEOCDLen) { 1306 /* looks like ZIP file got truncated */ 1307 ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n", 1308 kEOCDLen, len); 1309 return INVALID_OPERATION; 1310 } 1311 1312 /* this should probably be an assert() */ 1313 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) 1314 return UNKNOWN_ERROR; 1315 1316 mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); 1317 mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); 1318 mNumEntries = ZipEntry::getShortLE(&buf[0x08]); 1319 mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); 1320 mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); 1321 mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); 1322 mCommentLen = ZipEntry::getShortLE(&buf[0x14]); 1323 1324 // TODO: validate mCentralDirOffset 1325 1326 if (mCommentLen > 0) { 1327 if (kEOCDLen + mCommentLen > len) { 1328 ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n", 1329 kEOCDLen, mCommentLen, len); 1330 return UNKNOWN_ERROR; 1331 } 1332 mComment = new uint8_t[mCommentLen]; 1333 memcpy(mComment, buf + kEOCDLen, mCommentLen); 1334 } 1335 1336 return NO_ERROR; 1337 } 1338 1339 /* 1340 * Write an end-of-central-directory section. 1341 */ 1342 status_t ZipFile::EndOfCentralDir::write(FILE* fp) 1343 { 1344 uint8_t buf[kEOCDLen]; 1345 1346 ZipEntry::putLongLE(&buf[0x00], kSignature); 1347 ZipEntry::putShortLE(&buf[0x04], mDiskNumber); 1348 ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); 1349 ZipEntry::putShortLE(&buf[0x08], mNumEntries); 1350 ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); 1351 ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); 1352 ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); 1353 ZipEntry::putShortLE(&buf[0x14], mCommentLen); 1354 1355 if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) 1356 return UNKNOWN_ERROR; 1357 if (mCommentLen > 0) { 1358 assert(mComment != NULL); 1359 if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) 1360 return UNKNOWN_ERROR; 1361 } 1362 1363 return NO_ERROR; 1364 } 1365 1366 /* 1367 * Dump the contents of an EndOfCentralDir object. 1368 */ 1369 void ZipFile::EndOfCentralDir::dump(void) const 1370 { 1371 ALOGD(" EndOfCentralDir contents:\n"); 1372 ALOGD(" diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n", 1373 mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); 1374 ALOGD(" centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n", 1375 mCentralDirSize, mCentralDirOffset, mCommentLen); 1376 } 1377 1378