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 25 using namespace android; 26 27 /* 28 * Show program usage. 29 */ 30 void 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 */ 47 static 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 */ 100 static 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 */ 143 static 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 */ 193 int 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 262 bail: 263 if (wantUsage) { 264 usage(); 265 result = 2; 266 } 267 268 return result; 269 } 270