blob: a13b8d102d70645b9db31be9dacff716999d55cf [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
26#include <stdio.h>
27#include <string.h>
28#include <assert.h>
Dan Willemsen41bc4242015-11-04 14:08:20 -080029#include <inttypes.h>
Mathias Agopian3344b2e2009-06-05 14:55:48 -070030
31using namespace android;
32
33/*
34 * Initialize a new ZipEntry structure from a FILE* positioned at a
35 * CentralDirectoryEntry.
36 *
37 * On exit, the file pointer will be at the start of the next CDE or
38 * at the EOCD.
39 */
40status_t ZipEntry::initFromCDE(FILE* fp)
41{
42 status_t result;
43 long posn;
44 bool hasDD;
45
Steve Block2da72c62011-10-20 11:56:20 +010046 //ALOGV("initFromCDE ---\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070047
48 /* read the CDE */
49 result = mCDE.read(fp);
50 if (result != NO_ERROR) {
Steve Block15fab1a2011-12-20 16:25:43 +000051 ALOGD("mCDE.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070052 return result;
53 }
54
55 //mCDE.dump();
56
57 /* using the info in the CDE, go load up the LFH */
58 posn = ftell(fp);
59 if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -080060 ALOGD("local header seek failed (%" PRIu32 ")\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -070061 mCDE.mLocalHeaderRelOffset);
62 return UNKNOWN_ERROR;
63 }
64
65 result = mLFH.read(fp);
66 if (result != NO_ERROR) {
Steve Block15fab1a2011-12-20 16:25:43 +000067 ALOGD("mLFH.read failed\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070068 return result;
69 }
70
71 if (fseek(fp, posn, SEEK_SET) != 0)
72 return UNKNOWN_ERROR;
73
74 //mLFH.dump();
75
76 /*
77 * We *might* need to read the Data Descriptor at this point and
78 * integrate it into the LFH. If this bit is set, the CRC-32,
79 * compressed size, and uncompressed size will be zero. In practice
80 * these seem to be rare.
81 */
82 hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
83 if (hasDD) {
84 // do something clever
Steve Block15fab1a2011-12-20 16:25:43 +000085 //ALOGD("+++ has data descriptor\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070086 }
87
88 /*
89 * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
90 * flag is set, because the LFH is incomplete. (Not a problem, since we
91 * prefer the CDE values.)
92 */
93 if (!hasDD && !compareHeaders()) {
Steve Blockc0b74df2012-01-05 23:28:01 +000094 ALOGW("WARNING: header mismatch\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -070095 // keep going?
96 }
97
98 /*
99 * If the mVersionToExtract is greater than 20, we may have an
100 * issue unpacking the record -- could be encrypted, compressed
101 * with something we don't support, or use Zip64 extensions. We
102 * can defer worrying about that to when we're extracting data.
103 */
104
105 return NO_ERROR;
106}
107
108/*
109 * Initialize a new entry. Pass in the file name and an optional comment.
110 *
111 * Initializes the CDE and the LFH.
112 */
113void ZipEntry::initNew(const char* fileName, const char* comment)
114{
115 assert(fileName != NULL && *fileName != '\0'); // name required
116
117 /* most fields are properly initialized by constructor */
118 mCDE.mVersionMadeBy = kDefaultMadeBy;
119 mCDE.mVersionToExtract = kDefaultVersion;
120 mCDE.mCompressionMethod = kCompressStored;
121 mCDE.mFileNameLength = strlen(fileName);
122 if (comment != NULL)
123 mCDE.mFileCommentLength = strlen(comment);
124 mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
125
126 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800127 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700128 strcpy((char*) mCDE.mFileName, fileName);
129 }
130 if (mCDE.mFileCommentLength > 0) {
131 /* TODO: stop assuming null-terminated ASCII here? */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800132 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700133 strcpy((char*) mCDE.mFileComment, comment);
134 }
135
136 copyCDEtoLFH();
137}
138
139/*
140 * Initialize a new entry, starting with the ZipEntry from a different
141 * archive.
142 *
143 * Initializes the CDE and the LFH.
144 */
145status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
146 const ZipEntry* pEntry)
147{
148 /*
149 * Copy everything in the CDE over, then fix up the hairy bits.
150 */
151 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
152
153 if (mCDE.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800154 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700155 if (mCDE.mFileName == NULL)
156 return NO_MEMORY;
157 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
158 }
159 if (mCDE.mFileCommentLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800160 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700161 if (mCDE.mFileComment == NULL)
162 return NO_MEMORY;
163 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
164 }
165 if (mCDE.mExtraFieldLength > 0) {
166 /* we null-terminate this, though it may not be a string */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800167 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700168 if (mCDE.mExtraField == NULL)
169 return NO_MEMORY;
170 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
171 mCDE.mExtraFieldLength+1);
172 }
173
174 /* construct the LFH from the CDE */
175 copyCDEtoLFH();
176
177 /*
178 * The LFH "extra" field is independent of the CDE "extra", so we
179 * handle it here.
180 */
181 assert(mLFH.mExtraField == NULL);
182 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
183 if (mLFH.mExtraFieldLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800184 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700185 if (mLFH.mExtraField == NULL)
186 return NO_MEMORY;
187 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
188 mLFH.mExtraFieldLength+1);
189 }
190
191 return NO_ERROR;
192}
193
194/*
195 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
196 * potentially confuse something that put "extra" data in here earlier,
197 * but I can't find an actual problem.
198 */
199status_t ZipEntry::addPadding(int padding)
200{
201 if (padding <= 0)
202 return INVALID_OPERATION;
203
Steve Block934443b2012-01-04 20:07:45 +0000204 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700205 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
206
207 if (mLFH.mExtraFieldLength > 0) {
208 /* extend existing field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800209 uint8_t* newExtra;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700210
Dan Willemsen41bc4242015-11-04 14:08:20 -0800211 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700212 if (newExtra == NULL)
213 return NO_MEMORY;
214 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
215 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
216
217 delete[] mLFH.mExtraField;
218 mLFH.mExtraField = newExtra;
219 mLFH.mExtraFieldLength += padding;
220 } else {
221 /* create new field */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800222 mLFH.mExtraField = new uint8_t[padding];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700223 memset(mLFH.mExtraField, 0, padding);
224 mLFH.mExtraFieldLength = padding;
225 }
226
227 return NO_ERROR;
228}
229
230/*
231 * Set the fields in the LFH equal to the corresponding fields in the CDE.
232 *
233 * This does not touch the LFH "extra" field.
234 */
235void ZipEntry::copyCDEtoLFH(void)
236{
237 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
238 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
239 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
240 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
241 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
242 mLFH.mCRC32 = mCDE.mCRC32;
243 mLFH.mCompressedSize = mCDE.mCompressedSize;
244 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
245 mLFH.mFileNameLength = mCDE.mFileNameLength;
246 // the "extra field" is independent
247
248 delete[] mLFH.mFileName;
249 if (mLFH.mFileNameLength > 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800250 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700251 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
252 } else {
253 mLFH.mFileName = NULL;
254 }
255}
256
257/*
258 * Set some information about a file after we add it.
259 */
Dan Willemsen41bc4242015-11-04 14:08:20 -0800260void ZipEntry::setDataInfo(long uncompLen, long compLen, uint32_t crc32,
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700261 int compressionMethod)
262{
263 mCDE.mCompressionMethod = compressionMethod;
264 mCDE.mCRC32 = crc32;
265 mCDE.mCompressedSize = compLen;
266 mCDE.mUncompressedSize = uncompLen;
267 mCDE.mCompressionMethod = compressionMethod;
268 if (compressionMethod == kCompressDeflated) {
269 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
270 }
271 copyCDEtoLFH();
272}
273
274/*
275 * See if the data in mCDE and mLFH match up. This is mostly useful for
276 * debugging these classes, but it can be used to identify damaged
277 * archives.
278 *
279 * Returns "false" if they differ.
280 */
281bool ZipEntry::compareHeaders(void) const
282{
283 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block2da72c62011-10-20 11:56:20 +0100284 ALOGV("cmp: VersionToExtract\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700285 return false;
286 }
287 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block2da72c62011-10-20 11:56:20 +0100288 ALOGV("cmp: GPBitFlag\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700289 return false;
290 }
291 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block2da72c62011-10-20 11:56:20 +0100292 ALOGV("cmp: CompressionMethod\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700293 return false;
294 }
295 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block2da72c62011-10-20 11:56:20 +0100296 ALOGV("cmp: LastModFileTime\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700297 return false;
298 }
299 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block2da72c62011-10-20 11:56:20 +0100300 ALOGV("cmp: LastModFileDate\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700301 return false;
302 }
303 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block2da72c62011-10-20 11:56:20 +0100304 ALOGV("cmp: CRC32\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700305 return false;
306 }
307 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100308 ALOGV("cmp: CompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700309 return false;
310 }
311 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block2da72c62011-10-20 11:56:20 +0100312 ALOGV("cmp: UncompressedSize\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700313 return false;
314 }
315 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100316 ALOGV("cmp: FileNameLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700317 return false;
318 }
319#if 0 // this seems to be used for padding, not real data
320 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block2da72c62011-10-20 11:56:20 +0100321 ALOGV("cmp: ExtraFieldLength\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700322 return false;
323 }
324#endif
325 if (mCDE.mFileName != NULL) {
326 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block2da72c62011-10-20 11:56:20 +0100327 ALOGV("cmp: FileName\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700328 return false;
329 }
330 }
331
332 return true;
333}
334
335
336/*
337 * Convert the DOS date/time stamp into a UNIX time stamp.
338 */
339time_t ZipEntry::getModWhen(void) const
340{
341 struct tm parts;
342
343 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
344 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
345 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
346 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
347 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
348 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
349 parts.tm_wday = parts.tm_yday = 0;
350 parts.tm_isdst = -1; // DST info "not available"
351
352 return mktime(&parts);
353}
354
355/*
356 * Set the CDE/LFH timestamp from UNIX time.
357 */
358void ZipEntry::setModWhen(time_t when)
359{
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800360#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700361 struct tm tmResult;
362#endif
363 time_t even;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800364 uint16_t zdate, ztime;
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700365
366 struct tm* ptm;
367
368 /* round up to an even number of seconds */
369 even = (time_t)(((unsigned long)(when) + 1) & (~1));
370
371 /* expand */
Yabin Cuie9ae59b2014-11-13 11:51:37 -0800372#if !defined(_WIN32)
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700373 ptm = localtime_r(&even, &tmResult);
374#else
375 ptm = localtime(&even);
376#endif
377
378 int year;
379 year = ptm->tm_year;
380 if (year < 80)
381 year = 80;
382
383 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
384 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
385
386 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
387 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
388}
389
390
391/*
392 * ===========================================================================
393 * ZipEntry::LocalFileHeader
394 * ===========================================================================
395 */
396
397/*
398 * Read a local file header.
399 *
400 * On entry, "fp" points to the signature at the start of the header.
401 * On exit, "fp" points to the start of data.
402 */
403status_t ZipEntry::LocalFileHeader::read(FILE* fp)
404{
405 status_t result = NO_ERROR;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800406 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700407
408 assert(mFileName == NULL);
409 assert(mExtraField == NULL);
410
411 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
412 result = UNKNOWN_ERROR;
413 goto bail;
414 }
415
416 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000417 ALOGD("whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700418 result = UNKNOWN_ERROR;
419 goto bail;
420 }
421
422 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
423 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
424 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
425 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
426 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
427 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
428 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
429 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
430 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
431 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
432
433 // TODO: validate sizes
434
435 /* grab filename */
436 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800437 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700438 if (mFileName == NULL) {
439 result = NO_MEMORY;
440 goto bail;
441 }
442 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
443 result = UNKNOWN_ERROR;
444 goto bail;
445 }
446 mFileName[mFileNameLength] = '\0';
447 }
448
449 /* grab extra field */
450 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800451 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700452 if (mExtraField == NULL) {
453 result = NO_MEMORY;
454 goto bail;
455 }
456 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
457 result = UNKNOWN_ERROR;
458 goto bail;
459 }
460 mExtraField[mExtraFieldLength] = '\0';
461 }
462
463bail:
464 return result;
465}
466
467/*
468 * Write a local file header.
469 */
470status_t ZipEntry::LocalFileHeader::write(FILE* fp)
471{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800472 uint8_t buf[kLFHLen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700473
474 ZipEntry::putLongLE(&buf[0x00], kSignature);
475 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
476 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
477 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
478 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
479 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
480 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
481 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
482 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
483 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
484 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
485
486 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
487 return UNKNOWN_ERROR;
488
489 /* write filename */
490 if (mFileNameLength != 0) {
491 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
492 return UNKNOWN_ERROR;
493 }
494
495 /* write "extra field" */
496 if (mExtraFieldLength != 0) {
497 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
498 return UNKNOWN_ERROR;
499 }
500
501 return NO_ERROR;
502}
503
504
505/*
506 * Dump the contents of a LocalFileHeader object.
507 */
508void ZipEntry::LocalFileHeader::dump(void) const
509{
Steve Block15fab1a2011-12-20 16:25:43 +0000510 ALOGD(" LocalFileHeader contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800511 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700512 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800513 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700514 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800515 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700516 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800517 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700518 mFileNameLength, mExtraFieldLength);
519 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000520 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700521}
522
523
524/*
525 * ===========================================================================
526 * ZipEntry::CentralDirEntry
527 * ===========================================================================
528 */
529
530/*
531 * Read the central dir entry that appears next in the file.
532 *
533 * On entry, "fp" should be positioned on the signature bytes for the
534 * entry. On exit, "fp" will point at the signature word for the next
535 * entry or for the EOCD.
536 */
537status_t ZipEntry::CentralDirEntry::read(FILE* fp)
538{
539 status_t result = NO_ERROR;
Dan Willemsen41bc4242015-11-04 14:08:20 -0800540 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700541
542 /* no re-use */
543 assert(mFileName == NULL);
544 assert(mExtraField == NULL);
545 assert(mFileComment == NULL);
546
547 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
548 result = UNKNOWN_ERROR;
549 goto bail;
550 }
551
552 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block15fab1a2011-12-20 16:25:43 +0000553 ALOGD("Whoops: didn't find expected signature\n");
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700554 result = UNKNOWN_ERROR;
555 goto bail;
556 }
557
558 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
559 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
560 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
561 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
562 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
563 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
564 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
565 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
566 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
567 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
568 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
569 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
570 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
571 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
572 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
573 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
574
575 // TODO: validate sizes and offsets
576
577 /* grab filename */
578 if (mFileNameLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800579 mFileName = new uint8_t[mFileNameLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700580 if (mFileName == NULL) {
581 result = NO_MEMORY;
582 goto bail;
583 }
584 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
585 result = UNKNOWN_ERROR;
586 goto bail;
587 }
588 mFileName[mFileNameLength] = '\0';
589 }
590
591 /* read "extra field" */
592 if (mExtraFieldLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800593 mExtraField = new uint8_t[mExtraFieldLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700594 if (mExtraField == NULL) {
595 result = NO_MEMORY;
596 goto bail;
597 }
598 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
599 result = UNKNOWN_ERROR;
600 goto bail;
601 }
602 mExtraField[mExtraFieldLength] = '\0';
603 }
604
605
606 /* grab comment, if any */
607 if (mFileCommentLength != 0) {
Dan Willemsen41bc4242015-11-04 14:08:20 -0800608 mFileComment = new uint8_t[mFileCommentLength+1];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700609 if (mFileComment == NULL) {
610 result = NO_MEMORY;
611 goto bail;
612 }
613 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
614 {
615 result = UNKNOWN_ERROR;
616 goto bail;
617 }
618 mFileComment[mFileCommentLength] = '\0';
619 }
620
621bail:
622 return result;
623}
624
625/*
626 * Write a central dir entry.
627 */
628status_t ZipEntry::CentralDirEntry::write(FILE* fp)
629{
Dan Willemsen41bc4242015-11-04 14:08:20 -0800630 uint8_t buf[kCDELen];
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700631
632 ZipEntry::putLongLE(&buf[0x00], kSignature);
633 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
634 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
635 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
636 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
637 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
638 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
639 ZipEntry::putLongLE(&buf[0x10], mCRC32);
640 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
641 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
642 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
643 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
644 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
645 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
646 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
647 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
648 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
649
650 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
651 return UNKNOWN_ERROR;
652
653 /* write filename */
654 if (mFileNameLength != 0) {
655 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
656 return UNKNOWN_ERROR;
657 }
658
659 /* write "extra field" */
660 if (mExtraFieldLength != 0) {
661 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
662 return UNKNOWN_ERROR;
663 }
664
665 /* write comment */
666 if (mFileCommentLength != 0) {
667 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
668 return UNKNOWN_ERROR;
669 }
670
671 return NO_ERROR;
672}
673
674/*
675 * Dump the contents of a CentralDirEntry object.
676 */
677void ZipEntry::CentralDirEntry::dump(void) const
678{
Steve Block15fab1a2011-12-20 16:25:43 +0000679 ALOGD(" CentralDirEntry contents:\n");
Dan Willemsen41bc4242015-11-04 14:08:20 -0800680 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700681 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800682 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700683 mLastModFileTime, mLastModFileDate, mCRC32);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800684 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700685 mCompressedSize, mUncompressedSize);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800686 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700687 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Dan Willemsen41bc4242015-11-04 14:08:20 -0800688 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700689 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
690 mLocalHeaderRelOffset);
691
692 if (mFileName != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000693 ALOGD(" filename: '%s'\n", mFileName);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700694 if (mFileComment != NULL)
Steve Block15fab1a2011-12-20 16:25:43 +0000695 ALOGD(" comment: '%s'\n", mFileComment);
Mathias Agopian3344b2e2009-06-05 14:55:48 -0700696}
697