OSDN Git Service

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