Commit | Line | Data |
---|---|---|
1f170917 MA |
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 entries in a Zip archive. | |
19 | // | |
20 | ||
21 | #define LOG_TAG "zip" | |
22 | ||
23 | #include "ZipEntry.h" | |
24 | #include <utils/Log.h> | |
25 | ||
26 | #include <stdio.h> | |
27 | #include <string.h> | |
28 | #include <assert.h> | |
29 | ||
30 | using namespace android; | |
31 | ||
32 | /* | |
33 | * Initialize a new ZipEntry structure from a FILE* positioned at a | |
34 | * CentralDirectoryEntry. | |
35 | * | |
36 | * On exit, the file pointer will be at the start of the next CDE or | |
37 | * at the EOCD. | |
38 | */ | |
39 | status_t ZipEntry::initFromCDE(FILE* fp) | |
40 | { | |
41 | status_t result; | |
42 | long posn; | |
43 | bool hasDD; | |
44 | ||
6f7d9cb9 | 45 | //ALOGV("initFromCDE ---\n"); |
1f170917 MA |
46 | |
47 | /* read the CDE */ | |
48 | result = mCDE.read(fp); | |
49 | if (result != NO_ERROR) { | |
6cf0905b | 50 | ALOGD("mCDE.read failed\n"); |
1f170917 MA |
51 | return result; |
52 | } | |
53 | ||
54 | //mCDE.dump(); | |
55 | ||
56 | /* using the info in the CDE, go load up the LFH */ | |
57 | posn = ftell(fp); | |
58 | if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { | |
6cf0905b | 59 | ALOGD("local header seek failed (%ld)\n", |
1f170917 MA |
60 | mCDE.mLocalHeaderRelOffset); |
61 | return UNKNOWN_ERROR; | |
62 | } | |
63 | ||
64 | result = mLFH.read(fp); | |
65 | if (result != NO_ERROR) { | |
6cf0905b | 66 | ALOGD("mLFH.read failed\n"); |
1f170917 MA |
67 | return result; |
68 | } | |
69 | ||
70 | if (fseek(fp, posn, SEEK_SET) != 0) | |
71 | return UNKNOWN_ERROR; | |
72 | ||
73 | //mLFH.dump(); | |
74 | ||
75 | /* | |
76 | * We *might* need to read the Data Descriptor at this point and | |
77 | * integrate it into the LFH. If this bit is set, the CRC-32, | |
78 | * compressed size, and uncompressed size will be zero. In practice | |
79 | * these seem to be rare. | |
80 | */ | |
81 | hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; | |
82 | if (hasDD) { | |
83 | // do something clever | |
6cf0905b | 84 | //ALOGD("+++ has data descriptor\n"); |
1f170917 MA |
85 | } |
86 | ||
87 | /* | |
88 | * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" | |
89 | * flag is set, because the LFH is incomplete. (Not a problem, since we | |
90 | * prefer the CDE values.) | |
91 | */ | |
92 | if (!hasDD && !compareHeaders()) { | |
811e99b2 | 93 | ALOGW("warning: header mismatch\n"); |
1f170917 MA |
94 | // keep going? |
95 | } | |
96 | ||
97 | /* | |
98 | * If the mVersionToExtract is greater than 20, we may have an | |
99 | * issue unpacking the record -- could be encrypted, compressed | |
100 | * with something we don't support, or use Zip64 extensions. We | |
101 | * can defer worrying about that to when we're extracting data. | |
102 | */ | |
103 | ||
104 | return NO_ERROR; | |
105 | } | |
106 | ||
107 | /* | |
108 | * Initialize a new entry. Pass in the file name and an optional comment. | |
109 | * | |
110 | * Initializes the CDE and the LFH. | |
111 | */ | |
112 | void ZipEntry::initNew(const char* fileName, const char* comment) | |
113 | { | |
114 | assert(fileName != NULL && *fileName != '\0'); // name required | |
115 | ||
116 | /* most fields are properly initialized by constructor */ | |
117 | mCDE.mVersionMadeBy = kDefaultMadeBy; | |
118 | mCDE.mVersionToExtract = kDefaultVersion; | |
119 | mCDE.mCompressionMethod = kCompressStored; | |
120 | mCDE.mFileNameLength = strlen(fileName); | |
121 | if (comment != NULL) | |
122 | mCDE.mFileCommentLength = strlen(comment); | |
123 | mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does | |
124 | ||
125 | if (mCDE.mFileNameLength > 0) { | |
126 | mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; | |
127 | strcpy((char*) mCDE.mFileName, fileName); | |
128 | } | |
129 | if (mCDE.mFileCommentLength > 0) { | |
130 | /* TODO: stop assuming null-terminated ASCII here? */ | |
131 | mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; | |
132 | strcpy((char*) mCDE.mFileComment, comment); | |
133 | } | |
134 | ||
135 | copyCDEtoLFH(); | |
136 | } | |
137 | ||
138 | /* | |
139 | * Initialize a new entry, starting with the ZipEntry from a different | |
140 | * archive. | |
141 | * | |
142 | * Initializes the CDE and the LFH. | |
143 | */ | |
144 | status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, | |
145 | const ZipEntry* pEntry) | |
146 | { | |
147 | /* | |
148 | * Copy everything in the CDE over, then fix up the hairy bits. | |
149 | */ | |
150 | memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); | |
151 | ||
152 | if (mCDE.mFileNameLength > 0) { | |
153 | mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; | |
154 | if (mCDE.mFileName == NULL) | |
155 | return NO_MEMORY; | |
156 | strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); | |
157 | } | |
158 | if (mCDE.mFileCommentLength > 0) { | |
159 | mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; | |
160 | if (mCDE.mFileComment == NULL) | |
161 | return NO_MEMORY; | |
162 | strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); | |
163 | } | |
164 | if (mCDE.mExtraFieldLength > 0) { | |
165 | /* we null-terminate this, though it may not be a string */ | |
166 | mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; | |
167 | if (mCDE.mExtraField == NULL) | |
168 | return NO_MEMORY; | |
169 | memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, | |
170 | mCDE.mExtraFieldLength+1); | |
171 | } | |
172 | ||
173 | /* construct the LFH from the CDE */ | |
174 | copyCDEtoLFH(); | |
175 | ||
176 | /* | |
177 | * The LFH "extra" field is independent of the CDE "extra", so we | |
178 | * handle it here. | |
179 | */ | |
180 | assert(mLFH.mExtraField == NULL); | |
181 | mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; | |
182 | if (mLFH.mExtraFieldLength > 0) { | |
183 | mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; | |
184 | if (mLFH.mExtraField == NULL) | |
185 | return NO_MEMORY; | |
186 | memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, | |
187 | mLFH.mExtraFieldLength+1); | |
188 | } | |
189 | ||
190 | return NO_ERROR; | |
191 | } | |
192 | ||
193 | /* | |
194 | * Insert pad bytes in the LFH by tweaking the "extra" field. This will | |
195 | * potentially confuse something that put "extra" data in here earlier, | |
196 | * but I can't find an actual problem. | |
197 | */ | |
198 | status_t ZipEntry::addPadding(int padding) | |
199 | { | |
200 | if (padding <= 0) | |
201 | return INVALID_OPERATION; | |
202 | ||
1b4e8156 | 203 | //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n", |
1f170917 MA |
204 | // padding, mLFH.mExtraFieldLength, mCDE.mFileName); |
205 | ||
206 | if (mLFH.mExtraFieldLength > 0) { | |
207 | /* extend existing field */ | |
208 | unsigned char* newExtra; | |
209 | ||
210 | newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; | |
211 | if (newExtra == NULL) | |
212 | return NO_MEMORY; | |
213 | memset(newExtra + mLFH.mExtraFieldLength, 0, padding); | |
214 | memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); | |
215 | ||
216 | delete[] mLFH.mExtraField; | |
217 | mLFH.mExtraField = newExtra; | |
218 | mLFH.mExtraFieldLength += padding; | |
219 | } else { | |
220 | /* create new field */ | |
221 | mLFH.mExtraField = new unsigned char[padding]; | |
222 | memset(mLFH.mExtraField, 0, padding); | |
223 | mLFH.mExtraFieldLength = padding; | |
224 | } | |
225 | ||
226 | return NO_ERROR; | |
227 | } | |
228 | ||
229 | /* | |
230 | * Set the fields in the LFH equal to the corresponding fields in the CDE. | |
231 | * | |
232 | * This does not touch the LFH "extra" field. | |
233 | */ | |
234 | void ZipEntry::copyCDEtoLFH(void) | |
235 | { | |
236 | mLFH.mVersionToExtract = mCDE.mVersionToExtract; | |
237 | mLFH.mGPBitFlag = mCDE.mGPBitFlag; | |
238 | mLFH.mCompressionMethod = mCDE.mCompressionMethod; | |
239 | mLFH.mLastModFileTime = mCDE.mLastModFileTime; | |
240 | mLFH.mLastModFileDate = mCDE.mLastModFileDate; | |
241 | mLFH.mCRC32 = mCDE.mCRC32; | |
242 | mLFH.mCompressedSize = mCDE.mCompressedSize; | |
243 | mLFH.mUncompressedSize = mCDE.mUncompressedSize; | |
244 | mLFH.mFileNameLength = mCDE.mFileNameLength; | |
245 | // the "extra field" is independent | |
246 | ||
247 | delete[] mLFH.mFileName; | |
248 | if (mLFH.mFileNameLength > 0) { | |
249 | mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; | |
250 | strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); | |
251 | } else { | |
252 | mLFH.mFileName = NULL; | |
253 | } | |
254 | } | |
255 | ||
256 | /* | |
257 | * Set some information about a file after we add it. | |
258 | */ | |
259 | void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, | |
260 | int compressionMethod) | |
261 | { | |
262 | mCDE.mCompressionMethod = compressionMethod; | |
263 | mCDE.mCRC32 = crc32; | |
264 | mCDE.mCompressedSize = compLen; | |
265 | mCDE.mUncompressedSize = uncompLen; | |
266 | mCDE.mCompressionMethod = compressionMethod; | |
267 | if (compressionMethod == kCompressDeflated) { | |
268 | mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used | |
269 | } | |
270 | copyCDEtoLFH(); | |
271 | } | |
272 | ||
273 | /* | |
274 | * See if the data in mCDE and mLFH match up. This is mostly useful for | |
275 | * debugging these classes, but it can be used to identify damaged | |
276 | * archives. | |
277 | * | |
278 | * Returns "false" if they differ. | |
279 | */ | |
280 | bool ZipEntry::compareHeaders(void) const | |
281 | { | |
282 | if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { | |
6f7d9cb9 | 283 | ALOGV("cmp: VersionToExtract\n"); |
1f170917 MA |
284 | return false; |
285 | } | |
286 | if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { | |
6f7d9cb9 | 287 | ALOGV("cmp: GPBitFlag\n"); |
1f170917 MA |
288 | return false; |
289 | } | |
290 | if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { | |
6f7d9cb9 | 291 | ALOGV("cmp: CompressionMethod\n"); |
1f170917 MA |
292 | return false; |
293 | } | |
294 | if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { | |
6f7d9cb9 | 295 | ALOGV("cmp: LastModFileTime\n"); |
1f170917 MA |
296 | return false; |
297 | } | |
298 | if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { | |
6f7d9cb9 | 299 | ALOGV("cmp: LastModFileDate\n"); |
1f170917 MA |
300 | return false; |
301 | } | |
302 | if (mCDE.mCRC32 != mLFH.mCRC32) { | |
6f7d9cb9 | 303 | ALOGV("cmp: CRC32\n"); |
1f170917 MA |
304 | return false; |
305 | } | |
306 | if (mCDE.mCompressedSize != mLFH.mCompressedSize) { | |
6f7d9cb9 | 307 | ALOGV("cmp: CompressedSize\n"); |
1f170917 MA |
308 | return false; |
309 | } | |
310 | if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { | |
6f7d9cb9 | 311 | ALOGV("cmp: UncompressedSize\n"); |
1f170917 MA |
312 | return false; |
313 | } | |
314 | if (mCDE.mFileNameLength != mLFH.mFileNameLength) { | |
6f7d9cb9 | 315 | ALOGV("cmp: FileNameLength\n"); |
1f170917 MA |
316 | return false; |
317 | } | |
318 | #if 0 // this seems to be used for padding, not real data | |
319 | if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { | |
6f7d9cb9 | 320 | ALOGV("cmp: ExtraFieldLength\n"); |
1f170917 MA |
321 | return false; |
322 | } | |
323 | #endif | |
324 | if (mCDE.mFileName != NULL) { | |
325 | if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { | |
6f7d9cb9 | 326 | ALOGV("cmp: FileName\n"); |
1f170917 MA |
327 | return false; |
328 | } | |
329 | } | |
330 | ||
331 | return true; | |
332 | } | |
333 | ||
334 | ||
335 | /* | |
336 | * Convert the DOS date/time stamp into a UNIX time stamp. | |
337 | */ | |
338 | time_t ZipEntry::getModWhen(void) const | |
339 | { | |
340 | struct tm parts; | |
341 | ||
342 | parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; | |
343 | parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; | |
344 | parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; | |
345 | parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); | |
346 | parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; | |
347 | parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; | |
348 | parts.tm_wday = parts.tm_yday = 0; | |
349 | parts.tm_isdst = -1; // DST info "not available" | |
350 | ||
351 | return mktime(&parts); | |
352 | } | |
353 | ||
354 | /* | |
355 | * Set the CDE/LFH timestamp from UNIX time. | |
356 | */ | |
357 | void ZipEntry::setModWhen(time_t when) | |
358 | { | |
359 | #ifdef HAVE_LOCALTIME_R | |
360 | struct tm tmResult; | |
361 | #endif | |
362 | time_t even; | |
363 | unsigned short zdate, ztime; | |
364 | ||
365 | struct tm* ptm; | |
366 | ||
367 | /* round up to an even number of seconds */ | |
368 | even = (time_t)(((unsigned long)(when) + 1) & (~1)); | |
369 | ||
370 | /* expand */ | |
371 | #ifdef HAVE_LOCALTIME_R | |
372 | ptm = localtime_r(&even, &tmResult); | |
373 | #else | |
374 | ptm = localtime(&even); | |
375 | #endif | |
376 | ||
377 | int year; | |
378 | year = ptm->tm_year; | |
379 | if (year < 80) | |
380 | year = 80; | |
381 | ||
382 | zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; | |
383 | ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; | |
384 | ||
385 | mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; | |
386 | mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; | |
387 | } | |
388 | ||
389 | ||
390 | /* | |
391 | * =========================================================================== | |
392 | * ZipEntry::LocalFileHeader | |
393 | * =========================================================================== | |
394 | */ | |
395 | ||
396 | /* | |
397 | * Read a local file header. | |
398 | * | |
399 | * On entry, "fp" points to the signature at the start of the header. | |
400 | * On exit, "fp" points to the start of data. | |
401 | */ | |
402 | status_t ZipEntry::LocalFileHeader::read(FILE* fp) | |
403 | { | |
404 | status_t result = NO_ERROR; | |
405 | unsigned char buf[kLFHLen]; | |
406 | ||
407 | assert(mFileName == NULL); | |
408 | assert(mExtraField == NULL); | |
409 | ||
410 | if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { | |
411 | result = UNKNOWN_ERROR; | |
412 | goto bail; | |
413 | } | |
414 | ||
415 | if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { | |
6cf0905b | 416 | ALOGD("whoops: didn't find expected signature\n"); |
1f170917 MA |
417 | result = UNKNOWN_ERROR; |
418 | goto bail; | |
419 | } | |
420 | ||
421 | mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); | |
422 | mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); | |
423 | mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); | |
424 | mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); | |
425 | mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); | |
426 | mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); | |
427 | mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); | |
428 | mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); | |
429 | mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); | |
430 | mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); | |
431 | ||
432 | // TODO: validate sizes | |
433 | ||
434 | /* grab filename */ | |
435 | if (mFileNameLength != 0) { | |
436 | mFileName = new unsigned char[mFileNameLength+1]; | |
437 | if (mFileName == NULL) { | |
438 | result = NO_MEMORY; | |
439 | goto bail; | |
440 | } | |
441 | if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { | |
442 | result = UNKNOWN_ERROR; | |
443 | goto bail; | |
444 | } | |
445 | mFileName[mFileNameLength] = '\0'; | |
446 | } | |
447 | ||
448 | /* grab extra field */ | |
449 | if (mExtraFieldLength != 0) { | |
450 | mExtraField = new unsigned char[mExtraFieldLength+1]; | |
451 | if (mExtraField == NULL) { | |
452 | result = NO_MEMORY; | |
453 | goto bail; | |
454 | } | |
455 | if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { | |
456 | result = UNKNOWN_ERROR; | |
457 | goto bail; | |
458 | } | |
459 | mExtraField[mExtraFieldLength] = '\0'; | |
460 | } | |
461 | ||
462 | bail: | |
463 | return result; | |
464 | } | |
465 | ||
466 | /* | |
467 | * Write a local file header. | |
468 | */ | |
469 | status_t ZipEntry::LocalFileHeader::write(FILE* fp) | |
470 | { | |
471 | unsigned char buf[kLFHLen]; | |
472 | ||
473 | ZipEntry::putLongLE(&buf[0x00], kSignature); | |
474 | ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); | |
475 | ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); | |
476 | ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); | |
477 | ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); | |
478 | ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); | |
479 | ZipEntry::putLongLE(&buf[0x0e], mCRC32); | |
480 | ZipEntry::putLongLE(&buf[0x12], mCompressedSize); | |
481 | ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); | |
482 | ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); | |
483 | ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); | |
484 | ||
485 | if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) | |
486 | return UNKNOWN_ERROR; | |
487 | ||
488 | /* write filename */ | |
489 | if (mFileNameLength != 0) { | |
490 | if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) | |
491 | return UNKNOWN_ERROR; | |
492 | } | |
493 | ||
494 | /* write "extra field" */ | |
495 | if (mExtraFieldLength != 0) { | |
496 | if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) | |
497 | return UNKNOWN_ERROR; | |
498 | } | |
499 | ||
500 | return NO_ERROR; | |
501 | } | |
502 | ||
503 | ||
504 | /* | |
505 | * Dump the contents of a LocalFileHeader object. | |
506 | */ | |
507 | void ZipEntry::LocalFileHeader::dump(void) const | |
508 | { | |
6cf0905b SB |
509 | ALOGD(" LocalFileHeader contents:\n"); |
510 | ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", | |
1f170917 | 511 | mVersionToExtract, mGPBitFlag, mCompressionMethod); |
6cf0905b | 512 | ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", |
1f170917 | 513 | mLastModFileTime, mLastModFileDate, mCRC32); |
6cf0905b | 514 | ALOGD(" compressedSize=%lu uncompressedSize=%lu\n", |
1f170917 | 515 | mCompressedSize, mUncompressedSize); |
6cf0905b | 516 | ALOGD(" filenameLen=%u extraLen=%u\n", |
1f170917 MA |
517 | mFileNameLength, mExtraFieldLength); |
518 | if (mFileName != NULL) | |
6cf0905b | 519 | ALOGD(" filename: '%s'\n", mFileName); |
1f170917 MA |
520 | } |
521 | ||
522 | ||
523 | /* | |
524 | * =========================================================================== | |
525 | * ZipEntry::CentralDirEntry | |
526 | * =========================================================================== | |
527 | */ | |
528 | ||
529 | /* | |
530 | * Read the central dir entry that appears next in the file. | |
531 | * | |
532 | * On entry, "fp" should be positioned on the signature bytes for the | |
533 | * entry. On exit, "fp" will point at the signature word for the next | |
534 | * entry or for the EOCD. | |
535 | */ | |
536 | status_t ZipEntry::CentralDirEntry::read(FILE* fp) | |
537 | { | |
538 | status_t result = NO_ERROR; | |
539 | unsigned char buf[kCDELen]; | |
540 | ||
541 | /* no re-use */ | |
542 | assert(mFileName == NULL); | |
543 | assert(mExtraField == NULL); | |
544 | assert(mFileComment == NULL); | |
545 | ||
546 | if (fread(buf, 1, kCDELen, fp) != kCDELen) { | |
547 | result = UNKNOWN_ERROR; | |
548 | goto bail; | |
549 | } | |
550 | ||
551 | if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { | |
6cf0905b | 552 | ALOGD("Whoops: didn't find expected signature\n"); |
1f170917 MA |
553 | result = UNKNOWN_ERROR; |
554 | goto bail; | |
555 | } | |
556 | ||
557 | mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); | |
558 | mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); | |
559 | mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); | |
560 | mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); | |
561 | mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); | |
562 | mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); | |
563 | mCRC32 = ZipEntry::getLongLE(&buf[0x10]); | |
564 | mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); | |
565 | mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); | |
566 | mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); | |
567 | mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); | |
568 | mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); | |
569 | mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); | |
570 | mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); | |
571 | mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); | |
572 | mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); | |
573 | ||
574 | // TODO: validate sizes and offsets | |
575 | ||
576 | /* grab filename */ | |
577 | if (mFileNameLength != 0) { | |
578 | mFileName = new unsigned char[mFileNameLength+1]; | |
579 | if (mFileName == NULL) { | |
580 | result = NO_MEMORY; | |
581 | goto bail; | |
582 | } | |
583 | if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { | |
584 | result = UNKNOWN_ERROR; | |
585 | goto bail; | |
586 | } | |
587 | mFileName[mFileNameLength] = '\0'; | |
588 | } | |
589 | ||
590 | /* read "extra field" */ | |
591 | if (mExtraFieldLength != 0) { | |
592 | mExtraField = new unsigned char[mExtraFieldLength+1]; | |
593 | if (mExtraField == NULL) { | |
594 | result = NO_MEMORY; | |
595 | goto bail; | |
596 | } | |
597 | if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { | |
598 | result = UNKNOWN_ERROR; | |
599 | goto bail; | |
600 | } | |
601 | mExtraField[mExtraFieldLength] = '\0'; | |
602 | } | |
603 | ||
604 | ||
605 | /* grab comment, if any */ | |
606 | if (mFileCommentLength != 0) { | |
607 | mFileComment = new unsigned char[mFileCommentLength+1]; | |
608 | if (mFileComment == NULL) { | |
609 | result = NO_MEMORY; | |
610 | goto bail; | |
611 | } | |
612 | if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) | |
613 | { | |
614 | result = UNKNOWN_ERROR; | |
615 | goto bail; | |
616 | } | |
617 | mFileComment[mFileCommentLength] = '\0'; | |
618 | } | |
619 | ||
620 | bail: | |
621 | return result; | |
622 | } | |
623 | ||
624 | /* | |
625 | * Write a central dir entry. | |
626 | */ | |
627 | status_t ZipEntry::CentralDirEntry::write(FILE* fp) | |
628 | { | |
629 | unsigned char buf[kCDELen]; | |
630 | ||
631 | ZipEntry::putLongLE(&buf[0x00], kSignature); | |
632 | ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); | |
633 | ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); | |
634 | ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); | |
635 | ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); | |
636 | ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); | |
637 | ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); | |
638 | ZipEntry::putLongLE(&buf[0x10], mCRC32); | |
639 | ZipEntry::putLongLE(&buf[0x14], mCompressedSize); | |
640 | ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); | |
641 | ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); | |
642 | ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); | |
643 | ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); | |
644 | ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); | |
645 | ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); | |
646 | ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); | |
647 | ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); | |
648 | ||
649 | if (fwrite(buf, 1, kCDELen, fp) != kCDELen) | |
650 | return UNKNOWN_ERROR; | |
651 | ||
652 | /* write filename */ | |
653 | if (mFileNameLength != 0) { | |
654 | if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) | |
655 | return UNKNOWN_ERROR; | |
656 | } | |
657 | ||
658 | /* write "extra field" */ | |
659 | if (mExtraFieldLength != 0) { | |
660 | if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) | |
661 | return UNKNOWN_ERROR; | |
662 | } | |
663 | ||
664 | /* write comment */ | |
665 | if (mFileCommentLength != 0) { | |
666 | if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) | |
667 | return UNKNOWN_ERROR; | |
668 | } | |
669 | ||
670 | return NO_ERROR; | |
671 | } | |
672 | ||
673 | /* | |
674 | * Dump the contents of a CentralDirEntry object. | |
675 | */ | |
676 | void ZipEntry::CentralDirEntry::dump(void) const | |
677 | { | |
6cf0905b SB |
678 | ALOGD(" CentralDirEntry contents:\n"); |
679 | ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", | |
1f170917 | 680 | mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); |
6cf0905b | 681 | ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", |
1f170917 | 682 | mLastModFileTime, mLastModFileDate, mCRC32); |
6cf0905b | 683 | ALOGD(" compressedSize=%lu uncompressedSize=%lu\n", |
1f170917 | 684 | mCompressedSize, mUncompressedSize); |
6cf0905b | 685 | ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", |
1f170917 | 686 | mFileNameLength, mExtraFieldLength, mFileCommentLength); |
6cf0905b | 687 | ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", |
1f170917 MA |
688 | mDiskNumberStart, mInternalAttrs, mExternalAttrs, |
689 | mLocalHeaderRelOffset); | |
690 | ||
691 | if (mFileName != NULL) | |
6cf0905b | 692 | ALOGD(" filename: '%s'\n", mFileName); |
1f170917 | 693 | if (mFileComment != NULL) |
6cf0905b | 694 | ALOGD(" comment: '%s'\n", mFileComment); |
1f170917 MA |
695 | } |
696 |