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 /*
     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