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