2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 // Access to Zip archives.
29 using namespace android;
31 #define LOG(...) fprintf(stderr, __VA_ARGS__)
34 * Open a file and rewrite the headers
36 status_t ZipFile::rewrite(const char* zipFileName)
38 assert(mZipFp == NULL); // no reopen
41 mZipFp = fopen(zipFileName, "r+b");
44 LOG("fopen failed: %d\n", err);
49 * Load the central directory. If that fails, then this probably
50 * isn't a Zip archive.
52 return rewriteCentralDir();
56 * Find the central directory, read and rewrite the contents.
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.
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.
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.
73 status_t ZipFile::rewriteCentralDir(void)
77 off_t fileLength, seekStart;
81 fseek(mZipFp, 0, SEEK_END);
82 fileLength = ftell(mZipFp);
85 /* too small to be a ZIP archive? */
86 if (fileLength < EndOfCentralDir::kEOCDLen) {
87 LOG("Length is %ld -- too small\n", (long)fileLength);
92 buf = new uint8_t[EndOfCentralDir::kMaxEOCDSearch];
94 LOG("Failure allocating %d bytes for EOCD search",
95 EndOfCentralDir::kMaxEOCDSearch);
100 if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
101 seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
102 readAmount = EndOfCentralDir::kMaxEOCDSearch;
105 readAmount = (long) fileLength;
107 if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
108 LOG("Failure seeking to end of zip at %ld", (long) seekStart);
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);
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)
129 LOG("EOCD not found, not Zip\n");
134 /* extract eocd values */
135 result = mEOCD.readBuf(buf + i, readAmount - i);
137 LOG("Failure reading %ld bytes of EOCD values", readAmount - i);
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
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.
152 * The only thing we really need right now is the file comment, which
153 * we're hoping to preserve.
155 if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
156 LOG("Failure seeking to central dir offset %" PRIu32 "\n",
157 mEOCD.mCentralDirOffset);
163 * Loop through and read the central dir entries.
166 for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
167 ZipEntry* pEntry = new ZipEntry;
169 result = pEntry->initAndRewriteFromCDE(mZipFp);
171 LOG("initFromCDE failed\n");
181 * If all went well, we should now be back at the EOCD.
184 if (fread(checkBuf, 1, 4, mZipFp) != 4) {
185 LOG("EOCD check read failed\n");
189 if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
190 LOG("EOCD read check failed\n");
201 * ===========================================================================
202 * ZipFile::EndOfCentralDir
203 * ===========================================================================
207 * Read the end-of-central-dir fields.
209 * "buf" should be positioned at the EOCD signature, and should contain
210 * the entire EOCD area including the comment.
212 status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
214 uint16_t diskNumber, diskWithCentralDir, numEntries;
216 if (len < kEOCDLen) {
217 /* looks like ZIP file got truncated */
218 LOG(" Zip EOCD: expected >= %d bytes, found %d\n",
223 /* this should probably be an assert() */
224 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
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]);
233 if (diskNumber != 0 || diskWithCentralDir != 0 ||
234 numEntries != mTotalNumEntries)
236 LOG("Archive spanning not supported\n");