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
30using 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 */
39status_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 */
112void 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 */
144status_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 */
180status_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 */
216void 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 */
241void 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 */
262bool 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 */
320time_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 */
339void 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 */
384status_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
444bail:
445    return result;
446}
447
448/*
449 * Write a local file header.
450 */
451status_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 */
489void 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 */
518status_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
602bail:
603    return result;
604}
605
606/*
607 * Write a central dir entry.
608 */
609status_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 */
658void 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 */
682ZipEntry::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}
736