• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
openAlternateSuffix(const char * fileName,const char * suffix,int flags,char ** pCachedName)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  * Note: This should parallel the logic of dvmJarFileOpen.
87  */
dvmDexCacheStatus(const char * fileName)88 DexCacheStatus 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 
128 tryArchive:
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 
173 bail:
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  */
dvmJarFileOpen(const char * fileName,const char * odexOutputName,JarFile ** ppJarFile,bool isBootstrap)192 int 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 
241 tryArchive:
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 
357 bail:
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  */
dvmJarFileFree(JarFile * pJarFile)373 void 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