Home | History | Annotate | Download | only in libdex
      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  * Some utility functions for use with command-line utilities.
     18  */
     19 #include "DexFile.h"
     20 #include "ZipArchive.h"
     21 #include "CmdUtils.h"
     22 
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <strings.h>
     26 #include <fcntl.h>
     27 #include <errno.h>
     28 
     29 #ifndef O_BINARY
     30 #define O_BINARY 0
     31 #endif
     32 
     33 /*
     34  * Extract "classes.dex" from archive file.
     35  *
     36  * If "quiet" is set, don't report common errors.
     37  */
     38 UnzipToFileResult dexUnzipToFile(const char* zipFileName,
     39     const char* outFileName, bool quiet)
     40 {
     41     UnzipToFileResult result = kUTFRSuccess;
     42     static const char* kFileToExtract = "classes.dex";
     43     ZipArchive archive;
     44     ZipEntry entry;
     45     bool unlinkOnFailure = false;
     46     int fd = -1;
     47 
     48     if (dexZipOpenArchive(zipFileName, &archive) != 0) {
     49         if (!quiet) {
     50             fprintf(stderr, "Unable to open '%s' as zip archive\n",
     51                 zipFileName);
     52         }
     53         result = kUTFRNotZip;
     54         goto bail;
     55     }
     56 
     57     fd = open(outFileName, O_WRONLY | O_CREAT | O_EXCL, 0600);
     58     if (fd < 0) {
     59         fprintf(stderr, "Unable to create output file '%s': %s\n",
     60             outFileName, strerror(errno));
     61         result = kUTFROutputFileProblem;
     62         goto bail;
     63     }
     64 
     65     unlinkOnFailure = true;
     66 
     67     entry = dexZipFindEntry(&archive, kFileToExtract);
     68     if (entry == NULL) {
     69         if (!quiet) {
     70             fprintf(stderr, "Unable to find '%s' in '%s'\n",
     71                 kFileToExtract, zipFileName);
     72         }
     73         result = kUTFRNoClassesDex;
     74         goto bail;
     75     }
     76 
     77     if (dexZipExtractEntryToFile(&archive, entry, fd) != 0) {
     78         fprintf(stderr, "Extract of '%s' from '%s' failed\n",
     79             kFileToExtract, zipFileName);
     80         result = kUTFRBadZip;
     81         goto bail;
     82     }
     83 
     84 bail:
     85     if (fd >= 0)
     86         close(fd);
     87     if (unlinkOnFailure && result != kUTFRSuccess)
     88         unlink(outFileName);
     89     dexZipCloseArchive(&archive);
     90     return result;
     91 }
     92 
     93 /*
     94  * Map the specified DEX file read-only (possibly after expanding it into a
     95  * temp file from a Jar).  Pass in a MemMapping struct to hold the info.
     96  * If the file is an unoptimized DEX file, then byte-swapping and structural
     97  * verification are performed on it before the memory is made read-only.
     98  *
     99  * The temp file is deleted after the map succeeds.
    100  *
    101  * This is intended for use by tools (e.g. dexdump) that need to get a
    102  * read-only copy of a DEX file that could be in a number of different states.
    103  *
    104  * If "tempFileName" is NULL, a default value is used.  The temp file is
    105  * deleted after the map succeeds.
    106  *
    107  * If "quiet" is set, don't report common errors.
    108  *
    109  * Returns 0 (kUTFRSuccess) on success.
    110  */
    111 UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
    112     MemMapping* pMap, bool quiet)
    113 {
    114     UnzipToFileResult result = kUTFRGenericFailure;
    115     int len = strlen(fileName);
    116     char tempNameBuf[32];
    117     bool removeTemp = false;
    118     int fd = -1;
    119 
    120     if (len < 5) {
    121         if (!quiet) {
    122             fprintf(stderr,
    123                 "ERROR: filename must end in .dex, .zip, .jar, or .apk\n");
    124         }
    125         result = kUTFRBadArgs;
    126         goto bail;
    127     }
    128 
    129     if (strcasecmp(fileName + len -3, "dex") != 0) {
    130         if (tempFileName == NULL) {
    131             /*
    132              * Try .zip/.jar/.apk, all of which are Zip archives with
    133              * "classes.dex" inside.  We need to extract the compressed
    134              * data to a temp file, the location of which varies.
    135              *
    136              * On the device we must use /sdcard because most other
    137              * directories aren't writable (either because of permissions
    138              * or because the volume is mounted read-only).  On desktop
    139              * it's nice to use the designated temp directory.
    140              */
    141             if (access("/tmp", W_OK) == 0) {
    142                 sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
    143             } else if (access("/sdcard", W_OK) == 0) {
    144                 sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
    145             } else {
    146                 fprintf(stderr,
    147                     "NOTE: /tmp and /sdcard unavailable for temp files\n");
    148                 sprintf(tempNameBuf, "dex-temp-%d", getpid());
    149             }
    150 
    151             tempFileName = tempNameBuf;
    152         }
    153 
    154         result = dexUnzipToFile(fileName, tempFileName, quiet);
    155 
    156         if (result == kUTFRSuccess) {
    157             //printf("+++ Good unzip to '%s'\n", tempFileName);
    158             fileName = tempFileName;
    159             removeTemp = true;
    160         } else if (result == kUTFRNotZip) {
    161             if (!quiet) {
    162                 fprintf(stderr, "Not Zip, retrying as DEX\n");
    163             }
    164         } else {
    165             if (!quiet && result == kUTFRNoClassesDex) {
    166                 fprintf(stderr, "Zip has no classes.dex\n");
    167             }
    168             goto bail;
    169         }
    170     }
    171 
    172     result = kUTFRGenericFailure;
    173 
    174     /*
    175      * Pop open the (presumed) DEX file.
    176      */
    177     fd = open(fileName, O_RDONLY | O_BINARY);
    178     if (fd < 0) {
    179         if (!quiet) {
    180             fprintf(stderr, "ERROR: unable to open '%s': %s\n",
    181                 fileName, strerror(errno));
    182         }
    183         goto bail;
    184     }
    185 
    186     if (sysMapFileInShmemWritableReadOnly(fd, pMap) != 0) {
    187         fprintf(stderr, "ERROR: Unable to map '%s'\n", fileName);
    188         goto bail;
    189     }
    190 
    191     /*
    192      * This call will fail if the file exists on a filesystem that
    193      * doesn't support mprotect(). If that's the case, then the file
    194      * will have already been mapped private-writable by the previous
    195      * call, so we don't need to do anything special if this call
    196      * returns non-zero.
    197      */
    198     sysChangeMapAccess(pMap->addr, pMap->length, true, pMap);
    199 
    200     if (dexSwapAndVerifyIfNecessary((u1*) pMap->addr, pMap->length)) {
    201         fprintf(stderr, "ERROR: Failed structural verification of '%s'\n",
    202             fileName);
    203         goto bail;
    204     }
    205 
    206     /*
    207      * Similar to above, this call will fail if the file wasn't ever
    208      * read-only to begin with. This is innocuous, though it is
    209      * undesirable from a memory hygiene perspective.
    210      */
    211     sysChangeMapAccess(pMap->addr, pMap->length, false, pMap);
    212 
    213     /*
    214      * Success!  Close the file and return with the start/length in pMap.
    215      */
    216     result = kUTFRSuccess;
    217 
    218 bail:
    219     if (fd >= 0)
    220         close(fd);
    221     if (removeTemp) {
    222         /* this will fail if the OS doesn't allow removal of a mapped file */
    223         if (unlink(tempFileName) != 0) {
    224             fprintf(stderr, "WARNING: unable to remove temp '%s'\n",
    225                 tempFileName);
    226         }
    227     }
    228     return result;
    229 }
    230