blob: 9e3cb66d669d9e086729f61559c786d3bced0423 [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
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