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