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