text: /*
   * Copyright (C) 2006 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  //
  // Access to entries in a Zip archive.
  //
  
  #define LOG_TAG "zip"
  
  #include "ZipEntry.h"
  #include <utils/Log.h>
  
  #include <stdio.h>
  #include <string.h>
  #include <assert.h>
  
  using namespace android;
  
  /*
   * Initialize a new ZipEntry structure from a FILE* positioned at a
   * CentralDirectoryEntry.
   *
   * On exit, the file pointer will be at the start of the next CDE or
   * at the EOCD.
   */
  status_t ZipEntry::initFromCDE(FILE* fp)
  {
      status_t result;
      long posn;
      bool hasDD;
  
      //ALOGV("initFromCDE ---\n");
  
      /* read the CDE */
      result = mCDE.read(fp);
      if (result != NO_ERROR) {
          ALOGD("mCDE.read failed\n");
          return result;
      }
  
      //mCDE.dump();
  
      /* using the info in the CDE, go load up the LFH */
      posn = ftell(fp);
      if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
          ALOGD("local header seek failed (%ld)\n",
              mCDE.mLocalHeaderRelOffset);
          return UNKNOWN_ERROR;
      }
  
      result = mLFH.read(fp);
      if (result != NO_ERROR) {
          ALOGD("mLFH.read failed\n");
          return result;
      }
  
      if (fseek(fp, posn, SEEK_SET) != 0)
          return UNKNOWN_ERROR;
  
      //mLFH.dump();
  
      /*
       * We *might* need to read the Data Descriptor at this point and
       * integrate it into the LFH.  If this bit is set, the CRC-32,
       * compressed size, and uncompressed size will be zero.  In practice
       * these seem to be rare.
       */
      hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
      if (hasDD) {
          // do something clever
          //ALOGD("+++ has data descriptor\n");
      }
  
      /*
       * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
       * flag is set, because the LFH is incomplete.  (Not a problem, since we
       * prefer the CDE values.)
       */
      if (!hasDD && !compareHeaders()) {
          ALOGW("WARNING: header mismatch\n");
          // keep going?
      }
  
      /*
       * If the mVersionToExtract is greater than 20, we may have an
       * issue unpacking the record -- could be encrypted, compressed
       * with something we don't support, or use Zip64 extensions.  We
       * can defer worrying about that to when we're extracting data.
       */
  
      return NO_ERROR;
  }
  
  /*
   * Initialize a new entry.  Pass in the file name and an optional comment.
   *
   * Initializes the CDE and the LFH.
   */
  void ZipEntry::initNew(const char* fileName, const char* comment)
  {
      assert(fileName != NULL && *fileName != '\0');  // name required
  
      /* most fields are properly initialized by constructor */
      mCDE.mVersionMadeBy = kDefaultMadeBy;
      mCDE.mVersionToExtract = kDefaultVersion;
      mCDE.mCompressionMethod = kCompressStored;
      mCDE.mFileNameLength = strlen(fileName);
      if (comment != NULL)
          mCDE.mFileCommentLength = strlen(comment);
      mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does
  
      if (mCDE.mFileNameLength > 0) {
          mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
          strcpy((char*) mCDE.mFileName, fileName);
      }
      if (mCDE.mFileCommentLength > 0) {
          /* TODO: stop assuming null-terminated ASCII here? */
          mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
          strcpy((char*) mCDE.mFileComment, comment);
      }
  
      copyCDEtoLFH();
  }
  
  /*
   * Initialize a new entry, starting with the ZipEntry from a different
   * archive.
   *
   * Initializes the CDE and the LFH.
   */
  status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
      const ZipEntry* pEntry)
  {
      /*
       * Copy everything in the CDE over, then fix up the hairy bits.
       */
      memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
  
      if (mCDE.mFileNameLength > 0) {
          mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
          if (mCDE.mFileName == NULL)
              return NO_MEMORY;
          strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
      }
      if (mCDE.mFileCommentLength > 0) {
          mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
          if (mCDE.mFileComment == NULL)
              return NO_MEMORY;
          strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
      }
      if (mCDE.mExtraFieldLength > 0) {
          /* we null-terminate this, though it may not be a string */
          mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
          if (mCDE.mExtraField == NULL)
              return NO_MEMORY;
          memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
              mCDE.mExtraFieldLength+1);
      }
  
      /* construct the LFH from the CDE */
      copyCDEtoLFH();
  
      /*
       * The LFH "extra" field is independent of the CDE "extra", so we
       * handle it here.
       */
      assert(mLFH.mExtraField == NULL);
      mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
      if (mLFH.mExtraFieldLength > 0) {
          mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
          if (mLFH.mExtraField == NULL)
              return NO_MEMORY;
          memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
              mLFH.mExtraFieldLength+1);
      }
  
      return NO_ERROR;
  }
  
  /*
   * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
   * potentially confuse something that put "extra" data in here earlier,
   * but I can't find an actual problem.
   */
  status_t ZipEntry::addPadding(int padding)
  {
      if (padding <= 0)
          return INVALID_OPERATION;
  
      //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
      //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
  
      if (mLFH.mExtraFieldLength > 0) {
          /* extend existing field */
          unsigned char* newExtra;
  
          newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
          if (newExtra == NULL)
              return NO_MEMORY;
          memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
          memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
  
          delete[] mLFH.mExtraField;
          mLFH.mExtraField = newExtra;
          mLFH.mExtraFieldLength += padding;
      } else {
          /* create new field */
          mLFH.mExtraField = new unsigned char[padding];
          memset(mLFH.mExtraField, 0, padding);
          mLFH.mExtraFieldLength = padding;
      }
  
      return NO_ERROR;
  }
  
  /*
   * Set the fields in the LFH equal to the corresponding fields in the CDE.
   *
   * This does not touch the LFH "extra" field.
   */
  void ZipEntry::copyCDEtoLFH(void)
  {
      mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
      mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
      mLFH.mCompressionMethod = mCDE.mCompressionMethod;
      mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
      mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
      mLFH.mCRC32             = mCDE.mCRC32;
      mLFH.mCompressedSize    = mCDE.mCompressedSize;
      mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
      mLFH.mFileNameLength    = mCDE.mFileNameLength;
      // the "extra field" is independent
  
      delete[] mLFH.mFileName;
      if (mLFH.mFileNameLength > 0) {
          mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
          strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
      } else {
          mLFH.mFileName = NULL;
      }
  }
  
  /*
   * Set some information about a file after we add it.
   */
  void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
      int compressionMethod)
  {
      mCDE.mCompressionMethod = compressionMethod;
      mCDE.mCRC32 = crc32;
      mCDE.mCompressedSize = compLen;
      mCDE.mUncompressedSize = uncompLen;
      mCDE.mCompressionMethod = compressionMethod;
      if (compressionMethod == kCompressDeflated) {
          mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
      }
      copyCDEtoLFH();
  }
  
  /*
   * See if the data in mCDE and mLFH match up.  This is mostly useful for
   * debugging these classes, but it can be used to identify damaged
   * archives.
   *
   * Returns "false" if they differ.
   */
  bool ZipEntry::compareHeaders(void) const
  {
      if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
          ALOGV("cmp: VersionToExtract\n");
          return false;
      }
      if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
          ALOGV("cmp: GPBitFlag\n");
          return false;
      }
      if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
          ALOGV("cmp: CompressionMethod\n");
          return false;
      }
      if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
          ALOGV("cmp: LastModFileTime\n");
          return false;
      }
      if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
          ALOGV("cmp: LastModFileDate\n");
          return false;
      }
      if (mCDE.mCRC32 != mLFH.mCRC32) {
          ALOGV("cmp: CRC32\n");
          return false;
      }
      if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
          ALOGV("cmp: CompressedSize\n");
          return false;
      }
      if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
          ALOGV("cmp: UncompressedSize\n");
          return false;
      }
      if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
          ALOGV("cmp: FileNameLength\n");
          return false;
      }
  #if 0       // this seems to be used for padding, not real data
      if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
          ALOGV("cmp: ExtraFieldLength\n");
          return false;
      }
  #endif
      if (mCDE.mFileName != NULL) {
          if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
              ALOGV("cmp: FileName\n");
              return false;
          }
      }
  
      return true;
  }
  
  
  /*
   * Convert the DOS date/time stamp into a UNIX time stamp.
   */
  time_t ZipEntry::getModWhen(void) const
  {
      struct tm parts;
  
      parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
      parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
      parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
      parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
      parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
      parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
      parts.tm_wday = parts.tm_yday = 0;
      parts.tm_isdst = -1;        // DST info "not available"
  
      return mktime(&parts);
  }
  
  /*
   * Set the CDE/LFH timestamp from UNIX time.
   */
  void ZipEntry::setModWhen(time_t when)
  {
  #ifdef HAVE_LOCALTIME_R
      struct tm tmResult;
  #endif
      time_t even;
      unsigned short zdate, ztime;
  
      struct tm* ptm;
  
      /* round up to an even number of seconds */
      even = (time_t)(((unsigned long)(when) + 1) & (~1));
  
      /* expand */
  #ifdef HAVE_LOCALTIME_R
      ptm = localtime_r(&even, &tmResult);
  #else
      ptm = localtime(&even);
  #endif
  
      int year;
      year = ptm->tm_year;
      if (year < 80)
          year = 80;
  
      zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
      ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
  
      mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
      mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
  }
  
  
  /*
   * ===========================================================================
   *      ZipEntry::LocalFileHeader
   * ===========================================================================
   */
  
  /*
   * Read a local file header.
   *
   * On entry, "fp" points to the signature at the start of the header.
   * On exit, "fp" points to the start of data.
   */
  status_t ZipEntry::LocalFileHeader::read(FILE* fp)
  {
      status_t result = NO_ERROR;
      unsigned char buf[kLFHLen];
  
      assert(mFileName == NULL);
      assert(mExtraField == NULL);
  
      if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
          result = UNKNOWN_ERROR;
          goto bail;
      }
  
      if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
          ALOGD("whoops: didn't find expected signature\n");
          result = UNKNOWN_ERROR;
          goto bail;
      }
  
      mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
      mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
      mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
      mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
      mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
      mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
      mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
      mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
      mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
      mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
  
      // TODO: validate sizes
  
      /* grab filename */
      if (mFileNameLength != 0) {
          mFileName = new unsigned char[mFileNameLength+1];
          if (mFileName == NULL) {
              result = NO_MEMORY;
              goto bail;
          }
          if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
              result = UNKNOWN_ERROR;
              goto bail;
          }
          mFileName[mFileNameLength] = '\0';
      }
  
      /* grab extra field */
      if (mExtraFieldLength != 0) {
          mExtraField = new unsigned char[mExtraFieldLength+1];
          if (mExtraField == NULL) {
              result = NO_MEMORY;
              goto bail;
          }
          if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
              result = UNKNOWN_ERROR;
              goto bail;
          }
          mExtraField[mExtraFieldLength] = '\0';
      }
  
  bail:
      return result;
  }
  
  /*
   * Write a local file header.
   */
  status_t ZipEntry::LocalFileHeader::write(FILE* fp)
  {
      unsigned char buf[kLFHLen];
  
      ZipEntry::putLongLE(&buf[0x00], kSignature);
      ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
      ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
      ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
      ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
      ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
      ZipEntry::putLongLE(&buf[0x0e], mCRC32);
      ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
      ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
      ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
      ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
  
      if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
          return UNKNOWN_ERROR;
  
      /* write filename */
      if (mFileNameLength != 0) {
          if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
              return UNKNOWN_ERROR;
      }
  
      /* write "extra field" */
      if (mExtraFieldLength != 0) {
          if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
              return UNKNOWN_ERROR;
      }
  
      return NO_ERROR;
  }
  
  
  /*
   * Dump the contents of a LocalFileHeader object.
   */
  void ZipEntry::LocalFileHeader::dump(void) const
  {
      ALOGD(" LocalFileHeader contents:\n");
      ALOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
          mVersionToExtract, mGPBitFlag, mCompressionMethod);
      ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
          mLastModFileTime, mLastModFileDate, mCRC32);
      ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
          mCompressedSize, mUncompressedSize);
      ALOGD("  filenameLen=%u extraLen=%u\n",
          mFileNameLength, mExtraFieldLength);
      if (mFileName != NULL)
          ALOGD("  filename: '%s'\n", mFileName);
  }
  
  
  /*
   * ===========================================================================
   *      ZipEntry::CentralDirEntry
   * ===========================================================================
   */
  
  /*
   * Read the central dir entry that appears next in the file.
   *
   * On entry, "fp" should be positioned on the signature bytes for the
   * entry.  On exit, "fp" will point at the signature word for the next
   * entry or for the EOCD.
   */
  status_t ZipEntry::CentralDirEntry::read(FILE* fp)
  {
      status_t result = NO_ERROR;
      unsigned char buf[kCDELen];
  
      /* no re-use */
      assert(mFileName == NULL);
      assert(mExtraField == NULL);
      assert(mFileComment == NULL);
  
      if (fread(buf, 1, kCDELen, fp) != kCDELen) {
          result = UNKNOWN_ERROR;
          goto bail;
      }
  
      if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
          ALOGD("Whoops: didn't find expected signature\n");
          result = UNKNOWN_ERROR;
          goto bail;
      }
  
      mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
      mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
      mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
      mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
      mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
      mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
      mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
      mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
      mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
      mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
      mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
      mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
      mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
      mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
      mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
      mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
  
      // TODO: validate sizes and offsets
  
      /* grab filename */
      if (mFileNameLength != 0) {
          mFileName = new unsigned char[mFileNameLength+1];
          if (mFileName == NULL) {
              result = NO_MEMORY;
              goto bail;
          }
          if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
              result = UNKNOWN_ERROR;
              goto bail;
          }
          mFileName[mFileNameLength] = '\0';
      }
  
      /* read "extra field" */
      if (mExtraFieldLength != 0) {
          mExtraField = new unsigned char[mExtraFieldLength+1];
          if (mExtraField == NULL) {
              result = NO_MEMORY;
              goto bail;
          }
          if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
              result = UNKNOWN_ERROR;
              goto bail;
          }
          mExtraField[mExtraFieldLength] = '\0';
      }
  
  
      /* grab comment, if any */
      if (mFileCommentLength != 0) {
          mFileComment = new unsigned char[mFileCommentLength+1];
          if (mFileComment == NULL) {
              result = NO_MEMORY;
              goto bail;
          }
          if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
          {
              result = UNKNOWN_ERROR;
              goto bail;
          }
          mFileComment[mFileCommentLength] = '\0';
      }
  
  bail:
      return result;
  }
  
  /*
   * Write a central dir entry.
   */
  status_t ZipEntry::CentralDirEntry::write(FILE* fp)
  {
      unsigned char buf[kCDELen];
  
      ZipEntry::putLongLE(&buf[0x00], kSignature);
      ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
      ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
      ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
      ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
      ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
      ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
      ZipEntry::putLongLE(&buf[0x10], mCRC32);
      ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
      ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
      ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
      ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
      ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
      ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
      ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
      ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
      ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
  
      if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
          return UNKNOWN_ERROR;
  
      /* write filename */
      if (mFileNameLength != 0) {
          if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
              return UNKNOWN_ERROR;
      }
  
      /* write "extra field" */
      if (mExtraFieldLength != 0) {
          if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
              return UNKNOWN_ERROR;
      }
  
      /* write comment */
      if (mFileCommentLength != 0) {
          if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
              return UNKNOWN_ERROR;
      }
  
      return NO_ERROR;
  }
  
  /*
   * Dump the contents of a CentralDirEntry object.
   */
  void ZipEntry::CentralDirEntry::dump(void) const
  {
      ALOGD(" CentralDirEntry contents:\n");
      ALOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
          mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
      ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
          mLastModFileTime, mLastModFileDate, mCRC32);
      ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
          mCompressedSize, mUncompressedSize);
      ALOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
          mFileNameLength, mExtraFieldLength, mFileCommentLength);
      ALOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
          mDiskNumberStart, mInternalAttrs, mExternalAttrs,
          mLocalHeaderRelOffset);
  
      if (mFileName != NULL)
          ALOGD("  filename: '%s'\n", mFileName);
      if (mFileComment != NULL)
          ALOGD("  comment: '%s'\n", mFileComment);
  }
  
  