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 34 static 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 */ 45 static 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)); 77 bail: 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 DexCacheStatus dvmDexCacheStatus(const char *fileName) 87 { 88 ZipArchive archive; 89 char* cachedName = NULL; 90 int fd; 91 DexCacheStatus result = DEX_CACHE_ERROR; 92 ZipEntry entry; 93 94 /* Always treat elements of the bootclasspath as up-to-date. 95 * The fact that interpreted code is running at all means that this 96 * should be true. 97 */ 98 if (dvmClassPathContains(gDvm.bootClassPath, fileName)) { 99 return DEX_CACHE_OK; 100 } 101 102 //TODO: match dvmJarFileOpen()'s logic. Not super-important 103 // (the odex-first logic is only necessary for dexpreopt) 104 // but it would be nice to be consistent. 105 106 /* Try to find the dex file inside of the archive. 107 */ 108 if (dexZipOpenArchive(fileName, &archive) != 0) { 109 return DEX_CACHE_BAD_ARCHIVE; 110 } 111 entry = dexZipFindEntry(&archive, kDexInJarName); 112 if (entry != NULL) { 113 bool newFile = false; 114 115 /* 116 * See if there's an up-to-date copy of the optimized dex 117 * in the cache, but don't create one if there isn't. 118 */ 119 ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName); 120 cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); 121 if (cachedName == NULL) 122 return DEX_CACHE_BAD_ARCHIVE; 123 124 fd = dvmOpenCachedDexFile(fileName, cachedName, 125 dexGetZipEntryModTime(&archive, entry), 126 dexGetZipEntryCrc32(&archive, entry), 127 /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); 128 ALOGV("dvmOpenCachedDexFile returned fd %d", fd); 129 if (fd < 0) { 130 result = DEX_CACHE_STALE; 131 goto bail; 132 } 133 134 /* dvmOpenCachedDexFile locks the file as a side-effect. 135 * Unlock and close it. 136 */ 137 if (!dvmUnlockCachedDexFile(fd)) { 138 /* uh oh -- this process needs to exit or we'll wedge the system */ 139 ALOGE("Unable to unlock DEX file"); 140 goto bail; 141 } 142 143 /* When createIfMissing is false, dvmOpenCachedDexFile() only 144 * returns a valid fd if the cache file is up-to-date. 145 */ 146 } else { 147 /* 148 * There's no dex file in the jar file. See if there's an 149 * optimized dex file living alongside the jar. 150 */ 151 fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); 152 if (fd < 0) { 153 ALOGI("Zip is good, but no %s inside, and no .odex " 154 "file in the same directory", kDexInJarName); 155 result = DEX_CACHE_BAD_ARCHIVE; 156 goto bail; 157 } 158 159 ALOGV("Using alternate file (odex) for %s ...", fileName); 160 if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { 161 ALOGE("%s odex has stale dependencies", fileName); 162 ALOGE("odex source not available -- failing"); 163 result = DEX_CACHE_STALE_ODEX; 164 goto bail; 165 } else { 166 ALOGV("%s odex has good dependencies", fileName); 167 } 168 } 169 result = DEX_CACHE_OK; 170 171 bail: 172 dexZipCloseArchive(&archive); 173 free(cachedName); 174 if (fd >= 0) { 175 close(fd); 176 } 177 return result; 178 } 179 180 /* 181 * Open a Jar file. It's okay if it's just a Zip archive without all of 182 * the Jar trimmings, but we do insist on finding "classes.dex" inside 183 * or an appropriately-named ".odex" file alongside. 184 * 185 * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as 186 * being part of a different class loader. 187 */ 188 int dvmJarFileOpen(const char* fileName, const char* odexOutputName, 189 JarFile** ppJarFile, bool isBootstrap) 190 { 191 /* 192 * TODO: This function has been duplicated and modified to become 193 * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored. 194 */ 195 196 ZipArchive archive; 197 DvmDex* pDvmDex = NULL; 198 char* cachedName = NULL; 199 bool archiveOpen = false; 200 bool locked = false; 201 int fd = -1; 202 int result = -1; 203 204 /* Even if we're not going to look at the archive, we need to 205 * open it so we can stuff it into ppJarFile. 206 */ 207 if (dexZipOpenArchive(fileName, &archive) != 0) 208 goto bail; 209 archiveOpen = true; 210 211 /* If we fork/exec into dexopt, don't let it inherit the archive's fd. 212 */ 213 dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); 214 215 /* First, look for a ".odex" alongside the jar file. It will 216 * have the same name/path except for the extension. 217 */ 218 fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); 219 if (fd >= 0) { 220 ALOGV("Using alternate file (odex) for %s ...", fileName); 221 if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { 222 ALOGE("%s odex has stale dependencies", fileName); 223 free(cachedName); 224 cachedName = NULL; 225 close(fd); 226 fd = -1; 227 goto tryArchive; 228 } else { 229 ALOGV("%s odex has good dependencies", fileName); 230 //TODO: make sure that the .odex actually corresponds 231 // to the classes.dex inside the archive (if present). 232 // For typical use there will be no classes.dex. 233 } 234 } else { 235 ZipEntry entry; 236 237 tryArchive: 238 /* 239 * Pre-created .odex absent or stale. Look inside the jar for a 240 * "classes.dex". 241 */ 242 entry = dexZipFindEntry(&archive, kDexInJarName); 243 if (entry != NULL) { 244 bool newFile = false; 245 246 /* 247 * We've found the one we want. See if there's an up-to-date copy 248 * in the cache. 249 * 250 * On return, "fd" will be seeked just past the "opt" header. 251 * 252 * If a stale .odex file is present and classes.dex exists in 253 * the archive, this will *not* return an fd pointing to the 254 * .odex file; the fd will point into dalvik-cache like any 255 * other jar. 256 */ 257 if (odexOutputName == NULL) { 258 cachedName = dexOptGenerateCacheFileName(fileName, 259 kDexInJarName); 260 if (cachedName == NULL) 261 goto bail; 262 } else { 263 cachedName = strdup(odexOutputName); 264 } 265 ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", 266 fileName, cachedName); 267 fd = dvmOpenCachedDexFile(fileName, cachedName, 268 dexGetZipEntryModTime(&archive, entry), 269 dexGetZipEntryCrc32(&archive, entry), 270 isBootstrap, &newFile, /*createIfMissing=*/true); 271 if (fd < 0) { 272 ALOGI("Unable to open or create cache for %s (%s)", 273 fileName, cachedName); 274 goto bail; 275 } 276 locked = true; 277 278 /* 279 * If fd points to a new file (because there was no cached version, 280 * or the cached version was stale), generate the optimized DEX. 281 * The file descriptor returned is still locked, and is positioned 282 * just past the optimization header. 283 */ 284 if (newFile) { 285 u8 startWhen, extractWhen, endWhen; 286 bool result; 287 off_t dexOffset; 288 289 dexOffset = lseek(fd, 0, SEEK_CUR); 290 result = (dexOffset > 0); 291 292 if (result) { 293 startWhen = dvmGetRelativeTimeUsec(); 294 result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; 295 extractWhen = dvmGetRelativeTimeUsec(); 296 } 297 if (result) { 298 result = dvmOptimizeDexFile(fd, dexOffset, 299 dexGetZipEntryUncompLen(&archive, entry), 300 fileName, 301 dexGetZipEntryModTime(&archive, entry), 302 dexGetZipEntryCrc32(&archive, entry), 303 isBootstrap); 304 } 305 306 if (!result) { 307 ALOGE("Unable to extract+optimize DEX from '%s'", 308 fileName); 309 goto bail; 310 } 311 312 endWhen = dvmGetRelativeTimeUsec(); 313 ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", 314 fileName, 315 (int) (extractWhen - startWhen) / 1000, 316 (int) (endWhen - extractWhen) / 1000); 317 } 318 } else { 319 ALOGI("Zip is good, but no %s inside, and no valid .odex " 320 "file in the same directory", kDexInJarName); 321 goto bail; 322 } 323 } 324 325 /* 326 * Map the cached version. This immediately rewinds the fd, so it 327 * doesn't have to be seeked anywhere in particular. 328 */ 329 if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { 330 ALOGI("Unable to map %s in %s", kDexInJarName, fileName); 331 goto bail; 332 } 333 334 if (locked) { 335 /* unlock the fd */ 336 if (!dvmUnlockCachedDexFile(fd)) { 337 /* uh oh -- this process needs to exit or we'll wedge the system */ 338 ALOGE("Unable to unlock DEX file"); 339 goto bail; 340 } 341 locked = false; 342 } 343 344 ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName); 345 346 *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); 347 (*ppJarFile)->archive = archive; 348 (*ppJarFile)->cacheFileName = cachedName; 349 (*ppJarFile)->pDvmDex = pDvmDex; 350 cachedName = NULL; // don't free it below 351 result = 0; 352 353 bail: 354 /* clean up, closing the open file */ 355 if (archiveOpen && result != 0) 356 dexZipCloseArchive(&archive); 357 free(cachedName); 358 if (fd >= 0) { 359 if (locked) 360 (void) dvmUnlockCachedDexFile(fd); 361 close(fd); 362 } 363 return result; 364 } 365 366 /* 367 * Close a Jar file and free the struct. 368 */ 369 void dvmJarFileFree(JarFile* pJarFile) 370 { 371 if (pJarFile == NULL) 372 return; 373 374 dvmDexFileFree(pJarFile->pDvmDex); 375 dexZipCloseArchive(&pJarFile->archive); 376 free(pJarFile->cacheFileName); 377 free(pJarFile); 378 } 379