diff options
Diffstat (limited to 'libs/androidfw')
-rw-r--r-- | libs/androidfw/Android.mk | 7 | ||||
-rw-r--r-- | libs/androidfw/Asset.cpp | 4 | ||||
-rw-r--r-- | libs/androidfw/AssetManager.cpp | 91 | ||||
-rw-r--r-- | libs/androidfw/ObbFile.cpp | 2 | ||||
-rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 3 | ||||
-rw-r--r-- | libs/androidfw/ZipFileRO.cpp | 954 | ||||
-rw-r--r-- | libs/androidfw/ZipUtils.cpp | 233 | ||||
-rw-r--r-- | libs/androidfw/tests/Android.mk | 2 | ||||
-rw-r--r-- | libs/androidfw/tests/ZipUtils_test.cpp (renamed from libs/androidfw/tests/ZipFileRO_test.cpp) | 10 |
9 files changed, 276 insertions, 1030 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d80612b..d21197e 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -53,7 +53,7 @@ LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS LOCAL_C_INCLUDES := \ external/zlib -LOCAL_STATIC_LIBRARIES := liblog +LOCAL_STATIC_LIBRARIES := liblog libziparchive-host libutils include $(BUILD_HOST_STATIC_LIBRARY) @@ -72,9 +72,12 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libz +LOCAL_STATIC_LIBRARIES := libziparchive + LOCAL_C_INCLUDES := \ external/icu4c/common \ - external/zlib + external/zlib \ + system/core/include LOCAL_MODULE:= libandroidfw diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index cb7628d..ce6cc38 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -843,7 +843,7 @@ void _CompressedAsset::close(void) * The first time this is called, we expand the compressed data into a * buffer. */ -const void* _CompressedAsset::getBuffer(bool wordAligned) +const void* _CompressedAsset::getBuffer(bool) { unsigned char* buf = NULL; @@ -860,7 +860,7 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) } if (mMap != NULL) { - if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), + if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf, mUncompressedLen, mCompressedLen)) goto bail; } else { diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 6667daf..52ab361 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -165,7 +165,7 @@ AssetManager::~AssetManager(void) delete[] mVendor; } -bool AssetManager::addAssetPath(const String8& path, void** cookie) +bool AssetManager::addAssetPath(const String8& path, int32_t* cookie) { AutoMutex _l(mLock); @@ -192,7 +192,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) for (size_t i=0; i<mAssetPaths.size(); i++) { if (mAssetPaths[i].path == ap.path) { if (cookie) { - *cookie = (void*)(i+1); + *cookie = static_cast<int32_t>(i+1); } return true; } @@ -205,7 +205,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) // new paths are always added at the end if (cookie) { - *cookie = (void*)mAssetPaths.size(); + *cookie = static_cast<int32_t>(mAssetPaths.size()); } // add overlay packages for /system/framework; apps are handled by the @@ -305,10 +305,11 @@ bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entr if (entry == NULL) { return false; } - if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) { - return false; - } - return true; + + const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc); + zip->releaseEntry(entry); + + return gotInfo; } bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, @@ -394,17 +395,17 @@ bool AssetManager::addDefaultAssets() return addAssetPath(path, NULL); } -void* AssetManager::nextAssetPath(void* cookie) const +int32_t AssetManager::nextAssetPath(const int32_t cookie) const { AutoMutex _l(mLock); - size_t next = ((size_t)cookie)+1; - return next > mAssetPaths.size() ? NULL : (void*)next; + const size_t next = static_cast<size_t>(cookie) + 1; + return next > mAssetPaths.size() ? -1 : next; } -String8 AssetManager::getAssetPath(void* cookie) const +String8 AssetManager::getAssetPath(const int32_t cookie) const { AutoMutex _l(mLock); - const size_t which = ((size_t)cookie)-1; + const size_t which = static_cast<size_t>(cookie) - 1; if (which < mAssetPaths.size()) { return mAssetPaths[which].path; } @@ -432,7 +433,7 @@ void AssetManager::setLocaleLocked(const char* locale) delete[] mLocale; } mLocale = strdupNew(locale); - + updateResourceParamsLocked(); } @@ -574,15 +575,14 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) return NULL; } -Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) +Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, AccessMode mode) { - const size_t which = ((size_t)cookie)-1; + const size_t which = static_cast<size_t>(cookie) - 1; AutoMutex _l(mLock); LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - if (mCacheMode != CACHE_OFF && !mCacheValid) loadFileNameCacheLocked(); @@ -675,7 +675,7 @@ const ResTable* AssetManager::getResTable(bool required) const mZipSet.setZipResourceTableAsset(ap.path, ass); } } - + if (i == 0 && ass != NULL) { // If this is the first resource table in the asset // manager, then we are going to cache it so that we @@ -689,7 +689,7 @@ const ResTable* AssetManager::getResTable(bool required) const } } else { ALOGV("loading resource table %s\n", ap.path.string()); - Asset* ass = const_cast<AssetManager*>(this)-> + ass = const_cast<AssetManager*>(this)-> openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); @@ -821,16 +821,14 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m String8 path(fileName); /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); + ZipFileRO* pZip = getZipFileLocked(ap); if (pZip != NULL) { //printf("GOT zip, checking NA '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); + ZipEntryRO entry = pZip->findEntryByName(path.string()); if (entry != NULL) { //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + pZip->releaseEntry(entry); } } @@ -891,7 +889,7 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m /* look at the filesystem on disk */ String8 path(createPathNameLocked(ap, locale, vendor)); path.appendPath(fileName); - + String8 excludeName(path); excludeName.append(kExcludeExtension); if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { @@ -899,28 +897,28 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m //printf("+++ excluding '%s'\n", (const char*) excludeName); return kExcludedAsset; } - + pAsset = openAssetFromFileLocked(path, mode); - + if (pAsset == NULL) { /* try again, this time with ".gz" */ path.append(".gz"); pAsset = openAssetFromFileLocked(path, mode); } - + if (pAsset != NULL) pAsset->setAssetSource(path); } else { /* find in cache */ String8 path(createPathNameLocked(ap, locale, vendor)); path.appendPath(fileName); - + AssetDir::FileInfo tmpInfo; bool found = false; - + String8 excludeName(path); excludeName.append(kExcludeExtension); - + if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { /* go no farther */ //printf("+++ Excluding '%s'\n", (const char*) excludeName); @@ -975,17 +973,15 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m path.appendPath(fileName); /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); + ZipFileRO* pZip = getZipFileLocked(ap); if (pZip != NULL) { //printf("GOT zip, checking '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); + ZipEntryRO entry = pZip->findEntryByName(path.string()); if (entry != NULL) { //printf("FOUND in Zip file for %s/%s-%s\n", // appName, locale, vendor); pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + pZip->releaseEntry(entry); } } @@ -1209,7 +1205,7 @@ AssetDir* AssetManager::openDir(const char* dirName) * * Pass in "" for the root dir. */ -AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) +AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirName) { AutoMutex _l(mLock); @@ -1228,7 +1224,7 @@ AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) pMergedInfo = new SortedVector<AssetDir::FileInfo>; - const size_t which = ((size_t)cookie)-1; + const size_t which = static_cast<size_t>(cookie) - 1; if (which < mAssetPaths.size()) { const asset_path& ap = mAssetPaths.itemAt(which); @@ -1487,11 +1483,16 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg * semantics. */ int dirNameLen = dirName.length(); - for (int i = 0; i < pZip->getNumEntries(); i++) { - ZipEntryRO entry; + void *iterationCookie; + if (!pZip->startIteration(&iterationCookie)) { + ALOGW("ZipFileRO::startIteration returned false"); + return false; + } + + ZipEntryRO entry; + while ((entry = pZip->nextEntry(iterationCookie)) != NULL) { char nameBuf[256]; - entry = pZip->findEntryByIndex(i); if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { // TODO: fix this if we expect to have long names ALOGE("ARGH: name too long?\n"); @@ -1541,6 +1542,8 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg } } + pZip->endIteration(iterationCookie); + /* * Add the set of unique directories. */ @@ -1739,7 +1742,7 @@ bool AssetManager::fncScanAndMergeDirLocked( // XXX This is broken -- the filename cache needs to hold the base // asset path separately from its filename. - + partialPath = createPathNameLocked(ap, locale, vendor); if (dirName[0] != '\0') { partialPath.appendPath(dirName); @@ -1814,12 +1817,10 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) mResourceTableAsset(NULL), mResourceTable(NULL) { //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); - mZipFile = new ZipFileRO; ALOGV("+++ opening zip '%s'\n", mPath.string()); - if (mZipFile->open(mPath.string()) != NO_ERROR) { + mZipFile = ZipFileRO::open(mPath.string()); + if (mZipFile == NULL) { ALOGD("failed to open Zip archive '%s'\n", mPath.string()); - delete mZipFile; - mZipFile = NULL; } } diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp index 21e06c8..ec59f06 100644 --- a/libs/androidfw/ObbFile.cpp +++ b/libs/androidfw/ObbFile.cpp @@ -133,7 +133,7 @@ bool ObbFile::parseObbFile(int fd) { lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); - char *footer = new char[kFooterTagSize]; + char footer[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); if (actual != kFooterTagSize) { ALOGW("couldn't read footer signature: %s\n", strerror(errno)); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 1cc3563..990cf9e 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2293,6 +2293,9 @@ String8 ResTable_config::toString() const { case ResTable_config::UI_MODE_TYPE_APPLIANCE: res.append("appliance"); break; + case ResTable_config::UI_MODE_TYPE_WATCH: + res.append("watch"); + break; default: res.appendFormat("uiModeType=%d", dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index ec5f95c..1ab18ad 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -24,6 +24,7 @@ #include <utils/Compat.h> #include <utils/misc.h> #include <utils/threads.h> +#include <ziparchive/zip_archive.h> #include <zlib.h> @@ -43,619 +44,127 @@ using namespace android; -/* - * Zip file constants. - */ -#define kEOCDSignature 0x06054b50 -#define kEOCDLen 22 -#define kEOCDDiskNumber 4 // number of the current disk -#define kEOCDDiskNumberForCD 6 // disk number with the Central Directory -#define kEOCDNumEntries 8 // offset to #of entries in file -#define kEOCDTotalNumEntries 10 // offset to total #of entries in spanned archives -#define kEOCDSize 12 // size of the central directory -#define kEOCDFileOffset 16 // offset to central directory -#define kEOCDCommentSize 20 // offset to the length of the file comment - -#define kMaxCommentLen 65535 // longest possible in ushort -#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) - -#define kLFHSignature 0x04034b50 -#define kLFHLen 30 // excluding variable-len fields -#define kLFHGPBFlags 6 // offset to GPB flags -#define kLFHNameLen 26 // offset to filename length -#define kLFHExtraLen 28 // offset to extra length - -#define kCDESignature 0x02014b50 -#define kCDELen 46 // excluding variable-len fields -#define kCDEGPBFlags 8 // offset to GPB flags -#define kCDEMethod 10 // offset to compression method -#define kCDEModWhen 12 // offset to modification timestamp -#define kCDECRC 16 // offset to entry CRC -#define kCDECompLen 20 // offset to compressed length -#define kCDEUncompLen 24 // offset to uncompressed length -#define kCDENameLen 28 // offset to filename length -#define kCDEExtraLen 30 // offset to extra length -#define kCDECommentLen 32 // offset to comment length -#define kCDELocalOffset 42 // offset to local hdr - -/* General Purpose Bit Flag */ -#define kGPFEncryptedFlag (1 << 0) -#define kGPFUnsupportedMask (kGPFEncryptedFlag) +class _ZipEntryRO { +public: + ZipEntry entry; + ZipEntryName name; + void *cookie; -/* - * The values we return for ZipEntryRO use 0 as an invalid value, so we - * want to adjust the hash table index by a fixed amount. Using a large - * value helps insure that people don't mix & match arguments, e.g. to - * findEntryByIndex(). - */ -#define kZipEntryAdj 10000 + _ZipEntryRO() : cookie(NULL) { + } -ZipFileRO::~ZipFileRO() { - free(mHashTable); - if (mDirectoryMap) - mDirectoryMap->release(); - if (mFd >= 0) - TEMP_FAILURE_RETRY(close(mFd)); - if (mFileName) - free(mFileName); -} +private: + _ZipEntryRO(const _ZipEntryRO& other); + _ZipEntryRO& operator=(const _ZipEntryRO& other); +}; -/* - * Convert a ZipEntryRO to a hash table index, verifying that it's in a - * valid range. - */ -int ZipFileRO::entryToIndex(const ZipEntryRO entry) const -{ - long ent = ((intptr_t) entry) - kZipEntryAdj; - if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); - return -1; - } - return ent; +ZipFileRO::~ZipFileRO() { + CloseArchive(mHandle); + free(mFileName); } - /* * Open the specified file read-only. We memory-map the entire thing and * close the file before returning. */ -status_t ZipFileRO::open(const char* zipFileName) +/* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName) { - int fd = -1; - - assert(mDirectoryMap == NULL); - - /* - * Open and map the specified file. - */ - fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); - if (fd < 0) { - ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); - return NAME_NOT_FOUND; - } - - mFileLength = lseek64(fd, 0, SEEK_END); - if (mFileLength < kEOCDLen) { - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; - } - - if (mFileName != NULL) { - free(mFileName); + ZipArchiveHandle handle; + const int32_t error = OpenArchive(zipFileName, &handle); + if (error) { + ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error)); + return NULL; } - mFileName = strdup(zipFileName); - mFd = fd; + return new ZipFileRO(handle, strdup(zipFileName)); +} - /* - * Find the Central Directory and store its size and number of entries. - */ - if (!mapCentralDirectory()) { - goto bail; - } - /* - * Verify Central Directory and create data structures for fast access. - */ - if (!parseZipArchive()) { - goto bail; +ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const +{ + _ZipEntryRO* data = new _ZipEntryRO; + const int32_t error = FindEntry(mHandle, entryName, &(data->entry)); + if (error) { + delete data; + return NULL; } - return OK; + data->name.name = entryName; + data->name.name_length = strlen(entryName); -bail: - free(mFileName); - mFileName = NULL; - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; + return (ZipEntryRO) data; } /* - * Parse the Zip archive, verifying its contents and initializing internal - * data structures. + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. */ -bool ZipFileRO::mapCentralDirectory(void) +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const { - ssize_t readAmount = kMaxEOCDSearch; - if (readAmount > (ssize_t) mFileLength) - readAmount = mFileLength; - - if (readAmount < kEOCDSize) { - ALOGW("File too short to be a zip file"); - return false; - } - - unsigned char* scanBuf = (unsigned char*) malloc(readAmount); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Make sure this is a Zip archive. - */ - if (lseek64(mFd, 0, SEEK_SET) != 0) { - ALOGW("seek to start failed: %s", strerror(errno)); - free(scanBuf); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); - if (actual != (ssize_t) sizeof(int32_t)) { - ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); - free(scanBuf); - return false; - } - - unsigned int header = get4LE(scanBuf); - if (header != kLFHSignature) { - ALOGV("Not a Zip archive (found 0x%08x)\n", header); - free(scanBuf); - return false; - } + const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const ZipEntry& ze = zipEntry->entry; - /* - * Perform the traditional EOCD snipe hunt. - * - * We're searching for the End of Central Directory magic number, - * which appears at the start of the EOCD block. It's followed by - * 18 bytes of EOCD stuff and up to 64KB of archive comment. We - * need to read the last part of the file into a buffer, dig through - * it to find the magic number, parse some values out, and use those - * to determine the extent of the CD. - * - * We start by pulling in the last part of the file. - */ - off64_t searchStart = mFileLength - readAmount; - - if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { - ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); - free(scanBuf); - return false; - } - actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); - if (actual != (ssize_t) readAmount) { - ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", - (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Scan backward for the EOCD magic. In an archive without a trailing - * comment, we'll find it on the first try. (We may want to consider - * doing an initial minimal read; if we don't find it, retry with a - * second read as above.) - */ - int i; - for (i = readAmount - kEOCDLen; i >= 0; i--) { - if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { - ALOGV("+++ Found EOCD at buf+%d\n", i); - break; - } + if (pMethod != NULL) { + *pMethod = ze.method; } - if (i < 0) { - ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); - free(scanBuf); - return false; + if (pUncompLen != NULL) { + *pUncompLen = ze.uncompressed_length; } - - off64_t eocdOffset = searchStart + i; - const unsigned char* eocdPtr = scanBuf + i; - - assert(eocdOffset < mFileLength); - - /* - * Grab the CD offset and size, and the number of entries in the - * archive. After that, we can release our EOCD hunt buffer. - */ - unsigned int diskNumber = get2LE(eocdPtr + kEOCDDiskNumber); - unsigned int diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD); - unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); - unsigned int totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries); - unsigned int centralDirSize = get4LE(eocdPtr + kEOCDSize); - unsigned int centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset); - unsigned int commentSize = get2LE(eocdPtr + kEOCDCommentSize); - free(scanBuf); - - // Verify that they look reasonable. - if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) { - ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", - (long) centralDirOffset, centralDirSize, (long) eocdOffset); - return false; + if (pCompLen != NULL) { + *pCompLen = ze.compressed_length; } - if (numEntries == 0) { - ALOGW("empty archive?\n"); - return false; - } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { - ALOGW("spanned archives not supported"); - return false; - } - - // Check to see if comment is a sane size - if ((commentSize > (mFileLength - kEOCDLen)) - || (eocdOffset > (mFileLength - kEOCDLen) - commentSize)) { - ALOGW("comment size runs off end of file"); - return false; + if (pOffset != NULL) { + *pOffset = ze.offset; } - - ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", - numEntries, centralDirSize, centralDirOffset); - - mDirectoryMap = new FileMap(); - if (mDirectoryMap == NULL) { - ALOGW("Unable to create directory map: %s", strerror(errno)); - return false; + if (pModWhen != NULL) { + *pModWhen = ze.mod_time; } - - if (!mDirectoryMap->create(mFileName, mFd, centralDirOffset, centralDirSize, true)) { - ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, - (ZD_TYPE) centralDirOffset, (ZD_TYPE) (centralDirOffset + centralDirSize), strerror(errno)); - return false; + if (pCrc32 != NULL) { + *pCrc32 = ze.crc32; } - mNumEntries = numEntries; - mDirectoryOffset = centralDirOffset; - return true; } - -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -static unsigned int roundUpPower2(unsigned int val) +bool ZipFileRO::startIteration(void** cookie) { - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - -bool ZipFileRO::parseZipArchive(void) -{ - bool result = false; - const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); - size_t cdLength = mDirectoryMap->getDataLength(); - int numEntries = mNumEntries; - - /* - * Create hash table. We have a minimum 75% load factor, possibly as - * low as 50% after we round off to a power of 2. - */ - mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); - mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); - - /* - * Walk through the central directory, adding entries to the hash - * table. - */ - const unsigned char* ptr = cdPtr; - for (int i = 0; i < numEntries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - if (ptr + kCDELen > cdPtr + cdLength) { - ALOGW("Ran off the end (at %d)\n", i); - goto bail; - } - - long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset >= mDirectoryOffset) { - ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); - goto bail; - } - - unsigned int gpbf = get2LE(ptr + kCDEGPBFlags); - if ((gpbf & kGPFUnsupportedMask) != 0) { - ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); - goto bail; - } - - unsigned int nameLen = get2LE(ptr + kCDENameLen); - unsigned int extraLen = get2LE(ptr + kCDEExtraLen); - unsigned int commentLen = get2LE(ptr + kCDECommentLen); - - const char *name = (const char *) ptr + kCDELen; - - /* Check name for NULL characters */ - if (memchr(name, 0, nameLen) != NULL) { - ALOGW("Filename contains NUL byte"); - goto bail; - } - - /* add the CDE filename to the hash table */ - unsigned int hash = computeHash(name, nameLen); - addToHash(name, nameLen, hash); - - /* We don't care about the comment or extra data. */ - ptr += kCDELen + nameLen + extraLen + commentLen; - if ((size_t)(ptr - cdPtr) > cdLength) { - ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", - (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); - goto bail; - } + _ZipEntryRO* ze = new _ZipEntryRO; + int32_t error = StartIteration(mHandle, &(ze->cookie), NULL /* prefix */); + if (error) { + ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error)); + delete ze; + return false; } - ALOGV("+++ zip good scan %d entries\n", numEntries); - result = true; - -bail: - return result; -} - -/* - * Simple string hash function for non-null-terminated strings. - */ -/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) -{ - unsigned int hash = 0; - while (len--) - hash = hash * 31 + *str++; - - return hash; + *cookie = ze; + return true; } -/* - * Add a new entry to the hash table. - */ -void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +ZipEntryRO ZipFileRO::nextEntry(void* cookie) { - int ent = hash & (mHashTableSize-1); - - /* - * We over-allocate the table, so we're guaranteed to find an empty slot. - */ - while (mHashTable[ent].name != NULL) - ent = (ent + 1) & (mHashTableSize-1); - - mHashTable[ent].name = str; - mHashTable[ent].nameLen = strLen; -} - -/* - * Find a matching entry. - * - * Returns NULL if not found. - */ -ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const -{ - /* - * If the ZipFileRO instance is not initialized, the entry number will - * end up being garbage since mHashTableSize is -1. - */ - if (mHashTableSize <= 0) { - return NULL; - } - - int nameLen = strlen(fileName); - unsigned int hash = computeHash(fileName, nameLen); - int ent = hash & (mHashTableSize-1); - - while (mHashTable[ent].name != NULL) { - if (mHashTable[ent].nameLen == nameLen && - memcmp(mHashTable[ent].name, fileName, nameLen) == 0) - { - /* match */ - return (ZipEntryRO)(long)(ent + kZipEntryAdj); + _ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie); + int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name)); + if (error) { + if (error != -1) { + ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error)); } - - ent = (ent + 1) & (mHashTableSize-1); + return NULL; } - return NULL; + return &(ze->entry); } -/* - * Find the Nth entry. - * - * This currently involves walking through the sparse hash table, counting - * non-empty entries. If we need to speed this up we can either allocate - * a parallel lookup table or (perhaps better) provide an iterator interface. - */ -ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +void ZipFileRO::endIteration(void* cookie) { - if (idx < 0 || idx >= mNumEntries) { - ALOGW("Invalid index %d\n", idx); - return NULL; - } - - for (int ent = 0; ent < mHashTableSize; ent++) { - if (mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); - } - } - - return NULL; + delete reinterpret_cast<_ZipEntryRO*>(cookie); } -/* - * Get the useful fields from the zip entry. - * - * Returns "false" if the offsets to the fields or the contents of the fields - * appear to be bogus. - */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const +void ZipFileRO::releaseEntry(ZipEntryRO entry) const { - bool ret = false; - - const int ent = entryToIndex(entry); - if (ent < 0) { - ALOGW("cannot find entry"); - return false; - } - - HashEntry hashEntry = mHashTable[ent]; - - /* - * Recover the start of the central directory entry from the filename - * pointer. The filename is the first entry past the fixed-size data, - * so we can just subtract back from that. - */ - const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off64_t cdOffset = mDirectoryOffset; - - ptr -= kCDELen; - - int method = get2LE(ptr + kCDEMethod); - if (pMethod != NULL) - *pMethod = method; - - if (pModWhen != NULL) - *pModWhen = get4LE(ptr + kCDEModWhen); - if (pCrc32 != NULL) - *pCrc32 = get4LE(ptr + kCDECRC); - - size_t compLen = get4LE(ptr + kCDECompLen); - if (pCompLen != NULL) - *pCompLen = compLen; - size_t uncompLen = get4LE(ptr + kCDEUncompLen); - if (pUncompLen != NULL) - *pUncompLen = uncompLen; - - /* - * If requested, determine the offset of the start of the data. All we - * have is the offset to the Local File Header, which is variable size, - * so we have to read the contents of the struct to figure out where - * the actual data starts. - * - * We also need to make sure that the lengths are not so large that - * somebody trying to map the compressed or uncompressed data runs - * off the end of the mapped region. - * - * Note we don't verify compLen/uncompLen if they don't request the - * dataOffset, because dataOffset is expensive to determine. However, - * if they don't have the file offset, they're not likely to be doing - * anything with the contents. - */ - if (pOffset != NULL) { - long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= cdOffset) { - ALOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - - unsigned char lfhBuf[kLFHLen]; - -#ifdef HAVE_PREAD - /* - * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread64() instead of a lseek64() + read() to - * guarantee atomicity across the processes with the shared file - * descriptors. - */ - ssize_t actual = - TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); - - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: data=0x%08lx\n", - localHdrOffset, kLFHSignature, get4LE(lfhBuf)); - return false; - } -#else /* HAVE_PREAD */ - /* - * For hosts don't have pread64() we cannot guarantee atomic reads from - * an offset in a file. Android should never run on those platforms. - * File descriptors inherited from a fork() share file offsets and - * there would be nothing to protect from two different processes - * calling lseek64() concurrently. - */ - - { - AutoMutex _l(mFdLock); - - if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: offset=" ZD " data=0x%08lx\n", - localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); - return false; - } - } -#endif /* HAVE_PREAD */ - - unsigned int gpbf = get2LE(lfhBuf + kLFHGPBFlags); - if ((gpbf & kGPFUnsupportedMask) != 0) { - ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); - return false; - } - - off64_t dataOffset = localHdrOffset + kLFHLen - + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); - if (dataOffset >= cdOffset) { - ALOGW("bad data offset %ld in zip\n", (long) dataOffset); - return false; - } - - /* check lengths */ - if ((dataOffset >= cdOffset) || (compLen > (cdOffset - dataOffset))) { - ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); - return false; - } - - if (method == kCompressStored && - ((dataOffset >= cdOffset) || - (uncompLen > (cdOffset - dataOffset)))) - { - ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); - return false; - } - - *pOffset = dataOffset; - } - - return true; + delete reinterpret_cast<_ZipEntryRO*>(entry); } /* @@ -664,16 +173,17 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const { - int ent = entryToIndex(entry); - if (ent < 0) - return -1; + const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const uint16_t requiredSize = zipEntry->name.name_length + 1; - int nameLen = mHashTable[ent].nameLen; - if (bufLen < nameLen+1) - return nameLen+1; + if (bufLen < requiredSize) { + ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize); + return requiredSize; + } + + memcpy(buffer, zipEntry->name.name, requiredSize - 1); + buffer[requiredSize - 1] = '\0'; - memcpy(buffer, mHashTable[ent].name, nameLen); - buffer[nameLen] = '\0'; return 0; } @@ -682,32 +192,19 @@ int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) */ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const { - /* - * TODO: the efficient way to do this is to modify FileMap to allow - * sub-regions of a file to be mapped. A reference-counting scheme - * can manage the base memory mapping. For now, we just create a brand - * new mapping off of the Zip archive file descriptor. - */ - - FileMap* newMap; - int method; - size_t uncompLen; - size_t compLen; - off64_t offset; - - if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { - return NULL; - } + const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const ZipEntry& ze = zipEntry->entry; + int fd = GetFileDescriptor(mHandle); + size_t actualLen = 0; - size_t actualLen; - if (method == kCompressStored) { - actualLen = uncompLen; + if (ze.method == kCompressStored) { + actualLen = ze.uncompressed_length; } else { - actualLen = compLen; + actualLen = ze.compressed_length; } - newMap = new FileMap(); - if (!newMap->create(mFileName, mFd, offset, actualLen, true)) { + FileMap* newMap = new FileMap(); + if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) { newMap->release(); return NULL; } @@ -721,64 +218,17 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const * This doesn't verify the data's CRC, which might be useful for * uncompressed data. The caller should be able to manage it. */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const { - const size_t kSequentialMin = 32768; - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) { + _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry), + (uint8_t*) buffer, size); + if (error) { + ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); return false; } - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - FileMap *file; - - if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { - goto bail; - } - - file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - /* - * Experiment with madvise hint. When we want to uncompress a file, - * we pull some stuff out of the central dir entry and then hit a - * bunch of compressed or uncompressed data sequentially. The CDE - * visit will cause a limited amount of read-ahead because it's at - * the end of the file. We could end up doing lots of extra disk - * access if the file we're prying open is small. Bottom line is we - * probably don't want to turn MADV_SEQUENTIAL on and leave it on. - * - * So, if the compressed size of the file is above a certain minimum - * size, temporarily boost the read-ahead in the hope that the extra - * pair of system calls are negated by a reduction in page faults. - */ - if (compLen > kSequentialMin) - file->advise(FileMap::SEQUENTIAL); - - if (method == kCompressStored) { - memcpy(buffer, ptr, uncompLen); - } else { - if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto unmap; - } - - if (compLen > kSequentialMin) - file->advise(FileMap::NORMAL); - - result = true; - -unmap: - file->release(); -bail: - return result; + return true; } /* @@ -788,208 +238,12 @@ bail: */ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const { - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) { + _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); + const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd); + if (error) { + ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); return false; } - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - FileMap *file; - - if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) { - goto bail; - } - - file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - if (method == kCompressStored) { - ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); - if (actual < 0) { - ALOGE("Write failed: %s\n", strerror(errno)); - goto unmap; - } else if ((size_t) actual != uncompLen) { - ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", - (ZD_TYPE) actual, (ZD_TYPE) uncompLen); - goto unmap; - } else { - ALOGI("+++ successful write\n"); - } - } else { - if (!inflateBuffer(fd, ptr, uncompLen, compLen)) { - goto unmap; - } - } - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to another. - */ -/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) outBuf; - zstream.avail_out = uncompLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_FINISH); - if (zerr != Z_STREAM_END) { - ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to an open file descriptor. - */ -/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - const size_t kWriteBufSize = 32768; - unsigned char writeBuf[kWriteBufSize]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = sizeof(writeBuf); - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) - { - long writeSize = zstream.next_out - writeBuf; - int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); - if (cc < 0) { - ALOGW("write failed in inflate: %s", strerror(errno)); - goto z_bail; - } else if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); - goto z_bail; - } - - zstream.next_out = writeBuf; - zstream.avail_out = sizeof(writeBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; + return true; } diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 997eb7d..e9ac2fe 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -33,19 +33,29 @@ using namespace android; +static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + + +static const unsigned long kReadBufSize = 32768; + /* * Utility function that expands zip/gzip "deflate" compressed data * into a buffer. * - * "fd" is an open file positioned at the start of the "deflate" data + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data * "buf" must hold at least "uncompressedLen" bytes. */ -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, +/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf, long uncompressedLen, long compressedLen) { bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; + z_stream zstream; int zerr; unsigned long compRemaining; @@ -53,15 +63,12 @@ using namespace android; assert(uncompressedLen >= 0); assert(compressedLen >= 0); - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; compRemaining = compressedLen; /* * Initialize the zlib stream. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -71,10 +78,10 @@ using namespace android; zstream.avail_out = uncompressedLen; zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -99,18 +106,18 @@ using namespace android; ALOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); - int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); - if (cc < 0) { - ALOGW("inflate read failed: %s", strerror(errno)); - } else if (cc != (int) getSize) { - ALOGW("inflate read failed (%d vs %ld)", cc, getSize); + unsigned char* nextBuffer = NULL; + const unsigned long nextSize = reader.read(&nextBuffer, getSize); + + if (nextSize < getSize || nextBuffer == NULL) { + ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize); goto z_bail; } - compRemaining -= getSize; + compRemaining -= nextSize; - zstream.next_in = readBuf; - zstream.avail_in = getSize; + zstream.next_in = nextBuffer; + zstream.avail_in = nextSize; } /* uncompress the data */ @@ -138,121 +145,99 @@ z_bail: inflateEnd(&zstream); /* free up any allocated structures */ bail: - delete[] readBuf; return result; } -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * (This is a clone of the previous function, but it takes a FILE* instead - * of an fd. We could pass fileno(fd) to the above, but we can run into - * trouble when "fp" has a different notion of what fd's file position is.) - * - * "fp" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; +class FileReader { +public: + FileReader(FILE* fp) : + mFp(fp), mReadBuf(new unsigned char[kReadBufSize]) + { + } + + ~FileReader() { + delete[] mReadBuf; + } + + long read(unsigned char** nextBuffer, long readSize) const { + *nextBuffer = mReadBuf; + return fread(mReadBuf, 1, readSize, mFp); + } + + FILE* mFp; + unsigned char* mReadBuf; +}; + +class FdReader { +public: + FdReader(int fd) : + mFd(fd), mReadBuf(new unsigned char[kReadBufSize]) + { + } + + ~FdReader() { + delete[] mReadBuf; + } + + long read(unsigned char** nextBuffer, long readSize) const { + *nextBuffer = mReadBuf; + return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize)); + } + + int mFd; + unsigned char* mReadBuf; +}; + +class BufferReader { +public: + BufferReader(void* input, size_t inputSize) : + mInput(reinterpret_cast<unsigned char*>(input)), + mInputSize(inputSize), + mBufferReturned(false) + { } - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = fread(readBuf, 1, getSize, fp); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; + long read(unsigned char** nextBuffer, long readSize) { + if (!mBufferReturned) { + mBufferReturned = true; + *nextBuffer = mInput; + return mInputSize; } - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } + *nextBuffer = NULL; + return 0; + } - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); + unsigned char* mInput; + const size_t mInputSize; + bool mBufferReturned; +}; - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + FileReader reader(fp); + return ::inflateToBuffer<FileReader>(reader, buf, + uncompressedLen, compressedLen); +} - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + FdReader reader(fd); + return ::inflateToBuffer<FdReader>(reader, buf, + uncompressedLen, compressedLen); +} - // success! - result = true; +/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf, + long uncompressedLen, long compressedLen) +{ + BufferReader reader(in, compressedLen); + return ::inflateToBuffer<BufferReader>(reader, buf, + uncompressedLen, compressedLen); +} -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ -bail: - delete[] readBuf; - return result; -} /* * Look at the contents of a gzip archive. We want to know where the @@ -338,8 +323,8 @@ bail: fseek(fp, curPosn, SEEK_SET); *pCompressionMethod = method; - *pCRC32 = ZipFileRO::get4LE(&buf[0]); - *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + *pCRC32 = get4LE(&buf[0]); + *pUncompressedLen = get4LE(&buf[4]); return true; } diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 0522212..3c55375 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ - ZipFileRO_test.cpp + ZipUtils_test.cpp shared_libraries := \ libandroidfw \ diff --git a/libs/androidfw/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipUtils_test.cpp index cb9c721..c6038b5 100644 --- a/libs/androidfw/tests/ZipFileRO_test.cpp +++ b/libs/androidfw/tests/ZipUtils_test.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#define LOG_TAG "ZipFileRO_test" +#define LOG_TAG "ZipUtils_test" #include <utils/Log.h> -#include <androidfw/ZipFileRO.h> +#include <androidfw/ZipUtils.h> #include <gtest/gtest.h> @@ -25,7 +25,7 @@ namespace android { -class ZipFileROTest : public testing::Test { +class ZipUtilsTest : public testing::Test { protected: virtual void SetUp() { } @@ -34,13 +34,13 @@ protected: } }; -TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { +TEST_F(ZipUtilsTest, ZipTimeConvertSuccess) { struct tm t; // 2011-06-29 14:40:40 long when = 0x3EDD7514; - ZipFileRO::zipTimeToTimespec(when, &t); + ZipUtils::zipTimeToTimespec(when, &t); EXPECT_EQ(2011, t.tm_year + 1900) << "Year was improperly converted."; |