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 */ 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 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 */ 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 */ 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 */ 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 */ 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