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