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