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 Zip archives.
     19 //
     20 
     21 #define LOG_TAG "zip"
     22 
     23 #include <utils/Log.h>
     24 #include <ziparchive/zip_archive.h>
     25 
     26 #include "ZipFile.h"
     27 
     28 #include <zlib.h>
     29 
     30 #include "zopfli/deflate.h"
     31 
     32 #include <memory.h>
     33 #include <sys/stat.h>
     34 #include <errno.h>
     35 #include <assert.h>
     36 #include <inttypes.h>
     37 
     38 using namespace android;
     39 
     40 /*
     41  * Some environments require the "b", some choke on it.
     42  */
     43 #define FILE_OPEN_RO        "rb"
     44 #define FILE_OPEN_RW        "r+b"
     45 #define FILE_OPEN_RW_CREATE "w+b"
     46 
     47 /* should live somewhere else? */
     48 static status_t errnoToStatus(int err)
     49 {
     50     if (err == ENOENT)
     51         return NAME_NOT_FOUND;
     52     else if (err == EACCES)
     53         return PERMISSION_DENIED;
     54     else
     55         return UNKNOWN_ERROR;
     56 }
     57 
     58 /*
     59  * Open a file and parse its guts.
     60  */
     61 status_t ZipFile::open(const char* zipFileName, int flags)
     62 {
     63     bool newArchive = false;
     64 
     65     assert(mZipFp == NULL);     // no reopen
     66 
     67     if ((flags & kOpenTruncate))
     68         flags |= kOpenCreate;           // trunc implies create
     69 
     70     if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
     71         return INVALID_OPERATION;       // not both
     72     if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
     73         return INVALID_OPERATION;       // not neither
     74     if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
     75         return INVALID_OPERATION;       // create requires write
     76 
     77     if (flags & kOpenTruncate) {
     78         newArchive = true;
     79     } else {
     80         newArchive = (access(zipFileName, F_OK) != 0);
     81         if (!(flags & kOpenCreate) && newArchive) {
     82             /* not creating, must already exist */
     83             ALOGD("File %s does not exist", zipFileName);
     84             return NAME_NOT_FOUND;
     85         }
     86     }
     87 
     88     /* open the file */
     89     const char* openflags;
     90     if (flags & kOpenReadWrite) {
     91         if (newArchive)
     92             openflags = FILE_OPEN_RW_CREATE;
     93         else
     94             openflags = FILE_OPEN_RW;
     95     } else {
     96         openflags = FILE_OPEN_RO;
     97     }
     98     mZipFp = fopen(zipFileName, openflags);
     99     if (mZipFp == NULL) {
    100         int err = errno;
    101         ALOGD("fopen failed: %d\n", err);
    102         return errnoToStatus(err);
    103     }
    104 
    105     status_t result;
    106     if (!newArchive) {
    107         /*
    108          * Load the central directory.  If that fails, then this probably
    109          * isn't a Zip archive.
    110          */
    111         result = readCentralDir();
    112     } else {
    113         /*
    114          * Newly-created.  The EndOfCentralDir constructor actually
    115          * sets everything to be the way we want it (all zeroes).  We
    116          * set mNeedCDRewrite so that we create *something* if the
    117          * caller doesn't add any files.  (We could also just unlink
    118          * the file if it's brand new and nothing was added, but that's
    119          * probably doing more than we really should -- the user might
    120          * have a need for empty zip files.)
    121          */
    122         mNeedCDRewrite = true;
    123         result = OK;
    124     }
    125 
    126     if (flags & kOpenReadOnly)
    127         mReadOnly = true;
    128     else
    129         assert(!mReadOnly);
    130 
    131     return result;
    132 }
    133 
    134 /*
    135  * Return the Nth entry in the archive.
    136  */
    137 android::ZipEntry* ZipFile::getEntryByIndex(int idx) const
    138 {
    139     if (idx < 0 || idx >= (int) mEntries.size())
    140         return NULL;
    141 
    142     return mEntries[idx];
    143 }
    144 
    145 /*
    146  * Find an entry by name.
    147  */
    148 android::ZipEntry* ZipFile::getEntryByName(const char* fileName) const
    149 {
    150     /*
    151      * Do a stupid linear string-compare search.
    152      *
    153      * There are various ways to speed this up, especially since it's rare
    154      * to intermingle changes to the archive with "get by name" calls.  We
    155      * don't want to sort the mEntries vector itself, however, because
    156      * it's used to recreate the Central Directory.
    157      *
    158      * (Hash table works, parallel list of pointers in sorted order is good.)
    159      */
    160     int idx;
    161 
    162     for (idx = mEntries.size()-1; idx >= 0; idx--) {
    163         ZipEntry* pEntry = mEntries[idx];
    164         if (!pEntry->getDeleted() &&
    165             strcmp(fileName, pEntry->getFileName()) == 0)
    166         {
    167             return pEntry;
    168         }
    169     }
    170 
    171     return NULL;
    172 }
    173 
    174 /*
    175  * Empty the mEntries vector.
    176  */
    177 void ZipFile::discardEntries(void)
    178 {
    179     int count = mEntries.size();
    180 
    181     while (--count >= 0)
    182         delete mEntries[count];
    183 
    184     mEntries.clear();
    185 }
    186 
    187 
    188 /*
    189  * Find the central directory and read the contents.
    190  *
    191  * The fun thing about ZIP archives is that they may or may not be
    192  * readable from start to end.  In some cases, notably for archives
    193  * that were written to stdout, the only length information is in the
    194  * central directory at the end of the file.
    195  *
    196  * Of course, the central directory can be followed by a variable-length
    197  * comment field, so we have to scan through it backwards.  The comment
    198  * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
    199  * itself, plus apparently sometimes people throw random junk on the end
    200  * just for the fun of it.
    201  *
    202  * This is all a little wobbly.  If the wrong value ends up in the EOCD
    203  * area, we're hosed.  This appears to be the way that everbody handles
    204  * it though, so we're in pretty good company if this fails.
    205  */
    206 status_t ZipFile::readCentralDir(void)
    207 {
    208     status_t result = OK;
    209     uint8_t* buf = NULL;
    210     off_t fileLength, seekStart;
    211     long readAmount;
    212     int i;
    213 
    214     fseek(mZipFp, 0, SEEK_END);
    215     fileLength = ftell(mZipFp);
    216     rewind(mZipFp);
    217 
    218     /* too small to be a ZIP archive? */
    219     if (fileLength < EndOfCentralDir::kEOCDLen) {
    220         ALOGD("Length is %ld -- too small\n", (long)fileLength);
    221         result = INVALID_OPERATION;
    222         goto bail;
    223     }
    224 
    225     buf = new uint8_t[EndOfCentralDir::kMaxEOCDSearch];
    226     if (buf == NULL) {
    227         ALOGD("Failure allocating %d bytes for EOCD search",
    228              EndOfCentralDir::kMaxEOCDSearch);
    229         result = NO_MEMORY;
    230         goto bail;
    231     }
    232 
    233     if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
    234         seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
    235         readAmount = EndOfCentralDir::kMaxEOCDSearch;
    236     } else {
    237         seekStart = 0;
    238         readAmount = (long) fileLength;
    239     }
    240     if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
    241         ALOGD("Failure seeking to end of zip at %ld", (long) seekStart);
    242         result = UNKNOWN_ERROR;
    243         goto bail;
    244     }
    245 
    246     /* read the last part of the file into the buffer */
    247     if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
    248         ALOGD("short file? wanted %ld\n", readAmount);
    249         result = UNKNOWN_ERROR;
    250         goto bail;
    251     }
    252 
    253     /* find the end-of-central-dir magic */
    254     for (i = readAmount - 4; i >= 0; i--) {
    255         if (buf[i] == 0x50 &&
    256             ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
    257         {
    258             ALOGV("+++ Found EOCD at buf+%d\n", i);
    259             break;
    260         }
    261     }
    262     if (i < 0) {
    263         ALOGD("EOCD not found, not Zip\n");
    264         result = INVALID_OPERATION;
    265         goto bail;
    266     }
    267 
    268     /* extract eocd values */
    269     result = mEOCD.readBuf(buf + i, readAmount - i);
    270     if (result != OK) {
    271         ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
    272         goto bail;
    273     }
    274     //mEOCD.dump();
    275 
    276     if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
    277         mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
    278     {
    279         ALOGD("Archive spanning not supported\n");
    280         result = INVALID_OPERATION;
    281         goto bail;
    282     }
    283 
    284     /*
    285      * So far so good.  "mCentralDirSize" is the size in bytes of the
    286      * central directory, so we can just seek back that far to find it.
    287      * We can also seek forward mCentralDirOffset bytes from the
    288      * start of the file.
    289      *
    290      * We're not guaranteed to have the rest of the central dir in the
    291      * buffer, nor are we guaranteed that the central dir will have any
    292      * sort of convenient size.  We need to skip to the start of it and
    293      * read the header, then the other goodies.
    294      *
    295      * The only thing we really need right now is the file comment, which
    296      * we're hoping to preserve.
    297      */
    298     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
    299         ALOGD("Failure seeking to central dir offset %" PRIu32 "\n",
    300              mEOCD.mCentralDirOffset);
    301         result = UNKNOWN_ERROR;
    302         goto bail;
    303     }
    304 
    305     /*
    306      * Loop through and read the central dir entries.
    307      */
    308     ALOGV("Scanning %" PRIu16 " entries...\n", mEOCD.mTotalNumEntries);
    309     int entry;
    310     for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
    311         ZipEntry* pEntry = new ZipEntry;
    312 
    313         result = pEntry->initFromCDE(mZipFp);
    314         if (result != OK) {
    315             ALOGD("initFromCDE failed\n");
    316             delete pEntry;
    317             goto bail;
    318         }
    319 
    320         mEntries.add(pEntry);
    321     }
    322 
    323 
    324     /*
    325      * If all went well, we should now be back at the EOCD.
    326      */
    327     {
    328         uint8_t checkBuf[4];
    329         if (fread(checkBuf, 1, 4, mZipFp) != 4) {
    330             ALOGD("EOCD check read failed\n");
    331             result = INVALID_OPERATION;
    332             goto bail;
    333         }
    334         if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
    335             ALOGD("EOCD read check failed\n");
    336             result = UNKNOWN_ERROR;
    337             goto bail;
    338         }
    339         ALOGV("+++ EOCD read check passed\n");
    340     }
    341 
    342 bail:
    343     delete[] buf;
    344     return result;
    345 }
    346 
    347 
    348 /*
    349  * Add a new file to the archive.
    350  *
    351  * This requires creating and populating a ZipEntry structure, and copying
    352  * the data into the file at the appropriate position.  The "appropriate
    353  * position" is the current location of the central directory, which we
    354  * casually overwrite (we can put it back later).
    355  *
    356  * If we were concerned about safety, we would want to make all changes
    357  * in a temp file and then overwrite the original after everything was
    358  * safely written.  Not really a concern for us.
    359  */
    360 status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
    361     const char* storageName, int compressionMethod, ZipEntry** ppEntry)
    362 {
    363     ZipEntry* pEntry = NULL;
    364     status_t result = OK;
    365     long lfhPosn, startPosn, endPosn, uncompressedLen;
    366     FILE* inputFp = NULL;
    367     uint32_t crc;
    368     time_t modWhen;
    369 
    370     if (mReadOnly)
    371         return INVALID_OPERATION;
    372 
    373     assert(compressionMethod == ZipEntry::kCompressDeflated ||
    374            compressionMethod == ZipEntry::kCompressStored);
    375 
    376     /* make sure we're in a reasonable state */
    377     assert(mZipFp != NULL);
    378     assert(mEntries.size() == mEOCD.mTotalNumEntries);
    379 
    380     /* make sure it doesn't already exist */
    381     if (getEntryByName(storageName) != NULL)
    382         return ALREADY_EXISTS;
    383 
    384     if (!data) {
    385         inputFp = fopen(fileName, FILE_OPEN_RO);
    386         if (inputFp == NULL)
    387             return errnoToStatus(errno);
    388     }
    389 
    390     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
    391         result = UNKNOWN_ERROR;
    392         goto bail;
    393     }
    394 
    395     pEntry = new ZipEntry;
    396     pEntry->initNew(storageName, NULL);
    397 
    398     /*
    399      * From here on out, failures are more interesting.
    400      */
    401     mNeedCDRewrite = true;
    402 
    403     /*
    404      * Write the LFH, even though it's still mostly blank.  We need it
    405      * as a place-holder.  In theory the LFH isn't necessary, but in
    406      * practice some utilities demand it.
    407      */
    408     lfhPosn = ftell(mZipFp);
    409     pEntry->mLFH.write(mZipFp);
    410     startPosn = ftell(mZipFp);
    411 
    412     /*
    413      * Copy the data in, possibly compressing it as we go.
    414      */
    415     if (compressionMethod == ZipEntry::kCompressDeflated) {
    416         bool failed = false;
    417         result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
    418         if (result != OK) {
    419             ALOGD("compression failed, storing\n");
    420             failed = true;
    421         } else {
    422             /*
    423              * Make sure it has compressed "enough".  This probably ought
    424              * to be set through an API call, but I don't expect our
    425              * criteria to change over time.
    426              */
    427             long src = inputFp ? ftell(inputFp) : size;
    428             long dst = ftell(mZipFp) - startPosn;
    429             if (dst + (dst / 10) > src) {
    430                 ALOGD("insufficient compression (src=%ld dst=%ld), storing\n",
    431                     src, dst);
    432                 failed = true;
    433             }
    434         }
    435 
    436         if (failed) {
    437             compressionMethod = ZipEntry::kCompressStored;
    438             if (inputFp) rewind(inputFp);
    439             fseek(mZipFp, startPosn, SEEK_SET);
    440             /* fall through to kCompressStored case */
    441         }
    442     }
    443     /* handle "no compression" request, or failed compression from above */
    444     if (compressionMethod == ZipEntry::kCompressStored) {
    445         if (inputFp) {
    446             result = copyFpToFp(mZipFp, inputFp, &crc);
    447         } else {
    448             result = copyDataToFp(mZipFp, data, size, &crc);
    449         }
    450         if (result != OK) {
    451             // don't need to truncate; happens in CDE rewrite
    452             ALOGD("failed copying data in\n");
    453             goto bail;
    454         }
    455     }
    456 
    457     // currently seeked to end of file
    458     uncompressedLen = inputFp ? ftell(inputFp) : size;
    459 
    460     /*
    461      * We could write the "Data Descriptor", but there doesn't seem to
    462      * be any point since we're going to go back and write the LFH.
    463      *
    464      * Update file offsets.
    465      */
    466     endPosn = ftell(mZipFp);            // seeked to end of compressed data
    467 
    468     /*
    469      * Success!  Fill out new values.
    470      */
    471     pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
    472         compressionMethod);
    473     modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
    474     pEntry->setModWhen(modWhen);
    475     pEntry->setLFHOffset(lfhPosn);
    476     mEOCD.mNumEntries++;
    477     mEOCD.mTotalNumEntries++;
    478     mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
    479     mEOCD.mCentralDirOffset = endPosn;
    480 
    481     /*
    482      * Go back and write the LFH.
    483      */
    484     if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
    485         result = UNKNOWN_ERROR;
    486         goto bail;
    487     }
    488     pEntry->mLFH.write(mZipFp);
    489 
    490     /*
    491      * Add pEntry to the list.
    492      */
    493     mEntries.add(pEntry);
    494     if (ppEntry != NULL)
    495         *ppEntry = pEntry;
    496     pEntry = NULL;
    497 
    498 bail:
    499     if (inputFp != NULL)
    500         fclose(inputFp);
    501     delete pEntry;
    502     return result;
    503 }
    504 
    505 /*
    506  * Add an entry by copying it from another zip file.  If "padding" is
    507  * nonzero, the specified number of bytes will be added to the "extra"
    508  * field in the header.
    509  *
    510  * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
    511  */
    512 status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
    513     int padding, ZipEntry** ppEntry)
    514 {
    515     ZipEntry* pEntry = NULL;
    516     status_t result;
    517     long lfhPosn, endPosn;
    518 
    519     if (mReadOnly)
    520         return INVALID_OPERATION;
    521 
    522     /* make sure we're in a reasonable state */
    523     assert(mZipFp != NULL);
    524     assert(mEntries.size() == mEOCD.mTotalNumEntries);
    525 
    526     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
    527         result = UNKNOWN_ERROR;
    528         goto bail;
    529     }
    530 
    531     pEntry = new ZipEntry;
    532     if (pEntry == NULL) {
    533         result = NO_MEMORY;
    534         goto bail;
    535     }
    536 
    537     result = pEntry->initFromExternal(pSourceEntry);
    538     if (result != OK)
    539         goto bail;
    540     if (padding != 0) {
    541         result = pEntry->addPadding(padding);
    542         if (result != OK)
    543             goto bail;
    544     }
    545 
    546     /*
    547      * From here on out, failures are more interesting.
    548      */
    549     mNeedCDRewrite = true;
    550 
    551     /*
    552      * Write the LFH.  Since we're not recompressing the data, we already
    553      * have all of the fields filled out.
    554      */
    555     lfhPosn = ftell(mZipFp);
    556     pEntry->mLFH.write(mZipFp);
    557 
    558     /*
    559      * Copy the data over.
    560      *
    561      * If the "has data descriptor" flag is set, we want to copy the DD
    562      * fields as well.  This is a fixed-size area immediately following
    563      * the data.
    564      */
    565     if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
    566     {
    567         result = UNKNOWN_ERROR;
    568         goto bail;
    569     }
    570 
    571     off_t copyLen;
    572     copyLen = pSourceEntry->getCompressedLen();
    573     if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
    574         copyLen += ZipEntry::kDataDescriptorLen;
    575 
    576     if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
    577         != OK)
    578     {
    579         ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
    580         result = UNKNOWN_ERROR;
    581         goto bail;
    582     }
    583 
    584     /*
    585      * Update file offsets.
    586      */
    587     endPosn = ftell(mZipFp);
    588 
    589     /*
    590      * Success!  Fill out new values.
    591      */
    592     pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset
    593     mEOCD.mNumEntries++;
    594     mEOCD.mTotalNumEntries++;
    595     mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
    596     mEOCD.mCentralDirOffset = endPosn;
    597 
    598     /*
    599      * Add pEntry to the list.
    600      */
    601     mEntries.add(pEntry);
    602     if (ppEntry != NULL)
    603         *ppEntry = pEntry;
    604     pEntry = NULL;
    605 
    606     result = OK;
    607 
    608 bail:
    609     delete pEntry;
    610     return result;
    611 }
    612 
    613 /*
    614  * Add an entry by copying it from another zip file, recompressing with
    615  * Zopfli if already compressed.
    616  *
    617  * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
    618  */
    619 status_t ZipFile::addRecompress(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
    620     ZipEntry** ppEntry)
    621 {
    622     ZipEntry* pEntry = NULL;
    623     status_t result;
    624     long lfhPosn, startPosn, endPosn, uncompressedLen;
    625 
    626     if (mReadOnly)
    627         return INVALID_OPERATION;
    628 
    629     /* make sure we're in a reasonable state */
    630     assert(mZipFp != NULL);
    631     assert(mEntries.size() == mEOCD.mTotalNumEntries);
    632 
    633     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
    634         result = UNKNOWN_ERROR;
    635         goto bail;
    636     }
    637 
    638     pEntry = new ZipEntry;
    639     if (pEntry == NULL) {
    640         result = NO_MEMORY;
    641         goto bail;
    642     }
    643 
    644     result = pEntry->initFromExternal(pSourceEntry);
    645     if (result != OK)
    646         goto bail;
    647 
    648     /*
    649      * From here on out, failures are more interesting.
    650      */
    651     mNeedCDRewrite = true;
    652 
    653     /*
    654      * Write the LFH, even though it's still mostly blank.  We need it
    655      * as a place-holder.  In theory the LFH isn't necessary, but in
    656      * practice some utilities demand it.
    657      */
    658     lfhPosn = ftell(mZipFp);
    659     pEntry->mLFH.write(mZipFp);
    660     startPosn = ftell(mZipFp);
    661 
    662     /*
    663      * Copy the data over.
    664      *
    665      * If the "has data descriptor" flag is set, we want to copy the DD
    666      * fields as well.  This is a fixed-size area immediately following
    667      * the data.
    668      */
    669     if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
    670     {
    671         result = UNKNOWN_ERROR;
    672         goto bail;
    673     }
    674 
    675     uncompressedLen = pSourceEntry->getUncompressedLen();
    676 
    677     if (pSourceEntry->isCompressed()) {
    678         void *buf = pSourceZip->uncompress(pSourceEntry);
    679         if (buf == NULL) {
    680             result = NO_MEMORY;
    681             goto bail;
    682         }
    683         long startPosn = ftell(mZipFp);
    684         uint32_t crc;
    685         if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != OK) {
    686             ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName);
    687             result = UNKNOWN_ERROR;
    688             free(buf);
    689             goto bail;
    690         }
    691         long endPosn = ftell(mZipFp);
    692         pEntry->setDataInfo(uncompressedLen, endPosn - startPosn,
    693             pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated);
    694         free(buf);
    695     } else {
    696         off_t copyLen;
    697         copyLen = pSourceEntry->getCompressedLen();
    698         if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
    699             copyLen += ZipEntry::kDataDescriptorLen;
    700 
    701         if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
    702             != OK)
    703         {
    704             ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
    705             result = UNKNOWN_ERROR;
    706             goto bail;
    707         }
    708     }
    709 
    710     /*
    711      * Update file offsets.
    712      */
    713     endPosn = ftell(mZipFp);
    714 
    715     /*
    716      * Success!  Fill out new values.
    717      */
    718     pEntry->setLFHOffset(lfhPosn);
    719     mEOCD.mNumEntries++;
    720     mEOCD.mTotalNumEntries++;
    721     mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
    722     mEOCD.mCentralDirOffset = endPosn;
    723 
    724     /*
    725      * Go back and write the LFH.
    726      */
    727     if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
    728         result = UNKNOWN_ERROR;
    729         goto bail;
    730     }
    731     pEntry->mLFH.write(mZipFp);
    732 
    733     /*
    734      * Add pEntry to the list.
    735      */
    736     mEntries.add(pEntry);
    737     if (ppEntry != NULL)
    738         *ppEntry = pEntry;
    739     pEntry = NULL;
    740 
    741     result = OK;
    742 
    743 bail:
    744     delete pEntry;
    745     return result;
    746 }
    747 
    748 /*
    749  * Copy all of the bytes in "src" to "dst".
    750  *
    751  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
    752  * will be seeked immediately past the data.
    753  */
    754 status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32)
    755 {
    756     uint8_t tmpBuf[32768];
    757     size_t count;
    758 
    759     *pCRC32 = crc32(0L, Z_NULL, 0);
    760 
    761     while (1) {
    762         count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
    763         if (ferror(srcFp) || ferror(dstFp))
    764             return errnoToStatus(errno);
    765         if (count == 0)
    766             break;
    767 
    768         *pCRC32 = crc32(*pCRC32, tmpBuf, count);
    769 
    770         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
    771             ALOGD("fwrite %d bytes failed\n", (int) count);
    772             return UNKNOWN_ERROR;
    773         }
    774     }
    775 
    776     return OK;
    777 }
    778 
    779 /*
    780  * Copy all of the bytes in "src" to "dst".
    781  *
    782  * On exit, "dstFp" will be seeked immediately past the data.
    783  */
    784 status_t ZipFile::copyDataToFp(FILE* dstFp,
    785     const void* data, size_t size, uint32_t* pCRC32)
    786 {
    787     *pCRC32 = crc32(0L, Z_NULL, 0);
    788     if (size > 0) {
    789         *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
    790         if (fwrite(data, 1, size, dstFp) != size) {
    791             ALOGD("fwrite %d bytes failed\n", (int) size);
    792             return UNKNOWN_ERROR;
    793         }
    794     }
    795 
    796     return OK;
    797 }
    798 
    799 /*
    800  * Copy some of the bytes in "src" to "dst".
    801  *
    802  * If "pCRC32" is NULL, the CRC will not be computed.
    803  *
    804  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
    805  * will be seeked immediately past the data just written.
    806  */
    807 status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,
    808     uint32_t* pCRC32)
    809 {
    810     uint8_t tmpBuf[32768];
    811     size_t count;
    812 
    813     if (pCRC32 != NULL)
    814         *pCRC32 = crc32(0L, Z_NULL, 0);
    815 
    816     while (length) {
    817         size_t readSize;
    818 
    819         readSize = sizeof(tmpBuf);
    820         if (readSize > length)
    821             readSize = length;
    822 
    823         count = fread(tmpBuf, 1, readSize, srcFp);
    824         if (count != readSize) {     // error or unexpected EOF
    825             ALOGD("fread %d bytes failed\n", (int) readSize);
    826             return UNKNOWN_ERROR;
    827         }
    828 
    829         if (pCRC32 != NULL)
    830             *pCRC32 = crc32(*pCRC32, tmpBuf, count);
    831 
    832         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
    833             ALOGD("fwrite %d bytes failed\n", (int) count);
    834             return UNKNOWN_ERROR;
    835         }
    836 
    837         length -= readSize;
    838     }
    839 
    840     return OK;
    841 }
    842 
    843 /*
    844  * Compress all of the data in "srcFp" and write it to "dstFp".
    845  *
    846  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
    847  * will be seeked immediately past the compressed data.
    848  */
    849 status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
    850     const void* data, size_t size, uint32_t* pCRC32)
    851 {
    852     status_t result = OK;
    853     const size_t kBufSize = 1024 * 1024;
    854     uint8_t* inBuf = NULL;
    855     uint8_t* outBuf = NULL;
    856     size_t outSize = 0;
    857     bool atEof = false;     // no feof() aviailable yet
    858     uint32_t crc;
    859     ZopfliOptions options;
    860     unsigned char bp = 0;
    861 
    862     ZopfliInitOptions(&options);
    863 
    864     crc = crc32(0L, Z_NULL, 0);
    865 
    866     if (data) {
    867         crc = crc32(crc, (const unsigned char*)data, size);
    868         ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp,
    869             &outBuf, &outSize);
    870     } else {
    871         /*
    872          * Create an input buffer and an output buffer.
    873          */
    874         inBuf = new uint8_t[kBufSize];
    875         if (inBuf == NULL) {
    876             result = NO_MEMORY;
    877             goto bail;
    878         }
    879 
    880         /*
    881          * Loop while we have data.
    882          */
    883         do {
    884             size_t getSize;
    885             getSize = fread(inBuf, 1, kBufSize, srcFp);
    886             if (ferror(srcFp)) {
    887                 ALOGD("deflate read failed (errno=%d)\n", errno);
    888                 result = UNKNOWN_ERROR;
    889                 delete[] inBuf;
    890                 goto bail;
    891             }
    892             if (getSize < kBufSize) {
    893                 ALOGV("+++  got %d bytes, EOF reached\n",
    894                     (int)getSize);
    895                 atEof = true;
    896             }
    897 
    898             crc = crc32(crc, inBuf, getSize);
    899             ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize);
    900         } while (!atEof);
    901         delete[] inBuf;
    902     }
    903 
    904     ALOGV("+++ writing %d bytes\n", (int)outSize);
    905     if (fwrite(outBuf, 1, outSize, dstFp) != outSize) {
    906         ALOGD("write %d failed in deflate\n", (int)outSize);
    907         result = UNKNOWN_ERROR;
    908         goto bail;
    909     }
    910 
    911     *pCRC32 = crc;
    912 
    913 bail:
    914     free(outBuf);
    915 
    916     return result;
    917 }
    918 
    919 /*
    920  * Mark an entry as deleted.
    921  *
    922  * We will eventually need to crunch the file down, but if several files
    923  * are being removed (perhaps as part of an "update" process) we can make
    924  * things considerably faster by deferring the removal to "flush" time.
    925  */
    926 status_t ZipFile::remove(ZipEntry* pEntry)
    927 {
    928     /*
    929      * Should verify that pEntry is actually part of this archive, and
    930      * not some stray ZipEntry from a different file.
    931      */
    932 
    933     /* mark entry as deleted, and mark archive as dirty */
    934     pEntry->setDeleted();
    935     mNeedCDRewrite = true;
    936     return OK;
    937 }
    938 
    939 /*
    940  * Flush any pending writes.
    941  *
    942  * In particular, this will crunch out deleted entries, and write the
    943  * Central Directory and EOCD if we have stomped on them.
    944  */
    945 status_t ZipFile::flush(void)
    946 {
    947     status_t result = OK;
    948     long eocdPosn;
    949     int i, count;
    950 
    951     if (mReadOnly)
    952         return INVALID_OPERATION;
    953     if (!mNeedCDRewrite)
    954         return OK;
    955 
    956     assert(mZipFp != NULL);
    957 
    958     result = crunchArchive();
    959     if (result != OK)
    960         return result;
    961 
    962     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
    963         return UNKNOWN_ERROR;
    964 
    965     count = mEntries.size();
    966     for (i = 0; i < count; i++) {
    967         ZipEntry* pEntry = mEntries[i];
    968         pEntry->mCDE.write(mZipFp);
    969     }
    970 
    971     eocdPosn = ftell(mZipFp);
    972     mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
    973 
    974     mEOCD.write(mZipFp);
    975 
    976     /*
    977      * If we had some stuff bloat up during compression and get replaced
    978      * with plain files, or if we deleted some entries, there's a lot
    979      * of wasted space at the end of the file.  Remove it now.
    980      */
    981     if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
    982         ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
    983         // not fatal
    984     }
    985 
    986     /* should we clear the "newly added" flag in all entries now? */
    987 
    988     mNeedCDRewrite = false;
    989     return OK;
    990 }
    991 
    992 /*
    993  * Crunch deleted files out of an archive by shifting the later files down.
    994  *
    995  * Because we're not using a temp file, we do the operation inside the
    996  * current file.
    997  */
    998 status_t ZipFile::crunchArchive(void)
    999 {
   1000     status_t result = OK;
   1001     int i, count;
   1002     long delCount, adjust;
   1003 
   1004 #if 0
   1005     printf("CONTENTS:\n");
   1006     for (i = 0; i < (int) mEntries.size(); i++) {
   1007         printf(" %d: lfhOff=%ld del=%d\n",
   1008             i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
   1009     }
   1010     printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
   1011 #endif
   1012 
   1013     /*
   1014      * Roll through the set of files, shifting them as appropriate.  We
   1015      * could probably get a slight performance improvement by sliding
   1016      * multiple files down at once (because we could use larger reads
   1017      * when operating on batches of small files), but it's not that useful.
   1018      */
   1019     count = mEntries.size();
   1020     delCount = adjust = 0;
   1021     for (i = 0; i < count; i++) {
   1022         ZipEntry* pEntry = mEntries[i];
   1023         long span;
   1024 
   1025         if (pEntry->getLFHOffset() != 0) {
   1026             long nextOffset;
   1027 
   1028             /* Get the length of this entry by finding the offset
   1029              * of the next entry.  Directory entries don't have
   1030              * file offsets, so we need to find the next non-directory
   1031              * entry.
   1032              */
   1033             nextOffset = 0;
   1034             for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
   1035                 nextOffset = mEntries[ii]->getLFHOffset();
   1036             if (nextOffset == 0)
   1037                 nextOffset = mEOCD.mCentralDirOffset;
   1038             span = nextOffset - pEntry->getLFHOffset();
   1039 
   1040             assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
   1041         } else {
   1042             /* This is a directory entry.  It doesn't have
   1043              * any actual file contents, so there's no need to
   1044              * move anything.
   1045              */
   1046             span = 0;
   1047         }
   1048 
   1049         //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
   1050         //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
   1051 
   1052         if (pEntry->getDeleted()) {
   1053             adjust += span;
   1054             delCount++;
   1055 
   1056             delete pEntry;
   1057             mEntries.removeAt(i);
   1058 
   1059             /* adjust loop control */
   1060             count--;
   1061             i--;
   1062         } else if (span != 0 && adjust > 0) {
   1063             /* shuffle this entry back */
   1064             //printf("+++ Shuffling '%s' back %ld\n",
   1065             //    pEntry->getFileName(), adjust);
   1066             result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
   1067                         pEntry->getLFHOffset(), span);
   1068             if (result != OK) {
   1069                 /* this is why you use a temp file */
   1070                 ALOGE("error during crunch - archive is toast\n");
   1071                 return result;
   1072             }
   1073 
   1074             pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
   1075         }
   1076     }
   1077 
   1078     /*
   1079      * Fix EOCD info.  We have to wait until the end to do some of this
   1080      * because we use mCentralDirOffset to determine "span" for the
   1081      * last entry.
   1082      */
   1083     mEOCD.mCentralDirOffset -= adjust;
   1084     mEOCD.mNumEntries -= delCount;
   1085     mEOCD.mTotalNumEntries -= delCount;
   1086     mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
   1087 
   1088     assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
   1089     assert(mEOCD.mNumEntries == count);
   1090 
   1091     return result;
   1092 }
   1093 
   1094 /*
   1095  * Works like memmove(), but on pieces of a file.
   1096  */
   1097 status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
   1098 {
   1099     if (dst == src || n <= 0)
   1100         return OK;
   1101 
   1102     uint8_t readBuf[32768];
   1103 
   1104     if (dst < src) {
   1105         /* shift stuff toward start of file; must read from start */
   1106         while (n != 0) {
   1107             size_t getSize = sizeof(readBuf);
   1108             if (getSize > n)
   1109                 getSize = n;
   1110 
   1111             if (fseek(fp, (long) src, SEEK_SET) != 0) {
   1112                 ALOGD("filemove src seek %ld failed\n", (long) src);
   1113                 return UNKNOWN_ERROR;
   1114             }
   1115 
   1116             if (fread(readBuf, 1, getSize, fp) != getSize) {
   1117                 ALOGD("filemove read %ld off=%ld failed\n",
   1118                     (long) getSize, (long) src);
   1119                 return UNKNOWN_ERROR;
   1120             }
   1121 
   1122             if (fseek(fp, (long) dst, SEEK_SET) != 0) {
   1123                 ALOGD("filemove dst seek %ld failed\n", (long) dst);
   1124                 return UNKNOWN_ERROR;
   1125             }
   1126 
   1127             if (fwrite(readBuf, 1, getSize, fp) != getSize) {
   1128                 ALOGD("filemove write %ld off=%ld failed\n",
   1129                     (long) getSize, (long) dst);
   1130                 return UNKNOWN_ERROR;
   1131             }
   1132 
   1133             src += getSize;
   1134             dst += getSize;
   1135             n -= getSize;
   1136         }
   1137     } else {
   1138         /* shift stuff toward end of file; must read from end */
   1139         assert(false);      // write this someday, maybe
   1140         return UNKNOWN_ERROR;
   1141     }
   1142 
   1143     return OK;
   1144 }
   1145 
   1146 
   1147 /*
   1148  * Get the modification time from a file descriptor.
   1149  */
   1150 time_t ZipFile::getModTime(int fd)
   1151 {
   1152     struct stat sb;
   1153 
   1154     if (fstat(fd, &sb) < 0) {
   1155         ALOGD("HEY: fstat on fd %d failed\n", fd);
   1156         return (time_t) -1;
   1157     }
   1158 
   1159     return sb.st_mtime;
   1160 }
   1161 
   1162 
   1163 #if 0       /* this is a bad idea */
   1164 /*
   1165  * Get a copy of the Zip file descriptor.
   1166  *
   1167  * We don't allow this if the file was opened read-write because we tend
   1168  * to leave the file contents in an uncertain state between calls to
   1169  * flush().  The duplicated file descriptor should only be valid for reads.
   1170  */
   1171 int ZipFile::getZipFd(void) const
   1172 {
   1173     if (!mReadOnly)
   1174         return INVALID_OPERATION;
   1175     assert(mZipFp != NULL);
   1176 
   1177     int fd;
   1178     fd = dup(fileno(mZipFp));
   1179     if (fd < 0) {
   1180         ALOGD("didn't work, errno=%d\n", errno);
   1181     }
   1182 
   1183     return fd;
   1184 }
   1185 #endif
   1186 
   1187 
   1188 #if 0
   1189 /*
   1190  * Expand data.
   1191  */
   1192 bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
   1193 {
   1194     return false;
   1195 }
   1196 #endif
   1197 
   1198 class BufferWriter : public zip_archive::Writer {
   1199   public:
   1200     BufferWriter(void* buf, size_t size) : Writer(),
   1201         buf_(reinterpret_cast<uint8_t*>(buf)), size_(size), bytes_written_(0) {}
   1202 
   1203     bool Append(uint8_t* buf, size_t buf_size) override {
   1204         if (bytes_written_ + buf_size > size_) {
   1205             return false;
   1206         }
   1207 
   1208         memcpy(buf_ + bytes_written_, buf, buf_size);
   1209         bytes_written_ += buf_size;
   1210         return true;
   1211     }
   1212 
   1213   private:
   1214     uint8_t* const buf_;
   1215     const size_t size_;
   1216     size_t bytes_written_;
   1217 };
   1218 
   1219 class FileReader : public zip_archive::Reader {
   1220   public:
   1221     FileReader(FILE* fp) : Reader(), fp_(fp), current_offset_(0) {
   1222     }
   1223 
   1224     bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
   1225         // Data is usually requested sequentially, so this helps avoid pointless
   1226         // fseeks every time we perform a read. There's an impedence mismatch
   1227         // here because the original API was designed around pread and pwrite.
   1228         if (offset != current_offset_) {
   1229             if (fseek(fp_, offset, SEEK_SET) != 0) {
   1230                 return false;
   1231             }
   1232 
   1233             current_offset_ = offset;
   1234         }
   1235 
   1236         size_t read = fread(buf, 1, len, fp_);
   1237         if (read != len) {
   1238             return false;
   1239         }
   1240 
   1241         current_offset_ += read;
   1242         return true;
   1243     }
   1244 
   1245   private:
   1246     FILE* fp_;
   1247     mutable uint32_t current_offset_;
   1248 };
   1249 
   1250 // free the memory when you're done
   1251 void* ZipFile::uncompress(const ZipEntry* entry) const
   1252 {
   1253     size_t unlen = entry->getUncompressedLen();
   1254     size_t clen = entry->getCompressedLen();
   1255 
   1256     void* buf = malloc(unlen);
   1257     if (buf == NULL) {
   1258         return NULL;
   1259     }
   1260 
   1261     fseek(mZipFp, 0, SEEK_SET);
   1262 
   1263     off_t offset = entry->getFileOffset();
   1264     if (fseek(mZipFp, offset, SEEK_SET) != 0) {
   1265         goto bail;
   1266     }
   1267 
   1268     switch (entry->getCompressionMethod())
   1269     {
   1270         case ZipEntry::kCompressStored: {
   1271             ssize_t amt = fread(buf, 1, unlen, mZipFp);
   1272             if (amt != (ssize_t)unlen) {
   1273                 goto bail;
   1274             }
   1275 #if 0
   1276             printf("data...\n");
   1277             const unsigned char* p = (unsigned char*)buf;
   1278             const unsigned char* end = p+unlen;
   1279             for (int i=0; i<32 && p < end; i++) {
   1280                 printf("0x%08x ", (int)(offset+(i*0x10)));
   1281                 for (int j=0; j<0x10 && p < end; j++) {
   1282                     printf(" %02x", *p);
   1283                     p++;
   1284                 }
   1285                 printf("\n");
   1286             }
   1287 #endif
   1288 
   1289             }
   1290             break;
   1291         case ZipEntry::kCompressDeflated: {
   1292             const FileReader reader(mZipFp);
   1293             BufferWriter writer(buf, unlen);
   1294             if (zip_archive::Inflate(reader, clen, unlen, &writer, nullptr) != 0) {
   1295                 goto bail;
   1296             }
   1297             break;
   1298         }
   1299         default:
   1300             goto bail;
   1301     }
   1302     return buf;
   1303 
   1304 bail:
   1305     free(buf);
   1306     return NULL;
   1307 }
   1308 
   1309 
   1310 /*
   1311  * ===========================================================================
   1312  *      ZipFile::EndOfCentralDir
   1313  * ===========================================================================
   1314  */
   1315 
   1316 /*
   1317  * Read the end-of-central-dir fields.
   1318  *
   1319  * "buf" should be positioned at the EOCD signature, and should contain
   1320  * the entire EOCD area including the comment.
   1321  */
   1322 status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
   1323 {
   1324     /* don't allow re-use */
   1325     assert(mComment == NULL);
   1326 
   1327     if (len < kEOCDLen) {
   1328         /* looks like ZIP file got truncated */
   1329         ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
   1330             kEOCDLen, len);
   1331         return INVALID_OPERATION;
   1332     }
   1333 
   1334     /* this should probably be an assert() */
   1335     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
   1336         return UNKNOWN_ERROR;
   1337 
   1338     mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
   1339     mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
   1340     mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
   1341     mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
   1342     mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
   1343     mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
   1344     mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
   1345 
   1346     // TODO: validate mCentralDirOffset
   1347 
   1348     if (mCommentLen > 0) {
   1349         if (kEOCDLen + mCommentLen > len) {
   1350             ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n",
   1351                 kEOCDLen, mCommentLen, len);
   1352             return UNKNOWN_ERROR;
   1353         }
   1354         mComment = new uint8_t[mCommentLen];
   1355         memcpy(mComment, buf + kEOCDLen, mCommentLen);
   1356     }
   1357 
   1358     return OK;
   1359 }
   1360 
   1361 /*
   1362  * Write an end-of-central-directory section.
   1363  */
   1364 status_t ZipFile::EndOfCentralDir::write(FILE* fp)
   1365 {
   1366     uint8_t buf[kEOCDLen];
   1367 
   1368     ZipEntry::putLongLE(&buf[0x00], kSignature);
   1369     ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
   1370     ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
   1371     ZipEntry::putShortLE(&buf[0x08], mNumEntries);
   1372     ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
   1373     ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
   1374     ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
   1375     ZipEntry::putShortLE(&buf[0x14], mCommentLen);
   1376 
   1377     if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
   1378         return UNKNOWN_ERROR;
   1379     if (mCommentLen > 0) {
   1380         assert(mComment != NULL);
   1381         if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
   1382             return UNKNOWN_ERROR;
   1383     }
   1384 
   1385     return OK;
   1386 }
   1387 
   1388 /*
   1389  * Dump the contents of an EndOfCentralDir object.
   1390  */
   1391 void ZipFile::EndOfCentralDir::dump(void) const
   1392 {
   1393     ALOGD(" EndOfCentralDir contents:\n");
   1394     ALOGD("  diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n",
   1395         mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
   1396     ALOGD("  centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n",
   1397         mCentralDirSize, mCentralDirOffset, mCommentLen);
   1398 }
   1399 
   1400