diff options
-rw-r--r-- | cmds/bootanimation/BootAnimation.cpp | 98 | ||||
-rw-r--r-- | cmds/bootanimation/BootAnimation.h | 3 | ||||
-rw-r--r-- | core/jni/com_android_internal_content_NativeLibraryHelper.cpp | 32 | ||||
-rw-r--r-- | include/androidfw/ZipFileRO.h | 172 | ||||
-rw-r--r-- | include/androidfw/ZipUtils.h | 18 | ||||
-rw-r--r-- | libs/androidfw/Android.mk | 6 | ||||
-rw-r--r-- | libs/androidfw/Asset.cpp | 4 | ||||
-rw-r--r-- | libs/androidfw/AssetManager.cpp | 44 | ||||
-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 | ||||
-rw-r--r-- | tools/aapt/Android.mk | 3 |
13 files changed, 386 insertions, 1193 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0f610e9..7b4e6ee 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -63,14 +63,19 @@ extern "C" int clock_nanosleep(clockid_t clock_id, int flags, namespace android { +static const int ANIM_ENTRY_NAME_MAX = 256; + // --------------------------------------------------------------------------- -BootAnimation::BootAnimation() : Thread(false) +BootAnimation::BootAnimation() : Thread(false), mZip(NULL) { mSession = new SurfaceComposerClient(); } BootAnimation::~BootAnimation() { + if (mZip != NULL) { + delete mZip; + } } void BootAnimation::onFirstRef() { @@ -86,7 +91,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const { } -void BootAnimation::binderDied(const wp<IBinder>& who) +void BootAnimation::binderDied(const wp<IBinder>&) { // woah, surfaceflinger died! ALOGD("SurfaceFlinger died, exiting..."); @@ -268,8 +273,6 @@ status_t BootAnimation::readyToRun() { mFlingerSurfaceControl = control; mFlingerSurface = s; - mAndroidAnimation = true; - // If the device has encryption turned on or is in process // of being encrypted we show the encrypted boot animation. char decrypt[PROPERTY_VALUE_MAX]; @@ -277,16 +280,17 @@ status_t BootAnimation::readyToRun() { bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); + ZipFileRO* zipFile = NULL; if ((encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || + ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) || ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || + ((zipFile = ZipFileRO::open(USER_BOOTANIMATION_FILE)) != NULL)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { - mAndroidAnimation = false; + ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) { + mZip = zipFile; } return NO_ERROR; @@ -295,7 +299,9 @@ status_t BootAnimation::readyToRun() { bool BootAnimation::threadLoop() { bool r; - if (mAndroidAnimation) { + // We have no bootanimation file, so we use the stock android logo + // animation. + if (mZip == NULL) { r = android(); } else { r = movie(); @@ -392,11 +398,14 @@ void BootAnimation::checkExit() { bool BootAnimation::movie() { - ZipFileRO& zip(mZip); + ZipEntryRO desc = mZip->findEntryByName("desc.txt"); + ALOGE_IF(!desc, "couldn't find desc.txt"); + if (!desc) { + return false; + } - size_t numEntries = zip.getNumEntries(); - ZipEntryRO desc = zip.findEntryByName("desc.txt"); - FileMap* descMap = zip.createEntryFileMap(desc); + FileMap* descMap = mZip->createEntryFileMap(desc); + mZip->releaseEntry(desc); ALOGE_IF(!descMap, "descMap is null"); if (!descMap) { return false; @@ -415,7 +424,7 @@ bool BootAnimation::movie() String8 line(s, endl - s); const char* l = line.string(); int fps, width, height, count, pause; - char path[256]; + char path[ANIM_ENTRY_NAME_MAX]; char pathType; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { //LOGD("> w=%d, h=%d, fps=%d", width, height, fps); @@ -438,28 +447,37 @@ bool BootAnimation::movie() // read all the data structures const size_t pcount = animation.parts.size(); - for (size_t i=0 ; i<numEntries ; i++) { - char name[256]; - ZipEntryRO entry = zip.findEntryByIndex(i); - if (zip.getEntryFileName(entry, name, 256) == 0) { - const String8 entryName(name); - const String8 path(entryName.getPathDir()); - const String8 leaf(entryName.getPathLeaf()); - if (leaf.size() > 0) { - for (int j=0 ; j<pcount ; j++) { - if (path == animation.parts[j].path) { - int method; - // supports only stored png files - if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) { - if (method == ZipFileRO::kCompressStored) { - FileMap* map = zip.createEntryFileMap(entry); - if (map) { - Animation::Frame frame; - frame.name = leaf; - frame.map = map; - Animation::Part& part(animation.parts.editItemAt(j)); - part.frames.add(frame); - } + void *cookie = NULL; + if (!mZip->startIteration(&cookie)) { + return false; + } + + ZipEntryRO entry; + char name[ANIM_ENTRY_NAME_MAX]; + while ((entry = mZip->nextEntry(cookie)) != NULL) { + const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); + if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) { + ALOGE("Error fetching entry file name"); + continue; + } + + const String8 entryName(name); + const String8 path(entryName.getPathDir()); + const String8 leaf(entryName.getPathLeaf()); + if (leaf.size() > 0) { + for (size_t j=0 ; j<pcount ; j++) { + if (path == animation.parts[j].path) { + int method; + // supports only stored png files + if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { + if (method == ZipFileRO::kCompressStored) { + FileMap* map = mZip->createEntryFileMap(entry); + if (map) { + Animation::Frame frame; + frame.name = leaf; + frame.map = map; + Animation::Part& part(animation.parts.editItemAt(j)); + part.frames.add(frame); } } } @@ -468,6 +486,8 @@ bool BootAnimation::movie() } } + mZip->endIteration(cookie); + // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); @@ -494,7 +514,7 @@ bool BootAnimation::movie() Region clearReg(Rect(mWidth, mHeight)); clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); - for (int i=0 ; i<pcount ; i++) { + for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); glBindTexture(GL_TEXTURE_2D, 0); @@ -504,7 +524,7 @@ bool BootAnimation::movie() if(exitPending() && !part.playUntilComplete) break; - for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { + for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { const Animation::Frame& frame(part.frames[j]); nsecs_t lastFrame = systemTime(); @@ -564,7 +584,7 @@ bool BootAnimation::movie() // free the textures for this part if (part.count != 1) { - for (int j=0 ; j<fcount ; j++) { + for (size_t j=0 ; j<fcount ; j++) { const Animation::Frame& frame(part.frames[j]); glDeleteTextures(1, &frame.tid); } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index fa908eb..22963c2 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -95,8 +95,7 @@ private: EGLDisplay mSurface; sp<SurfaceControl> mFlingerSurfaceControl; sp<Surface> mFlingerSurface; - bool mAndroidAnimation; - ZipFileRO mZip; + ZipFileRO *mZip; }; // --------------------------------------------------------------------------- diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index bf5accd..00da0f7 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -21,7 +21,9 @@ #include <utils/Log.h> #include <androidfw/ZipFileRO.h> +#include <androidfw/ZipUtils.h> #include <ScopedUtfChars.h> +#include <UniquePtr.h> #include <zlib.h> @@ -143,7 +145,7 @@ isFileDifferent(const char* filePath, size_t fileSize, time_t modifiedTime, } static install_status_t -sumFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName) +sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char*) { size_t* total = (size_t*) arg; size_t uncompLen; @@ -178,7 +180,7 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_INVALID_APK; } else { struct tm t; - ZipFileRO::zipTimeToTimespec(when, &t); + ZipUtils::zipTimeToTimespec(when, &t); modTime = mktime(&t); } @@ -273,26 +275,25 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js ScopedUtfChars cpuAbi(env, javaCpuAbi); ScopedUtfChars cpuAbi2(env, javaCpuAbi2); - ZipFileRO zipFile; - - if (zipFile.open(filePath.c_str()) != NO_ERROR) { + UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str())); + if (zipFile.get() == NULL) { ALOGI("Couldn't open APK %s\n", filePath.c_str()); return INSTALL_FAILED_INVALID_APK; } - const int N = zipFile.getNumEntries(); - char fileName[PATH_MAX]; bool hasPrimaryAbi = false; - for (int i = 0; i < N; i++) { - const ZipEntryRO entry = zipFile.findEntryByIndex(i); - if (entry == NULL) { - continue; - } + void* cookie = NULL; + if (!zipFile->startIteration(&cookie)) { + ALOGI("Couldn't iterate over APK%s\n", filePath.c_str()); + return INSTALL_FAILED_INVALID_APK; + } + ZipEntryRO entry = NULL; + while ((entry = zipFile->nextEntry(cookie)) != NULL) { // Make sure this entry has a filename. - if (zipFile.getEntryFileName(entry, fileName, sizeof(fileName))) { + if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) { continue; } @@ -346,15 +347,18 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js && isFilenameSafe(lastSlash + 1)) || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) { - install_status_t ret = callFunc(env, callArg, &zipFile, entry, lastSlash + 1); + install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1); if (ret != INSTALL_SUCCEEDED) { ALOGV("Failure for entry %s", lastSlash + 1); + zipFile->endIteration(cookie); return ret; } } } + zipFile->endIteration(cookie); + return INSTALL_SUCCEEDED; } diff --git a/include/androidfw/ZipFileRO.h b/include/androidfw/ZipFileRO.h index 547e36a..ad5be12 100644 --- a/include/androidfw/ZipFileRO.h +++ b/include/androidfw/ZipFileRO.h @@ -40,6 +40,8 @@ #include <unistd.h> #include <time.h> +typedef void* ZipArchiveHandle; + namespace android { /* @@ -51,18 +53,13 @@ typedef void* ZipEntryRO; /* * Open a Zip archive for reading. * - * We want "open" and "find entry by name" to be fast operations, and we - * want to use as little memory as possible. We memory-map the file, - * and load a hash table with pointers to the filenames (which aren't - * null-terminated). The other fields are at a fixed offset from the - * filename, so we don't need to extract those (but we do need to byte-read - * and endian-swap them every time we want them). + * Implemented as a thin wrapper over system/core/libziparchive. + * + * "open" and "find entry by name" are fast operations and use as little + * memory as possible. * - * To speed comparisons when doing a lookup by name, we could make the mapping - * "private" (copy-on-write) and null-terminate the filenames after verifying - * the record structure. However, this requires a private mapping of - * every page that the Central Directory touches. Easier to tuck a copy - * of the string length into the hash table entry. + * We also support fast iteration over all entries in the file (with a + * stable, but unspecified iteration order). * * NOTE: If this is used on file descriptors inherited from a fork() operation, * you must be on a platform that implements pread() to guarantee correctness @@ -70,48 +67,44 @@ typedef void* ZipEntryRO; */ class ZipFileRO { public: - ZipFileRO() - : mFd(-1), mFileName(NULL), mFileLength(-1), - mDirectoryMap(NULL), - mNumEntries(-1), mDirectoryOffset(-1), - mHashTableSize(-1), mHashTable(NULL) - {} - - ~ZipFileRO(); + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; /* * Open an archive. */ - status_t open(const char* zipFileName); + static ZipFileRO* open(const char* zipFileName); /* * Find an entry, by name. Returns the entry identifier, or NULL if * not found. - * - * If two entries have the same name, one will be chosen at semi-random. */ - ZipEntryRO findEntryByName(const char* fileName) const; + ZipEntryRO findEntryByName(const char* entryName) const; + /* - * Return the #of entries in the Zip archive. + * Start iterating over the list of entries in the zip file. Requires + * a matching call to endIteration with the same cookie. */ - int getNumEntries(void) const { - return mNumEntries; - } + bool startIteration(void** cookie); + + /** + * Return the next entry in iteration order, or NULL if there are no more + * entries in this archive. + */ + ZipEntryRO nextEntry(void* cookie); + + void endIteration(void* cookie); + + void releaseEntry(ZipEntryRO entry) const; /* - * Return the Nth entry. Zip file entries are not stored in sorted - * order, and updated entries may appear at the end, so anyone walking - * the archive needs to avoid making ordering assumptions. We take - * that further by returning the Nth non-empty entry in the hash table - * rather than the Nth entry in the archive. - * - * Valid values are [0..numEntries). - * - * [This is currently O(n). If it needs to be fast we can allocate an - * additional data structure or provide an iterator interface.] + * Return the #of entries in the Zip archive. */ - ZipEntryRO findEntryByIndex(int idx) const; + int getNumEntries(); /* * Copy the filename into the supplied buffer. Returns 0 on success, @@ -149,112 +142,27 @@ public: * * Returns "true" on success. */ - bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + bool uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const; /* * Uncompress the data to an open file descriptor. */ bool uncompressEntry(ZipEntryRO entry, int fd) const; - /* Zip compression methods we support */ - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - /* - * Utility function: uncompress deflated data, buffer to buffer. - */ - static bool inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function: uncompress deflated data, buffer to fd. - */ - static bool inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function to convert ZIP's time format to a timespec struct. - */ - static inline void zipTimeToTimespec(long when, struct tm* timespec) { - const long date = when >> 16; - timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 - timespec->tm_mon = (date >> 5) & 0x0F; - timespec->tm_mday = date & 0x1F; - - timespec->tm_hour = (when >> 11) & 0x1F; - timespec->tm_min = (when >> 5) & 0x3F; - timespec->tm_sec = (when & 0x1F) << 1; - } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short get2LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } + ~ZipFileRO(); private: - /* these are private and not defined */ + /* these are private and not defined */ ZipFileRO(const ZipFileRO& src); ZipFileRO& operator=(const ZipFileRO& src); - /* locate and parse the central directory */ - bool mapCentralDirectory(void); - - /* parse the archive, prepping internal structures */ - bool parseZipArchive(void); - - /* add a new entry to the hash table */ - void addToHash(const char* str, int strLen, unsigned int hash); - - /* compute string hash code */ - static unsigned int computeHash(const char* str, int len); - - /* convert a ZipEntryRO back to a hash table index */ - int entryToIndex(const ZipEntryRO entry) const; - - /* - * One entry in the hash table. - */ - typedef struct HashEntry { - const char* name; - unsigned short nameLen; - //unsigned int hash; - } HashEntry; - - /* open Zip archive */ - int mFd; - - /* Lock for handling the file descriptor (seeks, etc) */ - mutable Mutex mFdLock; - - /* zip file name */ - char* mFileName; - - /* length of file */ - size_t mFileLength; - - /* mapped file */ - FileMap* mDirectoryMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* CD directory offset in the Zip archive */ - off64_t mDirectoryOffset; + ZipFileRO(ZipArchiveHandle handle, char* fileName) : mHandle(handle), + mFileName(fileName) + { + } - /* - * We know how many entries are in the Zip archive, so we have a - * fixed-size hash table. We probe for an empty slot. - */ - int mHashTableSize; - HashEntry* mHashTable; + const ZipArchiveHandle mHandle; + char* mFileName; }; }; // namespace android diff --git a/include/androidfw/ZipUtils.h b/include/androidfw/ZipUtils.h index 42c42b6..6bea25a 100644 --- a/include/androidfw/ZipUtils.h +++ b/include/androidfw/ZipUtils.h @@ -21,6 +21,7 @@ #define __LIBS_ZIPUTILS_H #include <stdio.h> +#include <time.h> namespace android { @@ -33,9 +34,11 @@ public: * General utility function for uncompressing "deflate" data from a file * to a buffer. */ + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, long compressedLen); - static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + static bool inflateToBuffer(void *in, void* buf, long uncompressedLen, long compressedLen); /* @@ -57,6 +60,19 @@ public: static bool examineGzip(FILE* fp, int* pCompressionMethod, long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + /* + * Utility function to convert ZIP's time format to a timespec struct. + */ + static inline void zipTimeToTimespec(long when, struct tm* timespec) { + const long date = when >> 16; + timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 + timespec->tm_mon = (date >> 5) & 0x0F; + timespec->tm_mday = date & 0x1F; + + timespec->tm_hour = (when >> 11) & 0x1F; + timespec->tm_min = (when >> 5) & 0x3F; + timespec->tm_sec = (when & 0x1F) << 1; + } private: ZipUtils() {} ~ZipUtils() {} diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d80612b..ba13d51 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -54,6 +54,7 @@ LOCAL_C_INCLUDES := \ external/zlib LOCAL_STATIC_LIBRARIES := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive-host include $(BUILD_HOST_STATIC_LIBRARY) @@ -72,9 +73,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 1066715..503c030 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -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, @@ -821,16 +822,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); } } @@ -975,17 +974,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); } } @@ -1487,11 +1484,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 +1543,8 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg } } + pZip->endIteration(iterationCookie); + /* * Add the set of unique directories. */ @@ -1814,12 +1818,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/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."; diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 452c60a..2949f8e 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -50,7 +50,8 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ libexpat \ libpng \ - liblog + liblog \ + libziparchive-host ifeq ($(HOST_OS),linux) LOCAL_LDLIBS += -lrt -ldl -lpthread |