ZipAlign.cpp revision e0b8d19f3159edbeecb7eea4b3d18ffeca3dd33d
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
25using namespace android;
26
27/*
28 * Show program usage.
29 */
30void 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
46static 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 */
66static 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 */
128static 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 */
171static 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 */
223int 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
301bail:
302    if (wantUsage) {
303        usage();
304        result = 2;
305    }
306
307    return result;
308}
309