Home | History | Annotate | Download | only in zipalign
      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     /*
    148      * Copy everything in the CDE over, then fix up the hairy bits.
    149      */
    150     memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
    151 
    152     if (mCDE.mFileNameLength > 0) {
    153         mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
    154         if (mCDE.mFileName == NULL)
    155             return NO_MEMORY;
    156         strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
    157     }
    158     if (mCDE.mFileCommentLength > 0) {
    159         mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
    160         if (mCDE.mFileComment == NULL)
    161             return NO_MEMORY;
    162         strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
    163     }
    164     if (mCDE.mExtraFieldLength > 0) {
    165         /* we null-terminate this, though it may not be a string */
    166         mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
    167         if (mCDE.mExtraField == NULL)
    168             return NO_MEMORY;
    169         memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
    170             mCDE.mExtraFieldLength+1);
    171     }
    172 
    173     /* construct the LFH from the CDE */
    174     copyCDEtoLFH();
    175 
    176     /*
    177      * The LFH "extra" field is independent of the CDE "extra", so we
    178      * handle it here.
    179      */
    180     assert(mLFH.mExtraField == NULL);
    181     mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
    182     if (mLFH.mExtraFieldLength > 0) {
    183         mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
    184         if (mLFH.mExtraField == NULL)
    185             return NO_MEMORY;
    186         memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
    187             mLFH.mExtraFieldLength+1);
    188     }
    189 
    190     return NO_ERROR;
    191 }
    192 
    193 /*
    194  * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
    195  * potentially confuse something that put "extra" data in here earlier,
    196  * but I can't find an actual problem.
    197  */
    198 status_t ZipEntry::addPadding(int padding)
    199 {
    200     if (padding <= 0)
    201         return INVALID_OPERATION;
    202 
    203     //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
    204     //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
    205 
    206     if (mLFH.mExtraFieldLength > 0) {
    207         /* extend existing field */
    208         unsigned char* newExtra;
    209 
    210         newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
    211         if (newExtra == NULL)
    212             return NO_MEMORY;
    213         memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
    214         memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
    215 
    216         delete[] mLFH.mExtraField;
    217         mLFH.mExtraField = newExtra;
    218         mLFH.mExtraFieldLength += padding;
    219     } else {
    220         /* create new field */
    221         mLFH.mExtraField = new unsigned char[padding];
    222         memset(mLFH.mExtraField, 0, padding);
    223         mLFH.mExtraFieldLength = padding;
    224     }
    225 
    226     return NO_ERROR;
    227 }
    228 
    229 /*
    230  * Set the fields in the LFH equal to the corresponding fields in the CDE.
    231  *
    232  * This does not touch the LFH "extra" field.
    233  */
    234 void ZipEntry::copyCDEtoLFH(void)
    235 {
    236     mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
    237     mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
    238     mLFH.mCompressionMethod = mCDE.mCompressionMethod;
    239     mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
    240     mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
    241     mLFH.mCRC32             = mCDE.mCRC32;
    242     mLFH.mCompressedSize    = mCDE.mCompressedSize;
    243     mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
    244     mLFH.mFileNameLength    = mCDE.mFileNameLength;
    245     // the "extra field" is independent
    246 
    247     delete[] mLFH.mFileName;
    248     if (mLFH.mFileNameLength > 0) {
    249         mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
    250         strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
    251     } else {
    252         mLFH.mFileName = NULL;
    253     }
    254 }
    255 
    256 /*
    257  * Set some information about a file after we add it.
    258  */
    259 void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
    260     int compressionMethod)
    261 {
    262     mCDE.mCompressionMethod = compressionMethod;
    263     mCDE.mCRC32 = crc32;
    264     mCDE.mCompressedSize = compLen;
    265     mCDE.mUncompressedSize = uncompLen;
    266     mCDE.mCompressionMethod = compressionMethod;
    267     if (compressionMethod == kCompressDeflated) {
    268         mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
    269     }
    270     copyCDEtoLFH();
    271 }
    272 
    273 /*
    274  * See if the data in mCDE and mLFH match up.  This is mostly useful for
    275  * debugging these classes, but it can be used to identify damaged
    276  * archives.
    277  *
    278  * Returns "false" if they differ.
    279  */
    280 bool ZipEntry::compareHeaders(void) const
    281 {
    282     if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
    283         ALOGV("cmp: VersionToExtract\n");
    284         return false;
    285     }
    286     if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
    287         ALOGV("cmp: GPBitFlag\n");
    288         return false;
    289     }
    290     if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
    291         ALOGV("cmp: CompressionMethod\n");
    292         return false;
    293     }
    294     if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
    295         ALOGV("cmp: LastModFileTime\n");
    296         return false;
    297     }
    298     if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
    299         ALOGV("cmp: LastModFileDate\n");
    300         return false;
    301     }
    302     if (mCDE.mCRC32 != mLFH.mCRC32) {
    303         ALOGV("cmp: CRC32\n");
    304         return false;
    305     }
    306     if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
    307         ALOGV("cmp: CompressedSize\n");
    308         return false;
    309     }
    310     if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
    311         ALOGV("cmp: UncompressedSize\n");
    312         return false;
    313     }
    314     if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
    315         ALOGV("cmp: FileNameLength\n");
    316         return false;
    317     }
    318 #if 0       // this seems to be used for padding, not real data
    319     if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
    320         ALOGV("cmp: ExtraFieldLength\n");
    321         return false;
    322     }
    323 #endif
    324     if (mCDE.mFileName != NULL) {
    325         if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
    326             ALOGV("cmp: FileName\n");
    327             return false;
    328         }
    329     }
    330 
    331     return true;
    332 }
    333 
    334 
    335 /*
    336  * Convert the DOS date/time stamp into a UNIX time stamp.
    337  */
    338 time_t ZipEntry::getModWhen(void) const
    339 {
    340     struct tm parts;
    341 
    342     parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
    343     parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
    344     parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
    345     parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
    346     parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
    347     parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
    348     parts.tm_wday = parts.tm_yday = 0;
    349     parts.tm_isdst = -1;        // DST info "not available"
    350 
    351     return mktime(&parts);
    352 }
    353 
    354 /*
    355  * Set the CDE/LFH timestamp from UNIX time.
    356  */
    357 void ZipEntry::setModWhen(time_t when)
    358 {
    359 #ifdef HAVE_LOCALTIME_R
    360     struct tm tmResult;
    361 #endif
    362     time_t even;
    363     unsigned short zdate, ztime;
    364 
    365     struct tm* ptm;
    366 
    367     /* round up to an even number of seconds */
    368     even = (time_t)(((unsigned long)(when) + 1) & (~1));
    369 
    370     /* expand */
    371 #ifdef HAVE_LOCALTIME_R
    372     ptm = localtime_r(&even, &tmResult);
    373 #else
    374     ptm = localtime(&even);
    375 #endif
    376 
    377     int year;
    378     year = ptm->tm_year;
    379     if (year < 80)
    380         year = 80;
    381 
    382     zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
    383     ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
    384 
    385     mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
    386     mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
    387 }
    388 
    389 
    390 /*
    391  * ===========================================================================
    392  *      ZipEntry::LocalFileHeader
    393  * ===========================================================================
    394  */
    395 
    396 /*
    397  * Read a local file header.
    398  *
    399  * On entry, "fp" points to the signature at the start of the header.
    400  * On exit, "fp" points to the start of data.
    401  */
    402 status_t ZipEntry::LocalFileHeader::read(FILE* fp)
    403 {
    404     status_t result = NO_ERROR;
    405     unsigned char buf[kLFHLen];
    406 
    407     assert(mFileName == NULL);
    408     assert(mExtraField == NULL);
    409 
    410     if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
    411         result = UNKNOWN_ERROR;
    412         goto bail;
    413     }
    414 
    415     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
    416         ALOGD("whoops: didn't find expected signature\n");
    417         result = UNKNOWN_ERROR;
    418         goto bail;
    419     }
    420 
    421     mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
    422     mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
    423     mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
    424     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
    425     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
    426     mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
    427     mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
    428     mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
    429     mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
    430     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
    431 
    432     // TODO: validate sizes
    433 
    434     /* grab filename */
    435     if (mFileNameLength != 0) {
    436         mFileName = new unsigned char[mFileNameLength+1];
    437         if (mFileName == NULL) {
    438             result = NO_MEMORY;
    439             goto bail;
    440         }
    441         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
    442             result = UNKNOWN_ERROR;
    443             goto bail;
    444         }
    445         mFileName[mFileNameLength] = '\0';
    446     }
    447 
    448     /* grab extra field */
    449     if (mExtraFieldLength != 0) {
    450         mExtraField = new unsigned char[mExtraFieldLength+1];
    451         if (mExtraField == NULL) {
    452             result = NO_MEMORY;
    453             goto bail;
    454         }
    455         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
    456             result = UNKNOWN_ERROR;
    457             goto bail;
    458         }
    459         mExtraField[mExtraFieldLength] = '\0';
    460     }
    461 
    462 bail:
    463     return result;
    464 }
    465 
    466 /*
    467  * Write a local file header.
    468  */
    469 status_t ZipEntry::LocalFileHeader::write(FILE* fp)
    470 {
    471     unsigned char buf[kLFHLen];
    472 
    473     ZipEntry::putLongLE(&buf[0x00], kSignature);
    474     ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
    475     ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
    476     ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
    477     ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
    478     ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
    479     ZipEntry::putLongLE(&buf[0x0e], mCRC32);
    480     ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
    481     ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
    482     ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
    483     ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
    484 
    485     if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
    486         return UNKNOWN_ERROR;
    487 
    488     /* write filename */
    489     if (mFileNameLength != 0) {
    490         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
    491             return UNKNOWN_ERROR;
    492     }
    493 
    494     /* write "extra field" */
    495     if (mExtraFieldLength != 0) {
    496         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
    497             return UNKNOWN_ERROR;
    498     }
    499 
    500     return NO_ERROR;
    501 }
    502 
    503 
    504 /*
    505  * Dump the contents of a LocalFileHeader object.
    506  */
    507 void ZipEntry::LocalFileHeader::dump(void) const
    508 {
    509     ALOGD(" LocalFileHeader contents:\n");
    510     ALOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
    511         mVersionToExtract, mGPBitFlag, mCompressionMethod);
    512     ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
    513         mLastModFileTime, mLastModFileDate, mCRC32);
    514     ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
    515         mCompressedSize, mUncompressedSize);
    516     ALOGD("  filenameLen=%u extraLen=%u\n",
    517         mFileNameLength, mExtraFieldLength);
    518     if (mFileName != NULL)
    519         ALOGD("  filename: '%s'\n", mFileName);
    520 }
    521 
    522 
    523 /*
    524  * ===========================================================================
    525  *      ZipEntry::CentralDirEntry
    526  * ===========================================================================
    527  */
    528 
    529 /*
    530  * Read the central dir entry that appears next in the file.
    531  *
    532  * On entry, "fp" should be positioned on the signature bytes for the
    533  * entry.  On exit, "fp" will point at the signature word for the next
    534  * entry or for the EOCD.
    535  */
    536 status_t ZipEntry::CentralDirEntry::read(FILE* fp)
    537 {
    538     status_t result = NO_ERROR;
    539     unsigned char buf[kCDELen];
    540 
    541     /* no re-use */
    542     assert(mFileName == NULL);
    543     assert(mExtraField == NULL);
    544     assert(mFileComment == NULL);
    545 
    546     if (fread(buf, 1, kCDELen, fp) != kCDELen) {
    547         result = UNKNOWN_ERROR;
    548         goto bail;
    549     }
    550 
    551     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
    552         ALOGD("Whoops: didn't find expected signature\n");
    553         result = UNKNOWN_ERROR;
    554         goto bail;
    555     }
    556 
    557     mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
    558     mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
    559     mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
    560     mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
    561     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
    562     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
    563     mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
    564     mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
    565     mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
    566     mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
    567     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
    568     mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
    569     mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
    570     mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
    571     mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
    572     mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
    573 
    574     // TODO: validate sizes and offsets
    575 
    576     /* grab filename */
    577     if (mFileNameLength != 0) {
    578         mFileName = new unsigned char[mFileNameLength+1];
    579         if (mFileName == NULL) {
    580             result = NO_MEMORY;
    581             goto bail;
    582         }
    583         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
    584             result = UNKNOWN_ERROR;
    585             goto bail;
    586         }
    587         mFileName[mFileNameLength] = '\0';
    588     }
    589 
    590     /* read "extra field" */
    591     if (mExtraFieldLength != 0) {
    592         mExtraField = new unsigned char[mExtraFieldLength+1];
    593         if (mExtraField == NULL) {
    594             result = NO_MEMORY;
    595             goto bail;
    596         }
    597         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
    598             result = UNKNOWN_ERROR;
    599             goto bail;
    600         }
    601         mExtraField[mExtraFieldLength] = '\0';
    602     }
    603 
    604 
    605     /* grab comment, if any */
    606     if (mFileCommentLength != 0) {
    607         mFileComment = new unsigned char[mFileCommentLength+1];
    608         if (mFileComment == NULL) {
    609             result = NO_MEMORY;
    610             goto bail;
    611         }
    612         if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
    613         {
    614             result = UNKNOWN_ERROR;
    615             goto bail;
    616         }
    617         mFileComment[mFileCommentLength] = '\0';
    618     }
    619 
    620 bail:
    621     return result;
    622 }
    623 
    624 /*
    625  * Write a central dir entry.
    626  */
    627 status_t ZipEntry::CentralDirEntry::write(FILE* fp)
    628 {
    629     unsigned char buf[kCDELen];
    630 
    631     ZipEntry::putLongLE(&buf[0x00], kSignature);
    632     ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
    633     ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
    634     ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
    635     ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
    636     ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
    637     ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
    638     ZipEntry::putLongLE(&buf[0x10], mCRC32);
    639     ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
    640     ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
    641     ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
    642     ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
    643     ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
    644     ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
    645     ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
    646     ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
    647     ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
    648 
    649     if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
    650         return UNKNOWN_ERROR;
    651 
    652     /* write filename */
    653     if (mFileNameLength != 0) {
    654         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
    655             return UNKNOWN_ERROR;
    656     }
    657 
    658     /* write "extra field" */
    659     if (mExtraFieldLength != 0) {
    660         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
    661             return UNKNOWN_ERROR;
    662     }
    663 
    664     /* write comment */
    665     if (mFileCommentLength != 0) {
    666         if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
    667             return UNKNOWN_ERROR;
    668     }
    669 
    670     return NO_ERROR;
    671 }
    672 
    673 /*
    674  * Dump the contents of a CentralDirEntry object.
    675  */
    676 void ZipEntry::CentralDirEntry::dump(void) const
    677 {
    678     ALOGD(" CentralDirEntry contents:\n");
    679     ALOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
    680         mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
    681     ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
    682         mLastModFileTime, mLastModFileDate, mCRC32);
    683     ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
    684         mCompressedSize, mUncompressedSize);
    685     ALOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
    686         mFileNameLength, mExtraFieldLength, mFileCommentLength);
    687     ALOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
    688         mDiskNumberStart, mInternalAttrs, mExternalAttrs,
    689         mLocalHeaderRelOffset);
    690 
    691     if (mFileName != NULL)
    692         ALOGD("  filename: '%s'\n", mFileName);
    693     if (mFileComment != NULL)
    694         ALOGD("  comment: '%s'\n", mFileComment);
    695 }
    696 
    697