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