JarFile.cpp revision 36e356c96640775f0a3f167bd2426ea0f0093b8b
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * Access the contents of a Jar file. 19 * 20 * This isn't actually concerned with any of the Jar-like elements; it 21 * just wants a zip archive with "classes.dex" inside. In Android the 22 * most common example is ".apk". 23 */ 24 25#include "Dalvik.h" 26#include "libdex/OptInvocation.h" 27 28#include <stdlib.h> 29#include <string.h> 30#include <zlib.h> 31#include <fcntl.h> 32#include <errno.h> 33 34static const char* kDexInJarName = "classes.dex"; 35 36/* 37 * Attempt to open a file whose name is similar to <fileName>, 38 * but with the supplied suffix. E.g., 39 * openAlternateSuffix("Home.apk", "dex", O_RDONLY) will attempt 40 * to open "Home.dex". If the open succeeds, a pointer to a 41 * malloc()ed copy of the opened file name will be put in <*pCachedName>. 42 * 43 * <flags> is passed directly to open(). O_CREAT is not supported. 44 */ 45static int openAlternateSuffix(const char *fileName, const char *suffix, 46 int flags, char **pCachedName) 47{ 48 char *buf, *c; 49 size_t fileNameLen = strlen(fileName); 50 size_t suffixLen = strlen(suffix); 51 size_t bufLen = fileNameLen + suffixLen + 1; 52 int fd = -1; 53 54 buf = (char*)malloc(bufLen); 55 if (buf == NULL) { 56 errno = ENOMEM; 57 return -1; 58 } 59 60 /* Copy the original filename into the buffer, find 61 * the last dot, and copy the suffix to just after it. 62 */ 63 memcpy(buf, fileName, fileNameLen + 1); 64 c = strrchr(buf, '.'); 65 if (c == NULL) { 66 errno = ENOENT; 67 goto bail; 68 } 69 memcpy(c + 1, suffix, suffixLen + 1); 70 71 fd = open(buf, flags); 72 if (fd >= 0) { 73 *pCachedName = buf; 74 return fd; 75 } 76 ALOGV("Couldn't open %s: %s", buf, strerror(errno)); 77bail: 78 free(buf); 79 return -1; 80} 81 82/* 83 * Checks the dependencies of the dex cache file corresponding 84 * to the jar file at the absolute path "fileName". 85 * 86 * Note: This should parallel the logic of dvmJarFileOpen. 87 */ 88DexCacheStatus dvmDexCacheStatus(const char *fileName) 89{ 90 ZipArchive archive; 91 char* cachedName = NULL; 92 int fd; 93 DexCacheStatus result = DEX_CACHE_ERROR; 94 ZipEntry entry; 95 96 /* Always treat elements of the bootclasspath as up-to-date. 97 * The fact that interpreted code is running at all means that this 98 * should be true. 99 */ 100 if (dvmClassPathContains(gDvm.bootClassPath, fileName)) { 101 return DEX_CACHE_OK; 102 } 103 104 /* Try to find the dex file inside of the archive. 105 */ 106 if (dexZipOpenArchive(fileName, &archive) != 0) { 107 return DEX_CACHE_BAD_ARCHIVE; 108 } 109 110 /* First, look for a ".odex" alongside the jar file. It will 111 * have the same name/path except for the extension. 112 */ 113 fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); 114 if (fd >= 0) { 115 ALOGV("Using alternate file (odex) for %s ...", fileName); 116 if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { 117 ALOGE("%s odex has stale dependencies", fileName); 118 free(cachedName); 119 cachedName = NULL; 120 close(fd); 121 fd = -1; 122 goto tryArchive; 123 } else { 124 ALOGV("%s odex has good dependencies", fileName); 125 } 126 } else { 127 128tryArchive: 129 /* 130 * Pre-created .odex absent or stale. Look inside the jar for a 131 * "classes.dex". 132 */ 133 entry = dexZipFindEntry(&archive, kDexInJarName); 134 if (entry != NULL) { 135 bool newFile = false; 136 137 /* 138 * See if there's an up-to-date copy of the optimized dex 139 * in the cache, but don't create one if there isn't. 140 */ 141 ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName); 142 cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); 143 if (cachedName == NULL) 144 return DEX_CACHE_BAD_ARCHIVE; 145 146 fd = dvmOpenCachedDexFile(fileName, cachedName, 147 dexGetZipEntryModTime(&archive, entry), 148 dexGetZipEntryCrc32(&archive, entry), 149 /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); 150 ALOGV("dvmOpenCachedDexFile returned fd %d", fd); 151 if (fd < 0) { 152 result = DEX_CACHE_STALE; 153 goto bail; 154 } 155 156 /* dvmOpenCachedDexFile locks the file as a side-effect. 157 * Unlock and close it. 158 */ 159 if (!dvmUnlockCachedDexFile(fd)) { 160 /* uh oh -- this process needs to exit or we'll wedge the system */ 161 ALOGE("Unable to unlock DEX file"); 162 goto bail; 163 } 164 } else { 165 ALOGI("Zip is good, but no %s inside, and no .odex " 166 "file in the same directory", kDexInJarName); 167 result = DEX_CACHE_BAD_ARCHIVE; 168 goto bail; 169 } 170 } 171 result = DEX_CACHE_OK; 172 173bail: 174 dexZipCloseArchive(&archive); 175 free(cachedName); 176 if (fd >= 0) { 177 close(fd); 178 } 179 return result; 180} 181 182/* 183 * Open a Jar file. It's okay if it's just a Zip archive without all of 184 * the Jar trimmings, but we do insist on finding "classes.dex" inside 185 * or an appropriately-named ".odex" file alongside. 186 * 187 * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as 188 * being part of a different class loader. 189 * 190 * Note: This should parallel the logic of dvmDexCacheStatus. 191 */ 192int dvmJarFileOpen(const char* fileName, const char* odexOutputName, 193 JarFile** ppJarFile, bool isBootstrap) 194{ 195 /* 196 * TODO: This function has been duplicated and modified to become 197 * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored. 198 */ 199 200 ZipArchive archive; 201 DvmDex* pDvmDex = NULL; 202 char* cachedName = NULL; 203 bool archiveOpen = false; 204 bool locked = false; 205 int fd = -1; 206 int result = -1; 207 208 /* Even if we're not going to look at the archive, we need to 209 * open it so we can stuff it into ppJarFile. 210 */ 211 if (dexZipOpenArchive(fileName, &archive) != 0) 212 goto bail; 213 archiveOpen = true; 214 215 /* If we fork/exec into dexopt, don't let it inherit the archive's fd. 216 */ 217 dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); 218 219 /* First, look for a ".odex" alongside the jar file. It will 220 * have the same name/path except for the extension. 221 */ 222 fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); 223 if (fd >= 0) { 224 ALOGV("Using alternate file (odex) for %s ...", fileName); 225 if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { 226 ALOGE("%s odex has stale dependencies", fileName); 227 free(cachedName); 228 cachedName = NULL; 229 close(fd); 230 fd = -1; 231 goto tryArchive; 232 } else { 233 ALOGV("%s odex has good dependencies", fileName); 234 //TODO: make sure that the .odex actually corresponds 235 // to the classes.dex inside the archive (if present). 236 // For typical use there will be no classes.dex. 237 } 238 } else { 239 ZipEntry entry; 240 241tryArchive: 242 /* 243 * Pre-created .odex absent or stale. Look inside the jar for a 244 * "classes.dex". 245 */ 246 entry = dexZipFindEntry(&archive, kDexInJarName); 247 if (entry != NULL) { 248 bool newFile = false; 249 250 /* 251 * We've found the one we want. See if there's an up-to-date copy 252 * in the cache. 253 * 254 * On return, "fd" will be seeked just past the "opt" header. 255 * 256 * If a stale .odex file is present and classes.dex exists in 257 * the archive, this will *not* return an fd pointing to the 258 * .odex file; the fd will point into dalvik-cache like any 259 * other jar. 260 */ 261 if (odexOutputName == NULL) { 262 cachedName = dexOptGenerateCacheFileName(fileName, 263 kDexInJarName); 264 if (cachedName == NULL) 265 goto bail; 266 } else { 267 cachedName = strdup(odexOutputName); 268 } 269 ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", 270 fileName, cachedName); 271 fd = dvmOpenCachedDexFile(fileName, cachedName, 272 dexGetZipEntryModTime(&archive, entry), 273 dexGetZipEntryCrc32(&archive, entry), 274 isBootstrap, &newFile, /*createIfMissing=*/true); 275 if (fd < 0) { 276 ALOGI("Unable to open or create cache for %s (%s)", 277 fileName, cachedName); 278 goto bail; 279 } 280 locked = true; 281 282 /* 283 * If fd points to a new file (because there was no cached version, 284 * or the cached version was stale), generate the optimized DEX. 285 * The file descriptor returned is still locked, and is positioned 286 * just past the optimization header. 287 */ 288 if (newFile) { 289 u8 startWhen, extractWhen, endWhen; 290 bool result; 291 off_t dexOffset; 292 293 dexOffset = lseek(fd, 0, SEEK_CUR); 294 result = (dexOffset > 0); 295 296 if (result) { 297 startWhen = dvmGetRelativeTimeUsec(); 298 result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; 299 extractWhen = dvmGetRelativeTimeUsec(); 300 } 301 if (result) { 302 result = dvmOptimizeDexFile(fd, dexOffset, 303 dexGetZipEntryUncompLen(&archive, entry), 304 fileName, 305 dexGetZipEntryModTime(&archive, entry), 306 dexGetZipEntryCrc32(&archive, entry), 307 isBootstrap); 308 } 309 310 if (!result) { 311 ALOGE("Unable to extract+optimize DEX from '%s'", 312 fileName); 313 goto bail; 314 } 315 316 endWhen = dvmGetRelativeTimeUsec(); 317 ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", 318 fileName, 319 (int) (extractWhen - startWhen) / 1000, 320 (int) (endWhen - extractWhen) / 1000); 321 } 322 } else { 323 ALOGI("Zip is good, but no %s inside, and no valid .odex " 324 "file in the same directory", kDexInJarName); 325 goto bail; 326 } 327 } 328 329 /* 330 * Map the cached version. This immediately rewinds the fd, so it 331 * doesn't have to be seeked anywhere in particular. 332 */ 333 if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { 334 ALOGI("Unable to map %s in %s", kDexInJarName, fileName); 335 goto bail; 336 } 337 338 if (locked) { 339 /* unlock the fd */ 340 if (!dvmUnlockCachedDexFile(fd)) { 341 /* uh oh -- this process needs to exit or we'll wedge the system */ 342 ALOGE("Unable to unlock DEX file"); 343 goto bail; 344 } 345 locked = false; 346 } 347 348 ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName); 349 350 *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); 351 (*ppJarFile)->archive = archive; 352 (*ppJarFile)->cacheFileName = cachedName; 353 (*ppJarFile)->pDvmDex = pDvmDex; 354 cachedName = NULL; // don't free it below 355 result = 0; 356 357bail: 358 /* clean up, closing the open file */ 359 if (archiveOpen && result != 0) 360 dexZipCloseArchive(&archive); 361 free(cachedName); 362 if (fd >= 0) { 363 if (locked) 364 (void) dvmUnlockCachedDexFile(fd); 365 close(fd); 366 } 367 return result; 368} 369 370/* 371 * Close a Jar file and free the struct. 372 */ 373void dvmJarFileFree(JarFile* pJarFile) 374{ 375 if (pJarFile == NULL) 376 return; 377 378 dvmDexFileFree(pJarFile->pDvmDex); 379 dexZipCloseArchive(&pJarFile->archive); 380 free(pJarFile->cacheFileName); 381 free(pJarFile); 382} 383