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