diff options
| author | Narayan Kamath <narayan@google.com> | 2013-12-13 10:48:38 +0000 |
|---|---|---|
| committer | Narayan Kamath <narayan@google.com> | 2013-12-13 14:24:20 +0000 |
| commit | ac4202a846965a6b5a88001a8ad3d5321edeb81c (patch) | |
| tree | 18b1f420ba0c5cb25df9273ff4a729931669cfd8 | |
| parent | 0433badc9b593cba85ec4e1f60ad1d75d1e99757 (diff) | |
| download | android_dalvik-ac4202a846965a6b5a88001a8ad3d5321edeb81c.tar.gz android_dalvik-ac4202a846965a6b5a88001a8ad3d5321edeb81c.tar.bz2 android_dalvik-ac4202a846965a6b5a88001a8ad3d5321edeb81c.zip | |
Use libziparchive for dalvik zip processing.
The API was similar enough that we could just delegate
to existing zip functions. The existing ZipArchiveHandle /
ZipEntry types have been reused instead of defining new
types.
The old ZipArchive type has been removed because it serves
no useful purpose, and no code was using it anyway.
Change-Id: I47f12344d1e6efc234fed50b1613f9f179a9b03f
| -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); } |
