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 //ALOGV("initFromCDE ---\n");
44
45 /* read the CDE */
46 status_t result = mCDE.read(fp);
47 if (result != OK) {
48 ALOGD("mCDE.read failed\n");
49 return result;
50 }
51
52 //mCDE.dump();
53
54 /* using the info in the CDE, go load up the LFH */
55 off_t posn = ftello(fp);
56 if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
57 ALOGD("local header seek failed (%" PRIu32 ")\n",
58 mCDE.mLocalHeaderRelOffset);
59 return UNKNOWN_ERROR;
60 }
61
62 result = mLFH.read(fp);
63 if (result != OK) {
64 ALOGD("mLFH.read failed\n");
65 return result;
66 }
67
68 if (fseeko(fp, posn, SEEK_SET) != 0)
69 return UNKNOWN_ERROR;
70
71 //mLFH.dump();
72
73 /*
74 * We *might* need to read the Data Descriptor at this point and
75 * integrate it into the LFH. If this bit is set, the CRC-32,
76 * compressed size, and uncompressed size will be zero. In practice
77 * these seem to be rare.
78 */
79 bool hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
80 if (hasDD) {
81 // do something clever
82 //ALOGD("+++ has data descriptor\n");
83 }
84
85 /*
86 * Check the LFH. Note that this will fail if the "kUsesDataDescr"
87 * flag is set, because the LFH is incomplete. (Not a problem, since we
88 * prefer the CDE values.)
89 */
90 if (!hasDD && !compareHeaders()) {
91 ALOGW("WARNING: header mismatch\n");
92 // keep going?
93 }
94
95 /*
96 * If the mVersionToExtract is greater than 20, we may have an
97 * issue unpacking the record -- could be encrypted, compressed
98 * with something we don't support, or use Zip64 extensions. We
99 * can defer worrying about that to when we're extracting data.
100 */
101
102 return OK;
103 }
104
105 /*
106 * Initialize a new entry. Pass in the file name and an optional comment.
107 *
108 * Initializes the CDE and the LFH.
109 */
initNew(const char * fileName,const char * comment)110 void ZipEntry::initNew(const char* fileName, const char* comment)
111 {
112 assert(fileName != NULL && *fileName != '\0'); // name required
113
114 /* most fields are properly initialized by constructor */
115 mCDE.mVersionMadeBy = kDefaultMadeBy;
116 mCDE.mVersionToExtract = kDefaultVersion;
117 mCDE.mCompressionMethod = kCompressStored;
118 mCDE.mFileNameLength = strlen(fileName);
119 if (comment != NULL)
120 mCDE.mFileCommentLength = strlen(comment);
121 mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
122
123 if (mCDE.mFileNameLength > 0) {
124 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
125 strcpy((char*) mCDE.mFileName, fileName);
126 }
127 if (mCDE.mFileCommentLength > 0) {
128 /* TODO: stop assuming null-terminated ASCII here? */
129 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
130 assert(comment != NULL);
131 strcpy((char*) mCDE.mFileComment, comment);
132 }
133
134 copyCDEtoLFH();
135 }
136
137 /*
138 * Initialize a new entry, starting with the ZipEntry from a different
139 * archive.
140 *
141 * Initializes the CDE and the LFH.
142 */
initFromExternal(const ZipEntry * pEntry)143 status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
144 {
145 /*
146 * Copy everything in the CDE over, then fix up the hairy bits.
147 */
148 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
149
150 if (mCDE.mFileNameLength > 0) {
151 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
152 if (mCDE.mFileName == NULL)
153 return NO_MEMORY;
154 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
155 }
156 if (mCDE.mFileCommentLength > 0) {
157 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
158 if (mCDE.mFileComment == NULL)
159 return NO_MEMORY;
160 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
161 }
162 if (mCDE.mExtraFieldLength > 0) {
163 /* we null-terminate this, though it may not be a string */
164 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
165 if (mCDE.mExtraField == NULL)
166 return NO_MEMORY;
167 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
168 mCDE.mExtraFieldLength+1);
169 }
170
171 /* construct the LFH from the CDE */
172 copyCDEtoLFH();
173
174 /*
175 * The LFH "extra" field is independent of the CDE "extra", so we
176 * handle it here.
177 */
178 assert(mLFH.mExtraField == NULL);
179 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
180 if (mLFH.mExtraFieldLength > 0) {
181 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
182 if (mLFH.mExtraField == NULL)
183 return NO_MEMORY;
184 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
185 mLFH.mExtraFieldLength+1);
186 }
187
188 return OK;
189 }
190
191 /*
192 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
193 * potentially confuse something that put "extra" data in here earlier,
194 * but I can't find an actual problem.
195 */
addPadding(int padding)196 status_t ZipEntry::addPadding(int padding)
197 {
198 if (padding <= 0)
199 return INVALID_OPERATION;
200
201 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
202 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
203
204 if (mLFH.mExtraFieldLength > 0) {
205 /* extend existing field */
206 uint8_t* newExtra;
207
208 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
209 if (newExtra == NULL)
210 return NO_MEMORY;
211 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
212 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
213
214 delete[] mLFH.mExtraField;
215 mLFH.mExtraField = newExtra;
216 mLFH.mExtraFieldLength += padding;
217 } else {
218 /* create new field */
219 mLFH.mExtraField = new uint8_t[padding];
220 memset(mLFH.mExtraField, 0, padding);
221 mLFH.mExtraFieldLength = padding;
222 }
223
224 return OK;
225 }
226
227 /*
228 * Set the fields in the LFH equal to the corresponding fields in the CDE.
229 *
230 * This does not touch the LFH "extra" field.
231 */
copyCDEtoLFH(void)232 void ZipEntry::copyCDEtoLFH(void)
233 {
234 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
235 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
236 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
237 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
238 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
239 mLFH.mCRC32 = mCDE.mCRC32;
240 mLFH.mCompressedSize = mCDE.mCompressedSize;
241 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
242 mLFH.mFileNameLength = mCDE.mFileNameLength;
243 // the "extra field" is independent
244
245 delete[] mLFH.mFileName;
246 if (mLFH.mFileNameLength > 0) {
247 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
248 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
249 } else {
250 mLFH.mFileName = NULL;
251 }
252 }
253
254 /*
255 * Set some information about a file after we add it.
256 */
setDataInfo(uint32_t uncompLen,uint32_t compLen,uint32_t crc32,uint32_t compressionMethod)257 void ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,
258 uint32_t compressionMethod)
259 {
260 mCDE.mCompressionMethod = compressionMethod;
261 mCDE.mCRC32 = crc32;
262 mCDE.mCompressedSize = compLen;
263 mCDE.mUncompressedSize = uncompLen;
264 mCDE.mCompressionMethod = compressionMethod;
265 if (compressionMethod == kCompressDeflated) {
266 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
267 }
268 copyCDEtoLFH();
269 }
270
271 /*
272 * See if the data in mCDE and mLFH match up. This is mostly useful for
273 * debugging these classes, but it can be used to identify damaged
274 * archives.
275 *
276 * Returns "false" if they differ.
277 */
compareHeaders(void) const278 bool ZipEntry::compareHeaders(void) const
279 {
280 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
281 ALOGV("cmp: VersionToExtract\n");
282 return false;
283 }
284 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
285 ALOGV("cmp: GPBitFlag\n");
286 return false;
287 }
288 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
289 ALOGV("cmp: CompressionMethod\n");
290 return false;
291 }
292 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
293 ALOGV("cmp: LastModFileTime\n");
294 return false;
295 }
296 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
297 ALOGV("cmp: LastModFileDate\n");
298 return false;
299 }
300 if (mCDE.mCRC32 != mLFH.mCRC32) {
301 ALOGV("cmp: CRC32\n");
302 return false;
303 }
304 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
305 ALOGV("cmp: CompressedSize\n");
306 return false;
307 }
308 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
309 ALOGV("cmp: UncompressedSize\n");
310 return false;
311 }
312 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
313 ALOGV("cmp: FileNameLength\n");
314 return false;
315 }
316 #if 0 // this seems to be used for padding, not real data
317 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
318 ALOGV("cmp: ExtraFieldLength\n");
319 return false;
320 }
321 #endif
322 if (mCDE.mFileName != NULL) {
323 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
324 ALOGV("cmp: FileName\n");
325 return false;
326 }
327 }
328
329 return true;
330 }
331
332
333 /*
334 * Convert the DOS date/time stamp into a UNIX time stamp.
335 */
getModWhen(void) const336 time_t ZipEntry::getModWhen(void) const
337 {
338 struct tm parts;
339
340 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
341 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
342 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
343 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
344 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
345 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
346 parts.tm_wday = parts.tm_yday = 0;
347 parts.tm_isdst = -1; // DST info "not available"
348
349 return mktime(&parts);
350 }
351
352 /*
353 * Set the CDE/LFH timestamp from UNIX time.
354 */
setModWhen(time_t when)355 void ZipEntry::setModWhen(time_t when)
356 {
357 #if !defined(_WIN32)
358 struct tm tmResult;
359 #endif
360 time_t even;
361 uint16_t zdate, ztime;
362
363 struct tm* ptm;
364
365 /* round up to an even number of seconds */
366 even = (when & 1) ? (when + 1) : when;
367
368 /* expand */
369 #if !defined(_WIN32)
370 ptm = localtime_r(&even, &tmResult);
371 #else
372 ptm = localtime(&even);
373 #endif
374
375 int year;
376 year = ptm->tm_year;
377 if (year < 80)
378 year = 80;
379
380 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
381 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
382
383 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
384 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
385 }
386
387
388 /*
389 * ===========================================================================
390 * ZipEntry::LocalFileHeader
391 * ===========================================================================
392 */
393
394 /*
395 * Read a local file header.
396 *
397 * On entry, "fp" points to the signature at the start of the header.
398 * On exit, "fp" points to the start of data.
399 */
read(FILE * fp)400 status_t ZipEntry::LocalFileHeader::read(FILE* fp)
401 {
402 status_t result = OK;
403 uint8_t buf[kLFHLen];
404
405 assert(mFileName == NULL);
406 assert(mExtraField == NULL);
407
408 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
409 result = UNKNOWN_ERROR;
410 goto bail;
411 }
412
413 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
414 ALOGD("whoops: didn't find expected signature\n");
415 result = UNKNOWN_ERROR;
416 goto bail;
417 }
418
419 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
420 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
421 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
422 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
423 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
424 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
425 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
426 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
427 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
428 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
429
430 // TODO: validate sizes
431
432 /* grab filename */
433 if (mFileNameLength != 0) {
434 mFileName = new uint8_t[mFileNameLength+1];
435 if (mFileName == NULL) {
436 result = NO_MEMORY;
437 goto bail;
438 }
439 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
440 result = UNKNOWN_ERROR;
441 goto bail;
442 }
443 mFileName[mFileNameLength] = '\0';
444 }
445
446 /* grab extra field */
447 if (mExtraFieldLength != 0) {
448 mExtraField = new uint8_t[mExtraFieldLength+1];
449 if (mExtraField == NULL) {
450 result = NO_MEMORY;
451 goto bail;
452 }
453 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
454 result = UNKNOWN_ERROR;
455 goto bail;
456 }
457 mExtraField[mExtraFieldLength] = '\0';
458 }
459
460 bail:
461 return result;
462 }
463
464 /*
465 * Write a local file header.
466 */
write(FILE * fp)467 status_t ZipEntry::LocalFileHeader::write(FILE* fp)
468 {
469 uint8_t buf[kLFHLen];
470
471 ZipEntry::putLongLE(&buf[0x00], kSignature);
472 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
473 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
474 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
475 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
476 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
477 ZipEntry::putLongLE(&buf[0x0e], mCRC32);
478 ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
479 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
480 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
481 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
482
483 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
484 return UNKNOWN_ERROR;
485
486 /* write filename */
487 if (mFileNameLength != 0) {
488 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
489 return UNKNOWN_ERROR;
490 }
491
492 /* write "extra field" */
493 if (mExtraFieldLength != 0) {
494 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
495 return UNKNOWN_ERROR;
496 }
497
498 return OK;
499 }
500
501
502 /*
503 * Dump the contents of a LocalFileHeader object.
504 */
dump(void) const505 void ZipEntry::LocalFileHeader::dump(void) const
506 {
507 ALOGD(" LocalFileHeader contents:\n");
508 ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
509 mVersionToExtract, mGPBitFlag, mCompressionMethod);
510 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
511 mLastModFileTime, mLastModFileDate, mCRC32);
512 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
513 mCompressedSize, mUncompressedSize);
514 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n",
515 mFileNameLength, mExtraFieldLength);
516 if (mFileName != NULL)
517 ALOGD(" filename: '%s'\n", mFileName);
518 }
519
520
521 /*
522 * ===========================================================================
523 * ZipEntry::CentralDirEntry
524 * ===========================================================================
525 */
526
527 /*
528 * Read the central dir entry that appears next in the file.
529 *
530 * On entry, "fp" should be positioned on the signature bytes for the
531 * entry. On exit, "fp" will point at the signature word for the next
532 * entry or for the EOCD.
533 */
read(FILE * fp)534 status_t ZipEntry::CentralDirEntry::read(FILE* fp)
535 {
536 status_t result = OK;
537 uint8_t buf[kCDELen];
538
539 /* no re-use */
540 assert(mFileName == NULL);
541 assert(mExtraField == NULL);
542 assert(mFileComment == NULL);
543
544 if (fread(buf, 1, kCDELen, fp) != kCDELen) {
545 result = UNKNOWN_ERROR;
546 goto bail;
547 }
548
549 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
550 ALOGD("Whoops: didn't find expected signature\n");
551 result = UNKNOWN_ERROR;
552 goto bail;
553 }
554
555 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
556 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
557 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
558 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
559 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
560 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
561 mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
562 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
563 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
564 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
565 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
566 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
567 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
568 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
569 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
570 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
571
572 // TODO: validate sizes and offsets
573
574 /* grab filename */
575 if (mFileNameLength != 0) {
576 mFileName = new uint8_t[mFileNameLength+1];
577 if (mFileName == NULL) {
578 result = NO_MEMORY;
579 goto bail;
580 }
581 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
582 result = UNKNOWN_ERROR;
583 goto bail;
584 }
585 mFileName[mFileNameLength] = '\0';
586 }
587
588 /* read "extra field" */
589 if (mExtraFieldLength != 0) {
590 mExtraField = new uint8_t[mExtraFieldLength+1];
591 if (mExtraField == NULL) {
592 result = NO_MEMORY;
593 goto bail;
594 }
595 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
596 result = UNKNOWN_ERROR;
597 goto bail;
598 }
599 mExtraField[mExtraFieldLength] = '\0';
600 }
601
602
603 /* grab comment, if any */
604 if (mFileCommentLength != 0) {
605 mFileComment = new uint8_t[mFileCommentLength+1];
606 if (mFileComment == NULL) {
607 result = NO_MEMORY;
608 goto bail;
609 }
610 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
611 {
612 result = UNKNOWN_ERROR;
613 goto bail;
614 }
615 mFileComment[mFileCommentLength] = '\0';
616 }
617
618 bail:
619 return result;
620 }
621
622 /*
623 * Write a central dir entry.
624 */
write(FILE * fp)625 status_t ZipEntry::CentralDirEntry::write(FILE* fp)
626 {
627 uint8_t buf[kCDELen];
628
629 ZipEntry::putLongLE(&buf[0x00], kSignature);
630 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
631 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
632 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
633 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
634 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
635 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
636 ZipEntry::putLongLE(&buf[0x10], mCRC32);
637 ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
638 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
639 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
640 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
641 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
642 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
643 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
644 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
645 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
646
647 if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
648 return UNKNOWN_ERROR;
649
650 /* write filename */
651 if (mFileNameLength != 0) {
652 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
653 return UNKNOWN_ERROR;
654 }
655
656 /* write "extra field" */
657 if (mExtraFieldLength != 0) {
658 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
659 return UNKNOWN_ERROR;
660 }
661
662 /* write comment */
663 if (mFileCommentLength != 0) {
664 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
665 return UNKNOWN_ERROR;
666 }
667
668 return OK;
669 }
670
671 /*
672 * Dump the contents of a CentralDirEntry object.
673 */
dump(void) const674 void ZipEntry::CentralDirEntry::dump(void) const
675 {
676 ALOGD(" CentralDirEntry contents:\n");
677 ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n",
678 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
679 ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n",
680 mLastModFileTime, mLastModFileDate, mCRC32);
681 ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n",
682 mCompressedSize, mUncompressedSize);
683 ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n",
684 mFileNameLength, mExtraFieldLength, mFileCommentLength);
685 ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n",
686 mDiskNumberStart, mInternalAttrs, mExternalAttrs,
687 mLocalHeaderRelOffset);
688
689 if (mFileName != NULL)
690 ALOGD(" filename: '%s'\n", mFileName);
691 if (mFileComment != NULL)
692 ALOGD(" comment: '%s'\n", mFileComment);
693 }
694
695 } // namespace android
696
697