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 _POSIX_THREAD_SAFE_FUNCTIONS // For mingw localtime_r().
22
23 #define LOG_TAG "zip"
24
25 #include "ZipEntry.h"
26 #include <utils/Log.h>
27
28 #include <assert.h>
29 #include <inttypes.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <time.h>
33
34 namespace android {
35
36 /*
37 * Initialize a new ZipEntry structure from a FILE* positioned at a
38 * CentralDirectoryEntry.
39 *
40 * On exit, the file pointer will be at the start of the next CDE or
41 * at the EOCD.
42 */
initFromCDE(FILE * fp)43 status_t ZipEntry::initFromCDE(FILE* fp)
44 {
45 //ALOGV("initFromCDE ---\n");
46
47 /* read the CDE */
48 status_t result = mCDE.read(fp);
49 if (result != OK) {
50 ALOGD("mCDE.read failed\n");
51 return result;
52 }
53
54 //mCDE.dump();
55
56 /* using the info in the CDE, go load up the LFH */
57 off_t posn = ftello(fp);
58 if (fseeko(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
59 ALOGD("local header seek failed (%" PRIu32 ")\n",
60 mCDE.mLocalHeaderRelOffset);
61 return UNKNOWN_ERROR;
62 }
63
64 result = mLFH.read(fp);
65 if (result != OK) {
66 ALOGD("mLFH.read failed\n");
67 return result;
68 }
69
70 if (fseeko(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 bool hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
82 if (hasDD) {
83 // do something clever
84 //ALOGD("+++ has data descriptor\n");
85 }
86
87 /*
88 * 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()) {
93 ALOGW("WARNING: header mismatch\n");
94 // 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 OK;
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 */
initNew(const char * fileName,const char * comment)112 void 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 uint8_t[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 uint8_t[mCDE.mFileCommentLength+1];
132 assert(comment != NULL);
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 */
initFromExternal(const ZipEntry * pEntry)145 status_t ZipEntry::initFromExternal(const ZipEntry* pEntry)
146 {
147 /*
148 * Copy everything in the CDE over, then fix up the hairy bits.
149 */
150 memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
151
152 if (mCDE.mFileNameLength > 0) {
153 mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1];
154 if (mCDE.mFileName == NULL)
155 return NO_MEMORY;
156 strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
157 }
158 if (mCDE.mFileCommentLength > 0) {
159 mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1];
160 if (mCDE.mFileComment == NULL)
161 return NO_MEMORY;
162 strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
163 }
164 if (mCDE.mExtraFieldLength > 0) {
165 /* we null-terminate this, though it may not be a string */
166 mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1];
167 if (mCDE.mExtraField == NULL)
168 return NO_MEMORY;
169 memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
170 mCDE.mExtraFieldLength+1);
171 }
172
173 /* construct the LFH from the CDE */
174 copyCDEtoLFH();
175
176 /*
177 * The LFH "extra" field is independent of the CDE "extra", so we
178 * handle it here.
179 */
180 assert(mLFH.mExtraField == NULL);
181 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
182 if (mLFH.mExtraFieldLength > 0) {
183 mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1];
184 if (mLFH.mExtraField == NULL)
185 return NO_MEMORY;
186 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
187 mLFH.mExtraFieldLength+1);
188 }
189
190 return OK;
191 }
192
193 /*
194 * Insert pad bytes in the LFH by tweaking the "extra" field. This will
195 * potentially confuse something that put "extra" data in here earlier,
196 * but I can't find an actual problem.
197 */
addPadding(int padding)198 status_t ZipEntry::addPadding(int padding)
199 {
200 if (padding <= 0)
201 return INVALID_OPERATION;
202
203 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
204 // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
205
206 if (mLFH.mExtraFieldLength > 0) {
207 /* extend existing field */
208 uint8_t* newExtra;
209
210 newExtra = new uint8_t[mLFH.mExtraFieldLength + padding];
211 if (newExtra == NULL)
212 return NO_MEMORY;
213 memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
214 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
215
216 delete[] mLFH.mExtraField;
217 mLFH.mExtraField = newExtra;
218 mLFH.mExtraFieldLength += padding;
219 } else {
220 /* create new field */
221 mLFH.mExtraField = new uint8_t[padding];
222 memset(mLFH.mExtraField, 0, padding);
223 mLFH.mExtraFieldLength = padding;
224 }
225
226 return OK;
227 }
228
229 /*
230 * Set the fields in the LFH equal to the corresponding fields in the CDE.
231 *
232 * This does not touch the LFH "extra" field.
233 */
copyCDEtoLFH(void)234 void ZipEntry::copyCDEtoLFH(void)
235 {
236 mLFH.mVersionToExtract = mCDE.mVersionToExtract;
237 mLFH.mGPBitFlag = mCDE.mGPBitFlag;
238 mLFH.mCompressionMethod = mCDE.mCompressionMethod;
239 mLFH.mLastModFileTime = mCDE.mLastModFileTime;
240 mLFH.mLastModFileDate = mCDE.mLastModFileDate;
241 mLFH.mCRC32 = mCDE.mCRC32;
242 mLFH.mCompressedSize = mCDE.mCompressedSize;
243 mLFH.mUncompressedSize = mCDE.mUncompressedSize;
244 mLFH.mFileNameLength = mCDE.mFileNameLength;
245 // the "extra field" is independent
246
247 delete[] mLFH.mFileName;
248 if (mLFH.mFileNameLength > 0) {
249 mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1];
250 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
251 } else {
252 mLFH.mFileName = NULL;
253 }
254 }
255
256 /*
257 * Set some information about a file after we add it.
258 */
setDataInfo(uint32_t uncompLen,uint32_t compLen,uint32_t crc32,uint32_t compressionMethod)259 void ZipEntry::setDataInfo(uint32_t uncompLen, uint32_t compLen, uint32_t crc32,
260 uint32_t compressionMethod)
261 {
262 mCDE.mCompressionMethod = compressionMethod;
263 mCDE.mCRC32 = crc32;
264 mCDE.mCompressedSize = compLen;
265 mCDE.mUncompressedSize = uncompLen;
266 mCDE.mCompressionMethod = compressionMethod;
267 if (compressionMethod == kCompressDeflated) {
268 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
269 }
270 copyCDEtoLFH();
271 }
272
273 /*
274 * See if the data in mCDE and mLFH match up. This is mostly useful for
275 * debugging these classes, but it can be used to identify damaged
276 * archives.
277 *
278 * Returns "false" if they differ.
279 */
compareHeaders(void) const280 bool ZipEntry::compareHeaders(void) const
281 {
282 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
283 ALOGV("cmp: VersionToExtract\n");
284 return false;
285 }
286 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
287 ALOGV("cmp: GPBitFlag\n");
288 return false;
289 }
290 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
291 ALOGV("cmp: CompressionMethod\n");
292 return false;
293 }
294 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
295 ALOGV("cmp: LastModFileTime\n");
296 return false;
297 }
298 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
299 ALOGV("cmp: LastModFileDate\n");
300 return false;
301 }
302 if (mCDE.mCRC32 != mLFH.mCRC32) {
303 ALOGV("cmp: CRC32\n");
304 return false;
305 }
306 if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
307 ALOGV("cmp: CompressedSize\n");
308 return false;
309 }
310 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
311 ALOGV("cmp: UncompressedSize\n");
312 return false;
313 }
314 if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
315 ALOGV("cmp: FileNameLength\n");
316 return false;
317 }
318 #if 0 // this seems to be used for padding, not real data
319 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
320 ALOGV("cmp: ExtraFieldLength\n");
321 return false;
322 }
323 #endif
324 if (mCDE.mFileName != NULL) {
325 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
326 ALOGV("cmp: FileName\n");
327 return false;
328 }
329 }
330
331 return true;
332 }
333
334
335 /*
336 * Convert the DOS date/time stamp into a UNIX time stamp.
337 */
getModWhen(void) const338 time_t ZipEntry::getModWhen(void) const
339 {
340 struct tm parts;
341
342 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
343 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
344 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
345 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
346 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
347 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
348 parts.tm_wday = parts.tm_yday = 0;
349 parts.tm_isdst = -1; // DST info "not available"
350
351 return mktime(&parts);
352 }
353
354 /*
355 * Set the CDE/LFH timestamp from UNIX time.
356 */
setModWhen(time_t when)357 void ZipEntry::setModWhen(time_t when)
358 {
359 /* round up to an even number of seconds */
360 time_t even = (when & 1) ? (when + 1) : when;
361
362 /* expand */
363 struct tm tmResult;
364 struct tm* ptm = localtime_r(&even, &tmResult);
365
366 // The earliest valid time for ZIP file entries is 1980-01-01. See:
367 // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html.
368 // Set any time before 1980 to 1980-01-01.
369 if (ptm->tm_year < 80) {
370 ptm->tm_year = 80;
371 ptm->tm_mon = 0;
372 ptm->tm_mday = 1;
373 ptm->tm_hour = 0;
374 ptm->tm_min = 0;
375 ptm->tm_sec = 0;
376 }
377
378 uint16_t zdate = static_cast<uint16_t>(
379 (ptm->tm_year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
380 uint16_t ztime = static_cast<uint16_t>(
381 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