ZipAlign.cpp revision 303213915dc15d3136c5410d2eeea10f5555d3bb
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] [-v] <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, "  -v: verbose output\n");
42}
43
44/*
45 * Copy all entries from "pZin" to "pZout", aligning as needed.
46 */
47static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment)
48{
49    int numEntries = pZin->getNumEntries();
50    ZipEntry* pEntry;
51    int bias = 0;
52    status_t status;
53
54    for (int i = 0; i < numEntries; i++) {
55        ZipEntry* pNewEntry;
56        int padding = 0;
57
58        pEntry = pZin->getEntryByIndex(i);
59        if (pEntry == NULL) {
60            fprintf(stderr, "ERROR: unable to retrieve entry %d\n", i);
61            return 1;
62        }
63
64        if (pEntry->isCompressed()) {
65            /* copy the entry without padding */
66            //printf("--- %s: orig at %ld len=%ld (compressed)\n",
67            //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
68            //    (long) pEntry->getUncompressedLen());
69
70        } else {
71            /*
72             * Copy the entry, adjusting as required.  We assume that the
73             * file position in the new file will be equal to the file
74             * position in the original.
75             */
76            long newOffset = pEntry->getFileOffset() + bias;
77            padding = (alignment - (newOffset % alignment)) % alignment;
78
79            //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n",
80            //    pEntry->getFileName(), (long) pEntry->getFileOffset(),
81            //    bias, (long) pEntry->getUncompressedLen(), padding);
82        }
83
84        status = pZout->add(pZin, pEntry, padding, &pNewEntry);
85        if (status != NO_ERROR)
86            return 1;
87        bias += padding;
88        //printf(" added '%s' at %ld (pad=%d)\n",
89        //    pNewEntry->getFileName(), (long) pNewEntry->getFileOffset(),
90        //    padding);
91    }
92
93    return 0;
94}
95
96/*
97 * Process a file.  We open the input and output files, failing if the
98 * output file exists and "force" wasn't specified.
99 */
100static int process(const char* inFileName, const char* outFileName,
101    int alignment, bool force)
102{
103    ZipFile zin, zout;
104
105    //printf("PROCESS: align=%d in='%s' out='%s' force=%d\n",
106    //    alignment, inFileName, outFileName, force);
107
108    /* this mode isn't supported -- do a trivial check */
109    if (strcmp(inFileName, outFileName) == 0) {
110        fprintf(stderr, "Input and output can't be same file\n");
111        return 1;
112    }
113
114    /* don't overwrite existing unless given permission */
115    if (!force && access(outFileName, F_OK) == 0) {
116        fprintf(stderr, "Output file '%s' exists\n", outFileName);
117        return 1;
118    }
119
120    if (zin.open(inFileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
121        fprintf(stderr, "Unable to open '%s' as zip archive\n", inFileName);
122        return 1;
123    }
124    if (zout.open(outFileName,
125            ZipFile::kOpenReadWrite|ZipFile::kOpenCreate|ZipFile::kOpenTruncate)
126        != NO_ERROR)
127    {
128        fprintf(stderr, "Unable to open '%s' as zip archive\n", outFileName);
129        return 1;
130    }
131
132    int result = copyAndAlign(&zin, &zout, alignment);
133    if (result != 0) {
134        printf("zipalign: failed rewriting '%s' to '%s'\n",
135            inFileName, outFileName);
136    }
137    return result;
138}
139
140/*
141 * Verify the alignment of a zip archive.
142 */
143static int verify(const char* fileName, int alignment, bool verbose)
144{
145    ZipFile zipFile;
146    bool foundBad = false;
147
148    if (verbose)
149        printf("Verifying alignment of %s (%d)...\n", fileName, alignment);
150
151    if (zipFile.open(fileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
152        fprintf(stderr, "Unable to open '%s' for verification\n", fileName);
153        return 1;
154    }
155
156    int numEntries = zipFile.getNumEntries();
157    ZipEntry* pEntry;
158
159    for (int i = 0; i < numEntries; i++) {
160        pEntry = zipFile.getEntryByIndex(i);
161        if (pEntry->isCompressed()) {
162            if (verbose) {
163                printf("%8ld %s (OK - compressed)\n",
164                    (long) pEntry->getFileOffset(), pEntry->getFileName());
165            }
166        } else {
167            long offset = pEntry->getFileOffset();
168            if ((offset % alignment) != 0) {
169                if (verbose) {
170                    printf("%8ld %s (BAD - %ld)\n",
171                        (long) offset, pEntry->getFileName(),
172                        offset % alignment);
173                }
174                foundBad = true;
175            } else {
176                if (verbose) {
177                    printf("%8ld %s (OK)\n",
178                        (long) offset, pEntry->getFileName());
179                }
180            }
181        }
182    }
183
184    if (verbose)
185        printf("Verification %s\n", foundBad ? "FAILED" : "succesful");
186
187    return foundBad ? 1 : 0;
188}
189
190/*
191 * Parse args.
192 */
193int main(int argc, char* const argv[])
194{
195    bool wantUsage = false;
196    bool check = false;
197    bool force = false;
198    bool verbose = false;
199    int result = 1;
200    int alignment;
201    char* endp;
202
203    if (argc < 4) {
204        wantUsage = true;
205        goto bail;
206    }
207
208    argc--;
209    argv++;
210
211    while (argc && argv[0][0] == '-') {
212        const char* cp = argv[0] +1;
213
214        while (*cp != '\0') {
215            switch (*cp) {
216            case 'c':
217                check = true;
218                break;
219            case 'f':
220                force = true;
221                break;
222            case 'v':
223                verbose = true;
224                break;
225            default:
226                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
227                wantUsage = true;
228                goto bail;
229            }
230
231            cp++;
232        }
233
234        argc--;
235        argv++;
236    }
237
238    if (!((check && argc == 2) || (!check && argc == 3))) {
239        wantUsage = true;
240        goto bail;
241    }
242
243    alignment = strtol(argv[0], &endp, 10);
244    if (*endp != '\0' || alignment <= 0) {
245        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
246        wantUsage = true;
247        goto bail;
248    }
249
250    if (check) {
251        /* check existing archive for correct alignment */
252        result = verify(argv[1], alignment, verbose);
253    } else {
254        /* create the new archive */
255        result = process(argv[1], argv[2], alignment, force);
256
257        /* trust, but verify */
258        if (result == 0)
259            result = verify(argv[2], alignment, verbose);
260    }
261
262bail:
263    if (wantUsage) {
264        usage();
265        result = 2;
266    }
267
268    return result;
269}
270