github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/make/tools/zipalign/ZipFile.cpp (about)

     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 = NO_ERROR;
   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 = NO_ERROR;
   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 != NO_ERROR) {
   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 != NO_ERROR) {
   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 = NO_ERROR;
   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 != NO_ERROR) {
   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 != NO_ERROR) {
   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 != NO_ERROR)
   539          goto bail;
   540      if (padding != 0) {
   541          result = pEntry->addPadding(padding);
   542          if (result != NO_ERROR)
   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          != NO_ERROR)
   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 = NO_ERROR;
   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 != NO_ERROR)
   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) != NO_ERROR) {
   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              != NO_ERROR)
   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 = NO_ERROR;
   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 NO_ERROR;
   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 NO_ERROR;
   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, long 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          long readSize;
   818  
   819          readSize = sizeof(tmpBuf);
   820          if (readSize > length)
   821              readSize = length;
   822  
   823          count = fread(tmpBuf, 1, readSize, srcFp);
   824          if ((long) 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 NO_ERROR;
   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 = NO_ERROR;
   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 NO_ERROR;
   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 = NO_ERROR;
   948      long eocdPosn;
   949      int i, count;
   950  
   951      if (mReadOnly)
   952          return INVALID_OPERATION;
   953      if (!mNeedCDRewrite)
   954          return NO_ERROR;
   955  
   956      assert(mZipFp != NULL);
   957  
   958      result = crunchArchive();
   959      if (result != NO_ERROR)
   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 NO_ERROR;
   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 = NO_ERROR;
  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 != NO_ERROR) {
  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 NO_ERROR;
  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 NO_ERROR;
  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 NO_ERROR;
  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 NO_ERROR;
  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