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 == 0)
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, startPosn, endPosn, 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     startPosn = ftell(mZipFp);
694 
695     /*
696      * Copy the data over.
697      *
698      * If the "has data descriptor" flag is set, we want to copy the DD
699      * fields as well.  This is a fixed-size area immediately following
700      * the data.
701      */
702     if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
703     {
704         result = UNKNOWN_ERROR;
705         goto bail;
706     }
707 
708     uncompressedLen = pSourceEntry->getUncompressedLen();
709 
710     if (pSourceEntry->isCompressed()) {
711         void *buf = pSourceZip->uncompress(pSourceEntry);
712         if (buf == NULL) {
713             result = NO_MEMORY;
714             goto bail;
715         }
716         long startPosn = ftell(mZipFp);
717         uint32_t crc;
718         if (compressFpToFp(mZipFp, NULL, buf, uncompressedLen, &crc) != OK) {
719             ALOGW("recompress of '%s' failed\n", pEntry->mCDE.mFileName);
720             result = UNKNOWN_ERROR;
721             free(buf);
722             goto bail;
723         }
724         long endPosn = ftell(mZipFp);
725         pEntry->setDataInfo(uncompressedLen, endPosn - startPosn,
726             pSourceEntry->getCRC32(), ZipEntry::kCompressDeflated);
727         free(buf);
728     } else {
729         off_t copyLen;
730         copyLen = pSourceEntry->getCompressedLen();
731         if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
732             copyLen += ZipEntry::kDataDescriptorLen;
733 
734         if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
735             != OK)
736         {
737             ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
738             result = UNKNOWN_ERROR;
739             goto bail;
740         }
741     }
742 
743     /*
744      * Update file offsets.
745      */
746     endPosn = ftell(mZipFp);
747 
748     /*
749      * Success!  Fill out new values.
750      */
751     pEntry->setLFHOffset(lfhPosn);
752     mEOCD.mNumEntries++;
753     mEOCD.mTotalNumEntries++;
754     mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
755     mEOCD.mCentralDirOffset = endPosn;
756 
757     /*
758      * Go back and write the LFH.
759      */
760     if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
761         result = UNKNOWN_ERROR;
762         goto bail;
763     }
764     pEntry->mLFH.write(mZipFp);
765 
766     /*
767      * Add pEntry to the list.
768      */
769     mEntries.add(pEntry);
770     if (ppEntry != NULL)
771         *ppEntry = pEntry;
772     pEntry = NULL;
773 
774     result = OK;
775 
776 bail:
777     delete pEntry;
778     return result;
779 }
780 
781 /*
782  * Copy all of the bytes in "src" to "dst".
783  *
784  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
785  * will be seeked immediately past the data.
786  */
copyFpToFp(FILE * dstFp,FILE * srcFp,uint32_t * pCRC32)787 status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, uint32_t* pCRC32)
788 {
789     uint8_t tmpBuf[32768];
790     size_t count;
791 
792     *pCRC32 = crc32(0L, Z_NULL, 0);
793 
794     while (1) {
795         count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
796         if (ferror(srcFp) || ferror(dstFp)) {
797             status_t status = errnoToStatus(errno);
798             ALOGW("fread %zu bytes failed, %s", count, strerror(errno));
799             return status;
800         }
801         if (count == 0)
802             break;
803 
804         *pCRC32 = crc32(*pCRC32, tmpBuf, count);
805 
806         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
807             ALOGW("fwrite %zu bytes failed, %s", count, strerror(errno));
808             return UNKNOWN_ERROR;
809         }
810     }
811 
812     return OK;
813 }
814 
815 /*
816  * Copy all of the bytes in "src" to "dst".
817  *
818  * On exit, "dstFp" will be seeked immediately past the data.
819  */
copyDataToFp(FILE * dstFp,const void * data,size_t size,uint32_t * pCRC32)820 status_t ZipFile::copyDataToFp(FILE* dstFp,
821     const void* data, size_t size, uint32_t* pCRC32)
822 {
823     *pCRC32 = crc32(0L, Z_NULL, 0);
824     if (size > 0) {
825         *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
826         if (fwrite(data, 1, size, dstFp) != size) {
827             ALOGW("fwrite %zu bytes failed, %s", size, strerror(errno));
828             return UNKNOWN_ERROR;
829         }
830     }
831 
832     return OK;
833 }
834 
835 /*
836  * Copy some of the bytes in "src" to "dst".
837  *
838  * If "pCRC32" is NULL, the CRC will not be computed.
839  *
840  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
841  * will be seeked immediately past the data just written.
842  */
copyPartialFpToFp(FILE * dstFp,FILE * srcFp,size_t length,uint32_t * pCRC32)843 status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, size_t length,
844     uint32_t* pCRC32)
845 {
846     uint8_t tmpBuf[32768];
847     size_t count;
848 
849     if (pCRC32 != NULL)
850         *pCRC32 = crc32(0L, Z_NULL, 0);
851 
852     while (length) {
853         size_t readSize;
854 
855         readSize = sizeof(tmpBuf);
856         if (readSize > length)
857             readSize = length;
858 
859         count = fread(tmpBuf, 1, readSize, srcFp);
860         if (count != readSize) {     // error or unexpected EOF
861             if (feof(srcFp)) {
862                 ALOGW("fread %zu bytes failed, unexpected EOF", readSize);
863             } else {
864                 ALOGW("fread %zu bytes failed, %s", readSize, strerror(errno));
865             }
866             return UNKNOWN_ERROR;
867         }
868 
869         if (pCRC32 != NULL)
870             *pCRC32 = crc32(*pCRC32, tmpBuf, count);
871 
872         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
873             ALOGW("fwrite %zu bytes failed, %s", count, strerror(errno));
874             return UNKNOWN_ERROR;
875         }
876 
877         length -= readSize;
878     }
879 
880     return OK;
881 }
882 
883 /*
884  * Compress all of the data in "srcFp" and write it to "dstFp".
885  *
886  * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
887  * will be seeked immediately past the compressed data.
888  */
compressFpToFp(FILE * dstFp,FILE * srcFp,const void * data,size_t size,uint32_t * pCRC32)889 status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
890     const void* data, size_t size, uint32_t* pCRC32)
891 {
892     status_t result = OK;
893     const size_t kBufSize = 1024 * 1024;
894     uint8_t* inBuf = NULL;
895     uint8_t* outBuf = NULL;
896     size_t outSize = 0;
897     bool atEof = false;     // no feof() aviailable yet
898     uint32_t crc;
899     ZopfliOptions options;
900     unsigned char bp = 0;
901 
902     ZopfliInitOptions(&options);
903 
904     crc = crc32(0L, Z_NULL, 0);
905 
906     if (data) {
907         crc = crc32(crc, (const unsigned char*)data, size);
908         ZopfliDeflate(&options, 2, true, (const unsigned char*)data, size, &bp,
909             &outBuf, &outSize);
910     } else {
911         /*
912          * Create an input buffer and an output buffer.
913          */
914         inBuf = new uint8_t[kBufSize];
915         if (inBuf == NULL) {
916             result = NO_MEMORY;
917             goto bail;
918         }
919 
920         /*
921          * Loop while we have data.
922          */
923         do {
924             size_t getSize;
925             getSize = fread(inBuf, 1, kBufSize, srcFp);
926             if (ferror(srcFp)) {
927                 ALOGD("deflate read failed (errno=%d)\n", errno);
928                 result = UNKNOWN_ERROR;
929                 delete[] inBuf;
930                 goto bail;
931             }
932             if (getSize < kBufSize) {
933                 ALOGV("+++  got %zu bytes, EOF reached\n", getSize);
934                 atEof = true;
935             }
936 
937             crc = crc32(crc, inBuf, getSize);
938             ZopfliDeflate(&options, 2, atEof, inBuf, getSize, &bp, &outBuf, &outSize);
939         } while (!atEof);
940         delete[] inBuf;
941     }
942 
943     ALOGV("+++ writing %zu bytes\n", outSize);
944     if (fwrite(outBuf, 1, outSize, dstFp) != outSize) {
945         ALOGW("fwrite %zu bytes failed, %s", outSize, strerror(errno));
946         result = UNKNOWN_ERROR;
947         goto bail;
948     }
949 
950     *pCRC32 = crc;
951 
952 bail:
953     free(outBuf);
954 
955     return result;
956 }
957 
958 /*
959  * Mark an entry as deleted.
960  *
961  * We will eventually need to crunch the file down, but if several files
962  * are being removed (perhaps as part of an "update" process) we can make
963  * things considerably faster by deferring the removal to "flush" time.
964  */
remove(ZipEntry * pEntry)965 status_t ZipFile::remove(ZipEntry* pEntry)
966 {
967     /*
968      * Should verify that pEntry is actually part of this archive, and
969      * not some stray ZipEntry from a different file.
970      */
971 
972     /* mark entry as deleted, and mark archive as dirty */
973     pEntry->setDeleted();
974     mNeedCDRewrite = true;
975     return OK;
976 }
977 
978 /*
979  * Flush any pending writes.
980  *
981  * In particular, this will crunch out deleted entries, and write the
982  * Central Directory and EOCD if we have stomped on them.
983  */
flush(void)984 status_t ZipFile::flush(void)
985 {
986     status_t result = OK;
987     long eocdPosn;
988     int i, count;
989 
990     if (mReadOnly)
991         return INVALID_OPERATION;
992     if (!mNeedCDRewrite)
993         return OK;
994 
995     assert(mZipFp != NULL);
996 
997     result = crunchArchive();
998     if (result != OK)
999         return result;
1000 
1001     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
1002         return UNKNOWN_ERROR;
1003 
1004     count = mEntries.size();
1005     for (i = 0; i < count; i++) {
1006         ZipEntry* pEntry = mEntries[i];
1007         pEntry->mCDE.write(mZipFp);
1008     }
1009 
1010     eocdPosn = ftell(mZipFp);
1011     mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
1012 
1013     mEOCD.write(mZipFp);
1014 
1015     /*
1016      * If we had some stuff bloat up during compression and get replaced
1017      * with plain files, or if we deleted some entries, there's a lot
1018      * of wasted space at the end of the file.  Remove it now.
1019      */
1020     if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
1021         ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
1022         // not fatal
1023     }
1024 
1025     /* should we clear the "newly added" flag in all entries now? */
1026 
1027     mNeedCDRewrite = false;
1028     return OK;
1029 }
1030 
1031 /*
1032  * Crunch deleted files out of an archive by shifting the later files down.
1033  *
1034  * Because we're not using a temp file, we do the operation inside the
1035  * current file.
1036  */
crunchArchive(void)1037 status_t ZipFile::crunchArchive(void)
1038 {
1039     status_t result = OK;
1040     int i, count;
1041     long delCount, adjust;
1042 
1043 #if 0
1044     printf("CONTENTS:\n");
1045     for (i = 0; i < (int) mEntries.size(); i++) {
1046         printf(" %d: lfhOff=%ld del=%d\n",
1047             i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
1048     }
1049     printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
1050 #endif
1051 
1052     /*
1053      * Roll through the set of files, shifting them as appropriate.  We
1054      * could probably get a slight performance improvement by sliding
1055      * multiple files down at once (because we could use larger reads
1056      * when operating on batches of small files), but it's not that useful.
1057      */
1058     count = mEntries.size();
1059     delCount = adjust = 0;
1060     for (i = 0; i < count; i++) {
1061         ZipEntry* pEntry = mEntries[i];
1062         long span;
1063 
1064         if (pEntry->getLFHOffset() != 0) {
1065             long nextOffset;
1066 
1067             /* Get the length of this entry by finding the offset
1068              * of the next entry.  Directory entries don't have
1069              * file offsets, so we need to find the next non-directory
1070              * entry.
1071              */
1072             nextOffset = 0;
1073             for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
1074                 nextOffset = mEntries[ii]->getLFHOffset();
1075             if (nextOffset == 0)
1076                 nextOffset = mEOCD.mCentralDirOffset;
1077             span = nextOffset - pEntry->getLFHOffset();
1078 
1079             assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
1080         } else {
1081             /* This is a directory entry.  It doesn't have
1082              * any actual file contents, so there's no need to
1083              * move anything.
1084              */
1085             span = 0;
1086         }
1087 
1088         //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
1089         //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
1090 
1091         if (pEntry->getDeleted()) {
1092             adjust += span;
1093             delCount++;
1094 
1095             delete pEntry;
1096             mEntries.removeAt(i);
1097 
1098             /* adjust loop control */
1099             count--;
1100             i--;
1101         } else if (span != 0 && adjust > 0) {
1102             /* shuffle this entry back */
1103             //printf("+++ Shuffling '%s' back %ld\n",
1104             //    pEntry->getFileName(), adjust);
1105             result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
1106                         pEntry->getLFHOffset(), span);
1107             if (result != OK) {
1108                 /* this is why you use a temp file */
1109                 ALOGE("error during crunch - archive is toast\n");
1110                 return result;
1111             }
1112 
1113             pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
1114         }
1115     }
1116 
1117     /*
1118      * Fix EOCD info.  We have to wait until the end to do some of this
1119      * because we use mCentralDirOffset to determine "span" for the
1120      * last entry.
1121      */
1122     mEOCD.mCentralDirOffset -= adjust;
1123     mEOCD.mNumEntries -= delCount;
1124     mEOCD.mTotalNumEntries -= delCount;
1125     mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
1126 
1127     assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
1128     assert(mEOCD.mNumEntries == count);
1129 
1130     return result;
1131 }
1132 
1133 /*
1134  * Works like memmove(), but on pieces of a file.
1135  */
filemove(FILE * fp,off_t dst,off_t src,size_t n)1136 status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
1137 {
1138     if (dst == src || n <= 0)
1139         return OK;
1140 
1141     uint8_t readBuf[32768];
1142 
1143     if (dst < src) {
1144         /* shift stuff toward start of file; must read from start */
1145         while (n != 0) {
1146             size_t getSize = sizeof(readBuf);
1147             if (getSize > n)
1148                 getSize = n;
1149 
1150             if (fseek(fp, (long) src, SEEK_SET) != 0) {
1151                 ALOGW("filemove src seek %ld failed, %s",
1152                     (long) src, strerror(errno));
1153                 return UNKNOWN_ERROR;
1154             }
1155 
1156             if (fread(readBuf, 1, getSize, fp) != getSize) {
1157                 if (feof(fp)) {
1158                     ALOGW("fread %zu bytes off=%ld failed, unexpected EOF",
1159                         getSize, (long) src);
1160                 } else {
1161                     ALOGW("fread %zu bytes off=%ld failed, %s",
1162                         getSize, (long) src, strerror(errno));
1163                 }
1164                 return UNKNOWN_ERROR;
1165             }
1166 
1167             if (fseek(fp, (long) dst, SEEK_SET) != 0) {
1168                 ALOGW("filemove dst seek %ld failed, %s",
1169                     (long) dst, strerror(errno));
1170                 return UNKNOWN_ERROR;
1171             }
1172 
1173             if (fwrite(readBuf, 1, getSize, fp) != getSize) {
1174                 ALOGW("filemove write %zu off=%ld failed, %s",
1175                     getSize, (long) dst, strerror(errno));
1176                 return UNKNOWN_ERROR;
1177             }
1178 
1179             src += getSize;
1180             dst += getSize;
1181             n -= getSize;
1182         }
1183     } else {
1184         /* shift stuff toward end of file; must read from end */
1185         assert(false);      // write this someday, maybe
1186         return UNKNOWN_ERROR;
1187     }
1188 
1189     return OK;
1190 }
1191 
1192 
1193 /*
1194  * Get the modification time from a file descriptor.
1195  */
getModTime(int fd)1196 time_t ZipFile::getModTime(int fd)
1197 {
1198     struct stat sb;
1199 
1200     if (fstat(fd, &sb) < 0) {
1201         ALOGD("HEY: fstat on fd %d failed\n", fd);
1202         return (time_t) -1;
1203     }
1204 
1205     return sb.st_mtime;
1206 }
1207 
1208 
1209 #if 0       /* this is a bad idea */
1210 /*
1211  * Get a copy of the Zip file descriptor.
1212  *
1213  * We don't allow this if the file was opened read-write because we tend
1214  * to leave the file contents in an uncertain state between calls to
1215  * flush().  The duplicated file descriptor should only be valid for reads.
1216  */
1217 int ZipFile::getZipFd(void) const
1218 {
1219     if (!mReadOnly)
1220         return INVALID_OPERATION;
1221     assert(mZipFp != NULL);
1222 
1223     int fd;
1224     fd = dup(fileno(mZipFp));
1225     if (fd < 0) {
1226         ALOGD("didn't work, errno=%d\n", errno);
1227     }
1228 
1229     return fd;
1230 }
1231 #endif
1232 
1233 
1234 #if 0
1235 /*
1236  * Expand data.
1237  */
1238 bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
1239 {
1240     return false;
1241 }
1242 #endif
1243 
1244 class BufferWriter : public zip_archive::Writer {
1245   public:
BufferWriter(void * buf,size_t size)1246     BufferWriter(void* buf, size_t size) : Writer(),
1247         buf_(reinterpret_cast<uint8_t*>(buf)), size_(size), bytes_written_(0) {}
1248 
Append(uint8_t * buf,size_t buf_size)1249     bool Append(uint8_t* buf, size_t buf_size) override {
1250         if (bytes_written_ + buf_size > size_) {
1251             return false;
1252         }
1253 
1254         memcpy(buf_ + bytes_written_, buf, buf_size);
1255         bytes_written_ += buf_size;
1256         return true;
1257     }
1258 
1259   private:
1260     uint8_t* const buf_;
1261     const size_t size_;
1262     size_t bytes_written_;
1263 };
1264 
1265 class FileReader : public zip_archive::Reader {
1266   public:
FileReader(FILE * fp)1267     FileReader(FILE* fp) : Reader(), fp_(fp), current_offset_(0) {
1268     }
1269 
ReadAtOffset(uint8_t * buf,size_t len,off64_t offset) const1270     bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
1271         // Data is usually requested sequentially, so this helps avoid pointless
1272         // fseeks every time we perform a read. There's an impedence mismatch
1273         // here because the original API was designed around pread and pwrite.
1274         if (offset != current_offset_) {
1275             if (fseek(fp_, offset, SEEK_SET) != 0) {
1276                 return false;
1277             }
1278 
1279             current_offset_ = offset;
1280         }
1281 
1282         size_t read = fread(buf, 1, len, fp_);
1283         if (read != len) {
1284             return false;
1285         }
1286 
1287         current_offset_ += read;
1288         return true;
1289     }
1290 
1291   private:
1292     FILE* fp_;
1293     mutable off64_t current_offset_;
1294 };
1295 
1296 // free the memory when you're done
uncompress(const ZipEntry * entry) const1297 void* ZipFile::uncompress(const ZipEntry* entry) const
1298 {
1299     size_t unlen = entry->getUncompressedLen();
1300     size_t clen = entry->getCompressedLen();
1301 
1302     void* buf = malloc(unlen);
1303     if (buf == NULL) {
1304         return NULL;
1305     }
1306 
1307     fseek(mZipFp, 0, SEEK_SET);
1308 
1309     off_t offset = entry->getFileOffset();
1310     if (fseek(mZipFp, offset, SEEK_SET) != 0) {
1311         goto bail;
1312     }
1313 
1314     switch (entry->getCompressionMethod())
1315     {
1316         case ZipEntry::kCompressStored: {
1317             ssize_t amt = fread(buf, 1, unlen, mZipFp);
1318             if (amt != (ssize_t)unlen) {
1319                 goto bail;
1320             }
1321 #if 0
1322             printf("data...\n");
1323             const unsigned char* p = (unsigned char*)buf;
1324             const unsigned char* end = p+unlen;
1325             for (int i=0; i<32 && p < end; i++) {
1326                 printf("0x%08x ", (int)(offset+(i*0x10)));
1327                 for (int j=0; j<0x10 && p < end; j++) {
1328                     printf(" %02x", *p);
1329                     p++;
1330                 }
1331                 printf("\n");
1332             }
1333 #endif
1334 
1335             }
1336             break;
1337         case ZipEntry::kCompressDeflated: {
1338             const FileReader reader(mZipFp);
1339             BufferWriter writer(buf, unlen);
1340             if (zip_archive::Inflate(reader, clen, unlen, &writer, nullptr) != 0) {
1341                 goto bail;
1342             }
1343             break;
1344         }
1345         default:
1346             goto bail;
1347     }
1348     return buf;
1349 
1350 bail:
1351     free(buf);
1352     return NULL;
1353 }
1354 
1355 
1356 /*
1357  * ===========================================================================
1358  *      ZipFile::EndOfCentralDir
1359  * ===========================================================================
1360  */
1361 
1362 /*
1363  * Read the end-of-central-dir fields.
1364  *
1365  * "buf" should be positioned at the EOCD signature, and should contain
1366  * the entire EOCD area including the comment.
1367  */
readBuf(const uint8_t * buf,int len)1368 status_t ZipFile::EndOfCentralDir::readBuf(const uint8_t* buf, int len)
1369 {
1370     /* don't allow re-use */
1371     assert(mComment == NULL);
1372 
1373     if (len < kEOCDLen) {
1374         /* looks like ZIP file got truncated */
1375         ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
1376             kEOCDLen, len);
1377         return INVALID_OPERATION;
1378     }
1379 
1380     /* this should probably be an assert() */
1381     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
1382         return UNKNOWN_ERROR;
1383 
1384     mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
1385     mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
1386     mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
1387     mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
1388     mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
1389     mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
1390     mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
1391 
1392     // TODO: validate mCentralDirOffset
1393 
1394     if (mCommentLen > 0) {
1395         if (kEOCDLen + mCommentLen > len) {
1396             ALOGD("EOCD(%d) + comment(%" PRIu16 ") exceeds len (%d)\n",
1397                 kEOCDLen, mCommentLen, len);
1398             return UNKNOWN_ERROR;
1399         }
1400         mComment = new uint8_t[mCommentLen];
1401         memcpy(mComment, buf + kEOCDLen, mCommentLen);
1402     }
1403 
1404     return OK;
1405 }
1406 
1407 /*
1408  * Write an end-of-central-directory section.
1409  */
write(FILE * fp)1410 status_t ZipFile::EndOfCentralDir::write(FILE* fp)
1411 {
1412     uint8_t buf[kEOCDLen];
1413 
1414     ZipEntry::putLongLE(&buf[0x00], kSignature);
1415     ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
1416     ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
1417     ZipEntry::putShortLE(&buf[0x08], mNumEntries);
1418     ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
1419     ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
1420     ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
1421     ZipEntry::putShortLE(&buf[0x14], mCommentLen);
1422 
1423     if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) {
1424         ALOGW("fwrite EOCD failed, %s", strerror(errno));
1425         return UNKNOWN_ERROR;
1426     }
1427     if (mCommentLen > 0) {
1428         assert(mComment != NULL);
1429         if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) {
1430             ALOGW("fwrite %d bytes failed, %s",
1431                 (int) mCommentLen, strerror(errno));
1432             return UNKNOWN_ERROR;
1433         }
1434     }
1435 
1436     return OK;
1437 }
1438 
1439 /*
1440  * Dump the contents of an EndOfCentralDir object.
1441  */
dump(void) const1442 void ZipFile::EndOfCentralDir::dump(void) const
1443 {
1444     ALOGD(" EndOfCentralDir contents:\n");
1445     ALOGD("  diskNum=%" PRIu16 " diskWCD=%" PRIu16 " numEnt=%" PRIu16 " totalNumEnt=%" PRIu16 "\n",
1446         mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
1447     ALOGD("  centDirSize=%" PRIu32 " centDirOff=%" PRIu32 " commentLen=%" PRIu32 "\n",
1448         mCentralDirSize, mCentralDirOffset, mCommentLen);
1449 }
1450 
1451 } // namespace android
1452