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
32static 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 */
43static 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));
75bail:
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 */
84DexCacheStatus 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
169bail:
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 */
186int 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
229tryArchive:
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;
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) == 0;
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
345bail:
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 */
361void 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