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