JarFile.cpp revision 60fc806b679a3655c228b4093058c59941a49cfe
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
34static 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 */
45static 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    LOGV("Couldn't open %s: %s", buf, strerror(errno));
77bail:
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 */
86DexCacheStatus 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        LOGV("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        LOGV("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            LOGE("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            LOGI("Zip is good, but no %s inside, and no .odex "
154                    "file in the same directory\n", kDexInJarName);
155            result = DEX_CACHE_BAD_ARCHIVE;
156            goto bail;
157        }
158
159        LOGV("Using alternate file (odex) for %s ...", fileName);
160        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
161            LOGE("%s odex has stale dependencies", fileName);
162            LOGE("odex source not available -- failing");
163            result = DEX_CACHE_STALE_ODEX;
164            goto bail;
165        } else {
166            LOGV("%s odex has good dependencies", fileName);
167        }
168    }
169    result = DEX_CACHE_OK;
170
171bail:
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 */
188int 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        LOGV("Using alternate file (odex) for %s ...", fileName);
221        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
222            LOGE("%s odex has stale dependencies", fileName);
223            free(cachedName);
224            cachedName = NULL;
225            close(fd);
226            fd = -1;
227            goto tryArchive;
228        } else {
229            LOGV("%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
237tryArchive:
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            LOGV("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                LOGI("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                    LOGE("Unable to extract+optimize DEX from '%s'",
308                        fileName);
309                    goto bail;
310                }
311
312                endWhen = dvmGetRelativeTimeUsec();
313                LOGD("DEX prep '%s': unzip in %dms, rewrite %dms",
314                    fileName,
315                    (int) (extractWhen - startWhen) / 1000,
316                    (int) (endWhen - extractWhen) / 1000);
317            }
318        } else {
319            LOGI("Zip is good, but no %s inside, and no valid .odex "
320                    "file in the same directory\n", 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        LOGI("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            LOGE("Unable to unlock DEX file");
339            goto bail;
340        }
341        locked = false;
342    }
343
344    LOGV("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
353bail:
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 */
369void 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