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 off_t 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 != OK)
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) != OK) {
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 != OK)
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) != OK) {
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("%8jd %s (OK - compressed)\n",
194 (intmax_t) pEntry->getFileOffset(), pEntry->getFileName());
195 }
196 } else {
197 off_t offset = pEntry->getFileOffset();
198 const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry);
199 if ((offset % alignTo) != 0) {
200 if (verbose) {
201 printf("%8jd %s (BAD - %jd)\n",
202 (intmax_t) offset, pEntry->getFileName(),
203 (intmax_t) (offset % alignTo));
204 }
205 foundBad = true;
206 } else {
207 if (verbose) {
208 printf("%8jd %s (OK)\n",
209 (intmax_t) 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