xref: /OK3568_Linux_fs/external/recovery/minzip/Zip.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright 2006 The Android Open Source Project
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Simple Zip file support.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun #include "safe_iop.h"
7*4882a593Smuzhiyun #include "zlib.h"
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <errno.h>
10*4882a593Smuzhiyun #include <fcntl.h>
11*4882a593Smuzhiyun #include <limits.h>
12*4882a593Smuzhiyun #include <stdint.h>     // for uintptr_t
13*4882a593Smuzhiyun #include <stdlib.h>
14*4882a593Smuzhiyun #include <sys/stat.h>   // for S_ISLNK()
15*4882a593Smuzhiyun #include <unistd.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #define LOG_TAG "minzip"
18*4882a593Smuzhiyun #include "Zip.h"
19*4882a593Smuzhiyun #include "Bits.h"
20*4882a593Smuzhiyun #include "Log.h"
21*4882a593Smuzhiyun #include "DirUtil.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #undef NDEBUG   // do this after including Log.h
24*4882a593Smuzhiyun #include <assert.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #define SORT_ENTRIES 1
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * Offset and length constants (java.util.zip naming convention).
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun enum {
32*4882a593Smuzhiyun     CENSIG = 0x02014b50,      // PK12
33*4882a593Smuzhiyun     CENHDR = 46,
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun     CENVEM =  4,
36*4882a593Smuzhiyun     CENVER =  6,
37*4882a593Smuzhiyun     CENFLG =  8,
38*4882a593Smuzhiyun     CENHOW = 10,
39*4882a593Smuzhiyun     CENTIM = 12,
40*4882a593Smuzhiyun     CENCRC = 16,
41*4882a593Smuzhiyun     CENSIZ = 20,
42*4882a593Smuzhiyun     CENLEN = 24,
43*4882a593Smuzhiyun     CENNAM = 28,
44*4882a593Smuzhiyun     CENEXT = 30,
45*4882a593Smuzhiyun     CENCOM = 32,
46*4882a593Smuzhiyun     CENDSK = 34,
47*4882a593Smuzhiyun     CENATT = 36,
48*4882a593Smuzhiyun     CENATX = 38,
49*4882a593Smuzhiyun     CENOFF = 42,
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun     ENDSIG = 0x06054b50,     // PK56
52*4882a593Smuzhiyun     ENDHDR = 22,
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun     ENDSUB =  8,
55*4882a593Smuzhiyun     ENDTOT = 10,
56*4882a593Smuzhiyun     ENDSIZ = 12,
57*4882a593Smuzhiyun     ENDOFF = 16,
58*4882a593Smuzhiyun     ENDCOM = 20,
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun     EXTSIG = 0x08074b50,     // PK78
61*4882a593Smuzhiyun     EXTHDR = 16,
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun     EXTCRC =  4,
64*4882a593Smuzhiyun     EXTSIZ =  8,
65*4882a593Smuzhiyun     EXTLEN = 12,
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun     LOCSIG = 0x04034b50,      // PK34
68*4882a593Smuzhiyun     LOCHDR = 30,
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun     LOCVER =  4,
71*4882a593Smuzhiyun     LOCFLG =  6,
72*4882a593Smuzhiyun     LOCHOW =  8,
73*4882a593Smuzhiyun     LOCTIM = 10,
74*4882a593Smuzhiyun     LOCCRC = 14,
75*4882a593Smuzhiyun     LOCSIZ = 18,
76*4882a593Smuzhiyun     LOCLEN = 22,
77*4882a593Smuzhiyun     LOCNAM = 26,
78*4882a593Smuzhiyun     LOCEXT = 28,
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun     STORED = 0,
81*4882a593Smuzhiyun     DEFLATED = 8,
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun     CENVEM_UNIX = 3 << 8,   // the high byte of CENVEM
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun /*
88*4882a593Smuzhiyun  * For debugging, dump the contents of a ZipEntry.
89*4882a593Smuzhiyun  */
90*4882a593Smuzhiyun #if 0
91*4882a593Smuzhiyun static void dumpEntry(const ZipEntry* pEntry)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun     LOGI(" %p '%.*s'\n", pEntry->fileName, pEntry->fileNameLen, pEntry->fileName);
94*4882a593Smuzhiyun     LOGI("   off=%ld comp=%ld uncomp=%ld how=%d\n", pEntry->offset,
95*4882a593Smuzhiyun          pEntry->compLen, pEntry->uncompLen, pEntry->compression);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun #endif
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun /*
100*4882a593Smuzhiyun  * (This is a mzHashTableLookup callback.)
101*4882a593Smuzhiyun  *
102*4882a593Smuzhiyun  * Compare two ZipEntry structs, by name.
103*4882a593Smuzhiyun  */
hashcmpZipEntry(const void * ventry1,const void * ventry2)104*4882a593Smuzhiyun static int hashcmpZipEntry(const void* ventry1, const void* ventry2)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun     const ZipEntry* entry1 = (const ZipEntry*) ventry1;
107*4882a593Smuzhiyun     const ZipEntry* entry2 = (const ZipEntry*) ventry2;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun     if (entry1->fileNameLen != entry2->fileNameLen)
110*4882a593Smuzhiyun         return entry1->fileNameLen - entry2->fileNameLen;
111*4882a593Smuzhiyun     return memcmp(entry1->fileName, entry2->fileName, entry1->fileNameLen);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /*
115*4882a593Smuzhiyun  * (This is a mzHashTableLookup callback.)
116*4882a593Smuzhiyun  *
117*4882a593Smuzhiyun  * find a ZipEntry struct by name.
118*4882a593Smuzhiyun  */
hashcmpZipName(const void * ventry,const void * vname)119*4882a593Smuzhiyun static int hashcmpZipName(const void* ventry, const void* vname)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun     const ZipEntry* entry = (const ZipEntry*) ventry;
122*4882a593Smuzhiyun     const char* name = (const char*) vname;
123*4882a593Smuzhiyun     unsigned int nameLen = strlen(name);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun     if (entry->fileNameLen != nameLen)
126*4882a593Smuzhiyun         return entry->fileNameLen - nameLen;
127*4882a593Smuzhiyun     return memcmp(entry->fileName, name, nameLen);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun /*
131*4882a593Smuzhiyun  * Compute the hash code for a ZipEntry filename.
132*4882a593Smuzhiyun  *
133*4882a593Smuzhiyun  * Not expected to be compatible with any other hash function, so we init
134*4882a593Smuzhiyun  * to 2 to ensure it doesn't happen to match.
135*4882a593Smuzhiyun  */
computeHash(const char * name,int nameLen)136*4882a593Smuzhiyun static unsigned int computeHash(const char* name, int nameLen)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun     unsigned int hash = 2;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun     while (nameLen--)
141*4882a593Smuzhiyun         hash = hash * 31 + *name++;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun     return hash;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
addEntryToHashTable(HashTable * pHash,ZipEntry * pEntry)146*4882a593Smuzhiyun static void addEntryToHashTable(HashTable* pHash, ZipEntry* pEntry)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun     unsigned int itemHash = computeHash(pEntry->fileName, pEntry->fileNameLen);
149*4882a593Smuzhiyun     const ZipEntry* found;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun     found = (const ZipEntry*)mzHashTableLookup(pHash,
152*4882a593Smuzhiyun                                                itemHash, pEntry, hashcmpZipEntry, true);
153*4882a593Smuzhiyun     if (found != pEntry) {
154*4882a593Smuzhiyun         LOGW("WARNING: duplicate entry '%.*s' in Zip\n",
155*4882a593Smuzhiyun              found->fileNameLen, found->fileName);
156*4882a593Smuzhiyun         /* keep going */
157*4882a593Smuzhiyun     }
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
validFilename(const char * fileName,unsigned int fileNameLen)160*4882a593Smuzhiyun static int validFilename(const char *fileName, unsigned int fileNameLen)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun     // Forbid super long filenames.
163*4882a593Smuzhiyun     if (fileNameLen >= PATH_MAX) {
164*4882a593Smuzhiyun         LOGW("Filename too long (%d chatacters)\n", fileNameLen);
165*4882a593Smuzhiyun         return 0;
166*4882a593Smuzhiyun     }
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun     // Require all characters to be printable ASCII (no NUL, no UTF-8, etc).
169*4882a593Smuzhiyun     unsigned int i;
170*4882a593Smuzhiyun     for (i = 0; i < fileNameLen; ++i) {
171*4882a593Smuzhiyun         if (fileName[i] < 32 || fileName[i] >= 127) {
172*4882a593Smuzhiyun             LOGW("Filename contains invalid character '\%03o'\n", fileName[i]);
173*4882a593Smuzhiyun             return 0;
174*4882a593Smuzhiyun         }
175*4882a593Smuzhiyun     }
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun     return 1;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun /*
181*4882a593Smuzhiyun  * Parse the contents of a Zip archive.  After confirming that the file
182*4882a593Smuzhiyun  * is in fact a Zip, we scan out the contents of the central directory and
183*4882a593Smuzhiyun  * store it in a hash table.
184*4882a593Smuzhiyun  *
185*4882a593Smuzhiyun  * Returns "true" on success.
186*4882a593Smuzhiyun  */
parseZipArchive(ZipArchive * pArchive,const MemMapping * pMap)187*4882a593Smuzhiyun static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun     bool result = false;
190*4882a593Smuzhiyun     const unsigned char* ptr;
191*4882a593Smuzhiyun     unsigned int i, numEntries, cdOffset;
192*4882a593Smuzhiyun     unsigned int val;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun     /*
195*4882a593Smuzhiyun      * The first 4 bytes of the file will either be the local header
196*4882a593Smuzhiyun      * signature for the first file (LOCSIG) or, if the archive doesn't
197*4882a593Smuzhiyun      * have any files in it, the end-of-central-directory signature (ENDSIG).
198*4882a593Smuzhiyun      */
199*4882a593Smuzhiyun     val = get4LE(pMap->addr);
200*4882a593Smuzhiyun     if (val == ENDSIG) {
201*4882a593Smuzhiyun         LOGI("Found Zip archive, but it looks empty\n");
202*4882a593Smuzhiyun         goto bail;
203*4882a593Smuzhiyun     } else if (val != LOCSIG) {
204*4882a593Smuzhiyun         LOGV("Not a Zip archive (found 0x%08x)\n", val);
205*4882a593Smuzhiyun         goto bail;
206*4882a593Smuzhiyun     }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun     /*
209*4882a593Smuzhiyun      * Find the EOCD.  We'll find it immediately unless they have a file
210*4882a593Smuzhiyun      * comment.
211*4882a593Smuzhiyun      */
212*4882a593Smuzhiyun     ptr = pMap->addr + pMap->length - ENDHDR;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun     while (ptr >= (const unsigned char*) pMap->addr) {
215*4882a593Smuzhiyun         if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
216*4882a593Smuzhiyun             break;
217*4882a593Smuzhiyun         ptr--;
218*4882a593Smuzhiyun     }
219*4882a593Smuzhiyun     if (ptr < (const unsigned char*) pMap->addr) {
220*4882a593Smuzhiyun         LOGI("Could not find end-of-central-directory in Zip\n");
221*4882a593Smuzhiyun         goto bail;
222*4882a593Smuzhiyun     }
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun     /*
225*4882a593Smuzhiyun      * There are two interesting items in the EOCD block: the number of
226*4882a593Smuzhiyun      * entries in the file, and the file offset of the start of the
227*4882a593Smuzhiyun      * central directory.
228*4882a593Smuzhiyun      */
229*4882a593Smuzhiyun     numEntries = get2LE(ptr + ENDSUB);
230*4882a593Smuzhiyun     cdOffset = get4LE(ptr + ENDOFF);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun     LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
233*4882a593Smuzhiyun     if (numEntries == 0 || cdOffset >= pMap->length) {
234*4882a593Smuzhiyun         LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
235*4882a593Smuzhiyun              numEntries, cdOffset, pMap->length);
236*4882a593Smuzhiyun         goto bail;
237*4882a593Smuzhiyun     }
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun     /*
240*4882a593Smuzhiyun      * Create data structures to hold entries.
241*4882a593Smuzhiyun      */
242*4882a593Smuzhiyun     pArchive->numEntries = numEntries;
243*4882a593Smuzhiyun     pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry));
244*4882a593Smuzhiyun     pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL);
245*4882a593Smuzhiyun     if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
246*4882a593Smuzhiyun         goto bail;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun     ptr = pMap->addr + cdOffset;
249*4882a593Smuzhiyun     for (i = 0; i < numEntries; i++) {
250*4882a593Smuzhiyun         ZipEntry* pEntry;
251*4882a593Smuzhiyun         unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
252*4882a593Smuzhiyun         const unsigned char* localHdr;
253*4882a593Smuzhiyun         const char *fileName;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun         if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) {
256*4882a593Smuzhiyun             LOGW("Ran off the end (at %d)\n", i);
257*4882a593Smuzhiyun             goto bail;
258*4882a593Smuzhiyun         }
259*4882a593Smuzhiyun         if (get4LE(ptr) != CENSIG) {
260*4882a593Smuzhiyun             LOGW("Missed a central dir sig (at %d)\n", i);
261*4882a593Smuzhiyun             goto bail;
262*4882a593Smuzhiyun         }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun         localHdrOffset = get4LE(ptr + CENOFF);
265*4882a593Smuzhiyun         fileNameLen = get2LE(ptr + CENNAM);
266*4882a593Smuzhiyun         extraLen = get2LE(ptr + CENEXT);
267*4882a593Smuzhiyun         commentLen = get2LE(ptr + CENCOM);
268*4882a593Smuzhiyun         fileName = (const char*)ptr + CENHDR;
269*4882a593Smuzhiyun         if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) {
270*4882a593Smuzhiyun             LOGW("Filename ran off the end (at %d)\n", i);
271*4882a593Smuzhiyun             goto bail;
272*4882a593Smuzhiyun         }
273*4882a593Smuzhiyun         if (!validFilename(fileName, fileNameLen)) {
274*4882a593Smuzhiyun             LOGW("Invalid filename (at %d)\n", i);
275*4882a593Smuzhiyun             goto bail;
276*4882a593Smuzhiyun         }
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun #if SORT_ENTRIES
279*4882a593Smuzhiyun         /* Figure out where this entry should go (binary search).
280*4882a593Smuzhiyun          */
281*4882a593Smuzhiyun         if (i > 0) {
282*4882a593Smuzhiyun             int low, high;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun             low = 0;
285*4882a593Smuzhiyun             high = i - 1;
286*4882a593Smuzhiyun             while (low <= high) {
287*4882a593Smuzhiyun                 int mid;
288*4882a593Smuzhiyun                 int diff;
289*4882a593Smuzhiyun                 int diffLen;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun                 mid = low + ((high - low) / 2); // avoid overflow
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun                 if (pArchive->pEntries[mid].fileNameLen < fileNameLen) {
294*4882a593Smuzhiyun                     diffLen = pArchive->pEntries[mid].fileNameLen;
295*4882a593Smuzhiyun                 } else {
296*4882a593Smuzhiyun                     diffLen = fileNameLen;
297*4882a593Smuzhiyun                 }
298*4882a593Smuzhiyun                 diff = strncmp(pArchive->pEntries[mid].fileName, fileName,
299*4882a593Smuzhiyun                                diffLen);
300*4882a593Smuzhiyun                 if (diff == 0) {
301*4882a593Smuzhiyun                     diff = pArchive->pEntries[mid].fileNameLen - fileNameLen;
302*4882a593Smuzhiyun                 }
303*4882a593Smuzhiyun                 if (diff < 0) {
304*4882a593Smuzhiyun                     low = mid + 1;
305*4882a593Smuzhiyun                 } else if (diff > 0) {
306*4882a593Smuzhiyun                     high = mid - 1;
307*4882a593Smuzhiyun                 } else {
308*4882a593Smuzhiyun                     high = mid;
309*4882a593Smuzhiyun                     break;
310*4882a593Smuzhiyun                 }
311*4882a593Smuzhiyun             }
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun             unsigned int target = high + 1;
314*4882a593Smuzhiyun             assert(target <= i);
315*4882a593Smuzhiyun             if (target != i) {
316*4882a593Smuzhiyun                 /* It belongs somewhere other than at the end of
317*4882a593Smuzhiyun                  * the list.  Make some room at [target].
318*4882a593Smuzhiyun                  */
319*4882a593Smuzhiyun                 memmove(pArchive->pEntries + target + 1,
320*4882a593Smuzhiyun                         pArchive->pEntries + target,
321*4882a593Smuzhiyun                         (i - target) * sizeof(ZipEntry));
322*4882a593Smuzhiyun             }
323*4882a593Smuzhiyun             pEntry = &pArchive->pEntries[target];
324*4882a593Smuzhiyun         } else {
325*4882a593Smuzhiyun             pEntry = &pArchive->pEntries[0];
326*4882a593Smuzhiyun         }
327*4882a593Smuzhiyun #else
328*4882a593Smuzhiyun         pEntry = &pArchive->pEntries[i];
329*4882a593Smuzhiyun #endif
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun         //LOGI("%d: localHdr=%d fnl=%d el=%d cl=%d\n",
332*4882a593Smuzhiyun         //    i, localHdrOffset, fileNameLen, extraLen, commentLen);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun         pEntry->fileNameLen = fileNameLen;
335*4882a593Smuzhiyun         pEntry->fileName = fileName;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun         pEntry->compLen = get4LE(ptr + CENSIZ);
338*4882a593Smuzhiyun         pEntry->uncompLen = get4LE(ptr + CENLEN);
339*4882a593Smuzhiyun         pEntry->compression = get2LE(ptr + CENHOW);
340*4882a593Smuzhiyun         pEntry->modTime = get4LE(ptr + CENTIM);
341*4882a593Smuzhiyun         pEntry->crc32 = get4LE(ptr + CENCRC);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun         /* These two are necessary for finding the mode of the file.
344*4882a593Smuzhiyun          */
345*4882a593Smuzhiyun         pEntry->versionMadeBy = get2LE(ptr + CENVEM);
346*4882a593Smuzhiyun         if ((pEntry->versionMadeBy & 0xff00) != 0 &&
347*4882a593Smuzhiyun             (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX) {
348*4882a593Smuzhiyun             LOGW("Incompatible \"version made by\": 0x%02x (at %d)\n",
349*4882a593Smuzhiyun                  pEntry->versionMadeBy >> 8, i);
350*4882a593Smuzhiyun             goto bail;
351*4882a593Smuzhiyun         }
352*4882a593Smuzhiyun         pEntry->externalFileAttributes = get4LE(ptr + CENATX);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun         // Perform pMap->addr + localHdrOffset, ensuring that it won't
355*4882a593Smuzhiyun         // overflow. This is needed because localHdrOffset is untrusted.
356*4882a593Smuzhiyun         if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr,
357*4882a593Smuzhiyun                       (uintptr_t)localHdrOffset)) {
358*4882a593Smuzhiyun             LOGW("Integer overflow adding in parseZipArchive\n");
359*4882a593Smuzhiyun             goto bail;
360*4882a593Smuzhiyun         }
361*4882a593Smuzhiyun         if ((uintptr_t)localHdr + LOCHDR >
362*4882a593Smuzhiyun             (uintptr_t)pMap->addr + pMap->length) {
363*4882a593Smuzhiyun             LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
364*4882a593Smuzhiyun             goto bail;
365*4882a593Smuzhiyun         }
366*4882a593Smuzhiyun         if (get4LE(localHdr) != LOCSIG) {
367*4882a593Smuzhiyun             LOGW("Missed a local header sig (at %d)\n", i);
368*4882a593Smuzhiyun             goto bail;
369*4882a593Smuzhiyun         }
370*4882a593Smuzhiyun         pEntry->offset = localHdrOffset + LOCHDR
371*4882a593Smuzhiyun                          + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT);
372*4882a593Smuzhiyun         if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) {
373*4882a593Smuzhiyun             LOGW("Integer overflow adding in parseZipArchive\n");
374*4882a593Smuzhiyun             goto bail;
375*4882a593Smuzhiyun         }
376*4882a593Smuzhiyun         if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) {
377*4882a593Smuzhiyun             LOGW("Data ran off the end (at %d)\n", i);
378*4882a593Smuzhiyun             goto bail;
379*4882a593Smuzhiyun         }
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun #if !SORT_ENTRIES
382*4882a593Smuzhiyun         /* Add to hash table; no need to lock here.
383*4882a593Smuzhiyun          * Can't do this now if we're sorting, because entries
384*4882a593Smuzhiyun          * will move around.
385*4882a593Smuzhiyun          */
386*4882a593Smuzhiyun         addEntryToHashTable(pArchive->pHash, pEntry);
387*4882a593Smuzhiyun #endif
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun         //dumpEntry(pEntry);
390*4882a593Smuzhiyun         ptr += CENHDR + fileNameLen + extraLen + commentLen;
391*4882a593Smuzhiyun     }
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun #if SORT_ENTRIES
394*4882a593Smuzhiyun     /* If we're sorting, we have to wait until all entries
395*4882a593Smuzhiyun      * are in their final places, otherwise the pointers will
396*4882a593Smuzhiyun      * probably point to the wrong things.
397*4882a593Smuzhiyun      */
398*4882a593Smuzhiyun     for (i = 0; i < numEntries; i++) {
399*4882a593Smuzhiyun         /* Add to hash table; no need to lock here.
400*4882a593Smuzhiyun          */
401*4882a593Smuzhiyun         addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]);
402*4882a593Smuzhiyun     }
403*4882a593Smuzhiyun #endif
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun     result = true;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun bail:
408*4882a593Smuzhiyun     if (!result) {
409*4882a593Smuzhiyun         mzHashTableFree(pArchive->pHash);
410*4882a593Smuzhiyun         pArchive->pHash = NULL;
411*4882a593Smuzhiyun     }
412*4882a593Smuzhiyun     return result;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun /*
416*4882a593Smuzhiyun  * Open a Zip archive and scan out the contents.
417*4882a593Smuzhiyun  *
418*4882a593Smuzhiyun  * The easiest way to do this is to mmap() the whole thing and do the
419*4882a593Smuzhiyun  * traditional backward scan for central directory.  Since the EOCD is
420*4882a593Smuzhiyun  * a relatively small bit at the end, we should end up only touching a
421*4882a593Smuzhiyun  * small set of pages.
422*4882a593Smuzhiyun  *
423*4882a593Smuzhiyun  * This will be called on non-Zip files, especially during startup, so
424*4882a593Smuzhiyun  * we don't want to be too noisy about failures.  (Do we want a "quiet"
425*4882a593Smuzhiyun  * flag?)
426*4882a593Smuzhiyun  *
427*4882a593Smuzhiyun  * On success, we fill out the contents of "pArchive".
428*4882a593Smuzhiyun  */
mzOpenZipArchive(const char * fileName,ZipArchive * pArchive)429*4882a593Smuzhiyun int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun     MemMapping map;
432*4882a593Smuzhiyun     int err;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun     LOGV("Opening archive '%s' %p\n", fileName, pArchive);
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun     map.addr = NULL;
437*4882a593Smuzhiyun     memset(pArchive, 0, sizeof(*pArchive));
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun     pArchive->fd = open(fileName, O_RDONLY, 0);
440*4882a593Smuzhiyun     if (pArchive->fd < 0) {
441*4882a593Smuzhiyun         err = errno ? errno : -1;
442*4882a593Smuzhiyun         LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
443*4882a593Smuzhiyun         goto bail;
444*4882a593Smuzhiyun     }
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun     if (sysMapFileInShmem(pArchive->fd, &map) != 0) {
447*4882a593Smuzhiyun         err = -1;
448*4882a593Smuzhiyun         LOGW("Map of '%s' failed\n", fileName);
449*4882a593Smuzhiyun         goto bail;
450*4882a593Smuzhiyun     }
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun     if (map.length < ENDHDR) {
453*4882a593Smuzhiyun         err = -1;
454*4882a593Smuzhiyun         LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
455*4882a593Smuzhiyun         goto bail;
456*4882a593Smuzhiyun     }
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun     if (!parseZipArchive(pArchive, &map)) {
459*4882a593Smuzhiyun         err = -1;
460*4882a593Smuzhiyun         LOGV("Parsing '%s' failed\n", fileName);
461*4882a593Smuzhiyun         goto bail;
462*4882a593Smuzhiyun     }
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun     err = 0;
465*4882a593Smuzhiyun     sysCopyMap(&pArchive->map, &map);
466*4882a593Smuzhiyun     map.addr = NULL;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun bail:
469*4882a593Smuzhiyun     if (err != 0)
470*4882a593Smuzhiyun         mzCloseZipArchive(pArchive);
471*4882a593Smuzhiyun     if (map.addr != NULL)
472*4882a593Smuzhiyun         sysReleaseShmem(&map);
473*4882a593Smuzhiyun     return err;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun /*
477*4882a593Smuzhiyun  * Close a ZipArchive, closing the file and freeing the contents.
478*4882a593Smuzhiyun  *
479*4882a593Smuzhiyun  * NOTE: the ZipArchive may not have been fully created.
480*4882a593Smuzhiyun  */
mzCloseZipArchive(ZipArchive * pArchive)481*4882a593Smuzhiyun void mzCloseZipArchive(ZipArchive* pArchive)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun     LOGV("Closing archive %p\n", pArchive);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun     if (pArchive->fd >= 0)
486*4882a593Smuzhiyun         close(pArchive->fd);
487*4882a593Smuzhiyun     if (pArchive->map.addr != NULL)
488*4882a593Smuzhiyun         sysReleaseShmem(&pArchive->map);
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun     free(pArchive->pEntries);
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun     mzHashTableFree(pArchive->pHash);
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun     pArchive->fd = -1;
495*4882a593Smuzhiyun     pArchive->pHash = NULL;
496*4882a593Smuzhiyun     pArchive->pEntries = NULL;
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun /*
500*4882a593Smuzhiyun  * Find a matching entry.
501*4882a593Smuzhiyun  *
502*4882a593Smuzhiyun  * Returns NULL if no matching entry found.
503*4882a593Smuzhiyun  */
mzFindZipEntry(const ZipArchive * pArchive,const char * entryName)504*4882a593Smuzhiyun const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
505*4882a593Smuzhiyun                                const char* entryName)
506*4882a593Smuzhiyun {
507*4882a593Smuzhiyun     unsigned int itemHash = computeHash(entryName, strlen(entryName));
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun     return (const ZipEntry*)mzHashTableLookup(pArchive->pHash,
510*4882a593Smuzhiyun                                               itemHash, (char*) entryName, hashcmpZipName, false);
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun /*
514*4882a593Smuzhiyun  * Return true if the entry is a symbolic link.
515*4882a593Smuzhiyun  */
mzIsZipEntrySymlink(const ZipEntry * pEntry)516*4882a593Smuzhiyun bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun     if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) {
519*4882a593Smuzhiyun         return S_ISLNK(pEntry->externalFileAttributes >> 16);
520*4882a593Smuzhiyun     }
521*4882a593Smuzhiyun     return false;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun /* Call processFunction on the uncompressed data of a STORED entry.
525*4882a593Smuzhiyun  */
processStoredEntry(const ZipArchive * pArchive,const ZipEntry * pEntry,ProcessZipEntryContentsFunction processFunction,void * cookie)526*4882a593Smuzhiyun static bool processStoredEntry(const ZipArchive *pArchive,
527*4882a593Smuzhiyun                                const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
528*4882a593Smuzhiyun                                void *cookie)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun     size_t bytesLeft = pEntry->compLen;
531*4882a593Smuzhiyun     while (bytesLeft > 0) {
532*4882a593Smuzhiyun         unsigned char buf[32 * 1024];
533*4882a593Smuzhiyun         ssize_t n;
534*4882a593Smuzhiyun         size_t count;
535*4882a593Smuzhiyun         bool ret;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun         count = bytesLeft;
538*4882a593Smuzhiyun         if (count > sizeof(buf)) {
539*4882a593Smuzhiyun             count = sizeof(buf);
540*4882a593Smuzhiyun         }
541*4882a593Smuzhiyun         n = read(pArchive->fd, buf, count);
542*4882a593Smuzhiyun         if (n < 0 || (size_t)n != count) {
543*4882a593Smuzhiyun             LOGE("Can't read %zu bytes from zip file: %ld\n", count, n);
544*4882a593Smuzhiyun             return false;
545*4882a593Smuzhiyun         }
546*4882a593Smuzhiyun         ret = processFunction(buf, n, cookie);
547*4882a593Smuzhiyun         if (!ret) {
548*4882a593Smuzhiyun             return false;
549*4882a593Smuzhiyun         }
550*4882a593Smuzhiyun         bytesLeft -= count;
551*4882a593Smuzhiyun     }
552*4882a593Smuzhiyun     return true;
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun 
processDeflatedEntry(const ZipArchive * pArchive,const ZipEntry * pEntry,ProcessZipEntryContentsFunction processFunction,void * cookie)555*4882a593Smuzhiyun static bool processDeflatedEntry(const ZipArchive *pArchive,
556*4882a593Smuzhiyun                                  const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
557*4882a593Smuzhiyun                                  void *cookie)
558*4882a593Smuzhiyun {
559*4882a593Smuzhiyun     long result = -1;
560*4882a593Smuzhiyun     unsigned char readBuf[32 * 1024];
561*4882a593Smuzhiyun     unsigned char procBuf[32 * 1024];
562*4882a593Smuzhiyun     z_stream zstream;
563*4882a593Smuzhiyun     int zerr;
564*4882a593Smuzhiyun     long compRemaining;
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun     compRemaining = pEntry->compLen;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun     /*
569*4882a593Smuzhiyun      * Initialize the zlib stream.
570*4882a593Smuzhiyun      */
571*4882a593Smuzhiyun     memset(&zstream, 0, sizeof(zstream));
572*4882a593Smuzhiyun     zstream.zalloc = Z_NULL;
573*4882a593Smuzhiyun     zstream.zfree = Z_NULL;
574*4882a593Smuzhiyun     zstream.opaque = Z_NULL;
575*4882a593Smuzhiyun     zstream.next_in = NULL;
576*4882a593Smuzhiyun     zstream.avail_in = 0;
577*4882a593Smuzhiyun     zstream.next_out = (Bytef*) procBuf;
578*4882a593Smuzhiyun     zstream.avail_out = sizeof(procBuf);
579*4882a593Smuzhiyun     zstream.data_type = Z_UNKNOWN;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun     /*
582*4882a593Smuzhiyun      * Use the undocumented "negative window bits" feature to tell zlib
583*4882a593Smuzhiyun      * that there's no zlib header waiting for it.
584*4882a593Smuzhiyun      */
585*4882a593Smuzhiyun     zerr = inflateInit2(&zstream, -MAX_WBITS);
586*4882a593Smuzhiyun     if (zerr != Z_OK) {
587*4882a593Smuzhiyun         if (zerr == Z_VERSION_ERROR) {
588*4882a593Smuzhiyun             LOGE("Installed zlib is not compatible with linked version (%s)\n",
589*4882a593Smuzhiyun                  ZLIB_VERSION);
590*4882a593Smuzhiyun         } else {
591*4882a593Smuzhiyun             LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
592*4882a593Smuzhiyun         }
593*4882a593Smuzhiyun         goto bail;
594*4882a593Smuzhiyun     }
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun     /*
597*4882a593Smuzhiyun      * Loop while we have data.
598*4882a593Smuzhiyun      */
599*4882a593Smuzhiyun     do {
600*4882a593Smuzhiyun         /* read as much as we can */
601*4882a593Smuzhiyun         if (zstream.avail_in == 0) {
602*4882a593Smuzhiyun             long getSize = (compRemaining > (long)sizeof(readBuf)) ?
603*4882a593Smuzhiyun                            (long)sizeof(readBuf) : compRemaining;
604*4882a593Smuzhiyun             LOGVV("+++ reading %ld bytes (%ld left)\n",
605*4882a593Smuzhiyun                   getSize, compRemaining);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun             int cc = read(pArchive->fd, readBuf, getSize);
608*4882a593Smuzhiyun             if (cc != (int) getSize) {
609*4882a593Smuzhiyun                 LOGW("inflate read failed (%d vs %ld)\n", cc, getSize);
610*4882a593Smuzhiyun                 goto z_bail;
611*4882a593Smuzhiyun             }
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun             compRemaining -= getSize;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun             zstream.next_in = readBuf;
616*4882a593Smuzhiyun             zstream.avail_in = getSize;
617*4882a593Smuzhiyun         }
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun         /* uncompress the data */
620*4882a593Smuzhiyun         zerr = inflate(&zstream, Z_NO_FLUSH);
621*4882a593Smuzhiyun         if (zerr != Z_OK && zerr != Z_STREAM_END) {
622*4882a593Smuzhiyun             LOGD("zlib inflate call failed (zerr=%d)\n", zerr);
623*4882a593Smuzhiyun             goto z_bail;
624*4882a593Smuzhiyun         }
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun         /* write when we're full or when we're done */
627*4882a593Smuzhiyun         if (zstream.avail_out == 0 ||
628*4882a593Smuzhiyun             (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf))) {
629*4882a593Smuzhiyun             long procSize = zstream.next_out - procBuf;
630*4882a593Smuzhiyun             LOGVV("+++ processing %d bytes\n", (int) procSize);
631*4882a593Smuzhiyun             bool ret = processFunction(procBuf, procSize, cookie);
632*4882a593Smuzhiyun             if (!ret) {
633*4882a593Smuzhiyun                 LOGW("Process function elected to fail (in inflate)\n");
634*4882a593Smuzhiyun                 goto z_bail;
635*4882a593Smuzhiyun             }
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun             zstream.next_out = procBuf;
638*4882a593Smuzhiyun             zstream.avail_out = sizeof(procBuf);
639*4882a593Smuzhiyun         }
640*4882a593Smuzhiyun     } while (zerr == Z_OK);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun     assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun     // success!
645*4882a593Smuzhiyun     result = zstream.total_out;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun z_bail:
648*4882a593Smuzhiyun     inflateEnd(&zstream);        /* free up any allocated structures */
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun bail:
651*4882a593Smuzhiyun     if (result != pEntry->uncompLen) {
652*4882a593Smuzhiyun         if (result != -1)        // error already shown?
653*4882a593Smuzhiyun             LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
654*4882a593Smuzhiyun                  result, pEntry->uncompLen);
655*4882a593Smuzhiyun         return false;
656*4882a593Smuzhiyun     }
657*4882a593Smuzhiyun     return true;
658*4882a593Smuzhiyun }
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun /*
661*4882a593Smuzhiyun  * Stream the uncompressed data through the supplied function,
662*4882a593Smuzhiyun  * passing cookie to it each time it gets called.  processFunction
663*4882a593Smuzhiyun  * may be called more than once.
664*4882a593Smuzhiyun  *
665*4882a593Smuzhiyun  * If processFunction returns false, the operation is abandoned and
666*4882a593Smuzhiyun  * mzProcessZipEntryContents() immediately returns false.
667*4882a593Smuzhiyun  *
668*4882a593Smuzhiyun  * This is useful for calculating the hash of an entry's uncompressed contents.
669*4882a593Smuzhiyun  */
mzProcessZipEntryContents(const ZipArchive * pArchive,const ZipEntry * pEntry,ProcessZipEntryContentsFunction processFunction,void * cookie)670*4882a593Smuzhiyun bool mzProcessZipEntryContents(const ZipArchive *pArchive,
671*4882a593Smuzhiyun                                const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
672*4882a593Smuzhiyun                                void *cookie)
673*4882a593Smuzhiyun {
674*4882a593Smuzhiyun     bool ret = false;
675*4882a593Smuzhiyun     off_t oldOff;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun     /* save current offset */
678*4882a593Smuzhiyun     oldOff = lseek(pArchive->fd, 0, SEEK_CUR);
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun     /* Seek to the beginning of the entry's compressed data. */
681*4882a593Smuzhiyun     lseek(pArchive->fd, pEntry->offset, SEEK_SET);
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun     switch (pEntry->compression) {
684*4882a593Smuzhiyun     case STORED:
685*4882a593Smuzhiyun         ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
686*4882a593Smuzhiyun         break;
687*4882a593Smuzhiyun     case DEFLATED:
688*4882a593Smuzhiyun         ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie);
689*4882a593Smuzhiyun         break;
690*4882a593Smuzhiyun     default:
691*4882a593Smuzhiyun         LOGE("Unsupported compression type %d for entry '%s'\n",
692*4882a593Smuzhiyun              pEntry->compression, pEntry->fileName);
693*4882a593Smuzhiyun         break;
694*4882a593Smuzhiyun     }
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun     /* restore file offset */
697*4882a593Smuzhiyun     lseek(pArchive->fd, oldOff, SEEK_SET);
698*4882a593Smuzhiyun     return ret;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun 
crcProcessFunction(const unsigned char * data,int dataLen,void * crc)701*4882a593Smuzhiyun static bool crcProcessFunction(const unsigned char *data, int dataLen,
702*4882a593Smuzhiyun                                void *crc)
703*4882a593Smuzhiyun {
704*4882a593Smuzhiyun     *(unsigned long *)crc = crc32(*(unsigned long *)crc, data, dataLen);
705*4882a593Smuzhiyun     return true;
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun /*
709*4882a593Smuzhiyun  * Check the CRC on this entry; return true if it is correct.
710*4882a593Smuzhiyun  * May do other internal checks as well.
711*4882a593Smuzhiyun  */
mzIsZipEntryIntact(const ZipArchive * pArchive,const ZipEntry * pEntry)712*4882a593Smuzhiyun bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry)
713*4882a593Smuzhiyun {
714*4882a593Smuzhiyun     unsigned long crc;
715*4882a593Smuzhiyun     bool ret;
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun     crc = crc32(0L, Z_NULL, 0);
718*4882a593Smuzhiyun     ret = mzProcessZipEntryContents(pArchive, pEntry, crcProcessFunction,
719*4882a593Smuzhiyun                                     (void *)&crc);
720*4882a593Smuzhiyun     if (!ret) {
721*4882a593Smuzhiyun         LOGE("Can't calculate CRC for entry\n");
722*4882a593Smuzhiyun         return false;
723*4882a593Smuzhiyun     }
724*4882a593Smuzhiyun     if (crc != (unsigned long)pEntry->crc32) {
725*4882a593Smuzhiyun         LOGW("CRC for entry %.*s (0x%08lx) != expected (0x%08lx)\n",
726*4882a593Smuzhiyun              pEntry->fileNameLen, pEntry->fileName, crc, pEntry->crc32);
727*4882a593Smuzhiyun         return false;
728*4882a593Smuzhiyun     }
729*4882a593Smuzhiyun     return true;
730*4882a593Smuzhiyun }
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun typedef struct {
733*4882a593Smuzhiyun     char *buf;
734*4882a593Smuzhiyun     int bufLen;
735*4882a593Smuzhiyun } CopyProcessArgs;
736*4882a593Smuzhiyun 
copyProcessFunction(const unsigned char * data,int dataLen,void * cookie)737*4882a593Smuzhiyun static bool copyProcessFunction(const unsigned char *data, int dataLen,
738*4882a593Smuzhiyun                                 void *cookie)
739*4882a593Smuzhiyun {
740*4882a593Smuzhiyun     CopyProcessArgs *args = (CopyProcessArgs *)cookie;
741*4882a593Smuzhiyun     if (dataLen <= args->bufLen) {
742*4882a593Smuzhiyun         memcpy(args->buf, data, dataLen);
743*4882a593Smuzhiyun         args->buf += dataLen;
744*4882a593Smuzhiyun         args->bufLen -= dataLen;
745*4882a593Smuzhiyun         return true;
746*4882a593Smuzhiyun     }
747*4882a593Smuzhiyun     return false;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun /*
751*4882a593Smuzhiyun  * Read an entry into a buffer allocated by the caller.
752*4882a593Smuzhiyun  */
mzReadZipEntry(const ZipArchive * pArchive,const ZipEntry * pEntry,char * buf,int bufLen)753*4882a593Smuzhiyun bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
754*4882a593Smuzhiyun                     char *buf, int bufLen)
755*4882a593Smuzhiyun {
756*4882a593Smuzhiyun     CopyProcessArgs args;
757*4882a593Smuzhiyun     bool ret;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun     args.buf = buf;
760*4882a593Smuzhiyun     args.bufLen = bufLen;
761*4882a593Smuzhiyun     ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
762*4882a593Smuzhiyun                                     (void *)&args);
763*4882a593Smuzhiyun     if (!ret) {
764*4882a593Smuzhiyun         LOGE("Can't extract entry to buffer.\n");
765*4882a593Smuzhiyun         return false;
766*4882a593Smuzhiyun     }
767*4882a593Smuzhiyun     return true;
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun 
writeProcessFunction(const unsigned char * data,int dataLen,void * cookie)770*4882a593Smuzhiyun static bool writeProcessFunction(const unsigned char *data, int dataLen,
771*4882a593Smuzhiyun                                  void *cookie)
772*4882a593Smuzhiyun {
773*4882a593Smuzhiyun     int fd = (intptr_t)cookie;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun     ssize_t soFar = 0;
776*4882a593Smuzhiyun     while (true) {
777*4882a593Smuzhiyun         ssize_t n = write(fd, data + soFar, dataLen - soFar);
778*4882a593Smuzhiyun         if (n <= 0) {
779*4882a593Smuzhiyun             LOGE("Error writing %ld bytes from zip file from %p: %s\n",
780*4882a593Smuzhiyun                  dataLen - soFar, data + soFar, strerror(errno));
781*4882a593Smuzhiyun             if (errno != EINTR) {
782*4882a593Smuzhiyun                 return false;
783*4882a593Smuzhiyun             }
784*4882a593Smuzhiyun         } else if (n > 0) {
785*4882a593Smuzhiyun             soFar += n;
786*4882a593Smuzhiyun             if (soFar == dataLen) return true;
787*4882a593Smuzhiyun             if (soFar > dataLen) {
788*4882a593Smuzhiyun                 LOGE("write overrun?  (%ld bytes instead of %d)\n",
789*4882a593Smuzhiyun                      soFar, dataLen);
790*4882a593Smuzhiyun                 return false;
791*4882a593Smuzhiyun             }
792*4882a593Smuzhiyun         }
793*4882a593Smuzhiyun     }
794*4882a593Smuzhiyun }
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun /*
797*4882a593Smuzhiyun  * Uncompress "pEntry" in "pArchive" to "fd" at the current offset.
798*4882a593Smuzhiyun  */
mzExtractZipEntryToFile(const ZipArchive * pArchive,const ZipEntry * pEntry,int fd)799*4882a593Smuzhiyun bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
800*4882a593Smuzhiyun                              const ZipEntry *pEntry, int fd)
801*4882a593Smuzhiyun {
802*4882a593Smuzhiyun     bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
803*4882a593Smuzhiyun                                          (void *)(intptr_t)fd);
804*4882a593Smuzhiyun     if (!ret) {
805*4882a593Smuzhiyun         LOGE("Can't extract entry to file.\n");
806*4882a593Smuzhiyun         return false;
807*4882a593Smuzhiyun     }
808*4882a593Smuzhiyun     return true;
809*4882a593Smuzhiyun }
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun typedef struct {
812*4882a593Smuzhiyun     unsigned char* buffer;
813*4882a593Smuzhiyun     long len;
814*4882a593Smuzhiyun } BufferExtractCookie;
815*4882a593Smuzhiyun 
bufferProcessFunction(const unsigned char * data,int dataLen,void * cookie)816*4882a593Smuzhiyun static bool bufferProcessFunction(const unsigned char *data, int dataLen,
817*4882a593Smuzhiyun                                   void *cookie)
818*4882a593Smuzhiyun {
819*4882a593Smuzhiyun     BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun     memmove(bec->buffer, data, dataLen);
822*4882a593Smuzhiyun     bec->buffer += dataLen;
823*4882a593Smuzhiyun     bec->len -= dataLen;
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun     return true;
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun /*
829*4882a593Smuzhiyun  * Uncompress "pEntry" in "pArchive" to buffer, which must be large
830*4882a593Smuzhiyun  * enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
831*4882a593Smuzhiyun  */
mzExtractZipEntryToBuffer(const ZipArchive * pArchive,const ZipEntry * pEntry,unsigned char * buffer)832*4882a593Smuzhiyun bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
833*4882a593Smuzhiyun                                const ZipEntry *pEntry, unsigned char *buffer)
834*4882a593Smuzhiyun {
835*4882a593Smuzhiyun     BufferExtractCookie bec;
836*4882a593Smuzhiyun     bec.buffer = buffer;
837*4882a593Smuzhiyun     bec.len = mzGetZipEntryUncompLen(pEntry);
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun     bool ret = mzProcessZipEntryContents(pArchive, pEntry,
840*4882a593Smuzhiyun                                          bufferProcessFunction, (void*)&bec);
841*4882a593Smuzhiyun     if (!ret || bec.len != 0) {
842*4882a593Smuzhiyun         LOGE("Can't extract entry to memory buffer.\n");
843*4882a593Smuzhiyun         return false;
844*4882a593Smuzhiyun     }
845*4882a593Smuzhiyun     return true;
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun /* Helper state to make path translation easier and less malloc-happy.
850*4882a593Smuzhiyun  */
851*4882a593Smuzhiyun typedef struct {
852*4882a593Smuzhiyun     const char *targetDir;
853*4882a593Smuzhiyun     const char *zipDir;
854*4882a593Smuzhiyun     char *buf;
855*4882a593Smuzhiyun     int targetDirLen;
856*4882a593Smuzhiyun     int zipDirLen;
857*4882a593Smuzhiyun     int bufLen;
858*4882a593Smuzhiyun } MzPathHelper;
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun /* Given the values of targetDir and zipDir in the helper,
861*4882a593Smuzhiyun  * return the target filename of the provided entry.
862*4882a593Smuzhiyun  * The helper must be initialized first.
863*4882a593Smuzhiyun  */
targetEntryPath(MzPathHelper * helper,ZipEntry * pEntry)864*4882a593Smuzhiyun static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun     int needLen;
867*4882a593Smuzhiyun     bool firstTime = (helper->buf == NULL);
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun     /* target file <-- targetDir + / + entry[zipDirLen:]
870*4882a593Smuzhiyun      */
871*4882a593Smuzhiyun     needLen = helper->targetDirLen + 1 +
872*4882a593Smuzhiyun               pEntry->fileNameLen - helper->zipDirLen + 1;
873*4882a593Smuzhiyun     if (needLen > helper->bufLen) {
874*4882a593Smuzhiyun         char *newBuf;
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun         needLen *= 2;
877*4882a593Smuzhiyun         newBuf = (char *)realloc(helper->buf, needLen);
878*4882a593Smuzhiyun         if (newBuf == NULL) {
879*4882a593Smuzhiyun             return NULL;
880*4882a593Smuzhiyun         }
881*4882a593Smuzhiyun         helper->buf = newBuf;
882*4882a593Smuzhiyun         helper->bufLen = needLen;
883*4882a593Smuzhiyun     }
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun     /* Every path will start with the target path and a slash.
886*4882a593Smuzhiyun      */
887*4882a593Smuzhiyun     if (firstTime) {
888*4882a593Smuzhiyun         char *p = helper->buf;
889*4882a593Smuzhiyun         memcpy(p, helper->targetDir, helper->targetDirLen);
890*4882a593Smuzhiyun         p += helper->targetDirLen;
891*4882a593Smuzhiyun         if (p == helper->buf || p[-1] != '/') {
892*4882a593Smuzhiyun             helper->targetDirLen += 1;
893*4882a593Smuzhiyun             *p++ = '/';
894*4882a593Smuzhiyun         }
895*4882a593Smuzhiyun     }
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun     /* Replace the custom part of the path with the appropriate
898*4882a593Smuzhiyun      * part of the entry's path.
899*4882a593Smuzhiyun      */
900*4882a593Smuzhiyun     char *epath = helper->buf + helper->targetDirLen;
901*4882a593Smuzhiyun     memcpy(epath, pEntry->fileName + helper->zipDirLen,
902*4882a593Smuzhiyun            pEntry->fileNameLen - helper->zipDirLen);
903*4882a593Smuzhiyun     epath += pEntry->fileNameLen - helper->zipDirLen;
904*4882a593Smuzhiyun     *epath = '\0';
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun     return helper->buf;
907*4882a593Smuzhiyun }
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun /*
910*4882a593Smuzhiyun  * Inflate all entries under zipDir to the directory specified by
911*4882a593Smuzhiyun  * targetDir, which must exist and be a writable directory.
912*4882a593Smuzhiyun  *
913*4882a593Smuzhiyun  * The immediate children of zipDir will become the immediate
914*4882a593Smuzhiyun  * children of targetDir; e.g., if the archive contains the entries
915*4882a593Smuzhiyun  *
916*4882a593Smuzhiyun  *     a/b/c/one
917*4882a593Smuzhiyun  *     a/b/c/two
918*4882a593Smuzhiyun  *     a/b/c/d/three
919*4882a593Smuzhiyun  *
920*4882a593Smuzhiyun  * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting
921*4882a593Smuzhiyun  * files will be
922*4882a593Smuzhiyun  *
923*4882a593Smuzhiyun  *     /tmp/one
924*4882a593Smuzhiyun  *     /tmp/two
925*4882a593Smuzhiyun  *     /tmp/d/three
926*4882a593Smuzhiyun  *
927*4882a593Smuzhiyun  * Returns true on success, false on failure.
928*4882a593Smuzhiyun  */
mzExtractRecursive(const ZipArchive * pArchive,const char * zipDir,const char * targetDir,int flags,const struct utimbuf * timestamp,void (* callback)(const char * fn,void *),void * cookie)929*4882a593Smuzhiyun bool mzExtractRecursive(const ZipArchive *pArchive,
930*4882a593Smuzhiyun                         const char *zipDir, const char *targetDir,
931*4882a593Smuzhiyun                         int flags, const struct utimbuf *timestamp,
932*4882a593Smuzhiyun                         void (*callback)(const char *fn, void *), void *cookie)
933*4882a593Smuzhiyun {
934*4882a593Smuzhiyun     if (zipDir[0] == '/') {
935*4882a593Smuzhiyun         LOGE("mzExtractRecursive(): zipDir must be a relative path.\n");
936*4882a593Smuzhiyun         return false;
937*4882a593Smuzhiyun     }
938*4882a593Smuzhiyun     if (targetDir[0] != '/') {
939*4882a593Smuzhiyun         LOGE("mzExtractRecursive(): targetDir must be an absolute path.\n");
940*4882a593Smuzhiyun         return false;
941*4882a593Smuzhiyun     }
942*4882a593Smuzhiyun 
943*4882a593Smuzhiyun     unsigned int zipDirLen;
944*4882a593Smuzhiyun     char *zpath;
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun     zipDirLen = strlen(zipDir);
947*4882a593Smuzhiyun     zpath = (char *)malloc(zipDirLen + 2);
948*4882a593Smuzhiyun     if (zpath == NULL) {
949*4882a593Smuzhiyun         LOGE("Can't allocate %d bytes for zip path\n", zipDirLen + 2);
950*4882a593Smuzhiyun         return false;
951*4882a593Smuzhiyun     }
952*4882a593Smuzhiyun     /* If zipDir is empty, we'll extract the entire zip file.
953*4882a593Smuzhiyun      * Otherwise, canonicalize the path.
954*4882a593Smuzhiyun      */
955*4882a593Smuzhiyun     if (zipDirLen > 0) {
956*4882a593Smuzhiyun         /* Make sure there's (hopefully, exactly one) slash at the
957*4882a593Smuzhiyun          * end of the path.  This way we don't need to worry about
958*4882a593Smuzhiyun          * accidentally extracting "one/twothree" when a path like
959*4882a593Smuzhiyun          * "one/two" is specified.
960*4882a593Smuzhiyun          */
961*4882a593Smuzhiyun         memcpy(zpath, zipDir, zipDirLen);
962*4882a593Smuzhiyun         if (zpath[zipDirLen - 1] != '/') {
963*4882a593Smuzhiyun             zpath[zipDirLen++] = '/';
964*4882a593Smuzhiyun         }
965*4882a593Smuzhiyun     }
966*4882a593Smuzhiyun     zpath[zipDirLen] = '\0';
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun     /* Set up the helper structure that we'll use to assemble paths.
969*4882a593Smuzhiyun      */
970*4882a593Smuzhiyun     MzPathHelper helper;
971*4882a593Smuzhiyun     helper.targetDir = targetDir;
972*4882a593Smuzhiyun     helper.targetDirLen = strlen(helper.targetDir);
973*4882a593Smuzhiyun     helper.zipDir = zpath;
974*4882a593Smuzhiyun     helper.zipDirLen = strlen(helper.zipDir);
975*4882a593Smuzhiyun     helper.buf = NULL;
976*4882a593Smuzhiyun     helper.bufLen = 0;
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun     /* Walk through the entries and extract anything whose path begins
979*4882a593Smuzhiyun      * with zpath.
980*4882a593Smuzhiyun     //TODO: since the entries are sorted, binary search for the first match
981*4882a593Smuzhiyun     //      and stop after the first non-match.
982*4882a593Smuzhiyun      */
983*4882a593Smuzhiyun     unsigned int i;
984*4882a593Smuzhiyun     bool seenMatch = false;
985*4882a593Smuzhiyun     int ok = true;
986*4882a593Smuzhiyun     for (i = 0; i < pArchive->numEntries; i++) {
987*4882a593Smuzhiyun         ZipEntry *pEntry = pArchive->pEntries + i;
988*4882a593Smuzhiyun         if (pEntry->fileNameLen < zipDirLen) {
989*4882a593Smuzhiyun //TODO: look out for a single empty directory entry that matches zpath, but
990*4882a593Smuzhiyun //      missing the trailing slash.  Most zip files seem to include
991*4882a593Smuzhiyun //      the trailing slash, but I think it's legal to leave it off.
992*4882a593Smuzhiyun //      e.g., zpath "a/b/", entry "a/b", with no children of the entry.
993*4882a593Smuzhiyun             /* No chance of matching.
994*4882a593Smuzhiyun              */
995*4882a593Smuzhiyun #if SORT_ENTRIES
996*4882a593Smuzhiyun             if (seenMatch) {
997*4882a593Smuzhiyun                 /* Since the entries are sorted, we can give up
998*4882a593Smuzhiyun                  * on the first mismatch after the first match.
999*4882a593Smuzhiyun                  */
1000*4882a593Smuzhiyun                 break;
1001*4882a593Smuzhiyun             }
1002*4882a593Smuzhiyun #endif
1003*4882a593Smuzhiyun             continue;
1004*4882a593Smuzhiyun         }
1005*4882a593Smuzhiyun         /* If zpath is empty, this strncmp() will match everything,
1006*4882a593Smuzhiyun          * which is what we want.
1007*4882a593Smuzhiyun          */
1008*4882a593Smuzhiyun         if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) {
1009*4882a593Smuzhiyun #if SORT_ENTRIES
1010*4882a593Smuzhiyun             if (seenMatch) {
1011*4882a593Smuzhiyun                 /* Since the entries are sorted, we can give up
1012*4882a593Smuzhiyun                  * on the first mismatch after the first match.
1013*4882a593Smuzhiyun                  */
1014*4882a593Smuzhiyun                 break;
1015*4882a593Smuzhiyun             }
1016*4882a593Smuzhiyun #endif
1017*4882a593Smuzhiyun             continue;
1018*4882a593Smuzhiyun         }
1019*4882a593Smuzhiyun         /* This entry begins with zipDir, so we'll extract it.
1020*4882a593Smuzhiyun          */
1021*4882a593Smuzhiyun         seenMatch = true;
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun         /* Find the target location of the entry.
1024*4882a593Smuzhiyun          */
1025*4882a593Smuzhiyun         const char *targetFile = targetEntryPath(&helper, pEntry);
1026*4882a593Smuzhiyun         if (targetFile == NULL) {
1027*4882a593Smuzhiyun             LOGE("Can't assemble target path for \"%.*s\"\n",
1028*4882a593Smuzhiyun                  pEntry->fileNameLen, pEntry->fileName);
1029*4882a593Smuzhiyun             ok = false;
1030*4882a593Smuzhiyun             break;
1031*4882a593Smuzhiyun         }
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun         /* With DRY_RUN set, invoke the callback but don't do anything else.
1034*4882a593Smuzhiyun          */
1035*4882a593Smuzhiyun         if (flags & MZ_EXTRACT_DRY_RUN) {
1036*4882a593Smuzhiyun             if (callback != NULL) callback(targetFile, cookie);
1037*4882a593Smuzhiyun             continue;
1038*4882a593Smuzhiyun         }
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun         /* Create the file or directory.
1041*4882a593Smuzhiyun          */
1042*4882a593Smuzhiyun #define UNZIP_DIRMODE 0755
1043*4882a593Smuzhiyun #define UNZIP_FILEMODE 0644
1044*4882a593Smuzhiyun         if (pEntry->fileName[pEntry->fileNameLen - 1] == '/') {
1045*4882a593Smuzhiyun             if (!(flags & MZ_EXTRACT_FILES_ONLY)) {
1046*4882a593Smuzhiyun                 int ret = dirCreateHierarchy(
1047*4882a593Smuzhiyun                               targetFile, UNZIP_DIRMODE, timestamp, false);
1048*4882a593Smuzhiyun                 if (ret != 0) {
1049*4882a593Smuzhiyun                     LOGE("Can't create containing directory for \"%s\": %s\n",
1050*4882a593Smuzhiyun                          targetFile, strerror(errno));
1051*4882a593Smuzhiyun                     ok = false;
1052*4882a593Smuzhiyun                     break;
1053*4882a593Smuzhiyun                 }
1054*4882a593Smuzhiyun                 LOGD("Extracted dir \"%s\"\n", targetFile);
1055*4882a593Smuzhiyun             }
1056*4882a593Smuzhiyun         } else {
1057*4882a593Smuzhiyun             /* This is not a directory.  First, make sure that
1058*4882a593Smuzhiyun              * the containing directory exists.
1059*4882a593Smuzhiyun              */
1060*4882a593Smuzhiyun             int ret = dirCreateHierarchy(
1061*4882a593Smuzhiyun                           targetFile, UNZIP_DIRMODE, timestamp, true);
1062*4882a593Smuzhiyun             if (ret != 0) {
1063*4882a593Smuzhiyun                 LOGE("Can't create containing directory for \"%s\": %s\n",
1064*4882a593Smuzhiyun                      targetFile, strerror(errno));
1065*4882a593Smuzhiyun                 ok = false;
1066*4882a593Smuzhiyun                 break;
1067*4882a593Smuzhiyun             }
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun             /* With FILES_ONLY set, we need to ignore metadata entirely,
1070*4882a593Smuzhiyun              * so treat symlinks as regular files.
1071*4882a593Smuzhiyun              */
1072*4882a593Smuzhiyun             if (!(flags & MZ_EXTRACT_FILES_ONLY) && mzIsZipEntrySymlink(pEntry)) {
1073*4882a593Smuzhiyun                 /* The entry is a symbolic link.
1074*4882a593Smuzhiyun                  * The relative target of the symlink is in the
1075*4882a593Smuzhiyun                  * data section of this entry.
1076*4882a593Smuzhiyun                  */
1077*4882a593Smuzhiyun                 if (pEntry->uncompLen == 0) {
1078*4882a593Smuzhiyun                     LOGE("Symlink entry \"%s\" has no target\n",
1079*4882a593Smuzhiyun                          targetFile);
1080*4882a593Smuzhiyun                     ok = false;
1081*4882a593Smuzhiyun                     break;
1082*4882a593Smuzhiyun                 }
1083*4882a593Smuzhiyun                 char *linkTarget = malloc(pEntry->uncompLen + 1);
1084*4882a593Smuzhiyun                 if (linkTarget == NULL) {
1085*4882a593Smuzhiyun                     ok = false;
1086*4882a593Smuzhiyun                     break;
1087*4882a593Smuzhiyun                 }
1088*4882a593Smuzhiyun                 ok = mzReadZipEntry(pArchive, pEntry, linkTarget,
1089*4882a593Smuzhiyun                                     pEntry->uncompLen);
1090*4882a593Smuzhiyun                 if (!ok) {
1091*4882a593Smuzhiyun                     LOGE("Can't read symlink target for \"%s\"\n",
1092*4882a593Smuzhiyun                          targetFile);
1093*4882a593Smuzhiyun                     free(linkTarget);
1094*4882a593Smuzhiyun                     break;
1095*4882a593Smuzhiyun                 }
1096*4882a593Smuzhiyun                 linkTarget[pEntry->uncompLen] = '\0';
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun                 /* Make the link.
1099*4882a593Smuzhiyun                  */
1100*4882a593Smuzhiyun                 ret = symlink(linkTarget, targetFile);
1101*4882a593Smuzhiyun                 if (ret != 0) {
1102*4882a593Smuzhiyun                     LOGE("Can't symlink \"%s\" to \"%s\": %s\n",
1103*4882a593Smuzhiyun                          targetFile, linkTarget, strerror(errno));
1104*4882a593Smuzhiyun                     free(linkTarget);
1105*4882a593Smuzhiyun                     ok = false;
1106*4882a593Smuzhiyun                     break;
1107*4882a593Smuzhiyun                 }
1108*4882a593Smuzhiyun                 LOGD("Extracted symlink \"%s\" -> \"%s\"\n",
1109*4882a593Smuzhiyun                      targetFile, linkTarget);
1110*4882a593Smuzhiyun                 free(linkTarget);
1111*4882a593Smuzhiyun             } else {
1112*4882a593Smuzhiyun                 /* The entry is a regular file.
1113*4882a593Smuzhiyun                  * Open the target for writing.
1114*4882a593Smuzhiyun                  */
1115*4882a593Smuzhiyun                 int fd = creat(targetFile, UNZIP_FILEMODE);
1116*4882a593Smuzhiyun                 if (fd < 0) {
1117*4882a593Smuzhiyun                     LOGE("Can't create target file \"%s\": %s\n",
1118*4882a593Smuzhiyun                          targetFile, strerror(errno));
1119*4882a593Smuzhiyun                     ok = false;
1120*4882a593Smuzhiyun                     break;
1121*4882a593Smuzhiyun                 }
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun                 bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
1124*4882a593Smuzhiyun                 close(fd);
1125*4882a593Smuzhiyun                 if (!ok) {
1126*4882a593Smuzhiyun                     LOGE("Error extracting \"%s\"\n", targetFile);
1127*4882a593Smuzhiyun                     ok = false;
1128*4882a593Smuzhiyun                     break;
1129*4882a593Smuzhiyun                 }
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun                 if (timestamp != NULL && utime(targetFile, timestamp)) {
1132*4882a593Smuzhiyun                     LOGE("Error touching \"%s\"\n", targetFile);
1133*4882a593Smuzhiyun                     ok = false;
1134*4882a593Smuzhiyun                     break;
1135*4882a593Smuzhiyun                 }
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun                 LOGD("Extracted file \"%s\"\n", targetFile);
1138*4882a593Smuzhiyun             }
1139*4882a593Smuzhiyun         }
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun         if (callback != NULL) callback(targetFile, cookie);
1142*4882a593Smuzhiyun     }
1143*4882a593Smuzhiyun 
1144*4882a593Smuzhiyun     free(helper.buf);
1145*4882a593Smuzhiyun     free(zpath);
1146*4882a593Smuzhiyun 
1147*4882a593Smuzhiyun     return ok;
1148*4882a593Smuzhiyun }
1149