Home | History | Annotate | Download | only in vm
      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