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