diff options
author | Carl Shapiro <cshapiro@google.com> | 2011-04-15 18:38:06 -0700 |
---|---|---|
committer | Carl Shapiro <cshapiro@google.com> | 2011-04-15 21:18:10 -0700 |
commit | d5c36b9040bd26a81219a7f399513526f9b46324 (patch) | |
tree | 921c49ef9ced8819389ef699ae61296741db71a5 /vm/JarFile.cpp | |
parent | c469fa622ebadfa3defc73a064e2e724f0ab7c75 (diff) | |
download | android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.tar.gz android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.tar.bz2 android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.zip |
Move the remaining non-compiler VM code into C++.
Change-Id: Id8693208d2741c55a7b0474d1264f2112019d11f
Diffstat (limited to 'vm/JarFile.cpp')
-rw-r--r-- | vm/JarFile.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/vm/JarFile.cpp b/vm/JarFile.cpp new file mode 100644 index 000000000..0288fef6a --- /dev/null +++ b/vm/JarFile.cpp @@ -0,0 +1,378 @@ +/* + * 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. + */ + +/* + * Access the contents of a Jar file. + * + * This isn't actually concerned with any of the Jar-like elements; it + * just wants a zip archive with "classes.dex" inside. In Android the + * most common example is ".apk". + */ + +#include "Dalvik.h" +#include "libdex/OptInvocation.h" + +#include <stdlib.h> +#include <string.h> +#include <zlib.h> +#include <fcntl.h> +#include <errno.h> + +static const char* kDexInJarName = "classes.dex"; + +/* + * Attempt to open a file whose name is similar to <fileName>, + * but with the supplied suffix. E.g., + * openAlternateSuffix("Home.apk", "dex", O_RDONLY) will attempt + * to open "Home.dex". If the open succeeds, a pointer to a + * malloc()ed copy of the opened file name will be put in <*pCachedName>. + * + * <flags> is passed directly to open(). O_CREAT is not supported. + */ +static int openAlternateSuffix(const char *fileName, const char *suffix, + int flags, char **pCachedName) +{ + char *buf, *c; + size_t fileNameLen = strlen(fileName); + size_t suffixLen = strlen(suffix); + size_t bufLen = fileNameLen + suffixLen + 1; + int fd = -1; + + buf = (char*)malloc(bufLen); + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + + /* Copy the original filename into the buffer, find + * the last dot, and copy the suffix to just after it. + */ + memcpy(buf, fileName, fileNameLen + 1); + c = strrchr(buf, '.'); + if (c == NULL) { + errno = ENOENT; + goto bail; + } + memcpy(c + 1, suffix, suffixLen + 1); + + fd = open(buf, flags); + if (fd >= 0) { + *pCachedName = buf; + return fd; + } + LOGV("Couldn't open %s: %s\n", buf, strerror(errno)); +bail: + free(buf); + return -1; +} + +/* + * Checks the dependencies of the dex cache file corresponding + * to the jar file at the absolute path "fileName". + */ +DexCacheStatus dvmDexCacheStatus(const char *fileName) +{ + ZipArchive archive; + char* cachedName = NULL; + int fd; + DexCacheStatus result = DEX_CACHE_ERROR; + ZipEntry entry; + + /* Always treat elements of the bootclasspath as up-to-date. + * The fact that interpreted code is running at all means that this + * should be true. + */ + if (dvmClassPathContains(gDvm.bootClassPath, fileName)) { + return DEX_CACHE_OK; + } + + //TODO: match dvmJarFileOpen()'s logic. Not super-important + // (the odex-first logic is only necessary for dexpreopt) + // but it would be nice to be consistent. + + /* Try to find the dex file inside of the archive. + */ + if (dexZipOpenArchive(fileName, &archive) != 0) { + return DEX_CACHE_BAD_ARCHIVE; + } + entry = dexZipFindEntry(&archive, kDexInJarName); + if (entry != NULL) { + bool newFile = false; + + /* + * See if there's an up-to-date copy of the optimized dex + * in the cache, but don't create one if there isn't. + */ + LOGV("dvmDexCacheStatus: Checking cache for %s\n", fileName); + cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); + if (cachedName == NULL) + return DEX_CACHE_BAD_ARCHIVE; + + fd = dvmOpenCachedDexFile(fileName, cachedName, + dexGetZipEntryModTime(&archive, entry), + dexGetZipEntryCrc32(&archive, entry), + /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); + LOGV("dvmOpenCachedDexFile returned fd %d\n", fd); + if (fd < 0) { + result = DEX_CACHE_STALE; + goto bail; + } + + /* dvmOpenCachedDexFile locks the file as a side-effect. + * Unlock and close it. + */ + if (!dvmUnlockCachedDexFile(fd)) { + /* uh oh -- this process needs to exit or we'll wedge the system */ + LOGE("Unable to unlock DEX file\n"); + goto bail; + } + + /* When createIfMissing is false, dvmOpenCachedDexFile() only + * returns a valid fd if the cache file is up-to-date. + */ + } else { + /* + * There's no dex file in the jar file. See if there's an + * optimized dex file living alongside the jar. + */ + fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); + if (fd < 0) { + LOGI("Zip is good, but no %s inside, and no .odex " + "file in the same directory\n", kDexInJarName); + result = DEX_CACHE_BAD_ARCHIVE; + goto bail; + } + + LOGV("Using alternate file (odex) for %s ...\n", fileName); + if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { + LOGE("%s odex has stale dependencies\n", fileName); + LOGE("odex source not available -- failing\n"); + result = DEX_CACHE_STALE_ODEX; + goto bail; + } else { + LOGV("%s odex has good dependencies\n", fileName); + } + } + result = DEX_CACHE_OK; + +bail: + dexZipCloseArchive(&archive); + free(cachedName); + if (fd >= 0) { + close(fd); + } + return result; +} + +/* + * Open a Jar file. It's okay if it's just a Zip archive without all of + * the Jar trimmings, but we do insist on finding "classes.dex" inside + * or an appropriately-named ".odex" file alongside. + * + * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as + * being part of a different class loader. + */ +int dvmJarFileOpen(const char* fileName, const char* odexOutputName, + JarFile** ppJarFile, bool isBootstrap) +{ + /* + * TODO: This function has been duplicated and modified to become + * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored. + */ + + ZipArchive archive; + DvmDex* pDvmDex = NULL; + char* cachedName = NULL; + bool archiveOpen = false; + bool locked = false; + int fd = -1; + int result = -1; + + /* Even if we're not going to look at the archive, we need to + * open it so we can stuff it into ppJarFile. + */ + if (dexZipOpenArchive(fileName, &archive) != 0) + goto bail; + archiveOpen = true; + + /* If we fork/exec into dexopt, don't let it inherit the archive's fd. + */ + dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); + + /* First, look for a ".odex" alongside the jar file. It will + * have the same name/path except for the extension. + */ + fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); + if (fd >= 0) { + LOGV("Using alternate file (odex) for %s ...\n", fileName); + if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { + LOGE("%s odex has stale dependencies\n", fileName); + free(cachedName); + cachedName = NULL; + close(fd); + fd = -1; + goto tryArchive; + } else { + LOGV("%s odex has good dependencies\n", fileName); + //TODO: make sure that the .odex actually corresponds + // to the classes.dex inside the archive (if present). + // For typical use there will be no classes.dex. + } + } else { + ZipEntry entry; + +tryArchive: + /* + * Pre-created .odex absent or stale. Look inside the jar for a + * "classes.dex". + */ + entry = dexZipFindEntry(&archive, kDexInJarName); + if (entry != NULL) { + bool newFile = false; + + /* + * We've found the one we want. See if there's an up-to-date copy + * in the cache. + * + * On return, "fd" will be seeked just past the "opt" header. + * + * If a stale .odex file is present and classes.dex exists in + * the archive, this will *not* return an fd pointing to the + * .odex file; the fd will point into dalvik-cache like any + * other jar. + */ + if (odexOutputName == NULL) { + cachedName = dexOptGenerateCacheFileName(fileName, + kDexInJarName); + if (cachedName == NULL) + goto bail; + } else { + cachedName = strdup(odexOutputName); + } + LOGV("dvmJarFileOpen: Checking cache for %s (%s)\n", + fileName, cachedName); + fd = dvmOpenCachedDexFile(fileName, cachedName, + dexGetZipEntryModTime(&archive, entry), + dexGetZipEntryCrc32(&archive, entry), + isBootstrap, &newFile, /*createIfMissing=*/true); + if (fd < 0) { + LOGI("Unable to open or create cache for %s (%s)\n", + fileName, cachedName); + goto bail; + } + locked = true; + + /* + * If fd points to a new file (because there was no cached version, + * or the cached version was stale), generate the optimized DEX. + * The file descriptor returned is still locked, and is positioned + * just past the optimization header. + */ + if (newFile) { + u8 startWhen, extractWhen, endWhen; + bool result; + off_t dexOffset; + + dexOffset = lseek(fd, 0, SEEK_CUR); + result = (dexOffset > 0); + + if (result) { + startWhen = dvmGetRelativeTimeUsec(); + result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; + extractWhen = dvmGetRelativeTimeUsec(); + } + if (result) { + result = dvmOptimizeDexFile(fd, dexOffset, + dexGetZipEntryUncompLen(&archive, entry), + fileName, + dexGetZipEntryModTime(&archive, entry), + dexGetZipEntryCrc32(&archive, entry), + isBootstrap); + } + + if (!result) { + LOGE("Unable to extract+optimize DEX from '%s'\n", + fileName); + goto bail; + } + + endWhen = dvmGetRelativeTimeUsec(); + LOGD("DEX prep '%s': unzip in %dms, rewrite %dms\n", + fileName, + (int) (extractWhen - startWhen) / 1000, + (int) (endWhen - extractWhen) / 1000); + } + } else { + LOGI("Zip is good, but no %s inside, and no valid .odex " + "file in the same directory\n", kDexInJarName); + goto bail; + } + } + + /* + * Map the cached version. This immediately rewinds the fd, so it + * doesn't have to be seeked anywhere in particular. + */ + if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { + LOGI("Unable to map %s in %s\n", kDexInJarName, fileName); + goto bail; + } + + if (locked) { + /* unlock the fd */ + if (!dvmUnlockCachedDexFile(fd)) { + /* uh oh -- this process needs to exit or we'll wedge the system */ + LOGE("Unable to unlock DEX file\n"); + goto bail; + } + locked = false; + } + + LOGV("Successfully opened '%s' in '%s'\n", kDexInJarName, fileName); + + *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); + (*ppJarFile)->archive = archive; + (*ppJarFile)->cacheFileName = cachedName; + (*ppJarFile)->pDvmDex = pDvmDex; + cachedName = NULL; // don't free it below + result = 0; + +bail: + /* clean up, closing the open file */ + if (archiveOpen && result != 0) + dexZipCloseArchive(&archive); + free(cachedName); + if (fd >= 0) { + if (locked) + (void) dvmUnlockCachedDexFile(fd); + close(fd); + } + return result; +} + +/* + * Close a Jar file and free the struct. + */ +void dvmJarFileFree(JarFile* pJarFile) +{ + if (pJarFile == NULL) + return; + + dvmDexFileFree(pJarFile->pDvmDex); + dexZipCloseArchive(&pJarFile->archive); + free(pJarFile->cacheFileName); + free(pJarFile); +} |