blob: 63d75d1057f69f74b43bab5d07219cc5ed5de139 [file] [log] [blame]
Mathias Agopian3344b2e2009-06-05 14:55:48 -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 entries in a Zip archive.
19//
20
21#define LOG_TAG "zip"
22
23#include "ZipEntry.h"
24#include <utils/Log.h>
25
Mathias Agopian3344b2e2009-06-05 14:55:48 -070026#include <assert.h>
Dan Willemsen41bc4242015-11-04 14:08:20 -080027#include <inttypes.h>
Mark Salyzyn404fd5b2016-10-17 10:20:33 -070028#include <stdio.h>
29#include <string.h>
30#include <time.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070031
32using namespace android;
33
34/*
35 * Initialize a new ZipEntry structure from a FILE* positioned at a
36 * CentralDirectoryEntry.
37 *
38 * On exit, the file pointer will be at the start of the next CDE or
39 * at the EOCD.
40 */
41status_t ZipEntry::initFromCDE(FILE* fp)
42{
43 status_t result;
44 long posn;
45 bool hasDD;
46
Steve Block2da72c62011-10-20 11:56:20 +010047 //ALOGV("initFromCDE ---\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070048
49 /* read the CDE */
50 result = mCDE.read(fp);
51 if (result != NO_ERROR) {
Steve Block15fab1a2011-12-20 16:25:43 +000052 ALOGD("mCDE.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070053 return result;
54 }
55
56 //mCDE.dump();
57
58 /* using the info in the CDE, go load up the LFH */
59 posn = ftell(fp);
60 if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -080061 ALOGD("local header seek failed (%" PRIu32 ")\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -070062 mCDE.mLocalHeaderRelOffset);
63 return UNKNOWN_ERROR;
64 }
65
66 result = mLFH.read(fp);
67 if (result != NO_ERROR) {
Steve Block15fab1a2011-12-20 16:25:43 +000068 ALOGD("mLFH.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070069 return result;
70 }
71
72 if (fseek(fp, posn, SEEK_SET) != 0)
73 return UNKNOWN_ERROR;
74
75 //mLFH.dump();
76
77 /*
78 * We *might* need to read the Data Descriptor at this point and
79 * integrate it into the LFH. If this bit is set, the CRC-32,
80 * compressed size, and uncompressed size will be zero. In practice
81 * these seem to be rare.
82 */
83 hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
84 if (hasDD) {
85 // do something clever
Steve Block15fab1a2011-12-20 16:25:43 +000086 //ALOGD("+++ has data descriptor\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070087 }
88
89 /*
90 * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
91 * flag is set, because the LFH is incomplete. (Not a problem, since we
92 * prefer the CDE values.)
93 */
94 if (!hasDD && !compareHeaders()) {
Steve Blockc0b74df2012-01-05 23:28:01 +000095 ALOGW("WARNING: header mismatch\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070096 // keep going?
97 }
98
99 /*
100 * If the mVersionToExtract is greater than 20, we may have an
101 * issue unpacking the record -- could be encrypted, compressed
102 * with something we don't support, or use Zip64 extensions. We
103 * can defer worrying about that to when we're extracting data.
104 */
105
106 return NO_ERROR;
107}
108
109/*
110 * Initialize a new entry. Pass in the file name and an optional comment.
111 *
112 * Initializes the CDE and the LFH.
113 */
114void ZipEntry::initNew(const char* fileName, const char* comment)
115{
116 assert(fileName != NULL && *fileName != '\0'); // name required
117
118 /* most fields are properly initialized by constructor */
119 mCDE.mVersionMadeBy = kDefaultMadeBy;
120 mCDE.mVersionToExtract = kDefaultVersion;
121 mCDE.mCompressionMethod = kCompressStored;
122 mCDE.mFileNameLength = strlen(fileName);
123 if (comment != NULL)
124 mCDE.mFileCommentLength = strlen(comment);
125 mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
126
127 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800128 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700129 strcpy((char*) mCDE.mFileName, fileName);
130 }
131 if (mCDE.mFileCommentLength > 0) {
132 /* TODO: stop assuming null-terminated ASCII here? */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800133 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Yunlian Jiang4f1a91c2016-10-04 17:23:03 -0700134 assert(comment != NULL);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700135 strcpy((char*) mCDE.mFileComment, comment);
136 }
137
138 copyCDEtoLFH();
139}
140
141/*
142 * Initialize a new entry, starting with the ZipEntry from a different
143 * archive.
144 *
145 * Initializes the CDE and the LFH.
146 */
Aurimas Liutikasaf1d7412016-02-11 18:11:21 -0800147status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700148{
149 /*
150 * Copy everything in the CDE over, then fix up the hairy bits.
151 */
152 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
153
154 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800155 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700156 if (mCDE.mFileName == NULL)
157 return NO_MEMORY;
158 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
159 }
160 if (mCDE.mFileCommentLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800161 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700162 if (mCDE.mFileComment == NULL)
163 return NO_MEMORY;
164 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
165 }
166 if (mCDE.mExtraFieldLength > 0) {
167 /* we null-terminate this, though it may not be a string */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800168 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700169 if (mCDE.mExtraField == NULL)
170 return NO_MEMORY;
171 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
172 mCDE.mExtraFieldLength+1);
173 }
174
175 /* construct the LFH from the CDE */
176 copyCDEtoLFH();
177
178 /*
179 * The LFH "extra" field is independent of the CDE "extra", so we
180 * handle it here.
181 */
182 assert(mLFH.mExtraField == NULL);
183 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
184 if (mLFH.mExtraFieldLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800185 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700186 if (mLFH.mExtraField == NULL)
187 return NO_MEMORY;
188 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
189 mLFH.mExtraFieldLength+1);
190 }
191
192 return NO_ERROR;
193}
194
195/*
196 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
197 * potentially confuse something that put "extra" data in here earlier,
198 * but I can't find an actual problem.
199 */
200status_t ZipEntry::addPadding(int padding)
201{
202 if (padding <= 0)
203 return INVALID_OPERATION;
204
Steve Block934443b2012-01-04 20:07:45 +0000205 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700206 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
207
208 if (mLFH.mExtraFieldLength > 0) {
209 /* extend existing field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800210 uint8_t* newExtra;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700211
Dan Willemsen41bc4242015-11-04 14:08:20 -0800212 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700213 if (newExtra == NULL)
214 return NO_MEMORY;
215 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
216 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
217
218 delete[] mLFH.mExtraField;
219 mLFH.mExtraField = newExtra;
220 mLFH.mExtraFieldLength += padding;
221 } else {
222 /* create new field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800223 mLFH.mExtraField = new uint8_t[padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700224 memset(mLFH.mExtraField, 0, padding);
225 mLFH.mExtraFieldLength = padding;
226 }
227
228 return NO_ERROR;
229}
230
231/*
232 * Set the fields in the LFH equal to the corresponding fields in the CDE.
233 *
234 * This does not touch the LFH "extra" field.
235 */
236void ZipEntry::copyCDEtoLFH(void)
237{
238 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
239 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
240 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
241 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
242 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
243 mLFH.mCRC32 = mCDE.mCRC32;
244 mLFH.mCompressedSize = mCDE.mCompressedSize;
245 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
246 mLFH.mFileNameLength = mCDE.mFileNameLength;
247 // the "extra field" is independent
248
249 delete[] mLFH.mFileName;
250 if (mLFH.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800251 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700252 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
253 } else {
254 mLFH.mFileName = NULL;
255 }
256}
257
258/*
259 * Set some information about a file after we add it.
260 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800261void ZipEntry::setDataInfo(long uncompLen, long compLen, uint32_t crc32,
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700262 int compressionMethod)
263{
264 mCDE.mCompressionMethod = compressionMethod;
265 mCDE.mCRC32 = crc32;
266 mCDE.mCompressedSize = compLen;
267 mCDE.mUncompressedSize = uncompLen;
268 mCDE.mCompressionMethod = compressionMethod;
269 if (compressionMethod == kCompressDeflated) {
270 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
271 }
272 copyCDEtoLFH();
273}
274
275/*
276 * See if the data in mCDE and mLFH match up. This is mostly useful for
277 * debugging these classes, but it can be used to identify damaged
278 * archives.
279 *
280 * Returns "false" if they differ.
281 */
282bool ZipEntry::compareHeaders(void) const
283{
284 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block2da72c62011-10-20 11:56:20 +0100285 ALOGV("cmp: VersionToExtract\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700286 return false;
287 }
288 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block2da72c62011-10-20 11:56:20 +0100289 ALOGV("cmp: GPBitFlag\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700290 return false;
291 }
292 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block2da72c62011-10-20 11:56:20 +0100293 ALOGV("cmp: CompressionMethod\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700294 return false;
295 }
296 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block2da72c62011-10-20 11:56:20 +0100297 ALOGV("cmp: LastModFileTime\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700298 return false;
299 }
300 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block2da72c62011-10-20 11:56:20 +0100301 ALOGV("cmp: LastModFileDate\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700302 return false;
303 }
304 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block2da72c62011-10-20 11:56:20 +0100305 ALOGV("cmp: CRC32\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700306 return false;
307 }
308 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100309 ALOGV("cmp: CompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700310 return false;
311 }
312 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100313 ALOGV("cmp: UncompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700314 return false;
315 }
316 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100317 ALOGV("cmp: FileNameLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700318 return false;
319 }
320#if 0 // this seems to be used for padding, not real data
321 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100322 ALOGV("cmp: ExtraFieldLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700323 return false;
324 }
325#endif
326 if (mCDE.mFileName != NULL) {
327 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block2da72c62011-10-20 11:56:20 +0100328 ALOGV("cmp: FileName\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700329 return false;
330 }
331 }
332
333 return true;
334}
335
336
337/*
338 * Convert the DOS date/time stamp into a UNIX time stamp.
339 */
340time_t ZipEntry::getModWhen(void) const
341{
342 struct tm parts;
343
344 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
345 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
346 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
347 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
348 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
349 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
350 parts.tm_wday = parts.tm_yday = 0;
351 parts.tm_isdst = -1; // DST info "not available"
352
353 return mktime(&parts);
354}
355
356/*
357 * Set the CDE/LFH timestamp from UNIX time.
358 */
359void ZipEntry::setModWhen(time_t when)
360{
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800361#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700362 struct tm tmResult;
363#endif
364 time_t even;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800365 uint16_t zdate, ztime;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700366
367 struct tm* ptm;
368
369 /* round up to an even number of seconds */
370 even = (time_t)(((unsigned long)(when) + 1) & (~1));
371
372 /* expand */
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800373#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700374 ptm = localtime_r(&even, &tmResult);
375#else
376 ptm = localtime(&even);
377#endif
378
379 int year;
380 year = ptm->tm_year;
381 if (year < 80)
382 year = 80;
383
384 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
385 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
386
387 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
388 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
389}
390
391
392/*
393 * ===========================================================================
394 * ZipEntry::LocalFileHeader
395 * ===========================================================================
396 */
397
398/*
399 * Read a local file header.
400 *
401 * On entry, "fp" points to the signature at the start of the header.
402 * On exit, "fp" points to the start of data.
403 */
404status_t ZipEntry::LocalFileHeader::read(FILE* fp)
405{
406 status_t result = NO_ERROR;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800407 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700408
409 assert(mFileName == NULL);
410 assert(mExtraField == NULL);
411
412 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
413 result = UNKNOWN_ERROR;
414 goto bail;
415 }
416
417 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000418 ALOGD("whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700419 result = UNKNOWN_ERROR;
420 goto bail;
421 }
422
423 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
424 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
425 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
426 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
427 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
428 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
429 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
430 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
431 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
432 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
433
434 // TODO: validate sizes
435
436 /* grab filename */
437 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800438 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700439 if (mFileName == NULL) {
440 result = NO_MEMORY;
441 goto bail;
442 }
443 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
444 result = UNKNOWN_ERROR;
445 goto bail;
446 }
447 mFileName[mFileNameLength] = '\0';
448 }
449
450 /* grab extra field */
451 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800452 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700453 if (mExtraField == NULL) {
454 result = NO_MEMORY;
455 goto bail;
456 }
457 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
458 result = UNKNOWN_ERROR;
459 goto bail;
460 }
461 mExtraField[mExtraFieldLength] = '\0';
462 }
463
464bail:
465 return result;
466}
467
468/*
469 * Write a local file header.
470 */
471status_t ZipEntry::LocalFileHeader::write(FILE* fp)
472{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800473 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700474
475 ZipEntry::putLongLE(&buf[0x00], kSignature);
476 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
477 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
478 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
479 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
480 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
481 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
482 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
483 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
484 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
485 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
486
487 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
488 return UNKNOWN_ERROR;
489
490 /* write filename */
491 if (mFileNameLength != 0) {
492 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
493 return UNKNOWN_ERROR;
494 }
495
496 /* write "extra field" */
497 if (mExtraFieldLength != 0) {
498 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
499 return UNKNOWN_ERROR;
500 }
501
502 return NO_ERROR;
503}
504
505
506/*
507 * Dump the contents of a LocalFileHeader object.
508 */
509void ZipEntry::LocalFileHeader::dump(void) const
510{
Steve Block15fab1a2011-12-20 16:25:43 +0000511 ALOGD(" LocalFileHeader contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800512 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700513 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800514 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700515 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800516 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700517 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800518 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700519 mFileNameLength, mExtraFieldLength);
520 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000521 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700522}
523
524
525/*
526 * ===========================================================================
527 * ZipEntry::CentralDirEntry
528 * ===========================================================================
529 */
530
531/*
532 * Read the central dir entry that appears next in the file.
533 *
534 * On entry, "fp" should be positioned on the signature bytes for the
535 * entry. On exit, "fp" will point at the signature word for the next
536 * entry or for the EOCD.
537 */
538status_t ZipEntry::CentralDirEntry::read(FILE* fp)
539{
540 status_t result = NO_ERROR;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800541 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700542
543 /* no re-use */
544 assert(mFileName == NULL);
545 assert(mExtraField == NULL);
546 assert(mFileComment == NULL);
547
548 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
549 result = UNKNOWN_ERROR;
550 goto bail;
551 }
552
553 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000554 ALOGD("Whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700555 result = UNKNOWN_ERROR;
556 goto bail;
557 }
558
559 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
560 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
561 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
562 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
563 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
564 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
565 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
566 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
567 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
568 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
569 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
570 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
571 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
572 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
573 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
574 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
575
576 // TODO: validate sizes and offsets
577
578 /* grab filename */
579 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800580 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700581 if (mFileName == NULL) {
582 result = NO_MEMORY;
583 goto bail;
584 }
585 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
586 result = UNKNOWN_ERROR;
587 goto bail;
588 }
589 mFileName[mFileNameLength] = '\0';
590 }
591
592 /* read "extra field" */
593 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800594 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700595 if (mExtraField == NULL) {
596 result = NO_MEMORY;
597 goto bail;
598 }
599 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
600 result = UNKNOWN_ERROR;
601 goto bail;
602 }
603 mExtraField[mExtraFieldLength] = '\0';
604 }
605
606
607 /* grab comment, if any */
608 if (mFileCommentLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800609 mFileComment = new uint8_t[mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700610 if (mFileComment == NULL) {
611 result = NO_MEMORY;
612 goto bail;
613 }
614 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
615 {
616 result = UNKNOWN_ERROR;
617 goto bail;
618 }
619 mFileComment[mFileCommentLength] = '\0';
620 }
621
622bail:
623 return result;
624}
625
626/*
627 * Write a central dir entry.
628 */
629status_t ZipEntry::CentralDirEntry::write(FILE* fp)
630{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800631 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700632
633 ZipEntry::putLongLE(&buf[0x00], kSignature);
634 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
635 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
636 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
637 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
638 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
639 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
640 ZipEntry::putLongLE(&buf[0x10], mCRC32);
641 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
642 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
643 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
644 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
645 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
646 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
647 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
648 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
649 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
650
651 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
652 return UNKNOWN_ERROR;
653
654 /* write filename */
655 if (mFileNameLength != 0) {
656 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
657 return UNKNOWN_ERROR;
658 }
659
660 /* write "extra field" */
661 if (mExtraFieldLength != 0) {
662 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
663 return UNKNOWN_ERROR;
664 }
665
666 /* write comment */
667 if (mFileCommentLength != 0) {
668 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
669 return UNKNOWN_ERROR;
670 }
671
672 return NO_ERROR;
673}
674
675/*
676 * Dump the contents of a CentralDirEntry object.
677 */
678void ZipEntry::CentralDirEntry::dump(void) const
679{
Steve Block15fab1a2011-12-20 16:25:43 +0000680 ALOGD(" CentralDirEntry contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800681 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700682 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800683 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700684 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800685 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700686 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800687 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700688 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800689 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700690 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
691 mLocalHeaderRelOffset);
692
693 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000694 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700695 if (mFileComment != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000696 ALOGD(" comment: '%s'\n", mFileComment);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700697}
698