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