ZipFile.cpp revision 48a621c2779c74cba3555c96e3fbc7b1989ac90b
1/* 2 * Copyright (C) 2006 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// Access to Zip archives. 19// 20 21#include "ZipFile.h" 22 23#include <memory.h> 24#include <sys/stat.h> 25#include <errno.h> 26#include <assert.h> 27 28using namespace android; 29 30#define LOG(...) fprintf(stderr, __VA_ARGS__) 31 32/* 33 * Open a file and rewrite the headers 34 */ 35status_t ZipFile::rewrite(const char* zipFileName) 36{ 37 assert(mZipFp == NULL); // no reopen 38 39 /* open the file */ 40 mZipFp = fopen(zipFileName, "r+b"); 41 if (mZipFp == NULL) { 42 int err = errno; 43 LOG("fopen failed: %d\n", err); 44 return -1; 45 } 46 47 /* 48 * Load the central directory. If that fails, then this probably 49 * isn't a Zip archive. 50 */ 51 return rewriteCentralDir(); 52} 53 54/* 55 * Find the central directory, read and rewrite the contents. 56 * 57 * The fun thing about ZIP archives is that they may or may not be 58 * readable from start to end. In some cases, notably for archives 59 * that were written to stdout, the only length information is in the 60 * central directory at the end of the file. 61 * 62 * Of course, the central directory can be followed by a variable-length 63 * comment field, so we have to scan through it backwards. The comment 64 * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff 65 * itself, plus apparently sometimes people throw random junk on the end 66 * just for the fun of it. 67 * 68 * This is all a little wobbly. If the wrong value ends up in the EOCD 69 * area, we're hosed. This appears to be the way that everbody handles 70 * it though, so we're in pretty good company if this fails. 71 */ 72status_t ZipFile::rewriteCentralDir(void) 73{ 74 status_t result = 0; 75 unsigned char* buf = NULL; 76 off_t fileLength, seekStart; 77 long readAmount; 78 int i; 79 80 fseek(mZipFp, 0, SEEK_END); 81 fileLength = ftell(mZipFp); 82 rewind(mZipFp); 83 84 /* too small to be a ZIP archive? */ 85 if (fileLength < EndOfCentralDir::kEOCDLen) { 86 LOG("Length is %ld -- too small\n", (long)fileLength); 87 result = -1; 88 goto bail; 89 } 90 91 buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; 92 if (buf == NULL) { 93 LOG("Failure allocating %d bytes for EOCD search", 94 EndOfCentralDir::kMaxEOCDSearch); 95 result = -1; 96 goto bail; 97 } 98 99 if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { 100 seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; 101 readAmount = EndOfCentralDir::kMaxEOCDSearch; 102 } else { 103 seekStart = 0; 104 readAmount = (long) fileLength; 105 } 106 if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { 107 LOG("Failure seeking to end of zip at %ld", (long) seekStart); 108 result = -1; 109 goto bail; 110 } 111 112 /* read the last part of the file into the buffer */ 113 if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { 114 LOG("short file? wanted %ld\n", readAmount); 115 result = -1; 116 goto bail; 117 } 118 119 /* find the end-of-central-dir magic */ 120 for (i = readAmount - 4; i >= 0; i--) { 121 if (buf[i] == 0x50 && 122 ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) 123 { 124 break; 125 } 126 } 127 if (i < 0) { 128 LOG("EOCD not found, not Zip\n"); 129 result = -1; 130 goto bail; 131 } 132 133 /* extract eocd values */ 134 result = mEOCD.readBuf(buf + i, readAmount - i); 135 if (result != 0) { 136 LOG("Failure reading %ld bytes of EOCD values", readAmount - i); 137 goto bail; 138 } 139 140 /* 141 * So far so good. "mCentralDirSize" is the size in bytes of the 142 * central directory, so we can just seek back that far to find it. 143 * We can also seek forward mCentralDirOffset bytes from the 144 * start of the file. 145 * 146 * We're not guaranteed to have the rest of the central dir in the 147 * buffer, nor are we guaranteed that the central dir will have any 148 * sort of convenient size. We need to skip to the start of it and 149 * read the header, then the other goodies. 150 * 151 * The only thing we really need right now is the file comment, which 152 * we're hoping to preserve. 153 */ 154 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { 155 LOG("Failure seeking to central dir offset %ld\n", 156 mEOCD.mCentralDirOffset); 157 result = -1; 158 goto bail; 159 } 160 161 /* 162 * Loop through and read the central dir entries. 163 */ 164 int entry; 165 for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { 166 ZipEntry* pEntry = new ZipEntry; 167 168 result = pEntry->initAndRewriteFromCDE(mZipFp); 169 if (result != 0) { 170 LOG("initFromCDE failed\n"); 171 delete pEntry; 172 goto bail; 173 } 174 175 delete pEntry; 176 } 177 178 179 /* 180 * If all went well, we should now be back at the EOCD. 181 */ 182 unsigned char checkBuf[4]; 183 if (fread(checkBuf, 1, 4, mZipFp) != 4) { 184 LOG("EOCD check read failed\n"); 185 result = -1; 186 goto bail; 187 } 188 if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { 189 LOG("EOCD read check failed\n"); 190 result = -1; 191 goto bail; 192 } 193 194bail: 195 delete[] buf; 196 return result; 197} 198 199/* 200 * =========================================================================== 201 * ZipFile::EndOfCentralDir 202 * =========================================================================== 203 */ 204 205/* 206 * Read the end-of-central-dir fields. 207 * 208 * "buf" should be positioned at the EOCD signature, and should contain 209 * the entire EOCD area including the comment. 210 */ 211status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) 212{ 213 unsigned short diskNumber, diskWithCentralDir, numEntries; 214 215 if (len < kEOCDLen) { 216 /* looks like ZIP file got truncated */ 217 LOG(" Zip EOCD: expected >= %d bytes, found %d\n", 218 kEOCDLen, len); 219 return -1; 220 } 221 222 /* this should probably be an assert() */ 223 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) 224 return -1; 225 226 diskNumber = ZipEntry::getShortLE(&buf[0x04]); 227 diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); 228 numEntries = ZipEntry::getShortLE(&buf[0x08]); 229 mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); 230 mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); 231 232 if (diskNumber != 0 || diskWithCentralDir != 0 || 233 numEntries != mTotalNumEntries) 234 { 235 LOG("Archive spanning not supported\n"); 236 return -1; 237 } 238 239 return 0; 240} 241