JarFile.cpp revision 36e356c96640775f0a3f167bd2426ea0f0093b8b
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    ALOGV("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 *
86 * Note: This should parallel the logic of dvmJarFileOpen.
87 */
88DexCacheStatus dvmDexCacheStatus(const char *fileName)
89{
90    ZipArchive archive;
91    char* cachedName = NULL;
92    int fd;
93    DexCacheStatus result = DEX_CACHE_ERROR;
94    ZipEntry entry;
95
96    /* Always treat elements of the bootclasspath as up-to-date.
97     * The fact that interpreted code is running at all means that this
98     * should be true.
99     */
100    if (dvmClassPathContains(gDvm.bootClassPath, fileName)) {
101        return DEX_CACHE_OK;
102    }
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
110    /* First, look for a ".odex" alongside the jar file.  It will
111     * have the same name/path except for the extension.
112     */
113    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
114    if (fd >= 0) {
115        ALOGV("Using alternate file (odex) for %s ...", fileName);
116        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
117            ALOGE("%s odex has stale dependencies", fileName);
118            free(cachedName);
119            cachedName = NULL;
120            close(fd);
121            fd = -1;
122            goto tryArchive;
123        } else {
124            ALOGV("%s odex has good dependencies", fileName);
125        }
126    } else {
127
128tryArchive:
129        /*
130         * Pre-created .odex absent or stale.  Look inside the jar for a
131         * "classes.dex".
132         */
133        entry = dexZipFindEntry(&archive, kDexInJarName);
134        if (entry != NULL) {
135            bool newFile = false;
136
137            /*
138             * See if there's an up-to-date copy of the optimized dex
139             * in the cache, but don't create one if there isn't.
140             */
141            ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName);
142            cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);
143            if (cachedName == NULL)
144                return DEX_CACHE_BAD_ARCHIVE;
145
146            fd = dvmOpenCachedDexFile(fileName, cachedName,
147                    dexGetZipEntryModTime(&archive, entry),
148                    dexGetZipEntryCrc32(&archive, entry),
149                    /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false);
150            ALOGV("dvmOpenCachedDexFile returned fd %d", fd);
151            if (fd < 0) {
152                result = DEX_CACHE_STALE;
153                goto bail;
154            }
155
156            /* dvmOpenCachedDexFile locks the file as a side-effect.
157             * Unlock and close it.
158             */
159            if (!dvmUnlockCachedDexFile(fd)) {
160                /* uh oh -- this process needs to exit or we'll wedge the system */
161                ALOGE("Unable to unlock DEX file");
162                goto bail;
163            }
164        } else {
165            ALOGI("Zip is good, but no %s inside, and no .odex "
166                    "file in the same directory", kDexInJarName);
167            result = DEX_CACHE_BAD_ARCHIVE;
168            goto bail;
169        }
170    }
171    result = DEX_CACHE_OK;
172
173bail:
174    dexZipCloseArchive(&archive);
175    free(cachedName);
176    if (fd >= 0) {
177        close(fd);
178    }
179    return result;
180}
181
182/*
183 * Open a Jar file.  It's okay if it's just a Zip archive without all of
184 * the Jar trimmings, but we do insist on finding "classes.dex" inside
185 * or an appropriately-named ".odex" file alongside.
186 *
187 * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as
188 * being part of a different class loader.
189 *
190 * Note: This should parallel the logic of dvmDexCacheStatus.
191 */
192int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
193    JarFile** ppJarFile, bool isBootstrap)
194{
195    /*
196     * TODO: This function has been duplicated and modified to become
197     * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored.
198     */
199
200    ZipArchive archive;
201    DvmDex* pDvmDex = NULL;
202    char* cachedName = NULL;
203    bool archiveOpen = false;
204    bool locked = false;
205    int fd = -1;
206    int result = -1;
207
208    /* Even if we're not going to look at the archive, we need to
209     * open it so we can stuff it into ppJarFile.
210     */
211    if (dexZipOpenArchive(fileName, &archive) != 0)
212        goto bail;
213    archiveOpen = true;
214
215    /* If we fork/exec into dexopt, don't let it inherit the archive's fd.
216     */
217    dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));
218
219    /* First, look for a ".odex" alongside the jar file.  It will
220     * have the same name/path except for the extension.
221     */
222    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
223    if (fd >= 0) {
224        ALOGV("Using alternate file (odex) for %s ...", fileName);
225        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
226            ALOGE("%s odex has stale dependencies", fileName);
227            free(cachedName);
228            cachedName = NULL;
229            close(fd);
230            fd = -1;
231            goto tryArchive;
232        } else {
233            ALOGV("%s odex has good dependencies", fileName);
234            //TODO: make sure that the .odex actually corresponds
235            //      to the classes.dex inside the archive (if present).
236            //      For typical use there will be no classes.dex.
237        }
238    } else {
239        ZipEntry entry;
240
241tryArchive:
242        /*
243         * Pre-created .odex absent or stale.  Look inside the jar for a
244         * "classes.dex".
245         */
246        entry = dexZipFindEntry(&archive, kDexInJarName);
247        if (entry != NULL) {
248            bool newFile = false;
249
250            /*
251             * We've found the one we want.  See if there's an up-to-date copy
252             * in the cache.
253             *
254             * On return, "fd" will be seeked just past the "opt" header.
255             *
256             * If a stale .odex file is present and classes.dex exists in
257             * the archive, this will *not* return an fd pointing to the
258             * .odex file; the fd will point into dalvik-cache like any
259             * other jar.
260             */
261            if (odexOutputName == NULL) {
262                cachedName = dexOptGenerateCacheFileName(fileName,
263                                kDexInJarName);
264                if (cachedName == NULL)
265                    goto bail;
266            } else {
267                cachedName = strdup(odexOutputName);
268            }
269            ALOGV("dvmJarFileOpen: Checking cache for %s (%s)",
270                fileName, cachedName);
271            fd = dvmOpenCachedDexFile(fileName, cachedName,
272                    dexGetZipEntryModTime(&archive, entry),
273                    dexGetZipEntryCrc32(&archive, entry),
274                    isBootstrap, &newFile, /*createIfMissing=*/true);
275            if (fd < 0) {
276                ALOGI("Unable to open or create cache for %s (%s)",
277                    fileName, cachedName);
278                goto bail;
279            }
280            locked = true;
281
282            /*
283             * If fd points to a new file (because there was no cached version,
284             * or the cached version was stale), generate the optimized DEX.
285             * The file descriptor returned is still locked, and is positioned
286             * just past the optimization header.
287             */
288            if (newFile) {
289                u8 startWhen, extractWhen, endWhen;
290                bool result;
291                off_t dexOffset;
292
293                dexOffset = lseek(fd, 0, SEEK_CUR);
294                result = (dexOffset > 0);
295
296                if (result) {
297                    startWhen = dvmGetRelativeTimeUsec();
298                    result = dexZipExtractEntryToFile(&archive, entry, fd) == 0;
299                    extractWhen = dvmGetRelativeTimeUsec();
300                }
301                if (result) {
302                    result = dvmOptimizeDexFile(fd, dexOffset,
303                                dexGetZipEntryUncompLen(&archive, entry),
304                                fileName,
305                                dexGetZipEntryModTime(&archive, entry),
306                                dexGetZipEntryCrc32(&archive, entry),
307                                isBootstrap);
308                }
309
310                if (!result) {
311                    ALOGE("Unable to extract+optimize DEX from '%s'",
312                        fileName);
313                    goto bail;
314                }
315
316                endWhen = dvmGetRelativeTimeUsec();
317                ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms",
318                    fileName,
319                    (int) (extractWhen - startWhen) / 1000,
320                    (int) (endWhen - extractWhen) / 1000);
321            }
322        } else {
323            ALOGI("Zip is good, but no %s inside, and no valid .odex "
324                    "file in the same directory", kDexInJarName);
325            goto bail;
326        }
327    }
328
329    /*
330     * Map the cached version.  This immediately rewinds the fd, so it
331     * doesn't have to be seeked anywhere in particular.
332     */
333    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
334        ALOGI("Unable to map %s in %s", kDexInJarName, fileName);
335        goto bail;
336    }
337
338    if (locked) {
339        /* unlock the fd */
340        if (!dvmUnlockCachedDexFile(fd)) {
341            /* uh oh -- this process needs to exit or we'll wedge the system */
342            ALOGE("Unable to unlock DEX file");
343            goto bail;
344        }
345        locked = false;
346    }
347
348    ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName);
349
350    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
351    (*ppJarFile)->archive = archive;
352    (*ppJarFile)->cacheFileName = cachedName;
353    (*ppJarFile)->pDvmDex = pDvmDex;
354    cachedName = NULL;      // don't free it below
355    result = 0;
356
357bail:
358    /* clean up, closing the open file */
359    if (archiveOpen && result != 0)
360        dexZipCloseArchive(&archive);
361    free(cachedName);
362    if (fd >= 0) {
363        if (locked)
364            (void) dvmUnlockCachedDexFile(fd);
365        close(fd);
366    }
367    return result;
368}
369
370/*
371 * Close a Jar file and free the struct.
372 */
373void dvmJarFileFree(JarFile* pJarFile)
374{
375    if (pJarFile == NULL)
376        return;
377
378    dvmDexFileFree(pJarFile->pDvmDex);
379    dexZipCloseArchive(&pJarFile->archive);
380    free(pJarFile->cacheFileName);
381    free(pJarFile);
382}
383