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