Home | History | Annotate | Download | only in aapt
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //
     18 // Access to entries in a Zip archive.
     19 //
     20 
     21 #define LOG_TAG "zip"
     22 
     23 #include "ZipEntry.h"
     24 #include <utils/Log.h>
     25 
     26 #include <stdio.h>
     27 #include <string.h>
     28 #include <assert.h>
     29 
     30 using 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  */
     39 status_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  */
    112 void 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  */
    144 status_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  */
    180 status_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  */
    216 void 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  */
    241 void 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  */
    262 bool 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  */
    320 time_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  */
    339 void 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  */
    384 status_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 
    444 bail:
    445     return result;
    446 }
    447 
    448 /*
    449  * Write a local file header.
    450  */
    451 status_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  */
    489 void 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  */
    518 status_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 
    602 bail:
    603     return result;
    604 }
    605 
    606 /*
    607  * Write a central dir entry.
    608  */
    609 status_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  */
    658 void 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  */
    682 ZipEntry::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