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