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
31using 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 */
40status_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 */
113void 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 */
145status_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 */
181status_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 */
217void 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 */
242void 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 */
263bool 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 */
321time_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 */
340void 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 */
385status_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
445bail:
446    return result;
447}
448
449/*
450 * Write a local file header.
451 */
452status_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 */
490void 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 */
519status_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
603bail:
604    return result;
605}
606
607/*
608 * Write a central dir entry.
609 */
610status_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 */
659void 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 */
683ZipEntry::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