blob: 3002a65bd53b5e136e9f9b4de4dd1f400dca4aa9 [file] [log] [blame]
Dan Willemsen48a621c2015-10-29 16:33:05 -07001/*
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>
Dan Willemsen41bc4242015-11-04 14:08:20 -080027#include <inttypes.h>
Dan Willemsen48a621c2015-10-29 16:33:05 -070028
29using namespace android;
30
31#define LOG(...) fprintf(stderr, __VA_ARGS__)
32
33/*
34 * Open a file and rewrite the headers
35 */
36status_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) {
Elliott Hughes04434732023-01-10 22:59:40 +000043 LOG("fopen \"%s\" failed: %s\n", zipFileName, strerror(errno));
Dan Willemsen48a621c2015-10-29 16:33:05 -070044 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{
Elliott Hughes04434732023-01-10 22:59:40 +000074 fseeko(mZipFp, 0, SEEK_END);
75 off_t fileLength = ftello(mZipFp);
Dan Willemsen48a621c2015-10-29 16:33:05 -070076 rewind(mZipFp);
77
78 /* too small to be a ZIP archive? */
79 if (fileLength < EndOfCentralDir::kEOCDLen) {
Elliott Hughes04434732023-01-10 22:59:40 +000080 LOG("Length is %lld -- too small\n", (long long) fileLength);
81 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -070082 }
83
Elliott Hughes04434732023-01-10 22:59:40 +000084 off_t seekStart;
85 size_t readAmount;
Dan Willemsen48a621c2015-10-29 16:33:05 -070086 if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
87 seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
88 readAmount = EndOfCentralDir::kMaxEOCDSearch;
89 } else {
90 seekStart = 0;
Elliott Hughes04434732023-01-10 22:59:40 +000091 readAmount = fileLength;
Dan Willemsen48a621c2015-10-29 16:33:05 -070092 }
Elliott Hughes04434732023-01-10 22:59:40 +000093 if (fseeko(mZipFp, seekStart, SEEK_SET) != 0) {
94 LOG("Failure seeking to end of zip at %lld", (long long) seekStart);
95 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -070096 }
97
98 /* read the last part of the file into the buffer */
Elliott Hughes04434732023-01-10 22:59:40 +000099 uint8_t buf[EndOfCentralDir::kMaxEOCDSearch];
100 if (fread(buf, 1, readAmount, mZipFp) != readAmount) {
101 LOG("short file? wanted %zu\n", readAmount);
102 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700103 }
104
105 /* find the end-of-central-dir magic */
Elliott Hughes04434732023-01-10 22:59:40 +0000106 int i;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700107 for (i = readAmount - 4; i >= 0; i--) {
108 if (buf[i] == 0x50 &&
109 ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
110 {
111 break;
112 }
113 }
114 if (i < 0) {
115 LOG("EOCD not found, not Zip\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000116 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700117 }
118
119 /* extract eocd values */
Elliott Hughes04434732023-01-10 22:59:40 +0000120 status_t result = mEOCD.readBuf(buf + i, readAmount - i);
Dan Willemsen48a621c2015-10-29 16:33:05 -0700121 if (result != 0) {
Elliott Hughes04434732023-01-10 22:59:40 +0000122 LOG("Failure reading %zu bytes of EOCD values", readAmount - i);
123 return result;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700124 }
125
126 /*
127 * So far so good. "mCentralDirSize" is the size in bytes of the
128 * central directory, so we can just seek back that far to find it.
129 * We can also seek forward mCentralDirOffset bytes from the
130 * start of the file.
131 *
132 * We're not guaranteed to have the rest of the central dir in the
133 * buffer, nor are we guaranteed that the central dir will have any
134 * sort of convenient size. We need to skip to the start of it and
135 * read the header, then the other goodies.
136 *
137 * The only thing we really need right now is the file comment, which
138 * we're hoping to preserve.
139 */
Elliott Hughes04434732023-01-10 22:59:40 +0000140 if (fseeko(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800141 LOG("Failure seeking to central dir offset %" PRIu32 "\n",
Dan Willemsen48a621c2015-10-29 16:33:05 -0700142 mEOCD.mCentralDirOffset);
Elliott Hughes04434732023-01-10 22:59:40 +0000143 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700144 }
145
146 /*
147 * Loop through and read the central dir entries.
148 */
Elliott Hughes04434732023-01-10 22:59:40 +0000149 for (int entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
Dan Willemsen48a621c2015-10-29 16:33:05 -0700150 ZipEntry* pEntry = new ZipEntry;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700151 result = pEntry->initAndRewriteFromCDE(mZipFp);
Elliott Hughes04434732023-01-10 22:59:40 +0000152 delete pEntry;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700153 if (result != 0) {
154 LOG("initFromCDE failed\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000155 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700156 }
Dan Willemsen48a621c2015-10-29 16:33:05 -0700157 }
158
Dan Willemsen48a621c2015-10-29 16:33:05 -0700159 /*
160 * If all went well, we should now be back at the EOCD.
161 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800162 uint8_t checkBuf[4];
Dan Willemsen48a621c2015-10-29 16:33:05 -0700163 if (fread(checkBuf, 1, 4, mZipFp) != 4) {
164 LOG("EOCD check read failed\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000165 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700166 }
167 if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
168 LOG("EOCD read check failed\n");
Elliott Hughes04434732023-01-10 22:59:40 +0000169 return -1;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700170 }
171
Elliott Hughes04434732023-01-10 22:59:40 +0000172 return 0;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700173}
174
175/*
176 * ===========================================================================
177 * ZipFile::EndOfCentralDir
178 * ===========================================================================
179 */
180
181/*
182 * Read the end-of-central-dir fields.
183 *
184 * "buf" should be positioned at the EOCD signature, and should contain
185 * the entire EOCD area including the comment.
186 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800187status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
Dan Willemsen48a621c2015-10-29 16:33:05 -0700188{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800189 uint16_t diskNumber, diskWithCentralDir, numEntries;
Dan Willemsen48a621c2015-10-29 16:33:05 -0700190
191 if (len < kEOCDLen) {
192 /* looks like ZIP file got truncated */
193 LOG(" Zip EOCD: expected >= %d bytes, found %d\n",
194 kEOCDLen, len);
195 return -1;
196 }
197
198 /* this should probably be an assert() */
199 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
200 return -1;
201
202 diskNumber = ZipEntry::getShortLE(&buf[0x04]);
203 diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
204 numEntries = ZipEntry::getShortLE(&buf[0x08]);
205 mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
206 mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
207
208 if (diskNumber != 0 || diskWithCentralDir != 0 ||
209 numEntries != mTotalNumEntries)
210 {
211 LOG("Archive spanning not supported\n");
212 return -1;
213 }
214
215 return 0;
216}