/* * 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; }