OSDN Git Service

eclair snapshot
[android-x86/build.git] / tools / zipalign / ZipEntry.cpp
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 <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29
30 using namespace android;
31
32 /*
33  * Initialize a new ZipEntry structure from a FILE* positioned at a
34  * CentralDirectoryEntry.
35  *
36  * On exit, the file pointer will be at the start of the next CDE or
37  * at the EOCD.
38  */
39 status_t ZipEntry::initFromCDE(FILE* fp)
40 {
41     status_t result;
42     long posn;
43     bool hasDD;
44
45     //LOGV("initFromCDE ---\n");
46
47     /* read the CDE */
48     result = mCDE.read(fp);
49     if (result != NO_ERROR) {
50         LOGD("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     posn = ftell(fp);
58     if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
59         LOGD("local header seek failed (%ld)\n",
60             mCDE.mLocalHeaderRelOffset);
61         return UNKNOWN_ERROR;
62     }
63
64     result = mLFH.read(fp);
65     if (result != NO_ERROR) {
66         LOGD("mLFH.read failed\n");
67         return result;
68     }
69
70     if (fseek(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     hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
82     if (hasDD) {
83         // do something clever
84         //LOGD("+++ has data descriptor\n");
85     }
86
87     /*
88      * Sanity-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         LOGW("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 NO_ERROR;
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  */
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 unsigned char[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 unsigned char[mCDE.mFileCommentLength+1];
132         strcpy((char*) mCDE.mFileComment, comment);
133     }
134
135     copyCDEtoLFH();
136 }
137
138 /*
139  * Initialize a new entry, starting with the ZipEntry from a different
140  * archive.
141  *
142  * Initializes the CDE and the LFH.
143  */
144 status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
145     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 unsigned char[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 unsigned char[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 unsigned char[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 unsigned char[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 NO_ERROR;
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  */
198 status_t ZipEntry::addPadding(int padding)
199 {
200     if (padding <= 0)
201         return INVALID_OPERATION;
202
203     //LOGI("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         unsigned char* newExtra;
209
210         newExtra = new unsigned char[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 unsigned char[padding];
222         memset(mLFH.mExtraField, 0, padding);
223         mLFH.mExtraFieldLength = padding;
224     }
225
226     return NO_ERROR;
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  */
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 unsigned char[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  */
259 void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
260     int 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  */
280 bool ZipEntry::compareHeaders(void) const
281 {
282     if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
283         LOGV("cmp: VersionToExtract\n");
284         return false;
285     }
286     if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
287         LOGV("cmp: GPBitFlag\n");
288         return false;
289     }
290     if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
291         LOGV("cmp: CompressionMethod\n");
292         return false;
293     }
294     if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
295         LOGV("cmp: LastModFileTime\n");
296         return false;
297     }
298     if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
299         LOGV("cmp: LastModFileDate\n");
300         return false;
301     }
302     if (mCDE.mCRC32 != mLFH.mCRC32) {
303         LOGV("cmp: CRC32\n");
304         return false;
305     }
306     if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
307         LOGV("cmp: CompressedSize\n");
308         return false;
309     }
310     if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
311         LOGV("cmp: UncompressedSize\n");
312         return false;
313     }
314     if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
315         LOGV("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         LOGV("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             LOGV("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  */
338 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  */
357 void ZipEntry::setModWhen(time_t when)
358 {
359 #ifdef HAVE_LOCALTIME_R
360     struct tm tmResult;
361 #endif
362     time_t even;
363     unsigned short zdate, ztime;
364
365     struct tm* ptm;
366
367     /* round up to an even number of seconds */
368     even = (time_t)(((unsigned long)(when) + 1) & (~1));
369
370     /* expand */
371 #ifdef HAVE_LOCALTIME_R
372     ptm = localtime_r(&even, &tmResult);
373 #else
374     ptm = localtime(&even);
375 #endif
376
377     int year;
378     year = ptm->tm_year;
379     if (year < 80)
380         year = 80;
381
382     zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
383     ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
384
385     mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
386     mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
387 }
388
389
390 /*
391  * ===========================================================================
392  *      ZipEntry::LocalFileHeader
393  * ===========================================================================
394  */
395
396 /*
397  * Read a local file header.
398  *
399  * On entry, "fp" points to the signature at the start of the header.
400  * On exit, "fp" points to the start of data.
401  */
402 status_t ZipEntry::LocalFileHeader::read(FILE* fp)
403 {
404     status_t result = NO_ERROR;
405     unsigned char buf[kLFHLen];
406
407     assert(mFileName == NULL);
408     assert(mExtraField == NULL);
409
410     if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
411         result = UNKNOWN_ERROR;
412         goto bail;
413     }
414
415     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
416         LOGD("whoops: didn't find expected signature\n");
417         result = UNKNOWN_ERROR;
418         goto bail;
419     }
420
421     mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
422     mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
423     mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
424     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
425     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
426     mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
427     mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
428     mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
429     mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
430     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
431
432     // TODO: validate sizes
433
434     /* grab filename */
435     if (mFileNameLength != 0) {
436         mFileName = new unsigned char[mFileNameLength+1];
437         if (mFileName == NULL) {
438             result = NO_MEMORY;
439             goto bail;
440         }
441         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
442             result = UNKNOWN_ERROR;
443             goto bail;
444         }
445         mFileName[mFileNameLength] = '\0';
446     }
447
448     /* grab extra field */
449     if (mExtraFieldLength != 0) {
450         mExtraField = new unsigned char[mExtraFieldLength+1];
451         if (mExtraField == NULL) {
452             result = NO_MEMORY;
453             goto bail;
454         }
455         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
456             result = UNKNOWN_ERROR;
457             goto bail;
458         }
459         mExtraField[mExtraFieldLength] = '\0';
460     }
461
462 bail:
463     return result;
464 }
465
466 /*
467  * Write a local file header.
468  */
469 status_t ZipEntry::LocalFileHeader::write(FILE* fp)
470 {
471     unsigned char buf[kLFHLen];
472
473     ZipEntry::putLongLE(&buf[0x00], kSignature);
474     ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
475     ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
476     ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
477     ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
478     ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
479     ZipEntry::putLongLE(&buf[0x0e], mCRC32);
480     ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
481     ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
482     ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
483     ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
484
485     if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
486         return UNKNOWN_ERROR;
487
488     /* write filename */
489     if (mFileNameLength != 0) {
490         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
491             return UNKNOWN_ERROR;
492     }
493
494     /* write "extra field" */
495     if (mExtraFieldLength != 0) {
496         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
497             return UNKNOWN_ERROR;
498     }
499
500     return NO_ERROR;
501 }
502
503
504 /*
505  * Dump the contents of a LocalFileHeader object.
506  */
507 void ZipEntry::LocalFileHeader::dump(void) const
508 {
509     LOGD(" LocalFileHeader contents:\n");
510     LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
511         mVersionToExtract, mGPBitFlag, mCompressionMethod);
512     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
513         mLastModFileTime, mLastModFileDate, mCRC32);
514     LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
515         mCompressedSize, mUncompressedSize);
516     LOGD("  filenameLen=%u extraLen=%u\n",
517         mFileNameLength, mExtraFieldLength);
518     if (mFileName != NULL)
519         LOGD("  filename: '%s'\n", mFileName);
520 }
521
522
523 /*
524  * ===========================================================================
525  *      ZipEntry::CentralDirEntry
526  * ===========================================================================
527  */
528
529 /*
530  * Read the central dir entry that appears next in the file.
531  *
532  * On entry, "fp" should be positioned on the signature bytes for the
533  * entry.  On exit, "fp" will point at the signature word for the next
534  * entry or for the EOCD.
535  */
536 status_t ZipEntry::CentralDirEntry::read(FILE* fp)
537 {
538     status_t result = NO_ERROR;
539     unsigned char buf[kCDELen];
540
541     /* no re-use */
542     assert(mFileName == NULL);
543     assert(mExtraField == NULL);
544     assert(mFileComment == NULL);
545
546     if (fread(buf, 1, kCDELen, fp) != kCDELen) {
547         result = UNKNOWN_ERROR;
548         goto bail;
549     }
550
551     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
552         LOGD("Whoops: didn't find expected signature\n");
553         result = UNKNOWN_ERROR;
554         goto bail;
555     }
556
557     mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
558     mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
559     mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
560     mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
561     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
562     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
563     mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
564     mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
565     mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
566     mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
567     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
568     mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
569     mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
570     mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
571     mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
572     mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
573
574     // TODO: validate sizes and offsets
575
576     /* grab filename */
577     if (mFileNameLength != 0) {
578         mFileName = new unsigned char[mFileNameLength+1];
579         if (mFileName == NULL) {
580             result = NO_MEMORY;
581             goto bail;
582         }
583         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
584             result = UNKNOWN_ERROR;
585             goto bail;
586         }
587         mFileName[mFileNameLength] = '\0';
588     }
589
590     /* read "extra field" */
591     if (mExtraFieldLength != 0) {
592         mExtraField = new unsigned char[mExtraFieldLength+1];
593         if (mExtraField == NULL) {
594             result = NO_MEMORY;
595             goto bail;
596         }
597         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
598             result = UNKNOWN_ERROR;
599             goto bail;
600         }
601         mExtraField[mExtraFieldLength] = '\0';
602     }
603
604
605     /* grab comment, if any */
606     if (mFileCommentLength != 0) {
607         mFileComment = new unsigned char[mFileCommentLength+1];
608         if (mFileComment == NULL) {
609             result = NO_MEMORY;
610             goto bail;
611         }
612         if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
613         {
614             result = UNKNOWN_ERROR;
615             goto bail;
616         }
617         mFileComment[mFileCommentLength] = '\0';
618     }
619
620 bail:
621     return result;
622 }
623
624 /*
625  * Write a central dir entry.
626  */
627 status_t ZipEntry::CentralDirEntry::write(FILE* fp)
628 {
629     unsigned char buf[kCDELen];
630
631     ZipEntry::putLongLE(&buf[0x00], kSignature);
632     ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
633     ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
634     ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
635     ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
636     ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
637     ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
638     ZipEntry::putLongLE(&buf[0x10], mCRC32);
639     ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
640     ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
641     ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
642     ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
643     ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
644     ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
645     ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
646     ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
647     ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
648
649     if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
650         return UNKNOWN_ERROR;
651
652     /* write filename */
653     if (mFileNameLength != 0) {
654         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
655             return UNKNOWN_ERROR;
656     }
657
658     /* write "extra field" */
659     if (mExtraFieldLength != 0) {
660         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
661             return UNKNOWN_ERROR;
662     }
663
664     /* write comment */
665     if (mFileCommentLength != 0) {
666         if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
667             return UNKNOWN_ERROR;
668     }
669
670     return NO_ERROR;
671 }
672
673 /*
674  * Dump the contents of a CentralDirEntry object.
675  */
676 void ZipEntry::CentralDirEntry::dump(void) const
677 {
678     LOGD(" CentralDirEntry contents:\n");
679     LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
680         mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
681     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
682         mLastModFileTime, mLastModFileDate, mCRC32);
683     LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
684         mCompressedSize, mUncompressedSize);
685     LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
686         mFileNameLength, mExtraFieldLength, mFileCommentLength);
687     LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
688         mDiskNumberStart, mInternalAttrs, mExternalAttrs,
689         mLocalHeaderRelOffset);
690
691     if (mFileName != NULL)
692         LOGD("  filename: '%s'\n", mFileName);
693     if (mFileComment != NULL)
694         LOGD("  comment: '%s'\n", mFileComment);
695 }
696