OSDN Git Service

Merge change 9384
[android-x86/dalvik.git] / vm / JarFile.c
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