blob: 54a8e9c9b247c88029020e7ca1f2c5bf725a68fc [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
26#include <stdio.h>
27#include <string.h>
28#include <assert.h>
29
30using 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 */
39status_t ZipEntry::initFromCDE(FILE* fp)
40{
41 status_t result;
42 long posn;
43 bool hasDD;
44
Steve Block71f2cf12011-10-20 11:56:00 +010045 //ALOGV("initFromCDE ---\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47 /* read the CDE */
48 result = mCDE.read(fp);
49 if (result != NO_ERROR) {
Steve Block5baa3a62011-12-20 16:23:08 +000050 ALOGD("mCDE.read failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 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) {
Steve Block5baa3a62011-12-20 16:23:08 +000059 ALOGD("local header seek failed (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 mCDE.mLocalHeaderRelOffset);
61 return UNKNOWN_ERROR;
62 }
63
64 result = mLFH.read(fp);
65 if (result != NO_ERROR) {
Steve Block5baa3a62011-12-20 16:23:08 +000066 ALOGD("mLFH.read failed\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 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
Steve Block5baa3a62011-12-20 16:23:08 +000084 //ALOGD("+++ has data descriptor\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 }
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()) {
Steve Block8564c8d2012-01-05 23:22:43 +000093 ALOGW("warning: header mismatch\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 // 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 */
112void 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 */
Andreas Gampe2412f842014-09-30 20:55:57 -0700144status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 const ZipEntry* pEntry)
146{
Andreas Gampe2412f842014-09-30 20:55:57 -0700147 mCDE = pEntry->mCDE;
148 // Check whether we got all the memory needed.
149 if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
150 (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
151 (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) {
152 return NO_MEMORY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154
155 /* construct the LFH from the CDE */
156 copyCDEtoLFH();
157
158 /*
159 * The LFH "extra" field is independent of the CDE "extra", so we
160 * handle it here.
161 */
162 assert(mLFH.mExtraField == NULL);
163 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
164 if (mLFH.mExtraFieldLength > 0) {
165 mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
166 if (mLFH.mExtraField == NULL)
167 return NO_MEMORY;
168 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
169 mLFH.mExtraFieldLength+1);
170 }
171
172 return NO_ERROR;
173}
174
175/*
176 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
177 * potentially confuse something that put "extra" data in here earlier,
178 * but I can't find an actual problem.
179 */
180status_t ZipEntry::addPadding(int padding)
181{
182 if (padding <= 0)
183 return INVALID_OPERATION;
184
Steve Block6215d3f2012-01-04 20:05:49 +0000185 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
187
188 if (mLFH.mExtraFieldLength > 0) {
189 /* extend existing field */
190 unsigned char* newExtra;
191
192 newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
193 if (newExtra == NULL)
194 return NO_MEMORY;
195 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
196 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
197
198 delete[] mLFH.mExtraField;
199 mLFH.mExtraField = newExtra;
200 mLFH.mExtraFieldLength += padding;
201 } else {
202 /* create new field */
203 mLFH.mExtraField = new unsigned char[padding];
204 memset(mLFH.mExtraField, 0, padding);
205 mLFH.mExtraFieldLength = padding;
206 }
207
208 return NO_ERROR;
209}
210
211/*
212 * Set the fields in the LFH equal to the corresponding fields in the CDE.
213 *
214 * This does not touch the LFH "extra" field.
215 */
216void ZipEntry::copyCDEtoLFH(void)
217{
218 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
219 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
220 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
221 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
222 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
223 mLFH.mCRC32 = mCDE.mCRC32;
224 mLFH.mCompressedSize = mCDE.mCompressedSize;
225 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
226 mLFH.mFileNameLength = mCDE.mFileNameLength;
227 // the "extra field" is independent
228
229 delete[] mLFH.mFileName;
230 if (mLFH.mFileNameLength > 0) {
231 mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
232 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
233 } else {
234 mLFH.mFileName = NULL;
235 }
236}
237
238/*
239 * Set some information about a file after we add it.
240 */
241void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
242 int compressionMethod)
243{
244 mCDE.mCompressionMethod = compressionMethod;
245 mCDE.mCRC32 = crc32;
246 mCDE.mCompressedSize = compLen;
247 mCDE.mUncompressedSize = uncompLen;
248 mCDE.mCompressionMethod = compressionMethod;
249 if (compressionMethod == kCompressDeflated) {
250 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
251 }
252 copyCDEtoLFH();
253}
254
255/*
256 * See if the data in mCDE and mLFH match up. This is mostly useful for
257 * debugging these classes, but it can be used to identify damaged
258 * archives.
259 *
260 * Returns "false" if they differ.
261 */
262bool ZipEntry::compareHeaders(void) const
263{
264 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
Steve Block71f2cf12011-10-20 11:56:00 +0100265 ALOGV("cmp: VersionToExtract\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 return false;
267 }
268 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
Steve Block71f2cf12011-10-20 11:56:00 +0100269 ALOGV("cmp: GPBitFlag\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 return false;
271 }
272 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
Steve Block71f2cf12011-10-20 11:56:00 +0100273 ALOGV("cmp: CompressionMethod\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 return false;
275 }
276 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
Steve Block71f2cf12011-10-20 11:56:00 +0100277 ALOGV("cmp: LastModFileTime\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 return false;
279 }
280 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
Steve Block71f2cf12011-10-20 11:56:00 +0100281 ALOGV("cmp: LastModFileDate\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 return false;
283 }
284 if (mCDE.mCRC32 != mLFH.mCRC32) {
Steve Block71f2cf12011-10-20 11:56:00 +0100285 ALOGV("cmp: CRC32\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 return false;
287 }
288 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
Steve Block71f2cf12011-10-20 11:56:00 +0100289 ALOGV("cmp: CompressedSize\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 return false;
291 }
292 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
Steve Block71f2cf12011-10-20 11:56:00 +0100293 ALOGV("cmp: UncompressedSize\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 return false;
295 }
296 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
Steve Block71f2cf12011-10-20 11:56:00 +0100297 ALOGV("cmp: FileNameLength\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 return false;
299 }
300#if 0 // this seems to be used for padding, not real data
301 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
Steve Block71f2cf12011-10-20 11:56:00 +0100302 ALOGV("cmp: ExtraFieldLength\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 return false;
304 }
305#endif
306 if (mCDE.mFileName != NULL) {
307 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
Steve Block71f2cf12011-10-20 11:56:00 +0100308 ALOGV("cmp: FileName\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 return false;
310 }
311 }
312
313 return true;
314}
315
316
317/*
318 * Convert the DOS date/time stamp into a UNIX time stamp.
319 */
320time_t ZipEntry::getModWhen(void) const
321{
322 struct tm parts;
323
324 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
325 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
326 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
327 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
328 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
329 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
330 parts.tm_wday = parts.tm_yday = 0;
331 parts.tm_isdst = -1; // DST info "not available"
332
333 return mktime(&parts);
334}
335
336/*
337 * Set the CDE/LFH timestamp from UNIX time.
338 */
339void ZipEntry::setModWhen(time_t when)
340{
Yabin Cui25d58a92014-11-13 09:57:22 -0800341#if !defined(_WIN32)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 struct tm tmResult;
343#endif
344 time_t even;
345 unsigned short zdate, ztime;
346
347 struct tm* ptm;
348
349 /* round up to an even number of seconds */
350 even = (time_t)(((unsigned long)(when) + 1) & (~1));
351
352 /* expand */
Yabin Cui25d58a92014-11-13 09:57:22 -0800353#if !defined(_WIN32)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 ptm = localtime_r(&even, &tmResult);
355#else
356 ptm = localtime(&even);
357#endif
358
359 int year;
360 year = ptm->tm_year;
361 if (year < 80)
362 year = 80;
363
364 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
365 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
366
367 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
368 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
369}
370
371
372/*
373 * ===========================================================================
374 * ZipEntry::LocalFileHeader
375 * ===========================================================================
376 */
377
378/*
379 * Read a local file header.
380 *
381 * On entry, "fp" points to the signature at the start of the header.
382 * On exit, "fp" points to the start of data.
383 */
384status_t ZipEntry::LocalFileHeader::read(FILE* fp)
385{
386 status_t result = NO_ERROR;
387 unsigned char buf[kLFHLen];
388
389 assert(mFileName == NULL);
390 assert(mExtraField == NULL);
391
392 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
393 result = UNKNOWN_ERROR;
394 goto bail;
395 }
396
397 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block5baa3a62011-12-20 16:23:08 +0000398 ALOGD("whoops: didn't find expected signature\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 result = UNKNOWN_ERROR;
400 goto bail;
401 }
402
403 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
404 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
405 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
406 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
407 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
408 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
409 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
410 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
411 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
412 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
413
414 // TODO: validate sizes
415
416 /* grab filename */
417 if (mFileNameLength != 0) {
418 mFileName = new unsigned char[mFileNameLength+1];
419 if (mFileName == NULL) {
420 result = NO_MEMORY;
421 goto bail;
422 }
423 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
424 result = UNKNOWN_ERROR;
425 goto bail;
426 }
427 mFileName[mFileNameLength] = '\0';
428 }
429
430 /* grab extra field */
431 if (mExtraFieldLength != 0) {
432 mExtraField = new unsigned char[mExtraFieldLength+1];
433 if (mExtraField == NULL) {
434 result = NO_MEMORY;
435 goto bail;
436 }
437 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
438 result = UNKNOWN_ERROR;
439 goto bail;
440 }
441 mExtraField[mExtraFieldLength] = '\0';
442 }
443
444bail:
445 return result;
446}
447
448/*
449 * Write a local file header.
450 */
451status_t ZipEntry::LocalFileHeader::write(FILE* fp)
452{
453 unsigned char buf[kLFHLen];
454
455 ZipEntry::putLongLE(&buf[0x00], kSignature);
456 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
457 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
458 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
459 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
460 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
461 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
462 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
463 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
464 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
465 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
466
467 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
468 return UNKNOWN_ERROR;
469
470 /* write filename */
471 if (mFileNameLength != 0) {
472 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
473 return UNKNOWN_ERROR;
474 }
475
476 /* write "extra field" */
477 if (mExtraFieldLength != 0) {
478 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
479 return UNKNOWN_ERROR;
480 }
481
482 return NO_ERROR;
483}
484
485
486/*
487 * Dump the contents of a LocalFileHeader object.
488 */
489void ZipEntry::LocalFileHeader::dump(void) const
490{
Steve Block5baa3a62011-12-20 16:23:08 +0000491 ALOGD(" LocalFileHeader contents:\n");
492 ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 mVersionToExtract, mGPBitFlag, mCompressionMethod);
Steve Block5baa3a62011-12-20 16:23:08 +0000494 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 mLastModFileTime, mLastModFileDate, mCRC32);
Steve Block5baa3a62011-12-20 16:23:08 +0000496 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 mCompressedSize, mUncompressedSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000498 ALOGD(" filenameLen=%u extraLen=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 mFileNameLength, mExtraFieldLength);
500 if (mFileName != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000501 ALOGD(" filename: '%s'\n", mFileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502}
503
504
505/*
506 * ===========================================================================
507 * ZipEntry::CentralDirEntry
508 * ===========================================================================
509 */
510
511/*
512 * Read the central dir entry that appears next in the file.
513 *
514 * On entry, "fp" should be positioned on the signature bytes for the
515 * entry. On exit, "fp" will point at the signature word for the next
516 * entry or for the EOCD.
517 */
518status_t ZipEntry::CentralDirEntry::read(FILE* fp)
519{
520 status_t result = NO_ERROR;
521 unsigned char buf[kCDELen];
522
523 /* no re-use */
524 assert(mFileName == NULL);
525 assert(mExtraField == NULL);
526 assert(mFileComment == NULL);
527
528 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
529 result = UNKNOWN_ERROR;
530 goto bail;
531 }
532
533 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
Steve Block5baa3a62011-12-20 16:23:08 +0000534 ALOGD("Whoops: didn't find expected signature\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 result = UNKNOWN_ERROR;
536 goto bail;
537 }
538
539 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
540 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
541 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
542 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
543 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
544 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
545 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
546 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
547 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
548 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
549 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
550 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
551 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
552 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
553 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
554 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
555
556 // TODO: validate sizes and offsets
557
558 /* grab filename */
559 if (mFileNameLength != 0) {
560 mFileName = new unsigned char[mFileNameLength+1];
561 if (mFileName == NULL) {
562 result = NO_MEMORY;
563 goto bail;
564 }
565 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
566 result = UNKNOWN_ERROR;
567 goto bail;
568 }
569 mFileName[mFileNameLength] = '\0';
570 }
571
572 /* read "extra field" */
573 if (mExtraFieldLength != 0) {
574 mExtraField = new unsigned char[mExtraFieldLength+1];
575 if (mExtraField == NULL) {
576 result = NO_MEMORY;
577 goto bail;
578 }
579 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
580 result = UNKNOWN_ERROR;
581 goto bail;
582 }
583 mExtraField[mExtraFieldLength] = '\0';
584 }
585
586
587 /* grab comment, if any */
588 if (mFileCommentLength != 0) {
589 mFileComment = new unsigned char[mFileCommentLength+1];
590 if (mFileComment == NULL) {
591 result = NO_MEMORY;
592 goto bail;
593 }
594 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
595 {
596 result = UNKNOWN_ERROR;
597 goto bail;
598 }
599 mFileComment[mFileCommentLength] = '\0';
600 }
601
602bail:
603 return result;
604}
605
606/*
607 * Write a central dir entry.
608 */
609status_t ZipEntry::CentralDirEntry::write(FILE* fp)
610{
611 unsigned char buf[kCDELen];
612
613 ZipEntry::putLongLE(&buf[0x00], kSignature);
614 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
615 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
616 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
617 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
618 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
619 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
620 ZipEntry::putLongLE(&buf[0x10], mCRC32);
621 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
622 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
623 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
624 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
625 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
626 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
627 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
628 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
629 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
630
631 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
632 return UNKNOWN_ERROR;
633
634 /* write filename */
635 if (mFileNameLength != 0) {
636 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
637 return UNKNOWN_ERROR;
638 }
639
640 /* write "extra field" */
641 if (mExtraFieldLength != 0) {
642 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
643 return UNKNOWN_ERROR;
644 }
645
646 /* write comment */
647 if (mFileCommentLength != 0) {
648 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
649 return UNKNOWN_ERROR;
650 }
651
652 return NO_ERROR;
653}
654
655/*
656 * Dump the contents of a CentralDirEntry object.
657 */
658void ZipEntry::CentralDirEntry::dump(void) const
659{
Steve Block5baa3a62011-12-20 16:23:08 +0000660 ALOGD(" CentralDirEntry contents:\n");
661 ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
Steve Block5baa3a62011-12-20 16:23:08 +0000663 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 mLastModFileTime, mLastModFileDate, mCRC32);
Steve Block5baa3a62011-12-20 16:23:08 +0000665 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 mCompressedSize, mUncompressedSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000667 ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 mFileNameLength, mExtraFieldLength, mFileCommentLength);
Steve Block5baa3a62011-12-20 16:23:08 +0000669 ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
671 mLocalHeaderRelOffset);
672
673 if (mFileName != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000674 ALOGD(" filename: '%s'\n", mFileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 if (mFileComment != NULL)
Steve Block5baa3a62011-12-20 16:23:08 +0000676 ALOGD(" comment: '%s'\n", mFileComment);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677}
678
Andreas Gampe2412f842014-09-30 20:55:57 -0700679/*
680 * Copy-assignment operator for CentralDirEntry.
681 */
682ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) {
683 if (this == &src) {
684 return *this;
685 }
686
687 // Free up old data.
688 delete[] mFileName;
689 delete[] mExtraField;
690 delete[] mFileComment;
691
692 // Copy scalars.
693 mVersionMadeBy = src.mVersionMadeBy;
694 mVersionToExtract = src.mVersionToExtract;
695 mGPBitFlag = src.mGPBitFlag;
696 mCompressionMethod = src.mCompressionMethod;
697 mLastModFileTime = src.mLastModFileTime;
698 mLastModFileDate = src.mLastModFileDate;
699 mCRC32 = src.mCRC32;
700 mCompressedSize = src.mCompressedSize;
701 mUncompressedSize = src.mUncompressedSize;
702 mFileNameLength = src.mFileNameLength;
703 mExtraFieldLength = src.mExtraFieldLength;
704 mFileCommentLength = src.mFileCommentLength;
705 mDiskNumberStart = src.mDiskNumberStart;
706 mInternalAttrs = src.mInternalAttrs;
707 mExternalAttrs = src.mExternalAttrs;
708 mLocalHeaderRelOffset = src.mLocalHeaderRelOffset;
709
710 // Copy strings, if necessary.
711 if (mFileNameLength > 0) {
712 mFileName = new unsigned char[mFileNameLength + 1];
713 if (mFileName != NULL)
714 strcpy((char*)mFileName, (char*)src.mFileName);
715 } else {
716 mFileName = NULL;
717 }
718 if (mFileCommentLength > 0) {
719 mFileComment = new unsigned char[mFileCommentLength + 1];
720 if (mFileComment != NULL)
721 strcpy((char*)mFileComment, (char*)src.mFileComment);
722 } else {
723 mFileComment = NULL;
724 }
725 if (mExtraFieldLength > 0) {
726 /* we null-terminate this, though it may not be a string */
727 mExtraField = new unsigned char[mExtraFieldLength + 1];
728 if (mExtraField != NULL)
729 memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1);
730 } else {
731 mExtraField = NULL;
732 }
733
734 return *this;
735}