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