コード例 #1
0
/*
 * 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;
    unsigned long 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) {
                LOGD("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) {
                    LOGD("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
                LOGD("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;

        scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
                        &compressedLen, &crc);
        if (!scanResult || method != ZipEntry::kCompressDeflated) {
            LOGD("this isn't a deflated gzip file?");
            result = UNKNOWN_ERROR;
            goto bail;
        }

        result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
        if (result != NO_ERROR) {
            LOGD("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;
}