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