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