/* * 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) { ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); free(cachedName); cachedName = NULL; close(fd); fd = -1; goto tryArchive; } else { ALOGV("%s odex has good dependencies", 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); } ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { ALOGI("Unable to open or create cache for %s (%s)", 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) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", fileName, (int) (extractWhen - startWhen) / 1000, (int) (endWhen - extractWhen) / 1000); } } else { ALOGI("Zip is good, but no %s inside, and no valid .odex " "file in the same directory", 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) { ALOGI("Unable to map %s in %s", 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 */ ALOGE("Unable to unlock DEX file"); goto bail; } locked = false; } ALOGV("Successfully opened '%s' in '%s'", 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; }
/* See documentation comment in header. */ int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName, RawDexFile** ppRawDexFile, bool isBootstrap) { /* * TODO: This duplicates a lot of code from dvmJarFileOpen() in * JarFile.c. This should be refactored. */ DvmDex* pDvmDex = NULL; char* cachedName = NULL; int result = -1; int dexFd = -1; int optFd = -1; u4 modTime = 0; u4 adler32 = 0; size_t fileSize = 0; bool newFile = false; bool locked = false; dexFd = open(fileName, O_RDONLY); if (dexFd < 0) goto bail; /* If we fork/exec into dexopt, don't let it inherit the open fd. */ dvmSetCloseOnExec(dexFd); if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) { ALOGE("Error with header for %s", fileName); goto bail; } if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { ALOGE("Error with stat for %s", fileName); goto bail; } /* * See if the cached file matches. If so, optFd will become a reference * to the cached file and will have been seeked to just past the "opt" * header. */ if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, NULL); if (cachedName == NULL) goto bail; } else { cachedName = strdup(odexOutputName); } ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)", fileName, cachedName); optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime, adler32, isBootstrap, &newFile, /*createIfMissing=*/true); if (optFd < 0) { ALOGI("Unable to open or create cache for %s (%s)", fileName, cachedName); goto bail; } locked = true; /* * If optFd 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, copyWhen, endWhen; bool result; off_t dexOffset; dexOffset = lseek(optFd, 0, SEEK_CUR); result = (dexOffset > 0); if (result) { startWhen = dvmGetRelativeTimeUsec(); result = copyFileToFile(optFd, dexFd, fileSize) == 0; copyWhen = dvmGetRelativeTimeUsec(); } if (result) { result = dvmOptimizeDexFile(optFd, dexOffset, fileSize, fileName, modTime, adler32, isBootstrap); } if (!result) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': copy in %dms, rewrite %dms", fileName, (int) (copyWhen - startWhen) / 1000, (int) (endWhen - copyWhen) / 1000); } /* * Map the cached version. This immediately rewinds the fd, so it * doesn't have to be seeked anywhere in particular. */ if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) { ALOGI("Unable to map cached %s", fileName); goto bail; } if (locked) { /* unlock the fd */ if (!dvmUnlockCachedDexFile(optFd)) { /* uh oh -- this process needs to exit or we'll wedge the system */ ALOGE("Unable to unlock DEX file"); goto bail; } locked = false; } ALOGV("Successfully opened '%s'", fileName); *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); (*ppRawDexFile)->cacheFileName = cachedName; (*ppRawDexFile)->pDvmDex = pDvmDex; cachedName = NULL; // don't free it below result = 0; bail: free(cachedName); if (dexFd >= 0) { close(dexFd); } if (optFd >= 0) { if (locked) (void) dvmUnlockCachedDexFile(optFd); close(optFd); } return result; }
int dvmJarFileOpen(const char* fileName, const char* odexOutputName, JarFile** ppJarFile, bool isBootstrap) { ZipArchive archive; DvmDex* pDvmDex = NULL; char* cachedName = NULL; bool archiveOpen = false; bool locked = false; int fd = -1; int result = -1; if (dexZipOpenArchive(fileName, &archive) != 0) goto bail; archiveOpen = true; dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); if (fd >= 0) { ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); free(cachedName); cachedName = NULL; close(fd); fd = -1; goto tryArchive; } else { ALOGV("%s odex has good dependencies", fileName); } } else { ZipEntry entry; tryArchive: entry = dexZipFindEntry(&archive, kDexInJarName); if (entry != NULL) { bool newFile = false; if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); if (cachedName == NULL) goto bail; } else { cachedName = strdup(odexOutputName); } ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { ALOGI("Unable to open or create cache for %s (%s)", fileName, cachedName); goto bail; } locked = true; /* Generate the optimized DEX. */ 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) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", fileName, (int) (extractWhen - startWhen) / 1000, (int) (endWhen - extractWhen) / 1000); } } else { ALOGI("Zip is good, but no %s inside, and no valid .odex " "file in the same directory", kDexInJarName); goto bail; } } if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { ALOGI("Unable to map %s in %s", kDexInJarName, fileName); goto bail; } if (locked) { /* unlock the fd */ if (!dvmUnlockCachedDexFile(fd)) { /* This process needs to exit or we'll wedge the system O_O */ ALOGE("Unable to unlock DEX file"); goto bail; } locked = false; } ALOGV("Successfully opened '%s' in '%s'", 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: if (archiveOpen && result != 0) dexZipCloseArchive(&archive); free(cachedName); if (fd >= 0) { if (locked) (void) dvmUnlockCachedDexFile(fd); close(fd); } return result; }