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