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