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