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