Home | History | Annotate | Download | only in zipalign
      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  * Zip alignment tool
     19  */
     20 #include "ZipFile.h"
     21 
     22 #include <stdlib.h>
     23 #include <stdio.h>
     24 
     25 using namespace android;
     26 
     27 /*
     28  * Show program usage.
     29  */
     30 void usage(void)
     31 {
     32     fprintf(stderr, "Zip alignment utility\n");
     33     fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
     34     fprintf(stderr,
     35         "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
     36         "       zipalign -c [-v] <align> infile.zip\n\n" );
     37     fprintf(stderr,
     38         "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
     39     fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
     40     fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
     41     fprintf(stderr, "  -p: page align stored shared object files\n");
     42     fprintf(stderr, "  -v: verbose output\n");
     43     fprintf(stderr, "  -z: recompress using Zopfli\n");
     44 }
     45 
     46 static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
     47     ZipEntry* pEntry) {
     48 
     49     static const int kPageAlignment = 4096;
     50 
     51     if (!pageAlignSharedLibs) {
     52         return defaultAlignment;
     53     }
     54 
     55     const char* ext = strrchr(pEntry->getFileName(), '.');
     56     if (ext && strcmp(ext, ".so") == 0) {
     57         return kPageAlignment;
     58     }
     59 
     60     return defaultAlignment;
     61 }
     62 
     63 /*
     64  * Copy all entries from "pZin" to "pZout", aligning as needed.
     65  */
     66 static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli,
     67     bool pageAlignSharedLibs)
     68 {
     69     int numEntries = pZin->getNumEntries();
     70     ZipEntry* pEntry;
     71     int bias = 0;
     72     status_t status;
     73 
     74     for (int i = 0; i < numEntries; i++) {
     75         ZipEntry* pNewEntry;
     76         int padding = 0;
     77 
     78         pEntry = pZin->getEntryByIndex(i);
     79         if (pEntry == NULL) {
     80             fprintf(stderr, "ERROR: unable to retrieve entry %d\n", i);
     81             return 1;
     82         }
     83 
     84         if (pEntry->isCompressed()) {
     85             /* copy the entry without padding */
     86             //printf("--- %s: orig at %ld len=%ld (compressed)\n",
     87             //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
     88             //    (long) pEntry->getUncompressedLen());
     89 
     90             if (zopfli) {
     91                 status = pZout->addRecompress(pZin, pEntry, &pNewEntry);
     92                 bias += pNewEntry->getCompressedLen() - pEntry->getCompressedLen();
     93             } else {
     94                 status = pZout->add(pZin, pEntry, padding, &pNewEntry);
     95             }
     96         } else {
     97             const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
     98 
     99             /*
    100              * Copy the entry, adjusting as required.  We assume that the
    101              * file position in the new file will be equal to the file
    102              * position in the original.
    103              */
    104             long newOffset = pEntry->getFileOffset() + bias;
    105             padding = (alignTo - (newOffset % alignTo)) % alignTo;
    106 
    107             //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n",
    108             //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
    109             //    bias, (long) pEntry->getUncompressedLen(), padding);
    110             status = pZout->add(pZin, pEntry, padding, &pNewEntry);
    111         }
    112 
    113         if (status != NO_ERROR)
    114             return 1;
    115         bias += padding;
    116         //printf(" added '%s' at %ld (pad=%d)\n",
    117         //    pNewEntry->getFileName(), (long) pNewEntry->getFileOffset(),
    118         //    padding);
    119     }
    120 
    121     return 0;
    122 }
    123 
    124 /*
    125  * Process a file.  We open the input and output files, failing if the
    126  * output file exists and "force" wasn't specified.
    127  */
    128 static int process(const char* inFileName, const char* outFileName,
    129     int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
    130 {
    131     ZipFile zin, zout;
    132 
    133     //printf("PROCESS: align=%d in='%s' out='%s' force=%d\n",
    134     //    alignment, inFileName, outFileName, force);
    135 
    136     /* this mode isn't supported -- do a trivial check */
    137     if (strcmp(inFileName, outFileName) == 0) {
    138         fprintf(stderr, "Input and output can't be same file\n");
    139         return 1;
    140     }
    141 
    142     /* don't overwrite existing unless given permission */
    143     if (!force && access(outFileName, F_OK) == 0) {
    144         fprintf(stderr, "Output file '%s' exists\n", outFileName);
    145         return 1;
    146     }
    147 
    148     if (zin.open(inFileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
    149         fprintf(stderr, "Unable to open '%s' as zip archive\n", inFileName);
    150         return 1;
    151     }
    152     if (zout.open(outFileName,
    153             ZipFile::kOpenReadWrite|ZipFile::kOpenCreate|ZipFile::kOpenTruncate)
    154         != NO_ERROR)
    155     {
    156         fprintf(stderr, "Unable to open '%s' as zip archive\n", outFileName);
    157         return 1;
    158     }
    159 
    160     int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs);
    161     if (result != 0) {
    162         printf("zipalign: failed rewriting '%s' to '%s'\n",
    163             inFileName, outFileName);
    164     }
    165     return result;
    166 }
    167 
    168 /*
    169  * Verify the alignment of a zip archive.
    170  */
    171 static int verify(const char* fileName, int alignment, bool verbose,
    172     bool pageAlignSharedLibs)
    173 {
    174     ZipFile zipFile;
    175     bool foundBad = false;
    176 
    177     if (verbose)
    178         printf("Verifying alignment of %s (%d)...\n", fileName, alignment);
    179 
    180     if (zipFile.open(fileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
    181         fprintf(stderr, "Unable to open '%s' for verification\n", fileName);
    182         return 1;
    183     }
    184 
    185     int numEntries = zipFile.getNumEntries();
    186     ZipEntry* pEntry;
    187 
    188     for (int i = 0; i < numEntries; i++) {
    189         pEntry = zipFile.getEntryByIndex(i);
    190         if (pEntry->isCompressed()) {
    191             if (verbose) {
    192                 printf("%8ld %s (OK - compressed)\n",
    193                     (long) pEntry->getFileOffset(), pEntry->getFileName());
    194             }
    195         } else {
    196             long offset = pEntry->getFileOffset();
    197             const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
    198             if ((offset % alignTo) != 0) {
    199                 if (verbose) {
    200                     printf("%8ld %s (BAD - %ld)\n",
    201                         (long) offset, pEntry->getFileName(),
    202                         offset % alignTo);
    203                 }
    204                 foundBad = true;
    205             } else {
    206                 if (verbose) {
    207                     printf("%8ld %s (OK)\n",
    208                         (long) offset, pEntry->getFileName());
    209                 }
    210             }
    211         }
    212     }
    213 
    214     if (verbose)
    215         printf("Verification %s\n", foundBad ? "FAILED" : "succesful");
    216 
    217     return foundBad ? 1 : 0;
    218 }
    219 
    220 /*
    221  * Parse args.
    222  */
    223 int main(int argc, char* const argv[])
    224 {
    225     bool wantUsage = false;
    226     bool check = false;
    227     bool force = false;
    228     bool verbose = false;
    229     bool zopfli = false;
    230     bool pageAlignSharedLibs = false;
    231     int result = 1;
    232     int alignment;
    233     char* endp;
    234 
    235     if (argc < 4) {
    236         wantUsage = true;
    237         goto bail;
    238     }
    239 
    240     argc--;
    241     argv++;
    242 
    243     while (argc && argv[0][0] == '-') {
    244         const char* cp = argv[0] +1;
    245 
    246         while (*cp != '\0') {
    247             switch (*cp) {
    248             case 'c':
    249                 check = true;
    250                 break;
    251             case 'f':
    252                 force = true;
    253                 break;
    254             case 'v':
    255                 verbose = true;
    256                 break;
    257             case 'z':
    258                 zopfli = true;
    259                 break;
    260             case 'p':
    261                 pageAlignSharedLibs = true;
    262                 break;
    263             default:
    264                 fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
    265                 wantUsage = true;
    266                 goto bail;
    267             }
    268 
    269             cp++;
    270         }
    271 
    272         argc--;
    273         argv++;
    274     }
    275 
    276     if (!((check && argc == 2) || (!check && argc == 3))) {
    277         wantUsage = true;
    278         goto bail;
    279     }
    280 
    281     alignment = strtol(argv[0], &endp, 10);
    282     if (*endp != '\0' || alignment <= 0) {
    283         fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
    284         wantUsage = true;
    285         goto bail;
    286     }
    287 
    288     if (check) {
    289         /* check existing archive for correct alignment */
    290         result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
    291     } else {
    292         /* create the new archive */
    293         result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
    294 
    295         /* trust, but verify */
    296         if (result == 0) {
    297             result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
    298         }
    299     }
    300 
    301 bail:
    302     if (wantUsage) {
    303         usage();
    304         result = 2;
    305     }
    306 
    307     return result;
    308 }
    309