1 /*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //
18 // Access to entries in a Zip archive.
19 //
20
21 #define LOG_TAG "zip"
22
23 #include "ZipEntry.h"
24 #include <utils/Log.h>
25
26 #include <assert.h>
27 #include <inttypes.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31
32 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 */
initFromCDE(FILE * fp)41 status_t ZipEntry::initFromCDE(FILE* fp)
42 {
43 status_t result;
44 long posn; // NOLINT(google-runtime-int), for ftell/fseek
45 bool hasDD;
46
47 //ALOGV("initFromCDE ---\n");
48
49 /* read the CDE */
50 result = mCDE.read(fp);
51 if (result != OK) {
52 ALOGD("mCDE.read failed\n");
53 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) {
61 ALOGD("local header seek failed (%" PRIu32 ")\n",
62 mCDE.mLocalHeaderRelOffset);
63 return UNKNOWN_ERROR;
64 }
65
66 result = mLFH.read(fp);
67 if (result != OK) {
68 ALOGD("mLFH.read failed\n");
69 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
86 //ALOGD("+++ has data descriptor\n");
87 }
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()) {
95 ALOGW("WARNING: header mismatch\n");
96 // 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 OK;
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 */
initNew(const char * fileName,const char * comment)114 void 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) {
128 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
129 strcpy((char*) mCDE.mFileName, fileName);
130 }
131 if (mCDE.mFileCommentLength > 0) {
132 /* TODO: stop assuming null-terminated ASCII here? */
133 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
134 assert(comment != NULL);
135 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 */
initFromExternal(const ZipEntry * pEntry)147 status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
148 {
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) {
155 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
156 if (mCDE.mFileName == NULL)
157 return NO_MEMORY;
158 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
159 }
160 if (mCDE.mFileCommentLength > 0) {
161 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
162 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 */
168 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
169 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) {
185 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
186 if (mLFH.mExtraField == NULL)
187 return NO_MEMORY;
188 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
189 mLFH.mExtraFieldLength+1);
190 }
191
192 return OK;
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 */
addPadding(int padding)200 status_t ZipEntry::addPadding(int padding)
201 {
202 if (padding <= 0)
203 return INVALID_OPERATION;
204
205 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
206 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
207
208 if (mLFH.mExtraFieldLength > 0) {
209 /* extend existing field */
210 uint8_t* newExtra;
211
212 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
213 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 */
223 mLFH.mExtraField = new uint8_t[padding];
224 memset(mLFH.mExtraField, 0, padding);
225 mLFH.mExtraFieldLength = padding;
226 }
227
228 return OK;
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 */
copyCDEtoLFH(void)236 void 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) {
251 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
252 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 */
setDataInfo(uint32_t uncompLen,uint32_t compLen,uint32_t crc32,uint32_t compressionMethod)261 void ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,
262 uint32_t 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 */
compareHeaders(void) const282 bool ZipEntry::compareHeaders(void) const
283 {
284 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
285 ALOGV("cmp: VersionToExtract\n");
286 return false;
287 }
288 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
289 ALOGV("cmp: GPBitFlag\n");
290 return false;
291 }
292 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
293 ALOGV("cmp: CompressionMethod\n");
294 return false;
295 }
296 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
297 ALOGV("cmp: LastModFileTime\n");
298 return false;
299 }
300 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
301 ALOGV("cmp: LastModFileDate\n");
302 return false;
303 }
304 if (mCDE.mCRC32 != mLFH.mCRC32) {
305 ALOGV("cmp: CRC32\n");
306 return false;
307 }
308 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
309 ALOGV("cmp: CompressedSize\n");
310 return false;
311 }
312 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
313 ALOGV("cmp: UncompressedSize\n");
314 return false;
315 }
316 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
317 ALOGV("cmp: FileNameLength\n");
318 return false;
319 }
320 #if 0 // this seems to be used for padding, not real data
321 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
322 ALOGV("cmp: ExtraFieldLength\n");
323 return false;
324 }
325 #endif
326 if (mCDE.mFileName != NULL) {
327 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
328 ALOGV("cmp: FileName\n");
329 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 */
getModWhen(void) const340 time_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 */
setModWhen(time_t when)359 void ZipEntry::setModWhen(time_t when)
360 {
361 #if !defined(_WIN32)
362 struct tm tmResult;
363 #endif
364 time_t even;
365 uint16_t zdate, ztime;
366
367 struct tm* ptm;
368
369 /* round up to an even number of seconds */
370 even = (when & 1) ? (when + 1) : when;
371
372 /* expand */
373 #if !defined(_WIN32)
374 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 */
read(FILE * fp)404 status_t ZipEntry::LocalFileHeader::read(FILE* fp)
405 {
406 status_t result = OK;
407 uint8_t buf[kLFHLen];
408
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) {
418 ALOGD("whoops: didn't find expected signature\n");
419 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) {
438 mFileName = new uint8_t[mFileNameLength+1];
439 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) {
452 mExtraField = new uint8_t[mExtraFieldLength+1];
453 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
464 bail:
465 return result;
466 }
467
468 /*
469 * Write a local file header.
470 */
write(FILE * fp)471 status_t ZipEntry::LocalFileHeader::write(FILE* fp)
472 {
473 uint8_t buf[kLFHLen];
474
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 OK;
503 }
504
505
506 /*
507 * Dump the contents of a LocalFileHeader object.
508 */
dump(void) const509 void ZipEntry::LocalFileHeader::dump(void) const
510 {
511 ALOGD(" LocalFileHeader contents:\n");
512 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
513 mVersionToExtract, mGPBitFlag, mCompressionMethod);
514 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
515 mLastModFileTime, mLastModFileDate, mCRC32);
516 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
517 mCompressedSize, mUncompressedSize);
518 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
519 mFileNameLength, mExtraFieldLength);
520 if (mFileName != NULL)
521 ALOGD(" filename: '%s'\n", mFileName);
522 }
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 */
read(FILE * fp)538 status_t ZipEntry::CentralDirEntry::read(FILE* fp)
539 {
540 status_t result = OK;
541 uint8_t buf[kCDELen];
542
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) {
554 ALOGD("Whoops: didn't find expected signature\n");
555 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) {
580 mFileName = new uint8_t[mFileNameLength+1];
581 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) {
594 mExtraField = new uint8_t[mExtraFieldLength+1];
595 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) {
609 mFileComment = new uint8_t[mFileCommentLength+1];
610 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
622 bail:
623 return result;
624 }
625
626 /*
627 * Write a central dir entry.
628 */
write(FILE * fp)629 status_t ZipEntry::CentralDirEntry::write(FILE* fp)
630 {
631 uint8_t buf[kCDELen];
632
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 OK;
673 }
674
675 /*
676 * Dump the contents of a CentralDirEntry object.
677 */
dump(void) const678 void ZipEntry::CentralDirEntry::dump(void) const
679 {
680 ALOGD(" CentralDirEntry contents:\n");
681 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
682 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
683 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
684 mLastModFileTime, mLastModFileDate, mCRC32);
685 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
686 mCompressedSize, mUncompressedSize);
687 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
688 mFileNameLength, mExtraFieldLength, mFileCommentLength);
689 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
690 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
691 mLocalHeaderRelOffset);
692
693 if (mFileName != NULL)
694 ALOGD(" filename: '%s'\n", mFileName);
695 if (mFileComment != NULL)
696 ALOGD(" comment: '%s'\n", mFileComment);
697 }
698
699 } // namespace android
700
701