blob: 5339285f566783b82b890db39617d20f3da13aca [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Mathias Agopian55e3d602009-06-05 14:56:35 -070023#include "ZipEntry.h"
Mathias Agopian8ae23352009-06-04 13:53:57 -070024#include <utils/Log.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Mark Salyzyn4d6c3722016-10-17 10:10:55 -070026#include <assert.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027#include <stdio.h>
28#include <string.h>
Mark Salyzyn4d6c3722016-10-17 10:10:55 -070029#include <time.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
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 Block71f2cf12011-10-20 11:56:00 +010046 //ALOGV("initFromCDE ---\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48 /* read the CDE */
49 result = mCDE.read(fp);
50 if (result != NO_ERROR) {
Steve Block5baa3a62011-12-20 16:23:08 +000051 ALOGD("mCDE.read failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 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) {
Steve Block5baa3a62011-12-20 16:23:08 +000060 ALOGD("local header seek failed (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 mCDE.mLocalHeaderRelOffset);
62 return UNKNOWN_ERROR;
63 }
64
65 result = mLFH.read(fp);
66 if (result != NO_ERROR) {
Steve Block5baa3a62011-12-20 16:23:08 +000067 ALOGD("mLFH.read failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 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 Block5baa3a62011-12-20 16:23:08 +000085 //ALOGD("+++ has data descriptor\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 }
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 Block8564c8d2012-01-05 23:22:43 +000094 ALOGW("warning: header mismatch\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 // 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) {
127 mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
128 strcpy((char*) mCDE.mFileName, fileName);
129 }
130 if (mCDE.mFileCommentLength > 0) {
131 /* TODO: stop assuming null-terminated ASCII here? */
132 mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
133 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 */
Andreas Gampe2412f842014-09-30 20:55:57 -0700145status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 const ZipEntry* pEntry)
147{
Andreas Gampe2412f842014-09-30 20:55:57 -0700148 mCDE = pEntry->mCDE;
149 // Check whether we got all the memory needed.
150 if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
151 (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
152 (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) {
153 return NO_MEMORY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 }
155
156 /* construct the LFH from the CDE */
157 copyCDEtoLFH();
158
159 /*
160 * The LFH "extra" field is independent of the CDE "extra", so we
161 * handle it here.
162 */
163 assert(mLFH.mExtraField == NULL);
164 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
165 if (mLFH.mExtraFieldLength > 0) {
166 mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
167 if (mLFH.mExtraField == NULL)
168 return NO_MEMORY;
169 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
170 mLFH.mExtraFieldLength+1);
171 }
172
173 return NO_ERROR;
174}
175
176/*
177 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
178 * potentially confuse something that put "extra" data in here earlier,
179 * but I can't find an actual problem.
180 */
181status_t ZipEntry::addPadding(int padding)
182{
183 if (padding <= 0)
184 return INVALID_OPERATION;
185
Steve Block6215d3f2012-01-04 20:05:49 +0000186 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
188
189 if (mLFH.mExtraFieldLength > 0) {
190 /* extend existing field */
191 unsigned char* newExtra;
192
193 newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
194 if (newExtra == NULL)
195 return NO_MEMORY;
196 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
197 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
198
199 delete[] mLFH.mExtraField;
200 mLFH.mExtraField = newExtra;
201 mLFH.mExtraFieldLength += padding;
202 } else {
203 /* create new field */
204 mLFH.mExtraField = new unsigned char[padding];
205 memset(mLFH.mExtraField, 0, padding);
206 mLFH.mExtraFieldLength = padding;
207 }
208
209 return NO_ERROR;
210}
211
212/*
213 * Set the fields in the LFH equal to the corresponding fields in the CDE.
214 *
215 * This does not touch the LFH "extra" field.
216 */
217void ZipEntry::copyCDEtoLFH(void)
218{
219 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
220 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
221 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
222 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
223 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
224 mLFH.mCRC32 = mCDE.mCRC32;
225 mLFH.mCompressedSize = mCDE.mCompressedSize;
226 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
227 mLFH.mFileNameLength = mCDE.mFileNameLength;
228 // the "extra field" is independent
229
230 delete[] mLFH.mFileName;
231 if (mLFH.mFileNameLength > 0) {
232 mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
233 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
234 } else {
235 mLFH.mFileName = NULL;
236 }
237}
238
239/*
240 * Set some information about a file after we add it.
241 */
242void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
243 int compressionMethod)
244{
245 mCDE.mCompressionMethod = compressionMethod;
246 mCDE.mCRC32 = crc32;
247 mCDE.mCompressedSize = compLen;
248 mCDE.mUncompressedSize = uncompLen;
249 mCDE.mCompressionMethod = compressionMethod;
250 if (compressionMethod == kCompressDeflated) {
251 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
252 }
253 copyCDEtoLFH();
254}
255
256/*
257 * See if the data in mCDE and mLFH match up. This is mostly useful for
258 * debugging these classes, but it can be used to identify damaged
259 * archives.
260 *
261 * Returns "false" if they differ.
262 */
263bool ZipEntry::compareHeaders(void) const
264{
265 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block71f2cf12011-10-20 11:56:00 +0100266 ALOGV("cmp: VersionToExtract\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 return false;
268 }
269 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block71f2cf12011-10-20 11:56:00 +0100270 ALOGV("cmp: GPBitFlag\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 return false;
272 }
273 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block71f2cf12011-10-20 11:56:00 +0100274 ALOGV("cmp: CompressionMethod\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 return false;
276 }
277 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block71f2cf12011-10-20 11:56:00 +0100278 ALOGV("cmp: LastModFileTime\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 return false;
280 }
281 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block71f2cf12011-10-20 11:56:00 +0100282 ALOGV("cmp: LastModFileDate\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 return false;
284 }
285 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block71f2cf12011-10-20 11:56:00 +0100286 ALOGV("cmp: CRC32\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 return false;
288 }
289 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block71f2cf12011-10-20 11:56:00 +0100290 ALOGV("cmp: CompressedSize\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 return false;
292 }
293 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block71f2cf12011-10-20 11:56:00 +0100294 ALOGV("cmp: UncompressedSize\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 return false;
296 }
297 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block71f2cf12011-10-20 11:56:00 +0100298 ALOGV("cmp: FileNameLength\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 return false;
300 }
301#if 0 // this seems to be used for padding, not real data
302 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block71f2cf12011-10-20 11:56:00 +0100303 ALOGV("cmp: ExtraFieldLength\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 return false;
305 }
306#endif
307 if (mCDE.mFileName != NULL) {
308 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block71f2cf12011-10-20 11:56:00 +0100309 ALOGV("cmp: FileName\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 return false;
311 }
312 }
313
314 return true;
315}
316
317
318/*
319 * Convert the DOS date/time stamp into a UNIX time stamp.
320 */
321time_t ZipEntry::getModWhen(void) const
322{
323 struct tm parts;
324
325 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
326 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
327 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
328 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
329 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
330 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
331 parts.tm_wday = parts.tm_yday = 0;
332 parts.tm_isdst = -1; // DST info "not available"
333
334 return mktime(&parts);
335}
336
337/*
338 * Set the CDE/LFH timestamp from UNIX time.
339 */
340void ZipEntry::setModWhen(time_t when)
341{
Yabin Cui25d58a92014-11-13 09:57:22 -0800342#if !defined(_WIN32)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 struct tm tmResult;
344#endif
345 time_t even;
346 unsigned short zdate, ztime;
347
348 struct tm* ptm;
349
350 /* round up to an even number of seconds */
351 even = (time_t)(((unsigned long)(when) + 1) & (~1));
352
353 /* expand */
Yabin Cui25d58a92014-11-13 09:57:22 -0800354#if !defined(_WIN32)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 ptm = localtime_r(&even, &tmResult);
356#else
357 ptm = localtime(&even);
358#endif
359
360 int year;
361 year = ptm->tm_year;
362 if (year < 80)
363 year = 80;
364
365 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
366 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
367
368 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
369 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
370}
371
372
373/*
374 * ===========================================================================
375 * ZipEntry::LocalFileHeader
376 * ===========================================================================
377 */
378
379/*
380 * Read a local file header.
381 *
382 * On entry, "fp" points to the signature at the start of the header.
383 * On exit, "fp" points to the start of data.
384 */
385status_t ZipEntry::LocalFileHeader::read(FILE* fp)
386{
387 status_t result = NO_ERROR;
388 unsigned char buf[kLFHLen];
389
390 assert(mFileName == NULL);
391 assert(mExtraField == NULL);
392
393 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
394 result = UNKNOWN_ERROR;
395 goto bail;
396 }
397
398 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block5baa3a62011-12-20 16:23:08 +0000399 ALOGD("whoops: didn't find expected signature\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 result = UNKNOWN_ERROR;
401 goto bail;
402 }
403
404 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
405 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
406 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
407 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
408 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
409 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
410 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
411 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
412 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
413 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
414
415 // TODO: validate sizes
416
417 /* grab filename */
418 if (mFileNameLength != 0) {
419 mFileName = new unsigned char[mFileNameLength+1];
420 if (mFileName == NULL) {
421 result = NO_MEMORY;
422 goto bail;
423 }
424 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
425 result = UNKNOWN_ERROR;
426 goto bail;
427 }
428 mFileName[mFileNameLength] = '\0';
429 }
430
431 /* grab extra field */
432 if (mExtraFieldLength != 0) {
433 mExtraField = new unsigned char[mExtraFieldLength+1];
434 if (mExtraField == NULL) {
435 result = NO_MEMORY;
436 goto bail;
437 }
438 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
439 result = UNKNOWN_ERROR;
440 goto bail;
441 }
442 mExtraField[mExtraFieldLength] = '\0';
443 }
444
445bail:
446 return result;
447}
448
449/*
450 * Write a local file header.
451 */
452status_t ZipEntry::LocalFileHeader::write(FILE* fp)
453{
454 unsigned char buf[kLFHLen];
455
456 ZipEntry::putLongLE(&buf[0x00], kSignature);
457 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
458 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
459 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
460 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
461 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
462 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
463 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
464 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
465 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
466 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
467
468 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
469 return UNKNOWN_ERROR;
470
471 /* write filename */
472 if (mFileNameLength != 0) {
473 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
474 return UNKNOWN_ERROR;
475 }
476
477 /* write "extra field" */
478 if (mExtraFieldLength != 0) {
479 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
480 return UNKNOWN_ERROR;
481 }
482
483 return NO_ERROR;
484}
485
486
487/*
488 * Dump the contents of a LocalFileHeader object.
489 */
490void ZipEntry::LocalFileHeader::dump(void) const
491{
Steve Block5baa3a62011-12-20 16:23:08 +0000492 ALOGD(" LocalFileHeader contents:\n");
493 ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Steve Block5baa3a62011-12-20 16:23:08 +0000495 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 mLastModFileTime, mLastModFileDate, mCRC32);
Steve Block5baa3a62011-12-20 16:23:08 +0000497 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 mCompressedSize, mUncompressedSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000499 ALOGD(" filenameLen=%u extraLen=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 mFileNameLength, mExtraFieldLength);
501 if (mFileName != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000502 ALOGD(" filename: '%s'\n", mFileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503}
504
505
506/*
507 * ===========================================================================
508 * ZipEntry::CentralDirEntry
509 * ===========================================================================
510 */
511
512/*
513 * Read the central dir entry that appears next in the file.
514 *
515 * On entry, "fp" should be positioned on the signature bytes for the
516 * entry. On exit, "fp" will point at the signature word for the next
517 * entry or for the EOCD.
518 */
519status_t ZipEntry::CentralDirEntry::read(FILE* fp)
520{
521 status_t result = NO_ERROR;
522 unsigned char buf[kCDELen];
523
524 /* no re-use */
525 assert(mFileName == NULL);
526 assert(mExtraField == NULL);
527 assert(mFileComment == NULL);
528
529 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
530 result = UNKNOWN_ERROR;
531 goto bail;
532 }
533
534 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block5baa3a62011-12-20 16:23:08 +0000535 ALOGD("Whoops: didn't find expected signature\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 result = UNKNOWN_ERROR;
537 goto bail;
538 }
539
540 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
541 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
542 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
543 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
544 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
545 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
546 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
547 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
548 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
549 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
550 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
551 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
552 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
553 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
554 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
555 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
556
557 // TODO: validate sizes and offsets
558
559 /* grab filename */
560 if (mFileNameLength != 0) {
561 mFileName = new unsigned char[mFileNameLength+1];
562 if (mFileName == NULL) {
563 result = NO_MEMORY;
564 goto bail;
565 }
566 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
567 result = UNKNOWN_ERROR;
568 goto bail;
569 }
570 mFileName[mFileNameLength] = '\0';
571 }
572
573 /* read "extra field" */
574 if (mExtraFieldLength != 0) {
575 mExtraField = new unsigned char[mExtraFieldLength+1];
576 if (mExtraField == NULL) {
577 result = NO_MEMORY;
578 goto bail;
579 }
580 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
581 result = UNKNOWN_ERROR;
582 goto bail;
583 }
584 mExtraField[mExtraFieldLength] = '\0';
585 }
586
587
588 /* grab comment, if any */
589 if (mFileCommentLength != 0) {
590 mFileComment = new unsigned char[mFileCommentLength+1];
591 if (mFileComment == NULL) {
592 result = NO_MEMORY;
593 goto bail;
594 }
595 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
596 {
597 result = UNKNOWN_ERROR;
598 goto bail;
599 }
600 mFileComment[mFileCommentLength] = '\0';
601 }
602
603bail:
604 return result;
605}
606
607/*
608 * Write a central dir entry.
609 */
610status_t ZipEntry::CentralDirEntry::write(FILE* fp)
611{
612 unsigned char buf[kCDELen];
613
614 ZipEntry::putLongLE(&buf[0x00], kSignature);
615 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
616 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
617 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
618 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
619 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
620 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
621 ZipEntry::putLongLE(&buf[0x10], mCRC32);
622 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
623 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
624 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
625 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
626 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
627 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
628 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
629 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
630 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
631
632 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
633 return UNKNOWN_ERROR;
634
635 /* write filename */
636 if (mFileNameLength != 0) {
637 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
638 return UNKNOWN_ERROR;
639 }
640
641 /* write "extra field" */
642 if (mExtraFieldLength != 0) {
643 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
644 return UNKNOWN_ERROR;
645 }
646
647 /* write comment */
648 if (mFileCommentLength != 0) {
649 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
650 return UNKNOWN_ERROR;
651 }
652
653 return NO_ERROR;
654}
655
656/*
657 * Dump the contents of a CentralDirEntry object.
658 */
659void ZipEntry::CentralDirEntry::dump(void) const
660{
Steve Block5baa3a62011-12-20 16:23:08 +0000661 ALOGD(" CentralDirEntry contents:\n");
662 ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Steve Block5baa3a62011-12-20 16:23:08 +0000664 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 mLastModFileTime, mLastModFileDate, mCRC32);
Steve Block5baa3a62011-12-20 16:23:08 +0000666 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 mCompressedSize, mUncompressedSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000668 ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Steve Block5baa3a62011-12-20 16:23:08 +0000670 ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
672 mLocalHeaderRelOffset);
673
674 if (mFileName != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000675 ALOGD(" filename: '%s'\n", mFileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 if (mFileComment != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000677 ALOGD(" comment: '%s'\n", mFileComment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678}
679
Andreas Gampe2412f842014-09-30 20:55:57 -0700680/*
681 * Copy-assignment operator for CentralDirEntry.
682 */
683ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) {
684 if (this == &src) {
685 return *this;
686 }
687
688 // Free up old data.
689 delete[] mFileName;
690 delete[] mExtraField;
691 delete[] mFileComment;
692
693 // Copy scalars.
694 mVersionMadeBy = src.mVersionMadeBy;
695 mVersionToExtract = src.mVersionToExtract;
696 mGPBitFlag = src.mGPBitFlag;
697 mCompressionMethod = src.mCompressionMethod;
698 mLastModFileTime = src.mLastModFileTime;
699 mLastModFileDate = src.mLastModFileDate;
700 mCRC32 = src.mCRC32;
701 mCompressedSize = src.mCompressedSize;
702 mUncompressedSize = src.mUncompressedSize;
703 mFileNameLength = src.mFileNameLength;
704 mExtraFieldLength = src.mExtraFieldLength;
705 mFileCommentLength = src.mFileCommentLength;
706 mDiskNumberStart = src.mDiskNumberStart;
707 mInternalAttrs = src.mInternalAttrs;
708 mExternalAttrs = src.mExternalAttrs;
709 mLocalHeaderRelOffset = src.mLocalHeaderRelOffset;
710
711 // Copy strings, if necessary.
712 if (mFileNameLength > 0) {
713 mFileName = new unsigned char[mFileNameLength + 1];
714 if (mFileName != NULL)
715 strcpy((char*)mFileName, (char*)src.mFileName);
716 } else {
717 mFileName = NULL;
718 }
719 if (mFileCommentLength > 0) {
720 mFileComment = new unsigned char[mFileCommentLength + 1];
721 if (mFileComment != NULL)
722 strcpy((char*)mFileComment, (char*)src.mFileComment);
723 } else {
724 mFileComment = NULL;
725 }
726 if (mExtraFieldLength > 0) {
727 /* we null-terminate this, though it may not be a string */
728 mExtraField = new unsigned char[mExtraFieldLength + 1];
729 if (mExtraField != NULL)
730 memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1);
731 } else {
732 mExtraField = NULL;
733 }
734
735 return *this;
736}