blob: c4c898e9b3398a31daaef891291a6c1343a59cf6 [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>
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}