|  | /* | 
|  | * Copyright (C) 2006 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | // | 
|  | // Access to Zip archives. | 
|  | // | 
|  |  | 
|  | #define LOG_TAG "zip" | 
|  |  | 
|  | #include <androidfw/ZipUtils.h> | 
|  | #include <utils/Log.h> | 
|  |  | 
|  | #include "ZipFile.h" | 
|  |  | 
|  | #include <zlib.h> | 
|  | #define DEF_MEM_LEVEL 8                // normally in zutil.h? | 
|  |  | 
|  | #include "zopfli/deflate.h" | 
|  |  | 
|  | #include <memory.h> | 
|  | #include <sys/stat.h> | 
|  | #include <errno.h> | 
|  | #include <assert.h> | 
|  | #include <inttypes.h> | 
|  |  | 
|  | using namespace android; | 
|  |  | 
|  | /* | 
|  | * Some environments require the "b", some choke on it. | 
|  | */ | 
|  | #define FILE_OPEN_RO        "rb" | 
|  | #define FILE_OPEN_RW        "r+b" | 
|  | #define FILE_OPEN_RW_CREATE "w+b" | 
|  |  | 
|  | /* should live somewhere else? */ | 
|  | static status_t errnoToStatus(int err) | 
|  | { | 
|  | if (err == ENOENT) | 
|  | return NAME_NOT_FOUND; | 
|  | else if (err == EACCES) | 
|  | return PERMISSION_DENIED; | 
|  | else | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Open a file and parse its guts. | 
|  | */ | 
|  | status_t ZipFile::open(const char* zipFileName, int flags) | 
|  | { | 
|  | bool newArchive = false; | 
|  |  | 
|  | assert(mZipFp == NULL);     // no reopen | 
|  |  | 
|  | if ((flags & kOpenTruncate)) | 
|  | flags |= kOpenCreate;           // trunc implies create | 
|  |  | 
|  | if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) | 
|  | return INVALID_OPERATION;       // not both | 
|  | if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) | 
|  | return INVALID_OPERATION;       // not neither | 
|  | if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) | 
|  | return INVALID_OPERATION;       // create requires write | 
|  |  | 
|  | if (flags & kOpenTruncate) { | 
|  | newArchive = true; | 
|  | } else { | 
|  | newArchive = (access(zipFileName, F_OK) != 0); | 
|  | if (!(flags & kOpenCreate) && newArchive) { | 
|  | /* not creating, must already exist */ | 
|  | ALOGD("File %s does not exist", zipFileName); | 
|  | return NAME_NOT_FOUND; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* open the file */ | 
|  | const char* openflags; | 
|  | if (flags & kOpenReadWrite) { | 
|  | if (newArchive) | 
|  | openflags = FILE_OPEN_RW_CREATE; | 
|  | else | 
|  | openflags = FILE_OPEN_RW; | 
|  | } else { | 
|  | openflags = FILE_OPEN_RO; | 
|  | } | 
|  | mZipFp = fopen(zipFileName, openflags); | 
|  | if (mZipFp == NULL) { | 
|  | int err = errno; | 
|  | ALOGD("fopen failed: %d\n", err); | 
|  | return errnoToStatus(err); | 
|  | } | 
|  |  | 
|  | status_t result; | 
|  | if (!newArchive) { | 
|  | /* | 
|  | * Load the central directory.  If that fails, then this probably | 
|  | * isn't a Zip archive. | 
|  | */ | 
|  | result = readCentralDir(); | 
|  | } else { | 
|  | /* | 
|  | * Newly-created.  The EndOfCentralDir constructor actually | 
|  | * sets everything to be the way we want it (all zeroes).  We | 
|  | * set mNeedCDRewrite so that we create *something* if the | 
|  | * caller doesn't add any files.  (We could also just unlink | 
|  | * the file if it's brand new and nothing was added, but that's | 
|  | * probably doing more than we really should -- the user might | 
|  | * have a need for empty zip files.) | 
|  | */ | 
|  | mNeedCDRewrite = true; | 
|  | result = NO_ERROR; | 
|  | } | 
|  |  | 
|  | if (flags & kOpenReadOnly) | 
|  | mReadOnly = true; | 
|  | else | 
|  | assert(!mReadOnly); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Return the Nth entry in the archive. | 
|  | */ | 
|  | ZipEntry* ZipFile::getEntryByIndex(int idx) const | 
|  | { | 
|  | if (idx < 0 || idx >= (int) mEntries.size()) | 
|  | return NULL; | 
|  |  | 
|  | return mEntries[idx]; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find an entry by name. | 
|  | */ | 
|  | ZipEntry* ZipFile::getEntryByName(const char* fileName) const | 
|  | { | 
|  | /* | 
|  | * Do a stupid linear string-compare search. | 
|  | * | 
|  | * There are various ways to speed this up, especially since it's rare | 
|  | * to intermingle changes to the archive with "get by name" calls.  We | 
|  | * don't want to sort the mEntries vector itself, however, because | 
|  | * it's used to recreate the Central Directory. | 
|  | * | 
|  | * (Hash table works, parallel list of pointers in sorted order is good.) | 
|  | */ | 
|  | int idx; | 
|  |  | 
|  | for (idx = mEntries.size()-1; idx >= 0; idx--) { | 
|  | ZipEntry* pEntry = mEntries[idx]; | 
|  | if (!pEntry->getDeleted() && | 
|  | strcmp(fileName, pEntry->getFileName()) == 0) | 
|  | { | 
|  | return pEntry; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Empty the mEntries vector. | 
|  | */ | 
|  | void ZipFile::discardEntries(void) | 
|  | { | 
|  | int count = mEntries.size(); | 
|  |  | 
|  | while (--count >= 0) | 
|  | delete mEntries[count]; | 
|  |  | 
|  | mEntries.clear(); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Find the central directory and read the contents. | 
|  | * | 
|  | * The fun thing about ZIP archives is that they may or may not be | 
|  | * readable from start to end.  In some cases, notably for archives | 
|  | * that were written to stdout, the only length information is in the | 
|  | * central directory at the end of the file. | 
|  | * | 
|  | * Of course, the central directory can be followed by a variable-length | 
|  | * comment field, so we have to scan through it backwards.  The comment | 
|  | * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff | 
|  | * itself, plus apparently sometimes people throw random junk on the end | 
|  | * just for the fun of it. | 
|  | * | 
|  | * This is all a little wobbly.  If the wrong value ends up in the EOCD | 
|  | * area, we're hosed.  This appears to be the way that everbody handles | 
|  | * it though, so we're in pretty good company if this fails. | 
|  | */ | 
|  | status_t ZipFile::readCentralDir(void) | 
|  | { | 
|  | status_t result = NO_ERROR; | 
|  | uint8_t* buf = NULL; | 
|  | off_t fileLength, seekStart; | 
|  | long readAmount; | 
|  | int i; | 
|  |  | 
|  | fseek(mZipFp, 0, SEEK_END); | 
|  | fileLength = ftell(mZipFp); | 
|  | rewind(mZipFp); | 
|  |  | 
|  | /* too small to be a ZIP archive? */ | 
|  | if (fileLength < EndOfCentralDir::kEOCDLen) { | 
|  | ALOGD("Length is %ld -- too small\n", (long)fileLength); | 
|  | result = INVALID_OPERATION; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | buf = new uint8_t[EndOfCentralDir::kMaxEOCDSearch]; | 
|  | if (buf == NULL) { | 
|  | ALOGD("Failure allocating %d bytes for EOCD search", | 
|  | EndOfCentralDir::kMaxEOCDSearch); | 
|  | result = NO_MEMORY; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { | 
|  | seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; | 
|  | readAmount = EndOfCentralDir::kMaxEOCDSearch; | 
|  | } else { | 
|  | seekStart = 0; | 
|  | readAmount = (long) fileLength; | 
|  | } | 
|  | if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { | 
|  | ALOGD("Failure seeking to end of zip at %ld", (long) seekStart); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* read the last part of the file into the buffer */ | 
|  | if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { | 
|  | ALOGD("short file? wanted %ld\n", readAmount); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* find the end-of-central-dir magic */ | 
|  | for (i = readAmount - 4; i >= 0; i--) { | 
|  | if (buf[i] == 0x50 && | 
|  | ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) | 
|  | { | 
|  | ALOGV("+++ Found EOCD at buf+%d\n", i); | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (i < 0) { | 
|  | ALOGD("EOCD not found, not Zip\n"); | 
|  | result = INVALID_OPERATION; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* extract eocd values */ | 
|  | result = mEOCD.readBuf(buf + i, readAmount - i); | 
|  | if (result != NO_ERROR) { | 
|  | ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i); | 
|  | goto bail; | 
|  | } | 
|  | //mEOCD.dump(); | 
|  |  | 
|  | if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || | 
|  | mEOCD.mNumEntries != mEOCD.mTotalNumEntries) | 
|  | { | 
|  | ALOGD("Archive spanning not supported\n"); | 
|  | result = INVALID_OPERATION; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * So far so good.  "mCentralDirSize" is the size in bytes of the | 
|  | * central directory, so we can just seek back that far to find it. | 
|  | * We can also seek forward mCentralDirOffset bytes from the | 
|  | * start of the file. | 
|  | * | 
|  | * We're not guaranteed to have the rest of the central dir in the | 
|  | * buffer, nor are we guaranteed that the central dir will have any | 
|  | * sort of convenient size.  We need to skip to the start of it and | 
|  | * read the header, then the other goodies. | 
|  | * | 
|  | * The only thing we really need right now is the file comment, which | 
|  | * we're hoping to preserve. | 
|  | */ | 
|  | if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { | 
|  | ALOGD("Failure seeking to central dir offset %" PRIu32 "\n", | 
|  | mEOCD.mCentralDirOffset); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Loop through and read the central dir entries. | 
|  | */ | 
|  | ALOGV("Scanning %" PRIu16 " entries...\n", mEOCD.mTotalNumEntries); | 
|  | int entry; | 
|  | for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { | 
|  | ZipEntry* pEntry = new ZipEntry; | 
|  |  | 
|  | result = pEntry->initFromCDE(mZipFp); | 
|  | if (result != NO_ERROR) { | 
|  | ALOGD("initFromCDE failed\n"); | 
|  | delete pEntry; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | mEntries.add(pEntry); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * If all went well, we should now be back at the EOCD. | 
|  | */ | 
|  | { | 
|  | uint8_t checkBuf[4]; | 
|  | if (fread(checkBuf, 1, 4, mZipFp) != 4) { | 
|  | ALOGD("EOCD check read failed\n"); | 
|  | result = INVALID_OPERATION; | 
|  | goto bail; | 
|  | } | 
|  | if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { | 
|  | ALOGD("EOCD read check failed\n"); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  | ALOGV("+++ EOCD read check passed\n"); | 
|  | } | 
|  |  | 
|  | bail: | 
|  | delete[] buf; | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Add a new file to the archive. | 
|  | * | 
|  | * This requires creating and populating a ZipEntry structure, and copying | 
|  | * the data into the file at the appropriate position.  The "appropriate | 
|  | * position" is the current location of the central directory, which we | 
|  | * casually overwrite (we can put it back later). | 
|  | * | 
|  | * If we were concerned about safety, we would want to make all changes | 
|  | * in a temp file and then overwrite the original after everything was | 
|  | * safely written.  Not really a concern for us. | 
|  | */ | 
|  | status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, | 
|  | const char* storageName, int sourceType, int compressionMethod, | 
|  | ZipEntry** ppEntry) | 
|  | { | 
|  | ZipEntry* pEntry = NULL; | 
|  | status_t result = NO_ERROR; | 
|  | long lfhPosn, startPosn, endPosn, uncompressedLen; | 
|  | FILE* inputFp = NULL; | 
|  | uint32_t crc; | 
|  | time_t modWhen; | 
|  |  | 
|  | if (mReadOnly) | 
|  | return INVALID_OPERATION; | 
|  |  | 
|  | assert(compressionMethod == ZipEntry::kCompressDeflated || | 
|  | compressionMethod == ZipEntry::kCompressStored); | 
|  |  | 
|  | /* make sure we're in a reasonable state */ | 
|  | assert(mZipFp != NULL); | 
|  | assert(mEntries.size() == mEOCD.mTotalNumEntries); | 
|  |  | 
|  | /* make sure it doesn't already exist */ | 
|  | if (getEntryByName(storageName) != NULL) | 
|  | return ALREADY_EXISTS; | 
|  |  | 
|  | if (!data) { | 
|  | inputFp = fopen(fileName, FILE_OPEN_RO); | 
|  | if (inputFp == NULL) | 
|  | return errnoToStatus(errno); | 
|  | } | 
|  |  | 
|  | if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | pEntry = new ZipEntry; | 
|  | pEntry->initNew(storageName, NULL); | 
|  |  | 
|  | /* | 
|  | * From here on out, failures are more interesting. | 
|  | */ | 
|  | mNeedCDRewrite = true; | 
|  |  | 
|  | /* | 
|  | * Write the LFH, even though it's still mostly blank.  We need it | 
|  | * as a place-holder.  In theory the LFH isn't necessary, but in | 
|  | * practice some utilities demand it. | 
|  | */ | 
|  | lfhPosn = ftell(mZipFp); | 
|  | pEntry->mLFH.write(mZipFp); | 
|  | startPosn = ftell(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Copy the data in, possibly compressing it as we go. | 
|  | */ | 
|  | if (sourceType == ZipEntry::kCompressStored) { | 
|  | if (compressionMethod == ZipEntry::kCompressDeflated) { | 
|  | bool failed = false; | 
|  | result = compressFpToFp(mZipFp, inputFp, data, size, &crc); | 
|  | if (result != NO_ERROR) { | 
|  | ALOGD("compression failed, storing\n"); | 
|  | failed = true; | 
|  | } else { | 
|  | /* | 
|  | * Make sure it has compressed "enough".  This probably ought | 
|  | * to be set through an API call, but I don't expect our | 
|  | * criteria to change over time. | 
|  | */ | 
|  | long src = inputFp ? ftell(inputFp) : size; | 
|  | long dst = ftell(mZipFp) - startPosn; | 
|  | if (dst + (dst / 10) > src) { | 
|  | ALOGD("insufficient compression (src=%ld dst=%ld), storing\n", | 
|  | src, dst); | 
|  | failed = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (failed) { | 
|  | compressionMethod = ZipEntry::kCompressStored; | 
|  | if (inputFp) rewind(inputFp); | 
|  | fseek(mZipFp, startPosn, SEEK_SET); | 
|  | /* fall through to kCompressStored case */ | 
|  | } | 
|  | } | 
|  | /* handle "no compression" request, or failed compression from above */ | 
|  | if (compressionMethod == ZipEntry::kCompressStored) { | 
|  | if (inputFp) { | 
|  | result = copyFpToFp(mZipFp, inputFp, &crc); | 
|  | } else { | 
|  | result = copyDataToFp(mZipFp, data, size, &crc); | 
|  | } | 
|  | if (result != NO_ERROR) { | 
|  | // don't need to truncate; happens in CDE rewrite | 
|  | ALOGD("failed copying data in\n"); | 
|  | goto bail; | 
|  | } | 
|  | } | 
|  |  | 
|  | // currently seeked to end of file | 
|  | uncompressedLen = inputFp ? ftell(inputFp) : size; | 
|  | } else if (sourceType == ZipEntry::kCompressDeflated) { | 
|  | /* we should support uncompressed-from-compressed, but it's not | 
|  | * important right now */ | 
|  | assert(compressionMethod == ZipEntry::kCompressDeflated); | 
|  |  | 
|  | bool scanResult; | 
|  | int method; | 
|  | long compressedLen; | 
|  | unsigned long longcrc; | 
|  |  | 
|  | scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, | 
|  | &compressedLen, &longcrc); | 
|  | if (!scanResult || method != ZipEntry::kCompressDeflated) { | 
|  | ALOGD("this isn't a deflated gzip file?"); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  | crc = longcrc; | 
|  |  | 
|  | result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); | 
|  | if (result != NO_ERROR) { | 
|  | ALOGD("failed copying gzip data in\n"); | 
|  | goto bail; | 
|  | } | 
|  | } else { | 
|  | assert(false); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We could write the "Data Descriptor", but there doesn't seem to | 
|  | * be any point since we're going to go back and write the LFH. | 
|  | * | 
|  | * Update file offsets. | 
|  | */ | 
|  | endPosn = ftell(mZipFp);            // seeked to end of compressed data | 
|  |  | 
|  | /* | 
|  | * Success!  Fill out new values. | 
|  | */ | 
|  | pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, | 
|  | compressionMethod); | 
|  | modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); | 
|  | pEntry->setModWhen(modWhen); | 
|  | pEntry->setLFHOffset(lfhPosn); | 
|  | mEOCD.mNumEntries++; | 
|  | mEOCD.mTotalNumEntries++; | 
|  | mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush() | 
|  | mEOCD.mCentralDirOffset = endPosn; | 
|  |  | 
|  | /* | 
|  | * Go back and write the LFH. | 
|  | */ | 
|  | if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  | pEntry->mLFH.write(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Add pEntry to the list. | 
|  | */ | 
|  | mEntries.add(pEntry); | 
|  | if (ppEntry != NULL) | 
|  | *ppEntry = pEntry; | 
|  | pEntry = NULL; | 
|  |  | 
|  | bail: | 
|  | if (inputFp != NULL) | 
|  | fclose(inputFp); | 
|  | delete pEntry; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add an entry by copying it from another zip file.  If "padding" is | 
|  | * nonzero, the specified number of bytes will be added to the "extra" | 
|  | * field in the header. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, | 
|  | int padding, ZipEntry** ppEntry) | 
|  | { | 
|  | ZipEntry* pEntry = NULL; | 
|  | status_t result; | 
|  | long lfhPosn, endPosn; | 
|  |  | 
|  | if (mReadOnly) | 
|  | return INVALID_OPERATION; | 
|  |  | 
|  | /* make sure we're in a reasonable state */ | 
|  | assert(mZipFp != NULL); | 
|  | assert(mEntries.size() == mEOCD.mTotalNumEntries); | 
|  |  | 
|  | if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | pEntry = new ZipEntry; | 
|  | if (pEntry == NULL) { | 
|  | result = NO_MEMORY; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | result = pEntry->initFromExternal(pSourceZip, pSourceEntry); | 
|  | if (result != NO_ERROR) | 
|  | goto bail; | 
|  | if (padding != 0) { | 
|  | result = pEntry->addPadding(padding); | 
|  | if (result != NO_ERROR) | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * From here on out, failures are more interesting. | 
|  | */ | 
|  | mNeedCDRewrite = true; | 
|  |  | 
|  | /* | 
|  | * Write the LFH.  Since we're not recompressing the data, we already | 
|  | * have all of the fields filled out. | 
|  | */ | 
|  | lfhPosn = ftell(mZipFp); | 
|  | pEntry->mLFH.write(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Copy the data over. | 
|  | * | 
|  | * If the "has data descriptor" flag is set, we want to copy the DD | 
|  | * fields as well.  This is a fixed-size area immediately following | 
|  | * the data. | 
|  | */ | 
|  | if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) | 
|  | { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | off_t copyLen; | 
|  | copyLen = pSourceEntry->getCompressedLen(); | 
|  | if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) | 
|  | copyLen += ZipEntry::kDataDescriptorLen; | 
|  |  | 
|  | if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) | 
|  | != NO_ERROR) | 
|  | { | 
|  | ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Update file offsets. | 
|  | */ | 
|  | endPosn = ftell(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Success!  Fill out new values. | 
|  | */ | 
|  | pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset | 
|  | mEOCD.mNumEntries++; | 
|  | mEOCD.mTotalNumEntries++; | 
|  | mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush() | 
|  | mEOCD.mCentralDirOffset = endPosn; | 
|  |  | 
|  | /* | 
|  | * Add pEntry to the list. | 
|  | */ | 
|  | mEntries.add(pEntry); | 
|  | if (ppEntry != NULL) | 
|  | *ppEntry = pEntry; | 
|  | pEntry = NULL; | 
|  |  | 
|  | result = NO_ERROR; | 
|  |  | 
|  | bail: | 
|  | delete pEntry; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add an entry by copying it from another zip file, recompressing with | 
|  | * Zopfli if already compressed. | 
|  | * | 
|  | * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. | 
|  | */ | 
|  | status_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, | 
|  | ZipEntry** ppEntry) | 
|  | { | 
|  | ZipEntry* pEntry = NULL; | 
|  | status_t result; | 
|  | long lfhPosn, startPosn, endPosn, uncompressedLen; | 
|  |  | 
|  | if (mReadOnly) | 
|  | return INVALID_OPERATION; | 
|  |  | 
|  | /* make sure we're in a reasonable state */ | 
|  | assert(mZipFp != NULL); | 
|  | assert(mEntries.size() == mEOCD.mTotalNumEntries); | 
|  |  | 
|  | if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | pEntry = new ZipEntry; | 
|  | if (pEntry == NULL) { | 
|  | result = NO_MEMORY; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | result = pEntry->initFromExternal(pSourceZip, pSourceEntry); | 
|  | if (result != NO_ERROR) | 
|  | goto bail; | 
|  |  | 
|  | /* | 
|  | * From here on out, failures are more interesting. | 
|  | */ | 
|  | mNeedCDRewrite = true; | 
|  |  | 
|  | /* | 
|  | * Write the LFH, even though it's still mostly blank.  We need it | 
|  | * as a place-holder.  In theory the LFH isn't necessary, but in | 
|  | * practice some utilities demand it. | 
|  | */ | 
|  | lfhPosn = ftell(mZipFp); | 
|  | pEntry->mLFH.write(mZipFp); | 
|  | startPosn = ftell(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Copy the data over. | 
|  | * | 
|  | * If the "has data descriptor" flag is set, we want to copy the DD | 
|  | * fields as well.  This is a fixed-size area immediately following | 
|  | * the data. | 
|  | */ | 
|  | if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) | 
|  | { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | uncompressedLen = pSourceEntry->getUncompressedLen(); | 
|  |  | 
|  | if (pSourceEntry->isCompressed()) { | 
|  | void *buf = pSourceZip->uncompress(pSourceEntry); | 
|  | if (buf == NULL) { | 
|  | result = NO_MEMORY; | 
|  | goto bail; | 
|  | } | 
|  | long startPosn = ftell(mZipFp); | 
|  | uint32_t crc; | 
|  | if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != NO_ERROR) { | 
|  | ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName); | 
|  | result = UNKNOWN_ERROR; | 
|  | free(buf); | 
|  | goto bail; | 
|  | } | 
|  | long endPosn = ftell(mZipFp); | 
|  | pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, | 
|  | pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated); | 
|  | free(buf); | 
|  | } else { | 
|  | off_t copyLen; | 
|  | copyLen = pSourceEntry->getCompressedLen(); | 
|  | if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) | 
|  | copyLen += ZipEntry::kDataDescriptorLen; | 
|  |  | 
|  | if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) | 
|  | != NO_ERROR) | 
|  | { | 
|  | ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Update file offsets. | 
|  | */ | 
|  | endPosn = ftell(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Success!  Fill out new values. | 
|  | */ | 
|  | pEntry->setLFHOffset(lfhPosn); | 
|  | mEOCD.mNumEntries++; | 
|  | mEOCD.mTotalNumEntries++; | 
|  | mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush() | 
|  | mEOCD.mCentralDirOffset = endPosn; | 
|  |  | 
|  | /* | 
|  | * Go back and write the LFH. | 
|  | */ | 
|  | if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { | 
|  | result = UNKNOWN_ERROR; | 
|  | goto bail; | 
|  | } | 
|  | pEntry->mLFH.write(mZipFp); | 
|  |  | 
|  | /* | 
|  | * Add pEntry to the list. | 
|  | */ | 
|  | mEntries.add(pEntry); | 
|  | if (ppEntry != NULL) | 
|  | *ppEntry = pEntry; | 
|  | pEntry = NULL; | 
|  |  | 
|  | result = NO_ERROR; | 
|  |  | 
|  | bail: | 
|  | delete pEntry; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Copy all of the bytes in "src" to "dst". | 
|  | * | 
|  | * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" | 
|  | * will be seeked immediately past the data. | 
|  | */ | 
|  | status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32) | 
|  | { | 
|  | uint8_t tmpBuf[32768]; | 
|  | size_t count; | 
|  |  | 
|  | *pCRC32 = crc32(0L, Z_NULL, 0); | 
|  |  | 
|  | while (1) { | 
|  | count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); | 
|  | if (ferror(srcFp) || ferror(dstFp)) | 
|  | return errnoToStatus(errno); | 
|  | if (count == 0) | 
|  | break; | 
|  |  | 
|  | *pCRC32 = crc32(*pCRC32, tmpBuf, count); | 
|  |  | 
|  | if (fwrite(tmpBuf, 1, count, dstFp) != count) { | 
|  | ALOGD("fwrite %d bytes failed\n", (int) count); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Copy all of the bytes in "src" to "dst". | 
|  | * | 
|  | * On exit, "dstFp" will be seeked immediately past the data. | 
|  | */ | 
|  | status_t ZipFile::copyDataToFp(FILE* dstFp, | 
|  | const void* data, size_t size, uint32_t* pCRC32) | 
|  | { | 
|  | size_t count; | 
|  |  | 
|  | *pCRC32 = crc32(0L, Z_NULL, 0); | 
|  | if (size > 0) { | 
|  | *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); | 
|  | if (fwrite(data, 1, size, dstFp) != size) { | 
|  | ALOGD("fwrite %d bytes failed\n", (int) size); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Copy some of the bytes in "src" to "dst". | 
|  | * | 
|  | * If "pCRC32" is NULL, the CRC will not be computed. | 
|  | * | 
|  | * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" | 
|  | * will be seeked immediately past the data just written. | 
|  | */ | 
|  | status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, | 
|  | uint32_t* pCRC32) | 
|  | { | 
|  | uint8_t tmpBuf[32768]; | 
|  | size_t count; | 
|  |  | 
|  | if (pCRC32 != NULL) | 
|  | *pCRC32 = crc32(0L, Z_NULL, 0); | 
|  |  | 
|  | while (length) { | 
|  | long readSize; | 
|  |  | 
|  | readSize = sizeof(tmpBuf); | 
|  | if (readSize > length) | 
|  | readSize = length; | 
|  |  | 
|  | count = fread(tmpBuf, 1, readSize, srcFp); | 
|  | if ((long) count != readSize) {     // error or unexpected EOF | 
|  | ALOGD("fread %d bytes failed\n", (int) readSize); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (pCRC32 != NULL) | 
|  | *pCRC32 = crc32(*pCRC32, tmpBuf, count); | 
|  |  | 
|  | if (fwrite(tmpBuf, 1, count, dstFp) != count) { | 
|  | ALOGD("fwrite %d bytes failed\n", (int) count); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | length -= readSize; | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Compress all of the data in "srcFp" and write it to "dstFp". | 
|  | * | 
|  | * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" | 
|  | * will be seeked immediately past the compressed data. | 
|  | */ | 
|  | status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, | 
|  | const void* data, size_t size, uint32_t* pCRC32) | 
|  | { | 
|  | status_t result = NO_ERROR; | 
|  | const size_t kBufSize = 1024 * 1024; | 
|  | uint8_t* inBuf = NULL; | 
|  | uint8_t* outBuf = NULL; | 
|  | size_t outSize = 0; | 
|  | bool atEof = false;     // no feof() aviailable yet | 
|  | uint32_t crc; | 
|  | ZopfliOptions options; | 
|  | unsigned char bp = 0; | 
|  |  | 
|  | ZopfliInitOptions(&options); | 
|  |  | 
|  | crc = crc32(0L, Z_NULL, 0); | 
|  |  | 
|  | if (data) { | 
|  | crc = crc32(crc, (const unsigned char*)data, size); | 
|  | ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp, | 
|  | &outBuf, &outSize); | 
|  | } else { | 
|  | /* | 
|  | * Create an input buffer and an output buffer. | 
|  | */ | 
|  | inBuf = new uint8_t[kBufSize]; | 
|  | if (inBuf == NULL) { | 
|  | result = NO_MEMORY; | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Loop while we have data. | 
|  | */ | 
|  | do { | 
|  | size_t getSize; | 
|  | getSize = fread(inBuf, 1, kBufSize, srcFp); | 
|  | if (ferror(srcFp)) { | 
|  | ALOGD("deflate read failed (errno=%d)\n", errno); | 
|  | delete[] inBuf; | 
|  | goto bail; | 
|  | } | 
|  | if (getSize < kBufSize) { | 
|  | ALOGV("+++  got %d bytes, EOF reached\n", | 
|  | (int)getSize); | 
|  | atEof = true; | 
|  | } | 
|  |  | 
|  | crc = crc32(crc, inBuf, getSize); | 
|  | ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize); | 
|  | } while (!atEof); | 
|  | delete[] inBuf; | 
|  | } | 
|  |  | 
|  | ALOGV("+++ writing %d bytes\n", (int)outSize); | 
|  | if (fwrite(outBuf, 1, outSize, dstFp) != outSize) { | 
|  | ALOGD("write %d failed in deflate\n", (int)outSize); | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | *pCRC32 = crc; | 
|  |  | 
|  | bail: | 
|  | free(outBuf); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mark an entry as deleted. | 
|  | * | 
|  | * We will eventually need to crunch the file down, but if several files | 
|  | * are being removed (perhaps as part of an "update" process) we can make | 
|  | * things considerably faster by deferring the removal to "flush" time. | 
|  | */ | 
|  | status_t ZipFile::remove(ZipEntry* pEntry) | 
|  | { | 
|  | /* | 
|  | * Should verify that pEntry is actually part of this archive, and | 
|  | * not some stray ZipEntry from a different file. | 
|  | */ | 
|  |  | 
|  | /* mark entry as deleted, and mark archive as dirty */ | 
|  | pEntry->setDeleted(); | 
|  | mNeedCDRewrite = true; | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Flush any pending writes. | 
|  | * | 
|  | * In particular, this will crunch out deleted entries, and write the | 
|  | * Central Directory and EOCD if we have stomped on them. | 
|  | */ | 
|  | status_t ZipFile::flush(void) | 
|  | { | 
|  | status_t result = NO_ERROR; | 
|  | long eocdPosn; | 
|  | int i, count; | 
|  |  | 
|  | if (mReadOnly) | 
|  | return INVALID_OPERATION; | 
|  | if (!mNeedCDRewrite) | 
|  | return NO_ERROR; | 
|  |  | 
|  | assert(mZipFp != NULL); | 
|  |  | 
|  | result = crunchArchive(); | 
|  | if (result != NO_ERROR) | 
|  | return result; | 
|  |  | 
|  | if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) | 
|  | return UNKNOWN_ERROR; | 
|  |  | 
|  | count = mEntries.size(); | 
|  | for (i = 0; i < count; i++) { | 
|  | ZipEntry* pEntry = mEntries[i]; | 
|  | pEntry->mCDE.write(mZipFp); | 
|  | } | 
|  |  | 
|  | eocdPosn = ftell(mZipFp); | 
|  | mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; | 
|  |  | 
|  | mEOCD.write(mZipFp); | 
|  |  | 
|  | /* | 
|  | * If we had some stuff bloat up during compression and get replaced | 
|  | * with plain files, or if we deleted some entries, there's a lot | 
|  | * of wasted space at the end of the file.  Remove it now. | 
|  | */ | 
|  | if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { | 
|  | ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); | 
|  | // not fatal | 
|  | } | 
|  |  | 
|  | /* should we clear the "newly added" flag in all entries now? */ | 
|  |  | 
|  | mNeedCDRewrite = false; | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Crunch deleted files out of an archive by shifting the later files down. | 
|  | * | 
|  | * Because we're not using a temp file, we do the operation inside the | 
|  | * current file. | 
|  | */ | 
|  | status_t ZipFile::crunchArchive(void) | 
|  | { | 
|  | status_t result = NO_ERROR; | 
|  | int i, count; | 
|  | long delCount, adjust; | 
|  |  | 
|  | #if 0 | 
|  | printf("CONTENTS:\n"); | 
|  | for (i = 0; i < (int) mEntries.size(); i++) { | 
|  | printf(" %d: lfhOff=%ld del=%d\n", | 
|  | i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); | 
|  | } | 
|  | printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset); | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Roll through the set of files, shifting them as appropriate.  We | 
|  | * could probably get a slight performance improvement by sliding | 
|  | * multiple files down at once (because we could use larger reads | 
|  | * when operating on batches of small files), but it's not that useful. | 
|  | */ | 
|  | count = mEntries.size(); | 
|  | delCount = adjust = 0; | 
|  | for (i = 0; i < count; i++) { | 
|  | ZipEntry* pEntry = mEntries[i]; | 
|  | long span; | 
|  |  | 
|  | if (pEntry->getLFHOffset() != 0) { | 
|  | long nextOffset; | 
|  |  | 
|  | /* Get the length of this entry by finding the offset | 
|  | * of the next entry.  Directory entries don't have | 
|  | * file offsets, so we need to find the next non-directory | 
|  | * entry. | 
|  | */ | 
|  | nextOffset = 0; | 
|  | for (int ii = i+1; nextOffset == 0 && ii < count; ii++) | 
|  | nextOffset = mEntries[ii]->getLFHOffset(); | 
|  | if (nextOffset == 0) | 
|  | nextOffset = mEOCD.mCentralDirOffset; | 
|  | span = nextOffset - pEntry->getLFHOffset(); | 
|  |  | 
|  | assert(span >= ZipEntry::LocalFileHeader::kLFHLen); | 
|  | } else { | 
|  | /* This is a directory entry.  It doesn't have | 
|  | * any actual file contents, so there's no need to | 
|  | * move anything. | 
|  | */ | 
|  | span = 0; | 
|  | } | 
|  |  | 
|  | //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", | 
|  | //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); | 
|  |  | 
|  | if (pEntry->getDeleted()) { | 
|  | adjust += span; | 
|  | delCount++; | 
|  |  | 
|  | delete pEntry; | 
|  | mEntries.removeAt(i); | 
|  |  | 
|  | /* adjust loop control */ | 
|  | count--; | 
|  | i--; | 
|  | } else if (span != 0 && adjust > 0) { | 
|  | /* shuffle this entry back */ | 
|  | //printf("+++ Shuffling '%s' back %ld\n", | 
|  | //    pEntry->getFileName(), adjust); | 
|  | result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, | 
|  | pEntry->getLFHOffset(), span); | 
|  | if (result != NO_ERROR) { | 
|  | /* this is why you use a temp file */ | 
|  | ALOGE("error during crunch - archive is toast\n"); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Fix EOCD info.  We have to wait until the end to do some of this | 
|  | * because we use mCentralDirOffset to determine "span" for the | 
|  | * last entry. | 
|  | */ | 
|  | mEOCD.mCentralDirOffset -= adjust; | 
|  | mEOCD.mNumEntries -= delCount; | 
|  | mEOCD.mTotalNumEntries -= delCount; | 
|  | mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush() | 
|  |  | 
|  | assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); | 
|  | assert(mEOCD.mNumEntries == count); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Works like memmove(), but on pieces of a file. | 
|  | */ | 
|  | status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) | 
|  | { | 
|  | if (dst == src || n <= 0) | 
|  | return NO_ERROR; | 
|  |  | 
|  | uint8_t readBuf[32768]; | 
|  |  | 
|  | if (dst < src) { | 
|  | /* shift stuff toward start of file; must read from start */ | 
|  | while (n != 0) { | 
|  | size_t getSize = sizeof(readBuf); | 
|  | if (getSize > n) | 
|  | getSize = n; | 
|  |  | 
|  | if (fseek(fp, (long) src, SEEK_SET) != 0) { | 
|  | ALOGD("filemove src seek %ld failed\n", (long) src); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (fread(readBuf, 1, getSize, fp) != getSize) { | 
|  | ALOGD("filemove read %ld off=%ld failed\n", | 
|  | (long) getSize, (long) src); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (fseek(fp, (long) dst, SEEK_SET) != 0) { | 
|  | ALOGD("filemove dst seek %ld failed\n", (long) dst); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (fwrite(readBuf, 1, getSize, fp) != getSize) { | 
|  | ALOGD("filemove write %ld off=%ld failed\n", | 
|  | (long) getSize, (long) dst); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | src += getSize; | 
|  | dst += getSize; | 
|  | n -= getSize; | 
|  | } | 
|  | } else { | 
|  | /* shift stuff toward end of file; must read from end */ | 
|  | assert(false);      // write this someday, maybe | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Get the modification time from a file descriptor. | 
|  | */ | 
|  | time_t ZipFile::getModTime(int fd) | 
|  | { | 
|  | struct stat sb; | 
|  |  | 
|  | if (fstat(fd, &sb) < 0) { | 
|  | ALOGD("HEY: fstat on fd %d failed\n", fd); | 
|  | return (time_t) -1; | 
|  | } | 
|  |  | 
|  | return sb.st_mtime; | 
|  | } | 
|  |  | 
|  |  | 
|  | #if 0       /* this is a bad idea */ | 
|  | /* | 
|  | * Get a copy of the Zip file descriptor. | 
|  | * | 
|  | * We don't allow this if the file was opened read-write because we tend | 
|  | * to leave the file contents in an uncertain state between calls to | 
|  | * flush().  The duplicated file descriptor should only be valid for reads. | 
|  | */ | 
|  | int ZipFile::getZipFd(void) const | 
|  | { | 
|  | if (!mReadOnly) | 
|  | return INVALID_OPERATION; | 
|  | assert(mZipFp != NULL); | 
|  |  | 
|  | int fd; | 
|  | fd = dup(fileno(mZipFp)); | 
|  | if (fd < 0) { | 
|  | ALOGD("didn't work, errno=%d\n", errno); | 
|  | } | 
|  |  | 
|  | return fd; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | /* | 
|  | * Expand data. | 
|  | */ | 
|  | bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const | 
|  | { | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // free the memory when you're done | 
|  | void* ZipFile::uncompress(const ZipEntry* entry) const | 
|  | { | 
|  | size_t unlen = entry->getUncompressedLen(); | 
|  | size_t clen = entry->getCompressedLen(); | 
|  |  | 
|  | void* buf = malloc(unlen); | 
|  | if (buf == NULL) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | fseek(mZipFp, 0, SEEK_SET); | 
|  |  | 
|  | off_t offset = entry->getFileOffset(); | 
|  | if (fseek(mZipFp, offset, SEEK_SET) != 0) { | 
|  | goto bail; | 
|  | } | 
|  |  | 
|  | switch (entry->getCompressionMethod()) | 
|  | { | 
|  | case ZipEntry::kCompressStored: { | 
|  | ssize_t amt = fread(buf, 1, unlen, mZipFp); | 
|  | if (amt != (ssize_t)unlen) { | 
|  | goto bail; | 
|  | } | 
|  | #if 0 | 
|  | printf("data...\n"); | 
|  | const unsigned char* p = (unsigned char*)buf; | 
|  | const unsigned char* end = p+unlen; | 
|  | for (int i=0; i<32 && p < end; i++) { | 
|  | printf("0x%08x ", (int)(offset+(i*0x10))); | 
|  | for (int j=0; j<0x10 && p < end; j++) { | 
|  | printf(" %02x", *p); | 
|  | p++; | 
|  | } | 
|  | printf("\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | } | 
|  | break; | 
|  | case ZipEntry::kCompressDeflated: { | 
|  | if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { | 
|  | goto bail; | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: | 
|  | goto bail; | 
|  | } | 
|  | return buf; | 
|  |  | 
|  | bail: | 
|  | free(buf); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * =========================================================================== | 
|  | *      ZipFile::EndOfCentralDir | 
|  | * =========================================================================== | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Read the end-of-central-dir fields. | 
|  | * | 
|  | * "buf" should be positioned at the EOCD signature, and should contain | 
|  | * the entire EOCD area including the comment. | 
|  | */ | 
|  | status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len) | 
|  | { | 
|  | /* don't allow re-use */ | 
|  | assert(mComment == NULL); | 
|  |  | 
|  | if (len < kEOCDLen) { | 
|  | /* looks like ZIP file got truncated */ | 
|  | ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n", | 
|  | kEOCDLen, len); | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  |  | 
|  | /* this should probably be an assert() */ | 
|  | if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) | 
|  | return UNKNOWN_ERROR; | 
|  |  | 
|  | mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); | 
|  | mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); | 
|  | mNumEntries = ZipEntry::getShortLE(&buf[0x08]); | 
|  | mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); | 
|  | mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); | 
|  | mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); | 
|  | mCommentLen = ZipEntry::getShortLE(&buf[0x14]); | 
|  |  | 
|  | // TODO: validate mCentralDirOffset | 
|  |  | 
|  | if (mCommentLen > 0) { | 
|  | if (kEOCDLen + mCommentLen > len) { | 
|  | ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n", | 
|  | kEOCDLen, mCommentLen, len); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | mComment = new uint8_t[mCommentLen]; | 
|  | memcpy(mComment, buf + kEOCDLen, mCommentLen); | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write an end-of-central-directory section. | 
|  | */ | 
|  | status_t ZipFile::EndOfCentralDir::write(FILE* fp) | 
|  | { | 
|  | uint8_t buf[kEOCDLen]; | 
|  |  | 
|  | ZipEntry::putLongLE(&buf[0x00], kSignature); | 
|  | ZipEntry::putShortLE(&buf[0x04], mDiskNumber); | 
|  | ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); | 
|  | ZipEntry::putShortLE(&buf[0x08], mNumEntries); | 
|  | ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); | 
|  | ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); | 
|  | ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); | 
|  | ZipEntry::putShortLE(&buf[0x14], mCommentLen); | 
|  |  | 
|  | if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) | 
|  | return UNKNOWN_ERROR; | 
|  | if (mCommentLen > 0) { | 
|  | assert(mComment != NULL); | 
|  | if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Dump the contents of an EndOfCentralDir object. | 
|  | */ | 
|  | void ZipFile::EndOfCentralDir::dump(void) const | 
|  | { | 
|  | ALOGD(" EndOfCentralDir contents:\n"); | 
|  | ALOGD("  diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n", | 
|  | mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); | 
|  | ALOGD("  centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n", | 
|  | mCentralDirSize, mCentralDirOffset, mCommentLen); | 
|  | } | 
|  |  |