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