diff options
-rw-r--r-- | dexdump/Android.mk | 3 | ||||
-rw-r--r-- | dexlist/Android.mk | 3 | ||||
-rw-r--r-- | dexopt/OptMain.cpp | 34 | ||||
-rw-r--r-- | libdex/Android.mk | 6 | ||||
-rw-r--r-- | libdex/CmdUtils.cpp | 7 | ||||
-rw-r--r-- | libdex/ZipArchive.cpp | 813 | ||||
-rw-r--r-- | libdex/ZipArchive.h | 136 | ||||
-rw-r--r-- | vm/Android.mk | 1 | ||||
-rw-r--r-- | vm/Dalvik.h | 1 | ||||
-rw-r--r-- | vm/JarFile.cpp | 32 | ||||
-rw-r--r-- | vm/JarFile.h | 4 | ||||
-rw-r--r-- | vm/ReconfigureDvm.mk | 1 | ||||
-rw-r--r-- | vm/oo/Class.cpp | 4 |
13 files changed, 63 insertions, 982 deletions
diff --git a/dexdump/Android.mk b/dexdump/Android.mk index b825146d6..cb0564b0f 100644 --- a/dexdump/Android.mk +++ b/dexdump/Android.mk @@ -26,7 +26,8 @@ dexdump_c_includes := \ dexdump_shared_libraries := dexdump_static_libraries := \ - libdex + libdex \ + libutils ## ## diff --git a/dexlist/Android.mk b/dexlist/Android.mk index 62b00874a..e13b82507 100644 --- a/dexlist/Android.mk +++ b/dexlist/Android.mk @@ -26,7 +26,8 @@ dexdump_c_includes := \ dexdump_shared_libraries := dexdump_static_libraries := \ - libdex + libdex \ + libutils include $(CLEAR_VARS) LOCAL_MODULE := dexlist diff --git a/dexopt/OptMain.cpp b/dexopt/OptMain.cpp index 3cdf5bea2..f948ff4c9 100644 --- a/dexopt/OptMain.cpp +++ b/dexopt/OptMain.cpp @@ -58,10 +58,8 @@ static int extractAndProcessZip(int zipFd, int cacheFd, const char* debugFileName, bool isBootstrap, const char* bootClassPath, const char* dexoptFlagStr) { - ZipArchive zippy; + ZipArchiveHandle zippy; ZipEntry zipEntry; - size_t uncompLen; - long modWhen, crc32; off_t dexOffset; int err; int result = -1; @@ -69,8 +67,6 @@ static int extractAndProcessZip(int zipFd, int cacheFd, DexClassVerifyMode verifyMode = VERIFY_MODE_ALL; DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED; - memset(&zippy, 0, sizeof(zippy)); - /* make sure we're still at the start of an empty file */ if (lseek(cacheFd, 0, SEEK_END) != 0) { ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName); @@ -93,36 +89,21 @@ static int extractAndProcessZip(int zipFd, int cacheFd, /* * Open the zip archive, find the DEX entry. */ - if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) { + if (dexZipOpenArchiveFd(zipFd, debugFileName, &zippy) != 0) { ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName); goto bail; } - zipEntry = dexZipFindEntry(&zippy, kClassesDex); - if (zipEntry == NULL) { + if (dexZipFindEntry(zippy, kClassesDex, &zipEntry) != 0) { ALOGW("DexOptZ: zip archive '%s' does not include %s", debugFileName, kClassesDex); goto bail; } /* - * Extract some info about the zip entry. - */ - if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL, - &modWhen, &crc32) != 0) - { - ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName); - goto bail; - } - - uncompLen = uncompLen; - modWhen = modWhen; - crc32 = crc32; - - /* * Extract the DEX data into the cache file at the current offset. */ - if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) { + if (dexZipExtractEntryToFile(zippy, &zipEntry, cacheFd) != 0) { ALOGW("DexOptZ: extraction of %s from %s failed", kClassesDex, debugFileName); goto bail; @@ -183,8 +164,9 @@ static int extractAndProcessZip(int zipFd, int cacheFd, //vmStarted = 1; /* do the optimization */ - if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName, - modWhen, crc32, isBootstrap)) + if (!dvmContinueOptimization(cacheFd, dexOffset, + zipEntry.uncompressed_length, debugFileName, + zipEntry.mod_time, zipEntry.crc32, isBootstrap)) { ALOGE("Optimization failed"); goto bail; @@ -195,7 +177,7 @@ static int extractAndProcessZip(int zipFd, int cacheFd, result = 0; bail: - dexZipCloseArchive(&zippy); + dexZipCloseArchive(zippy); return result; } diff --git a/libdex/Android.mk b/libdex/Android.mk index 7ab00139f..0a4639627 100644 --- a/libdex/Android.mk +++ b/libdex/Android.mk @@ -32,7 +32,6 @@ dex_src_files := \ OptInvocation.cpp \ sha1.cpp \ SysUtil.cpp \ - ZipArchive.cpp dex_include_files := \ dalvik \ @@ -51,6 +50,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(dex_src_files) LOCAL_C_INCLUDES += $(dex_include_files) LOCAL_STATIC_LIBRARIES := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive +LOCAL_SHARED_LIBRARIES := libutils LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libdex include $(BUILD_STATIC_LIBRARY) @@ -66,7 +67,8 @@ endif # !SDK_ONLY include $(CLEAR_VARS) LOCAL_SRC_FILES := $(dex_src_files) LOCAL_C_INCLUDES += $(dex_include_files) -LOCAL_STATIC_LIBRARIES := liblog +LOCAL_STATIC_LIBRARIES := liblog libutils +LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive-host LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libdex include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libdex/CmdUtils.cpp b/libdex/CmdUtils.cpp index ff737a362..bf89444cc 100644 --- a/libdex/CmdUtils.cpp +++ b/libdex/CmdUtils.cpp @@ -40,7 +40,7 @@ UnzipToFileResult dexUnzipToFile(const char* zipFileName, { UnzipToFileResult result = kUTFRSuccess; static const char* kFileToExtract = "classes.dex"; - ZipArchive archive; + ZipArchiveHandle archive; ZipEntry entry; bool unlinkOnFailure = false; int fd = -1; @@ -64,8 +64,7 @@ UnzipToFileResult dexUnzipToFile(const char* zipFileName, unlinkOnFailure = true; - entry = dexZipFindEntry(&archive, kFileToExtract); - if (entry == NULL) { + if (dexZipFindEntry(archive, kFileToExtract, &entry) != 0) { if (!quiet) { fprintf(stderr, "Unable to find '%s' in '%s'\n", kFileToExtract, zipFileName); @@ -74,7 +73,7 @@ UnzipToFileResult dexUnzipToFile(const char* zipFileName, goto bail; } - if (dexZipExtractEntryToFile(&archive, entry, fd) != 0) { + if (dexZipExtractEntryToFile(archive, &entry, fd) != 0) { fprintf(stderr, "Extract of '%s' from '%s' failed\n", kFileToExtract, zipFileName); result = kUTFRBadZip; diff --git a/libdex/ZipArchive.cpp b/libdex/ZipArchive.cpp deleted file mode 100644 index f70a5dfbd..000000000 --- a/libdex/ZipArchive.cpp +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Read-only access to Zip archives, with minimal heap allocation. - */ -#include "ZipArchive.h" - -#include <zlib.h> - -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> - -#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd -#include <utils/Compat.h> // For off64_t and lseek64 on Mac - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -/* - * 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) - -/* - * 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 - -/* - * Convert a ZipEntry to a hash table index, verifying that it's in a - * valid range. - */ -static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry) -{ - long ent = ((long) entry) - kZipEntryAdj; - if (ent < 0 || ent >= pArchive->mHashTableSize || - pArchive->mHashTable[ent].name == NULL) - { - ALOGW("Zip: invalid ZipEntry %p (%ld)", entry, ent); - return -1; - } - return ent; -} - -/* - * Simple string hash function for non-null-terminated strings. - */ -static unsigned int computeHash(const char* str, int len) -{ - unsigned int hash = 0; - - while (len--) - hash = hash * 31 + *str++; - - return hash; -} - -/* - * Add a new entry to the hash table. - */ -static void addToHash(ZipArchive* pArchive, const char* str, int strLen, - unsigned int hash) -{ - const int hashTableSize = pArchive->mHashTableSize; - int ent = hash & (hashTableSize - 1); - - /* - * We over-allocated the table, so we're guaranteed to find an empty slot. - */ - while (pArchive->mHashTable[ent].name != NULL) - ent = (ent + 1) & (hashTableSize-1); - - pArchive->mHashTable[ent].name = str; - pArchive->mHashTable[ent].nameLen = strLen; -} - -/* - * Get 2 little-endian bytes. - */ -static u2 get2LE(unsigned char const* pSrc) -{ - return pSrc[0] | (pSrc[1] << 8); -} - -/* - * Get 4 little-endian bytes. - */ -static u4 get4LE(unsigned char const* pSrc) -{ - u4 result; - - result = pSrc[0]; - result |= pSrc[1] << 8; - result |= pSrc[2] << 16; - result |= pSrc[3] << 24; - - return result; -} - -static int mapCentralDirectory0(int fd, const char* debugFileName, - ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf) -{ - /* - * Make sure this is a Zip archive. - */ - if (lseek64(pArchive->mFd, 0, SEEK_SET) != 0) { - ALOGW("seek to start failed: %s", strerror(errno)); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, sizeof(int32_t))); - if (actual != (ssize_t) sizeof(int32_t)) { - ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); - return false; - } - - unsigned int header = get4LE(scanBuf); - if (header != kLFHSignature) { - ALOGV("Not a Zip archive (found 0x%08x)\n", header); - return false; - } - - /* - * 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 = fileLength - readAmount; - - if (lseek64(pArchive->mFd, searchStart, SEEK_SET) != searchStart) { - ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); - return false; - } - actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, readAmount)); - if (actual != (ssize_t) readAmount) { - ALOGW("Zip: read %zd, expected %zd. Failed: %s\n", - actual, readAmount, strerror(errno)); - 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", i); - break; - } - } - if (i < 0) { - ALOGD("Zip: EOCD not found, %s is not zip", debugFileName); - return -1; - } - - off64_t eocdOffset = searchStart + i; - const u1* eocdPtr = scanBuf + i; - - assert(eocdOffset < fileLength); - - /* - * Grab the CD offset and size, and the number of entries in the - * archive. Verify that they look reasonable. - */ - u4 diskNumber = get2LE(eocdPtr + kEOCDDiskNumber); - u4 diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD); - u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); - u4 totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries); - u4 centralDirSize = get4LE(eocdPtr + kEOCDSize); - u4 centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset); - u4 commentSize = get2LE(eocdPtr + kEOCDCommentSize); - - // 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 (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 (((size_t) commentSize > (fileLength - kEOCDLen)) - || (eocdOffset > (fileLength - kEOCDLen) - commentSize)) { - ALOGW("comment size runs off end of file"); - return false; - } - - ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", - numEntries, centralDirSize, centralDirOffset); - - /* - * It all looks good. Create a mapping for the CD, and set the fields - * in pArchive. - */ - if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize, - &pArchive->mDirectoryMap) != 0) - { - ALOGW("Zip: cd map failed"); - return -1; - } - - pArchive->mNumEntries = numEntries; - pArchive->mDirectoryOffset = centralDirOffset; - - return 0; -} - -/* - * Find the zip Central Directory and memory-map it. - * - * On success, returns 0 after populating fields from the EOCD area: - * mDirectoryOffset - * mDirectoryMap - * mNumEntries - */ -static int mapCentralDirectory(int fd, const char* debugFileName, - ZipArchive* pArchive) -{ - /* - * Get and test file length. - */ - off64_t fileLength = lseek64(fd, 0, SEEK_END); - if (fileLength < kEOCDLen) { - ALOGV("Zip: length %ld is too small to be zip", (long) fileLength); - return -1; - } - - /* - * 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. - */ - size_t readAmount = kMaxEOCDSearch; - if (fileLength < off_t(readAmount)) - readAmount = fileLength; - - u1* scanBuf = (u1*) malloc(readAmount); - if (scanBuf == NULL) { - return -1; - } - - int result = mapCentralDirectory0(fd, debugFileName, pArchive, - fileLength, readAmount, scanBuf); - - free(scanBuf); - return result; -} - -/* - * Parses the Zip archive's Central Directory. Allocates and populates the - * hash table. - * - * Returns 0 on success. - */ -static int parseZipArchive(ZipArchive* pArchive) -{ - int result = -1; - const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; - size_t cdLength = pArchive->mDirectoryMap.length; - int numEntries = pArchive->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. There must be at - * least one unused entry to avoid an infinite loop during creation. - */ - pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3); - pArchive->mHashTable = (ZipHashEntry*) - calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry)); - - /* - * Walk through the central directory, adding entries to the hash - * table and verifying values. - */ - const u1* ptr = cdPtr; - int i; - for (i = 0; i < numEntries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Zip: missed a central dir sig (at %d)", i); - goto bail; - } - if (ptr + kCDELen > cdPtr + cdLength) { - ALOGW("Zip: ran off the end (at %d)", i); - goto bail; - } - - long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset >= pArchive->mDirectoryOffset) { - ALOGW("Zip: bad LFH offset %ld at entry %d", 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, extraLen, commentLen, hash; - nameLen = get2LE(ptr + kCDENameLen); - extraLen = get2LE(ptr + kCDEExtraLen); - 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 */ - hash = computeHash(name, nameLen); - addToHash(pArchive, 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("Zip: bad CD advance (%d vs %zd) at entry %d", - (int) (ptr - cdPtr), cdLength, i); - goto bail; - } - } - ALOGV("+++ zip good scan %d entries", numEntries); - - result = 0; - -bail: - return result; -} - -/* - * Open the specified file read-only. We examine the contents and verify - * that it appears to be a valid zip file. - * - * This will be called on non-Zip files, especially during VM startup, so - * we don't want to be too noisy about certain types of failure. (Do - * we want a "quiet" flag?) - * - * On success, we fill out the contents of "pArchive" and return 0. On - * failure we return the errno value. - */ -int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive) -{ - int fd, err; - - ALOGV("Opening as zip '%s' %p", fileName, pArchive); - - memset(pArchive, 0, sizeof(ZipArchive)); - - fd = open(fileName, O_RDONLY | O_BINARY, 0); - if (fd < 0) { - err = errno ? errno : -1; - ALOGV("Unable to open '%s': %s", fileName, strerror(err)); - return err; - } - - return dexZipPrepArchive(fd, fileName, pArchive); -} - -/* - * Prepare to access a ZipArchive through an open file descriptor. - * - * On success, we fill out the contents of "pArchive" and return 0. - */ -int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive) -{ - int result = -1; - - memset(pArchive, 0, sizeof(*pArchive)); - pArchive->mFd = fd; - - if (mapCentralDirectory(fd, debugFileName, pArchive) != 0) - goto bail; - - if (parseZipArchive(pArchive) != 0) { - ALOGV("Zip: parsing '%s' failed", debugFileName); - goto bail; - } - - /* success */ - result = 0; - -bail: - if (result != 0) - dexZipCloseArchive(pArchive); - return result; -} - - -/* - * Close a ZipArchive, closing the file and freeing the contents. - * - * NOTE: the ZipArchive may not have been fully created. - */ -void dexZipCloseArchive(ZipArchive* pArchive) -{ - ALOGV("Closing archive %p", pArchive); - - if (pArchive->mFd >= 0) - close(pArchive->mFd); - - sysReleaseShmem(&pArchive->mDirectoryMap); - - free(pArchive->mHashTable); - - /* ensure nobody tries to use the ZipArchive after it's closed */ - pArchive->mDirectoryOffset = -1; - pArchive->mFd = -1; - pArchive->mNumEntries = -1; - pArchive->mHashTableSize = -1; - pArchive->mHashTable = NULL; -} - - -/* - * Find a matching entry. - * - * Returns 0 if not found. - */ -ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName) -{ - int nameLen = strlen(entryName); - unsigned int hash = computeHash(entryName, nameLen); - const int hashTableSize = pArchive->mHashTableSize; - int ent = hash & (hashTableSize-1); - - while (pArchive->mHashTable[ent].name != NULL) { - if (pArchive->mHashTable[ent].nameLen == nameLen && - memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0) - { - /* match */ - return (ZipEntry)(long)(ent + kZipEntryAdj); - } - - ent = (ent + 1) & (hashTableSize-1); - } - - return NULL; -} - -#if 0 -/* - * 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. - */ -ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx) -{ - if (idx < 0 || idx >= pArchive->mNumEntries) { - ALOGW("Invalid index %d", idx); - return NULL; - } - - int ent; - for (ent = 0; ent < pArchive->mHashTableSize; ent++) { - if (pArchive->mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntry) (ent + kZipEntryAdj); - } - } - - return NULL; -} -#endif - -/* - * Get the useful fields from the zip entry. - * - * Returns non-zero if the contents of the fields (particularly the data - * offset) appear to be bogus. - */ -int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry, - int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset, - long* pModWhen, long* pCrc32) -{ - int ent = entryToIndex(pArchive, entry); - if (ent < 0) - return -1; - - /* - * 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* basePtr = (const unsigned char*) - pArchive->mDirectoryMap.addr; - const unsigned char* ptr = (const unsigned char*) - pArchive->mHashTable[ent].name; - off_t cdOffset = pArchive->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 = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= cdOffset) { - ALOGW("Zip: bad local hdr offset in zip"); - return -1; - } - - u1 lfhBuf[kLFHLen]; - if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - ALOGW("Zip: failed seeking to lfh at offset %ld", localHdrOffset); - return -1; - } - ssize_t actual = - TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - ALOGW("Zip: failed reading lfh from offset %ld", localHdrOffset); - return -1; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - ALOGW("Zip: didn't find signature at start of lfh, offset=%ld", - localHdrOffset); - return -1; - } - - u4 gpbf = get2LE(lfhBuf + kLFHGPBFlags); - if ((gpbf & kGPFUnsupportedMask) != 0) { - ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); - return -1; - } - - off64_t dataOffset = localHdrOffset + kLFHLen - + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); - if (dataOffset >= cdOffset) { - ALOGW("Zip: bad data offset %ld in zip", (long) dataOffset); - return -1; - } - - /* check lengths */ - if ((off_t)(dataOffset + compLen) > cdOffset) { - ALOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)", - (long) dataOffset, compLen, (long) cdOffset); - return -1; - } - - if (method == kCompressStored && - (off_t)(dataOffset + uncompLen) > cdOffset) - { - ALOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)", - (long) dataOffset, uncompLen, (long) cdOffset); - return -1; - } - - *pOffset = dataOffset; - } - return 0; -} - -/* - * Uncompress "deflate" data from the archive's file to an open file - * descriptor. - */ -static int inflateToFile(int outFd, int inFd, size_t uncompLen, size_t compLen) -{ - int result = -1; - const size_t kBufSize = 32768; - unsigned char* readBuf = (unsigned char*) malloc(kBufSize); - unsigned char* writeBuf = (unsigned char*) malloc(kBufSize); - z_stream zstream; - int zerr; - - if (readBuf == NULL || writeBuf == NULL) - goto bail; - - /* - * 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 = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = kBufSize; - 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)", - ZLIB_VERSION); - } else { - ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* read as much as we can */ - if (zstream.avail_in == 0) { - size_t getSize = (compLen > kBufSize) ? kBufSize : compLen; - - ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize)); - if (actual != (ssize_t) getSize) { - ALOGW("Zip: inflate read failed (%d vs %zd)", - (int)actual, getSize); - goto z_bail; - } - - compLen -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", - 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 != kBufSize)) - { - size_t writeSize = zstream.next_out - writeBuf; - if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0) - goto z_bail; - - zstream.next_out = writeBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)", - zstream.total_out, uncompLen); - goto z_bail; - } - - result = 0; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - free(readBuf); - free(writeBuf); - return result; -} - -/* - * Uncompress an entry, in its entirety, to an open file descriptor. - * - * TODO: this doesn't verify the data's CRC, but probably should (especially - * for uncompressed data). - */ -int dexZipExtractEntryToFile(const ZipArchive* pArchive, - const ZipEntry entry, int fd) -{ - int result = -1; - int ent = entryToIndex(pArchive, entry); - if (ent < 0) { - ALOGW("Zip: extract can't find entry %p", entry); - goto bail; - } - - int method; - size_t uncompLen, compLen; - off_t dataOffset; - - if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen, - &dataOffset, NULL, NULL) != 0) - { - goto bail; - } - if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) { - ALOGW("Zip: lseek to data at %ld failed", (long) dataOffset); - goto bail; - } - - if (method == kCompressStored) { - if (sysCopyFileToFile(fd, pArchive->mFd, uncompLen) != 0) - goto bail; - } else { - if (inflateToFile(fd, pArchive->mFd, uncompLen, compLen) != 0) - goto bail; - } - - result = 0; - -bail: - return result; -} diff --git a/libdex/ZipArchive.h b/libdex/ZipArchive.h index df5c49a6b..c4794ce0d 100644 --- a/libdex/ZipArchive.h +++ b/libdex/ZipArchive.h @@ -20,75 +20,20 @@ #ifndef LIBDEX_ZIPARCHIVE_H_ #define LIBDEX_ZIPARCHIVE_H_ +#include <ziparchive/zip_archive.h> + #include "SysUtil.h" #include "DexFile.h" // need DEX_INLINE /* - * Trivial typedef to ensure that ZipEntry is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntry; - -/* - * One entry in the hash table. - */ -struct ZipHashEntry { - const char* name; - unsigned short nameLen; -}; - -/* - * Read-only Zip archive. - * - * 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 zip - * central directory, 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). - * - * It's possible that somebody has handed us a massive (~1GB) zip archive, - * so we can't expect to mmap the entire file. - * - * 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. - */ -struct ZipArchive { - /* open Zip archive */ - int mFd; - - /* mapped central directory area */ - off_t mDirectoryOffset; - MemMapping mDirectoryMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* - * We know how many entries are in the Zip archive, so we can have a - * fixed-size hash table. We probe on collisions. - */ - int mHashTableSize; - ZipHashEntry* mHashTable; -}; - -/* Zip compression methods we support */ -enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate -}; - - -/* * Open a Zip archive. * * On success, returns 0 and populates "pArchive". Returns nonzero errno * value on failure. */ -int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive); +DEX_INLINE int dexZipOpenArchive(const char* fileName, ZipArchiveHandle* pArchive) { + return OpenArchive(fileName, pArchive); +} /* * Like dexZipOpenArchive, but takes a file descriptor open for reading @@ -97,7 +42,10 @@ int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive); * * "debugFileName" will appear in error messages, but is not otherwise used. */ -int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive); +DEX_INLINE int dexZipOpenArchiveFd(int fd, const char* debugFileName, + ZipArchiveHandle* pArchive) { + return OpenArchiveFd(fd, debugFileName, pArchive); +} /* * Close archive, releasing resources associated with it. @@ -105,62 +53,24 @@ int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive); * Depending on the implementation this could unmap pages used by classes * stored in a Jar. This should only be done after unloading classes. */ -void dexZipCloseArchive(ZipArchive* pArchive); +DEX_INLINE void dexZipCloseArchive(ZipArchiveHandle archive) { + CloseArchive(archive); +} /* * Return the archive's file descriptor. */ -DEX_INLINE int dexZipGetArchiveFd(const ZipArchive* pArchive) { - return pArchive->mFd; +DEX_INLINE int dexZipGetArchiveFd(const ZipArchiveHandle pArchive) { + return GetFileDescriptor(pArchive); } /* * Find an entry in the Zip archive, by name. Returns NULL if the entry * was not found. */ -ZipEntry dexZipFindEntry(const ZipArchive* pArchive, - const char* entryName); - -/* - * Retrieve one or more of the "interesting" fields. Non-NULL pointers - * are filled in. - * - * Returns 0 on success. - */ -int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry, - int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset, - long* pModWhen, long* pCrc32); - -/* - * Simple accessors. - */ -DEX_INLINE long dexGetZipEntryOffset(const ZipArchive* pArchive, - const ZipEntry entry) -{ - off_t val = 0; - dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, &val, NULL, NULL); - return (long) val; -} -DEX_INLINE size_t dexGetZipEntryUncompLen(const ZipArchive* pArchive, - const ZipEntry entry) -{ - size_t val = 0; - dexZipGetEntryInfo(pArchive, entry, NULL, &val, NULL, NULL, NULL, NULL); - return val; -} -DEX_INLINE long dexGetZipEntryModTime(const ZipArchive* pArchive, - const ZipEntry entry) -{ - long val = 0; - dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, &val, NULL); - return val; -} -DEX_INLINE long dexGetZipEntryCrc32(const ZipArchive* pArchive, - const ZipEntry entry) -{ - long val = 0; - dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, NULL, &val); - return val; +DEX_INLINE int dexZipFindEntry(const ZipArchiveHandle pArchive, + const char* entryName, ZipEntry* data) { + return FindEntry(pArchive, entryName, data); } /* @@ -168,13 +78,9 @@ DEX_INLINE long dexGetZipEntryCrc32(const ZipArchive* pArchive, * * Returns 0 on success. */ -int dexZipExtractEntryToFile(const ZipArchive* pArchive, - const ZipEntry entry, int fd); - -/* - * Utility function to compute a CRC-32. - */ -u4 dexInitCrc32(void); -u4 dexComputeCrc32(u4 crc, const void* buf, size_t len); +DEX_INLINE int dexZipExtractEntryToFile(ZipArchiveHandle handle, + ZipEntry* entry, int fd) { + return ExtractEntryToFile(handle, entry, fd); +} #endif // LIBDEX_ZIPARCHIVE_H_ diff --git a/vm/Android.mk b/vm/Android.mk index a5c58ad76..1c1f91a67 100644 --- a/vm/Android.mk +++ b/vm/Android.mk @@ -135,6 +135,7 @@ ifeq ($(WITH_HOST_DALVIK),true) # time. When building this target as a regular static library, certain # dependencies like expat are not found by the linker. LOCAL_WHOLE_STATIC_LIBRARIES += libexpat libcutils libdex liblog libz + LOCAL_STATIC_LIBRARIES += libutils # The libffi from the source tree should never be used by host builds. # The recommendation is that host builds should always either diff --git a/vm/Dalvik.h b/vm/Dalvik.h index eecbf8d37..27174e72b 100644 --- a/vm/Dalvik.h +++ b/vm/Dalvik.h @@ -30,7 +30,6 @@ #include "libdex/DexFile.h" #include "libdex/DexProto.h" #include "libdex/DexUtf.h" -#include "libdex/ZipArchive.h" #include "DvmDex.h" #include "RawDexFile.h" #include "Sync.h" diff --git a/vm/JarFile.cpp b/vm/JarFile.cpp index 3a037a057..e56f730e3 100644 --- a/vm/JarFile.cpp +++ b/vm/JarFile.cpp @@ -24,6 +24,7 @@ #include "Dalvik.h" #include "libdex/OptInvocation.h" +#include "JarFile.h" #include <stdlib.h> #include <string.h> @@ -85,7 +86,7 @@ bail: */ DexCacheStatus dvmDexCacheStatus(const char *fileName) { - ZipArchive archive; + ZipArchiveHandle archive; char* cachedName = NULL; int fd; DexCacheStatus result = DEX_CACHE_ERROR; @@ -108,8 +109,7 @@ DexCacheStatus dvmDexCacheStatus(const char *fileName) if (dexZipOpenArchive(fileName, &archive) != 0) { return DEX_CACHE_BAD_ARCHIVE; } - entry = dexZipFindEntry(&archive, kDexInJarName); - if (entry != NULL) { + if (dexZipFindEntry(archive, kDexInJarName, &entry) == 0) { bool newFile = false; /* @@ -122,8 +122,7 @@ DexCacheStatus dvmDexCacheStatus(const char *fileName) return DEX_CACHE_BAD_ARCHIVE; fd = dvmOpenCachedDexFile(fileName, cachedName, - dexGetZipEntryModTime(&archive, entry), - dexGetZipEntryCrc32(&archive, entry), + entry.mod_time, entry.crc32, /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); ALOGV("dvmOpenCachedDexFile returned fd %d", fd); if (fd < 0) { @@ -169,7 +168,7 @@ DexCacheStatus dvmDexCacheStatus(const char *fileName) result = DEX_CACHE_OK; bail: - dexZipCloseArchive(&archive); + dexZipCloseArchive(archive); free(cachedName); if (fd >= 0) { close(fd); @@ -193,7 +192,7 @@ int dvmJarFileOpen(const char* fileName, const char* odexOutputName, * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored. */ - ZipArchive archive; + ZipArchiveHandle archive; DvmDex* pDvmDex = NULL; char* cachedName = NULL; bool archiveOpen = false; @@ -210,7 +209,7 @@ int dvmJarFileOpen(const char* fileName, const char* odexOutputName, /* If we fork/exec into dexopt, don't let it inherit the archive's fd. */ - dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); + dvmSetCloseOnExec(dexZipGetArchiveFd(archive)); /* First, look for a ".odex" alongside the jar file. It will * have the same name/path except for the extension. @@ -239,8 +238,7 @@ tryArchive: * Pre-created .odex absent or stale. Look inside the jar for a * "classes.dex". */ - entry = dexZipFindEntry(&archive, kDexInJarName); - if (entry != NULL) { + if (dexZipFindEntry(archive, kDexInJarName, &entry) == 0) { bool newFile = false; /* @@ -265,8 +263,8 @@ tryArchive: ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, - dexGetZipEntryModTime(&archive, entry), - dexGetZipEntryCrc32(&archive, entry), + entry.mod_time, + entry.crc32, isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { ALOGI("Unable to open or create cache for %s (%s)", @@ -291,15 +289,15 @@ tryArchive: if (result) { startWhen = dvmGetRelativeTimeUsec(); - result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; + result = dexZipExtractEntryToFile(archive, &entry, fd) == 0; extractWhen = dvmGetRelativeTimeUsec(); } if (result) { result = dvmOptimizeDexFile(fd, dexOffset, - dexGetZipEntryUncompLen(&archive, entry), + entry.uncompressed_length, fileName, - dexGetZipEntryModTime(&archive, entry), - dexGetZipEntryCrc32(&archive, entry), + entry.mod_time, + entry.crc32, isBootstrap); } @@ -353,7 +351,7 @@ tryArchive: bail: /* clean up, closing the open file */ if (archiveOpen && result != 0) - dexZipCloseArchive(&archive); + dexZipCloseArchive(archive); free(cachedName); if (fd >= 0) { if (locked) diff --git a/vm/JarFile.h b/vm/JarFile.h index d8fd99881..b3a8a0f2e 100644 --- a/vm/JarFile.h +++ b/vm/JarFile.h @@ -19,12 +19,14 @@ #ifndef DALVIK_JARFILE_H_ #define DALVIK_JARFILE_H_ +#include "libdex/ZipArchive.h" + /* * This represents an open, scanned Jar file. (It's actually for any Zip * archive that happens to hold a Dex file.) */ struct JarFile { - ZipArchive archive; + ZipArchiveHandle archive; //MemMapping map; char* cacheFileName; DvmDex* pDvmDex; diff --git a/vm/ReconfigureDvm.mk b/vm/ReconfigureDvm.mk index e6b29d5a7..d7ad1ccc9 100644 --- a/vm/ReconfigureDvm.mk +++ b/vm/ReconfigureDvm.mk @@ -28,6 +28,7 @@ LOCAL_SHARED_LIBRARIES += \ liblog \ libnativehelper \ libselinux \ + libutils \ libz LOCAL_STATIC_LIBRARIES += libdex diff --git a/vm/oo/Class.cpp b/vm/oo/Class.cpp index db5340ef5..904d53db5 100644 --- a/vm/oo/Class.cpp +++ b/vm/oo/Class.cpp @@ -23,6 +23,7 @@ #include "Dalvik.h" #include "libdex/DexClass.h" +#include "libdex/ZipArchive.h" #include "analysis/Optimize.h" #include <stdlib.h> @@ -886,6 +887,7 @@ StringObject* dvmGetBootPathResource(const char* name, int idx) const int kUrlOverhead = 13; // worst case for Jar URL const ClassPathEntry* cpe = gDvm.bootClassPath; StringObject* urlObj = NULL; + ZipEntry entry; ALOGV("+++ searching for resource '%s' in %d(%s)", name, idx, cpe[idx].fileName); @@ -904,7 +906,7 @@ StringObject* dvmGetBootPathResource(const char* name, int idx) case kCpeJar: { JarFile* pJarFile = (JarFile*) cpe->ptr; - if (dexZipFindEntry(&pJarFile->archive, name) == NULL) + if (dexZipFindEntry(pJarFile->archive, name, &entry) != 0) goto bail; sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name); } |