OSDN Git Service

[DO NOT MERGE] Fix vulnerability in MemoryIntArray - fix build file
[android-x86/frameworks-base.git] / tools / aapt / 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     //ALOGV("initFromCDE ---\n");
46
47     /* read the CDE */
48     result = mCDE.read(fp);
49     if (result != NO_ERROR) {
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     posn = ftell(fp);
58     if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
59         ALOGD("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         ALOGD("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         //ALOGD("+++ 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         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 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     mCDE = pEntry->mCDE;
148     // Check whether we got all the memory needed.
149     if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
150             (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
151             (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) {
152         return NO_MEMORY;
153     }
154
155     /* construct the LFH from the CDE */
156     copyCDEtoLFH();
157
158     /*
159      * The LFH "extra" field is independent of the CDE "extra", so we
160      * handle it here.
161      */
162     assert(mLFH.mExtraField == NULL);
163     mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
164     if (mLFH.mExtraFieldLength > 0) {
165         mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
166         if (mLFH.mExtraField == NULL)
167             return NO_MEMORY;
168         memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
169             mLFH.mExtraFieldLength+1);
170     }
171
172     return NO_ERROR;
173 }
174
175 /*
176  * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
177  * potentially confuse something that put "extra" data in here earlier,
178  * but I can't find an actual problem.
179  */
180 status_t ZipEntry::addPadding(int padding)
181 {
182     if (padding <= 0)
183         return INVALID_OPERATION;
184
185     //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
186     //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
187
188     if (mLFH.mExtraFieldLength > 0) {
189         /* extend existing field */
190         unsigned char* newExtra;
191
192         newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
193         if (newExtra == NULL)
194             return NO_MEMORY;
195         memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
196         memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
197
198         delete[] mLFH.mExtraField;
199         mLFH.mExtraField = newExtra;
200         mLFH.mExtraFieldLength += padding;
201     } else {
202         /* create new field */
203         mLFH.mExtraField = new unsigned char[padding];
204         memset(mLFH.mExtraField, 0, padding);
205         mLFH.mExtraFieldLength = padding;
206     }
207
208     return NO_ERROR;
209 }
210
211 /*
212  * Set the fields in the LFH equal to the corresponding fields in the CDE.
213  *
214  * This does not touch the LFH "extra" field.
215  */
216 void ZipEntry::copyCDEtoLFH(void)
217 {
218     mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
219     mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
220     mLFH.mCompressionMethod = mCDE.mCompressionMethod;
221     mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
222     mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
223     mLFH.mCRC32             = mCDE.mCRC32;
224     mLFH.mCompressedSize    = mCDE.mCompressedSize;
225     mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
226     mLFH.mFileNameLength    = mCDE.mFileNameLength;
227     // the "extra field" is independent
228
229     delete[] mLFH.mFileName;
230     if (mLFH.mFileNameLength > 0) {
231         mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
232         strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
233     } else {
234         mLFH.mFileName = NULL;
235     }
236 }
237
238 /*
239  * Set some information about a file after we add it.
240  */
241 void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
242     int compressionMethod)
243 {
244     mCDE.mCompressionMethod = compressionMethod;
245     mCDE.mCRC32 = crc32;
246     mCDE.mCompressedSize = compLen;
247     mCDE.mUncompressedSize = uncompLen;
248     mCDE.mCompressionMethod = compressionMethod;
249     if (compressionMethod == kCompressDeflated) {
250         mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
251     }
252     copyCDEtoLFH();
253 }
254
255 /*
256  * See if the data in mCDE and mLFH match up.  This is mostly useful for
257  * debugging these classes, but it can be used to identify damaged
258  * archives.
259  *
260  * Returns "false" if they differ.
261  */
262 bool ZipEntry::compareHeaders(void) const
263 {
264     if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
265         ALOGV("cmp: VersionToExtract\n");
266         return false;
267     }
268     if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
269         ALOGV("cmp: GPBitFlag\n");
270         return false;
271     }
272     if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
273         ALOGV("cmp: CompressionMethod\n");
274         return false;
275     }
276     if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
277         ALOGV("cmp: LastModFileTime\n");
278         return false;
279     }
280     if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
281         ALOGV("cmp: LastModFileDate\n");
282         return false;
283     }
284     if (mCDE.mCRC32 != mLFH.mCRC32) {
285         ALOGV("cmp: CRC32\n");
286         return false;
287     }
288     if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
289         ALOGV("cmp: CompressedSize\n");
290         return false;
291     }
292     if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
293         ALOGV("cmp: UncompressedSize\n");
294         return false;
295     }
296     if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
297         ALOGV("cmp: FileNameLength\n");
298         return false;
299     }
300 #if 0       // this seems to be used for padding, not real data
301     if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
302         ALOGV("cmp: ExtraFieldLength\n");
303         return false;
304     }
305 #endif
306     if (mCDE.mFileName != NULL) {
307         if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
308             ALOGV("cmp: FileName\n");
309             return false;
310         }
311     }
312
313     return true;
314 }
315
316
317 /*
318  * Convert the DOS date/time stamp into a UNIX time stamp.
319  */
320 time_t ZipEntry::getModWhen(void) const
321 {
322     struct tm parts;
323
324     parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
325     parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
326     parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
327     parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
328     parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
329     parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
330     parts.tm_wday = parts.tm_yday = 0;
331     parts.tm_isdst = -1;        // DST info "not available"
332
333     return mktime(&parts);
334 }
335
336 /*
337  * Set the CDE/LFH timestamp from UNIX time.
338  */
339 void ZipEntry::setModWhen(time_t when)
340 {
341 #if !defined(_WIN32)
342     struct tm tmResult;
343 #endif
344     time_t even;
345     unsigned short zdate, ztime;
346
347     struct tm* ptm;
348
349     /* round up to an even number of seconds */
350     even = (time_t)(((unsigned long)(when) + 1) & (~1));
351
352     /* expand */
353 #if !defined(_WIN32)
354     ptm = localtime_r(&even, &tmResult);
355 #else
356     ptm = localtime(&even);
357 #endif
358
359     int year;
360     year = ptm->tm_year;
361     if (year < 80)
362         year = 80;
363
364     zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
365     ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
366
367     mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
368     mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
369 }
370
371
372 /*
373  * ===========================================================================
374  *      ZipEntry::LocalFileHeader
375  * ===========================================================================
376  */
377
378 /*
379  * Read a local file header.
380  *
381  * On entry, "fp" points to the signature at the start of the header.
382  * On exit, "fp" points to the start of data.
383  */
384 status_t ZipEntry::LocalFileHeader::read(FILE* fp)
385 {
386     status_t result = NO_ERROR;
387     unsigned char buf[kLFHLen];
388
389     assert(mFileName == NULL);
390     assert(mExtraField == NULL);
391
392     if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
393         result = UNKNOWN_ERROR;
394         goto bail;
395     }
396
397     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
398         ALOGD("whoops: didn't find expected signature\n");
399         result = UNKNOWN_ERROR;
400         goto bail;
401     }
402
403     mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
404     mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
405     mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
406     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
407     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
408     mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
409     mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
410     mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
411     mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
412     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
413
414     // TODO: validate sizes
415
416     /* grab filename */
417     if (mFileNameLength != 0) {
418         mFileName = new unsigned char[mFileNameLength+1];
419         if (mFileName == NULL) {
420             result = NO_MEMORY;
421             goto bail;
422         }
423         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
424             result = UNKNOWN_ERROR;
425             goto bail;
426         }
427         mFileName[mFileNameLength] = '\0';
428     }
429
430     /* grab extra field */
431     if (mExtraFieldLength != 0) {
432         mExtraField = new unsigned char[mExtraFieldLength+1];
433         if (mExtraField == NULL) {
434             result = NO_MEMORY;
435             goto bail;
436         }
437         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
438             result = UNKNOWN_ERROR;
439             goto bail;
440         }
441         mExtraField[mExtraFieldLength] = '\0';
442     }
443
444 bail:
445     return result;
446 }
447
448 /*
449  * Write a local file header.
450  */
451 status_t ZipEntry::LocalFileHeader::write(FILE* fp)
452 {
453     unsigned char buf[kLFHLen];
454
455     ZipEntry::putLongLE(&buf[0x00], kSignature);
456     ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
457     ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
458     ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
459     ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
460     ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
461     ZipEntry::putLongLE(&buf[0x0e], mCRC32);
462     ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
463     ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
464     ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
465     ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
466
467     if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
468         return UNKNOWN_ERROR;
469
470     /* write filename */
471     if (mFileNameLength != 0) {
472         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
473             return UNKNOWN_ERROR;
474     }
475
476     /* write "extra field" */
477     if (mExtraFieldLength != 0) {
478         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
479             return UNKNOWN_ERROR;
480     }
481
482     return NO_ERROR;
483 }
484
485
486 /*
487  * Dump the contents of a LocalFileHeader object.
488  */
489 void ZipEntry::LocalFileHeader::dump(void) const
490 {
491     ALOGD(" LocalFileHeader contents:\n");
492     ALOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
493         mVersionToExtract, mGPBitFlag, mCompressionMethod);
494     ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
495         mLastModFileTime, mLastModFileDate, mCRC32);
496     ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
497         mCompressedSize, mUncompressedSize);
498     ALOGD("  filenameLen=%u extraLen=%u\n",
499         mFileNameLength, mExtraFieldLength);
500     if (mFileName != NULL)
501         ALOGD("  filename: '%s'\n", mFileName);
502 }
503
504
505 /*
506  * ===========================================================================
507  *      ZipEntry::CentralDirEntry
508  * ===========================================================================
509  */
510
511 /*
512  * Read the central dir entry that appears next in the file.
513  *
514  * On entry, "fp" should be positioned on the signature bytes for the
515  * entry.  On exit, "fp" will point at the signature word for the next
516  * entry or for the EOCD.
517  */
518 status_t ZipEntry::CentralDirEntry::read(FILE* fp)
519 {
520     status_t result = NO_ERROR;
521     unsigned char buf[kCDELen];
522
523     /* no re-use */
524     assert(mFileName == NULL);
525     assert(mExtraField == NULL);
526     assert(mFileComment == NULL);
527
528     if (fread(buf, 1, kCDELen, fp) != kCDELen) {
529         result = UNKNOWN_ERROR;
530         goto bail;
531     }
532
533     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
534         ALOGD("Whoops: didn't find expected signature\n");
535         result = UNKNOWN_ERROR;
536         goto bail;
537     }
538
539     mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
540     mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
541     mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
542     mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
543     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
544     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
545     mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
546     mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
547     mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
548     mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
549     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
550     mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
551     mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
552     mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
553     mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
554     mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
555
556     // TODO: validate sizes and offsets
557
558     /* grab filename */
559     if (mFileNameLength != 0) {
560         mFileName = new unsigned char[mFileNameLength+1];
561         if (mFileName == NULL) {
562             result = NO_MEMORY;
563             goto bail;
564         }
565         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
566             result = UNKNOWN_ERROR;
567             goto bail;
568         }
569         mFileName[mFileNameLength] = '\0';
570     }
571
572     /* read "extra field" */
573     if (mExtraFieldLength != 0) {
574         mExtraField = new unsigned char[mExtraFieldLength+1];
575         if (mExtraField == NULL) {
576             result = NO_MEMORY;
577             goto bail;
578         }
579         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
580             result = UNKNOWN_ERROR;
581             goto bail;
582         }
583         mExtraField[mExtraFieldLength] = '\0';
584     }
585
586
587     /* grab comment, if any */
588     if (mFileCommentLength != 0) {
589         mFileComment = new unsigned char[mFileCommentLength+1];
590         if (mFileComment == NULL) {
591             result = NO_MEMORY;
592             goto bail;
593         }
594         if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
595         {
596             result = UNKNOWN_ERROR;
597             goto bail;
598         }
599         mFileComment[mFileCommentLength] = '\0';
600     }
601
602 bail:
603     return result;
604 }
605
606 /*
607  * Write a central dir entry.
608  */
609 status_t ZipEntry::CentralDirEntry::write(FILE* fp)
610 {
611     unsigned char buf[kCDELen];
612
613     ZipEntry::putLongLE(&buf[0x00], kSignature);
614     ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
615     ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
616     ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
617     ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
618     ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
619     ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
620     ZipEntry::putLongLE(&buf[0x10], mCRC32);
621     ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
622     ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
623     ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
624     ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
625     ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
626     ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
627     ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
628     ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
629     ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
630
631     if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
632         return UNKNOWN_ERROR;
633
634     /* write filename */
635     if (mFileNameLength != 0) {
636         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
637             return UNKNOWN_ERROR;
638     }
639
640     /* write "extra field" */
641     if (mExtraFieldLength != 0) {
642         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
643             return UNKNOWN_ERROR;
644     }
645
646     /* write comment */
647     if (mFileCommentLength != 0) {
648         if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
649             return UNKNOWN_ERROR;
650     }
651
652     return NO_ERROR;
653 }
654
655 /*
656  * Dump the contents of a CentralDirEntry object.
657  */
658 void ZipEntry::CentralDirEntry::dump(void) const
659 {
660     ALOGD(" CentralDirEntry contents:\n");
661     ALOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
662         mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
663     ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
664         mLastModFileTime, mLastModFileDate, mCRC32);
665     ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
666         mCompressedSize, mUncompressedSize);
667     ALOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
668         mFileNameLength, mExtraFieldLength, mFileCommentLength);
669     ALOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
670         mDiskNumberStart, mInternalAttrs, mExternalAttrs,
671         mLocalHeaderRelOffset);
672
673     if (mFileName != NULL)
674         ALOGD("  filename: '%s'\n", mFileName);
675     if (mFileComment != NULL)
676         ALOGD("  comment: '%s'\n", mFileComment);
677 }
678
679 /*
680  * Copy-assignment operator for CentralDirEntry.
681  */
682 ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) {
683     if (this == &src) {
684         return *this;
685     }
686
687     // Free up old data.
688     delete[] mFileName;
689     delete[] mExtraField;
690     delete[] mFileComment;
691
692     // Copy scalars.
693     mVersionMadeBy = src.mVersionMadeBy;
694     mVersionToExtract = src.mVersionToExtract;
695     mGPBitFlag = src.mGPBitFlag;
696     mCompressionMethod = src.mCompressionMethod;
697     mLastModFileTime = src.mLastModFileTime;
698     mLastModFileDate = src.mLastModFileDate;
699     mCRC32 = src.mCRC32;
700     mCompressedSize = src.mCompressedSize;
701     mUncompressedSize = src.mUncompressedSize;
702     mFileNameLength = src.mFileNameLength;
703     mExtraFieldLength = src.mExtraFieldLength;
704     mFileCommentLength = src.mFileCommentLength;
705     mDiskNumberStart = src.mDiskNumberStart;
706     mInternalAttrs = src.mInternalAttrs;
707     mExternalAttrs = src.mExternalAttrs;
708     mLocalHeaderRelOffset = src.mLocalHeaderRelOffset;
709
710     // Copy strings, if necessary.
711     if (mFileNameLength > 0) {
712         mFileName = new unsigned char[mFileNameLength + 1];
713         if (mFileName != NULL)
714             strcpy((char*)mFileName, (char*)src.mFileName);
715     } else {
716         mFileName = NULL;
717     }
718     if (mFileCommentLength > 0) {
719         mFileComment = new unsigned char[mFileCommentLength + 1];
720         if (mFileComment != NULL)
721             strcpy((char*)mFileComment, (char*)src.mFileComment);
722     } else {
723         mFileComment = NULL;
724     }
725     if (mExtraFieldLength > 0) {
726         /* we null-terminate this, though it may not be a string */
727         mExtraField = new unsigned char[mExtraFieldLength + 1];
728         if (mExtraField != NULL)
729             memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1);
730     } else {
731         mExtraField = NULL;
732     }
733
734     return *this;
735 }