• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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