blob: d557daa7f63d378038c5156da95187193c02f433 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Simple Zip file support.
5 */
6#include "safe_iop.h"
7#include "zlib.h"
8
9#include <errno.h>
10#include <fcntl.h>
11#include <limits.h>
12#include <stdint.h> // for uintptr_t
13#include <stdlib.h>
14#include <sys/stat.h> // for S_ISLNK()
15#include <unistd.h>
16
17#define LOG_TAG "minzip"
18#include "Zip.h"
19#include "Bits.h"
20#include "Log.h"
21#include "DirUtil.h"
22
23#undef NDEBUG // do this after including Log.h
24#include <assert.h>
25
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070026#include <selinux/label.h>
27#include <selinux/selinux.h>
28
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080029#define SORT_ENTRIES 1
30
31/*
32 * Offset and length constants (java.util.zip naming convention).
33 */
34enum {
35 CENSIG = 0x02014b50, // PK12
36 CENHDR = 46,
37
38 CENVEM = 4,
39 CENVER = 6,
40 CENFLG = 8,
41 CENHOW = 10,
42 CENTIM = 12,
43 CENCRC = 16,
44 CENSIZ = 20,
45 CENLEN = 24,
46 CENNAM = 28,
Doug Zongker596271f2009-04-29 16:52:04 -070047 CENEXT = 30,
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080048 CENCOM = 32,
49 CENDSK = 34,
50 CENATT = 36,
51 CENATX = 38,
52 CENOFF = 42,
53
54 ENDSIG = 0x06054b50, // PK56
55 ENDHDR = 22,
56
57 ENDSUB = 8,
58 ENDTOT = 10,
59 ENDSIZ = 12,
60 ENDOFF = 16,
61 ENDCOM = 20,
62
63 EXTSIG = 0x08074b50, // PK78
64 EXTHDR = 16,
65
66 EXTCRC = 4,
67 EXTSIZ = 8,
68 EXTLEN = 12,
69
70 LOCSIG = 0x04034b50, // PK34
71 LOCHDR = 30,
Doug Zongker596271f2009-04-29 16:52:04 -070072
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080073 LOCVER = 4,
74 LOCFLG = 6,
75 LOCHOW = 8,
76 LOCTIM = 10,
77 LOCCRC = 14,
Doug Zongker596271f2009-04-29 16:52:04 -070078 LOCSIZ = 18,
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080079 LOCLEN = 22,
80 LOCNAM = 26,
81 LOCEXT = 28,
82
83 STORED = 0,
84 DEFLATED = 8,
85
86 CENVEM_UNIX = 3 << 8, // the high byte of CENVEM
87};
88
89
90/*
91 * For debugging, dump the contents of a ZipEntry.
92 */
93#if 0
94static void dumpEntry(const ZipEntry* pEntry)
95{
96 LOGI(" %p '%.*s'\n", pEntry->fileName,pEntry->fileNameLen,pEntry->fileName);
caozhiyuan2584f9c2016-03-11 15:18:29 +080097 LOGI(" off=%u comp=%u uncomp=%u how=%d\n", pEntry->offset,
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080098 pEntry->compLen, pEntry->uncompLen, pEntry->compression);
99}
100#endif
101
102/*
103 * (This is a mzHashTableLookup callback.)
104 *
105 * Compare two ZipEntry structs, by name.
106 */
107static int hashcmpZipEntry(const void* ventry1, const void* ventry2)
108{
109 const ZipEntry* entry1 = (const ZipEntry*) ventry1;
110 const ZipEntry* entry2 = (const ZipEntry*) ventry2;
111
112 if (entry1->fileNameLen != entry2->fileNameLen)
113 return entry1->fileNameLen - entry2->fileNameLen;
114 return memcmp(entry1->fileName, entry2->fileName, entry1->fileNameLen);
115}
116
117/*
118 * (This is a mzHashTableLookup callback.)
119 *
120 * find a ZipEntry struct by name.
121 */
122static int hashcmpZipName(const void* ventry, const void* vname)
123{
124 const ZipEntry* entry = (const ZipEntry*) ventry;
125 const char* name = (const char*) vname;
126 unsigned int nameLen = strlen(name);
127
128 if (entry->fileNameLen != nameLen)
129 return entry->fileNameLen - nameLen;
130 return memcmp(entry->fileName, name, nameLen);
131}
132
133/*
134 * Compute the hash code for a ZipEntry filename.
135 *
136 * Not expected to be compatible with any other hash function, so we init
137 * to 2 to ensure it doesn't happen to match.
138 */
139static unsigned int computeHash(const char* name, int nameLen)
140{
141 unsigned int hash = 2;
142
143 while (nameLen--)
144 hash = hash * 31 + *name++;
145
146 return hash;
147}
148
149static void addEntryToHashTable(HashTable* pHash, ZipEntry* pEntry)
150{
151 unsigned int itemHash = computeHash(pEntry->fileName, pEntry->fileNameLen);
152 const ZipEntry* found;
153
154 found = (const ZipEntry*)mzHashTableLookup(pHash,
155 itemHash, pEntry, hashcmpZipEntry, true);
156 if (found != pEntry) {
157 LOGW("WARNING: duplicate entry '%.*s' in Zip\n",
158 found->fileNameLen, found->fileName);
159 /* keep going */
160 }
161}
162
163static int validFilename(const char *fileName, unsigned int fileNameLen)
164{
165 // Forbid super long filenames.
166 if (fileNameLen >= PATH_MAX) {
167 LOGW("Filename too long (%d chatacters)\n", fileNameLen);
168 return 0;
169 }
170
171 // Require all characters to be printable ASCII (no NUL, no UTF-8, etc).
172 unsigned int i;
173 for (i = 0; i < fileNameLen; ++i) {
174 if (fileName[i] < 32 || fileName[i] >= 127) {
175 LOGW("Filename contains invalid character '\%03o'\n", fileName[i]);
176 return 0;
177 }
178 }
179
180 return 1;
181}
182
183/*
184 * Parse the contents of a Zip archive. After confirming that the file
185 * is in fact a Zip, we scan out the contents of the central directory and
186 * store it in a hash table.
187 *
188 * Returns "true" on success.
189 */
Doug Zongker99916f02014-01-13 14:16:58 -0800190static bool parseZipArchive(ZipArchive* pArchive)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800191{
192 bool result = false;
193 const unsigned char* ptr;
194 unsigned int i, numEntries, cdOffset;
195 unsigned int val;
196
197 /*
198 * The first 4 bytes of the file will either be the local header
199 * signature for the first file (LOCSIG) or, if the archive doesn't
200 * have any files in it, the end-of-central-directory signature (ENDSIG).
201 */
Doug Zongker99916f02014-01-13 14:16:58 -0800202 val = get4LE(pArchive->addr);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800203 if (val == ENDSIG) {
Tao Baodd4d9812015-07-15 14:13:06 -0700204 LOGW("Found Zip archive, but it looks empty\n");
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800205 goto bail;
206 } else if (val != LOCSIG) {
Tao Baodd4d9812015-07-15 14:13:06 -0700207 LOGW("Not a Zip archive (found 0x%08x)\n", val);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800208 goto bail;
209 }
210
211 /*
212 * Find the EOCD. We'll find it immediately unless they have a file
213 * comment.
214 */
Doug Zongker99916f02014-01-13 14:16:58 -0800215 ptr = pArchive->addr + pArchive->length - ENDHDR;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800216
Doug Zongker99916f02014-01-13 14:16:58 -0800217 while (ptr >= (const unsigned char*) pArchive->addr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800218 if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
219 break;
220 ptr--;
221 }
Doug Zongker99916f02014-01-13 14:16:58 -0800222 if (ptr < (const unsigned char*) pArchive->addr) {
Tao Baodd4d9812015-07-15 14:13:06 -0700223 LOGW("Could not find end-of-central-directory in Zip\n");
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800224 goto bail;
225 }
226
227 /*
228 * There are two interesting items in the EOCD block: the number of
229 * entries in the file, and the file offset of the start of the
230 * central directory.
231 */
232 numEntries = get2LE(ptr + ENDSUB);
233 cdOffset = get4LE(ptr + ENDOFF);
234
235 LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
Doug Zongker99916f02014-01-13 14:16:58 -0800236 if (numEntries == 0 || cdOffset >= pArchive->length) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800237 LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
Doug Zongker99916f02014-01-13 14:16:58 -0800238 numEntries, cdOffset, pArchive->length);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800239 goto bail;
240 }
241
242 /*
243 * Create data structures to hold entries.
244 */
245 pArchive->numEntries = numEntries;
246 pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry));
247 pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL);
248 if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
249 goto bail;
250
Doug Zongker99916f02014-01-13 14:16:58 -0800251 ptr = pArchive->addr + cdOffset;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800252 for (i = 0; i < numEntries; i++) {
253 ZipEntry* pEntry;
254 unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
255 const unsigned char* localHdr;
256 const char *fileName;
257
Doug Zongker99916f02014-01-13 14:16:58 -0800258 if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800259 LOGW("Ran off the end (at %d)\n", i);
260 goto bail;
261 }
262 if (get4LE(ptr) != CENSIG) {
263 LOGW("Missed a central dir sig (at %d)\n", i);
264 goto bail;
265 }
266
267 localHdrOffset = get4LE(ptr + CENOFF);
268 fileNameLen = get2LE(ptr + CENNAM);
269 extraLen = get2LE(ptr + CENEXT);
270 commentLen = get2LE(ptr + CENCOM);
271 fileName = (const char*)ptr + CENHDR;
Doug Zongker99916f02014-01-13 14:16:58 -0800272 if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800273 LOGW("Filename ran off the end (at %d)\n", i);
274 goto bail;
275 }
276 if (!validFilename(fileName, fileNameLen)) {
277 LOGW("Invalid filename (at %d)\n", i);
278 goto bail;
279 }
280
281#if SORT_ENTRIES
282 /* Figure out where this entry should go (binary search).
283 */
284 if (i > 0) {
285 int low, high;
286
287 low = 0;
288 high = i - 1;
289 while (low <= high) {
290 int mid;
291 int diff;
292 int diffLen;
293
294 mid = low + ((high - low) / 2); // avoid overflow
295
296 if (pArchive->pEntries[mid].fileNameLen < fileNameLen) {
297 diffLen = pArchive->pEntries[mid].fileNameLen;
298 } else {
299 diffLen = fileNameLen;
300 }
301 diff = strncmp(pArchive->pEntries[mid].fileName, fileName,
302 diffLen);
303 if (diff == 0) {
304 diff = pArchive->pEntries[mid].fileNameLen - fileNameLen;
305 }
306 if (diff < 0) {
307 low = mid + 1;
308 } else if (diff > 0) {
309 high = mid - 1;
310 } else {
311 high = mid;
312 break;
313 }
314 }
315
316 unsigned int target = high + 1;
317 assert(target <= i);
318 if (target != i) {
319 /* It belongs somewhere other than at the end of
320 * the list. Make some room at [target].
321 */
322 memmove(pArchive->pEntries + target + 1,
323 pArchive->pEntries + target,
324 (i - target) * sizeof(ZipEntry));
325 }
326 pEntry = &pArchive->pEntries[target];
327 } else {
328 pEntry = &pArchive->pEntries[0];
329 }
330#else
331 pEntry = &pArchive->pEntries[i];
332#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800333 pEntry->fileNameLen = fileNameLen;
334 pEntry->fileName = fileName;
335
336 pEntry->compLen = get4LE(ptr + CENSIZ);
337 pEntry->uncompLen = get4LE(ptr + CENLEN);
338 pEntry->compression = get2LE(ptr + CENHOW);
339 pEntry->modTime = get4LE(ptr + CENTIM);
340 pEntry->crc32 = get4LE(ptr + CENCRC);
341
342 /* These two are necessary for finding the mode of the file.
343 */
344 pEntry->versionMadeBy = get2LE(ptr + CENVEM);
345 if ((pEntry->versionMadeBy & 0xff00) != 0 &&
346 (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX)
347 {
348 LOGW("Incompatible \"version made by\": 0x%02x (at %d)\n",
349 pEntry->versionMadeBy >> 8, i);
350 goto bail;
351 }
352 pEntry->externalFileAttributes = get4LE(ptr + CENATX);
353
Doug Zongker99916f02014-01-13 14:16:58 -0800354 // Perform pArchive->addr + localHdrOffset, ensuring that it won't
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800355 // overflow. This is needed because localHdrOffset is untrusted.
Doug Zongker99916f02014-01-13 14:16:58 -0800356 if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr,
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800357 (uintptr_t)localHdrOffset)) {
358 LOGW("Integer overflow adding in parseZipArchive\n");
359 goto bail;
360 }
361 if ((uintptr_t)localHdr + LOCHDR >
Doug Zongker99916f02014-01-13 14:16:58 -0800362 (uintptr_t)pArchive->addr + pArchive->length) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800363 LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
364 goto bail;
365 }
366 if (get4LE(localHdr) != LOCSIG) {
367 LOGW("Missed a local header sig (at %d)\n", i);
368 goto bail;
369 }
370 pEntry->offset = localHdrOffset + LOCHDR
371 + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT);
372 if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) {
373 LOGW("Integer overflow adding in parseZipArchive\n");
374 goto bail;
375 }
Doug Zongker99916f02014-01-13 14:16:58 -0800376 if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800377 LOGW("Data ran off the end (at %d)\n", i);
378 goto bail;
379 }
380
381#if !SORT_ENTRIES
382 /* Add to hash table; no need to lock here.
383 * Can't do this now if we're sorting, because entries
384 * will move around.
385 */
386 addEntryToHashTable(pArchive->pHash, pEntry);
387#endif
388
389 //dumpEntry(pEntry);
390 ptr += CENHDR + fileNameLen + extraLen + commentLen;
391 }
392
393#if SORT_ENTRIES
394 /* If we're sorting, we have to wait until all entries
395 * are in their final places, otherwise the pointers will
396 * probably point to the wrong things.
397 */
398 for (i = 0; i < numEntries; i++) {
399 /* Add to hash table; no need to lock here.
400 */
401 addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]);
402 }
403#endif
404
405 result = true;
406
407bail:
408 if (!result) {
409 mzHashTableFree(pArchive->pHash);
410 pArchive->pHash = NULL;
411 }
412 return result;
413}
414
415/*
416 * Open a Zip archive and scan out the contents.
417 *
418 * The easiest way to do this is to mmap() the whole thing and do the
419 * traditional backward scan for central directory. Since the EOCD is
420 * a relatively small bit at the end, we should end up only touching a
421 * small set of pages.
422 *
423 * This will be called on non-Zip files, especially during startup, so
424 * we don't want to be too noisy about failures. (Do we want a "quiet"
425 * flag?)
426 *
427 * On success, we fill out the contents of "pArchive".
428 */
Doug Zongker99916f02014-01-13 14:16:58 -0800429int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800430{
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800431 int err;
432
Doug Zongker99916f02014-01-13 14:16:58 -0800433 if (length < ENDHDR) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800434 err = -1;
Tao Baodd4d9812015-07-15 14:13:06 -0700435 LOGW("Archive %p is too small to be zip (%zd)\n", pArchive, length);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800436 goto bail;
437 }
438
Doug Zongker99916f02014-01-13 14:16:58 -0800439 pArchive->addr = addr;
440 pArchive->length = length;
441
442 if (!parseZipArchive(pArchive)) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800443 err = -1;
Tao Baodd4d9812015-07-15 14:13:06 -0700444 LOGW("Parsing archive %p failed\n", pArchive);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800445 goto bail;
446 }
447
448 err = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800449
450bail:
451 if (err != 0)
452 mzCloseZipArchive(pArchive);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800453 return err;
454}
455
456/*
457 * Close a ZipArchive, closing the file and freeing the contents.
458 *
459 * NOTE: the ZipArchive may not have been fully created.
460 */
461void mzCloseZipArchive(ZipArchive* pArchive)
462{
463 LOGV("Closing archive %p\n", pArchive);
464
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800465 free(pArchive->pEntries);
466
467 mzHashTableFree(pArchive->pHash);
468
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800469 pArchive->pHash = NULL;
470 pArchive->pEntries = NULL;
471}
472
473/*
474 * Find a matching entry.
475 *
476 * Returns NULL if no matching entry found.
477 */
478const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
479 const char* entryName)
480{
481 unsigned int itemHash = computeHash(entryName, strlen(entryName));
482
483 return (const ZipEntry*)mzHashTableLookup(pArchive->pHash,
484 itemHash, (char*) entryName, hashcmpZipName, false);
485}
486
487/*
488 * Return true if the entry is a symbolic link.
489 */
Narayan Kamath3e700cf2015-02-23 13:29:16 +0000490static bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800491{
492 if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) {
493 return S_ISLNK(pEntry->externalFileAttributes >> 16);
494 }
495 return false;
496}
497
498/* Call processFunction on the uncompressed data of a STORED entry.
499 */
500static bool processStoredEntry(const ZipArchive *pArchive,
501 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
502 void *cookie)
503{
Doug Zongker99916f02014-01-13 14:16:58 -0800504 return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800505}
506
507static bool processDeflatedEntry(const ZipArchive *pArchive,
508 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
509 void *cookie)
510{
caozhiyuan2584f9c2016-03-11 15:18:29 +0800511 bool success = false;
512 unsigned long totalOut = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800513 unsigned char procBuf[32 * 1024];
514 z_stream zstream;
515 int zerr;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800516
517 /*
518 * Initialize the zlib stream.
519 */
520 memset(&zstream, 0, sizeof(zstream));
521 zstream.zalloc = Z_NULL;
522 zstream.zfree = Z_NULL;
523 zstream.opaque = Z_NULL;
Doug Zongker99916f02014-01-13 14:16:58 -0800524 zstream.next_in = pArchive->addr + pEntry->offset;
525 zstream.avail_in = pEntry->compLen;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800526 zstream.next_out = (Bytef*) procBuf;
527 zstream.avail_out = sizeof(procBuf);
528 zstream.data_type = Z_UNKNOWN;
529
530 /*
531 * Use the undocumented "negative window bits" feature to tell zlib
532 * that there's no zlib header waiting for it.
533 */
534 zerr = inflateInit2(&zstream, -MAX_WBITS);
535 if (zerr != Z_OK) {
536 if (zerr == Z_VERSION_ERROR) {
537 LOGE("Installed zlib is not compatible with linked version (%s)\n",
538 ZLIB_VERSION);
539 } else {
540 LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
541 }
542 goto bail;
543 }
544
545 /*
546 * Loop while we have data.
547 */
548 do {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800549 /* uncompress the data */
550 zerr = inflate(&zstream, Z_NO_FLUSH);
551 if (zerr != Z_OK && zerr != Z_STREAM_END) {
Tao Baodd4d9812015-07-15 14:13:06 -0700552 LOGW("zlib inflate call failed (zerr=%d)\n", zerr);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800553 goto z_bail;
554 }
555
556 /* write when we're full or when we're done */
557 if (zstream.avail_out == 0 ||
558 (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf)))
559 {
560 long procSize = zstream.next_out - procBuf;
561 LOGVV("+++ processing %d bytes\n", (int) procSize);
562 bool ret = processFunction(procBuf, procSize, cookie);
563 if (!ret) {
564 LOGW("Process function elected to fail (in inflate)\n");
565 goto z_bail;
566 }
567
568 zstream.next_out = procBuf;
569 zstream.avail_out = sizeof(procBuf);
570 }
571 } while (zerr == Z_OK);
572
573 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
574
575 // success!
caozhiyuan2584f9c2016-03-11 15:18:29 +0800576 totalOut = zstream.total_out;
577 success = true;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800578
579z_bail:
580 inflateEnd(&zstream); /* free up any allocated structures */
581
582bail:
caozhiyuan2584f9c2016-03-11 15:18:29 +0800583 if (totalOut != pEntry->uncompLen) {
584 if (success) { // error already shown?
585 LOGW("Size mismatch on inflated file (%lu vs %u)\n", totalOut, pEntry->uncompLen);
586 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800587 return false;
588 }
589 return true;
590}
591
592/*
593 * Stream the uncompressed data through the supplied function,
594 * passing cookie to it each time it gets called. processFunction
595 * may be called more than once.
596 *
597 * If processFunction returns false, the operation is abandoned and
598 * mzProcessZipEntryContents() immediately returns false.
599 *
600 * This is useful for calculating the hash of an entry's uncompressed contents.
601 */
602bool mzProcessZipEntryContents(const ZipArchive *pArchive,
603 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
604 void *cookie)
605{
606 bool ret = false;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800607
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800608 switch (pEntry->compression) {
609 case STORED:
610 ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
611 break;
612 case DEFLATED:
613 ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie);
614 break;
615 default:
616 LOGE("Unsupported compression type %d for entry '%s'\n",
617 pEntry->compression, pEntry->fileName);
618 break;
619 }
620
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800621 return ret;
622}
623
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800624typedef struct {
625 char *buf;
626 int bufLen;
627} CopyProcessArgs;
628
629static bool copyProcessFunction(const unsigned char *data, int dataLen,
630 void *cookie)
631{
632 CopyProcessArgs *args = (CopyProcessArgs *)cookie;
633 if (dataLen <= args->bufLen) {
634 memcpy(args->buf, data, dataLen);
635 args->buf += dataLen;
636 args->bufLen -= dataLen;
637 return true;
638 }
639 return false;
640}
641
642/*
643 * Read an entry into a buffer allocated by the caller.
644 */
645bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
646 char *buf, int bufLen)
647{
648 CopyProcessArgs args;
649 bool ret;
Doug Zongker596271f2009-04-29 16:52:04 -0700650
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800651 args.buf = buf;
652 args.bufLen = bufLen;
653 ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
654 (void *)&args);
655 if (!ret) {
656 LOGE("Can't extract entry to buffer.\n");
657 return false;
658 }
659 return true;
660}
661
662static bool writeProcessFunction(const unsigned char *data, int dataLen,
Doug Zongker683c4622009-05-05 17:50:21 -0700663 void *cookie)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800664{
Colin Cross92cdf9c2014-02-05 17:30:31 -0800665 int fd = (int)(intptr_t)cookie;
Michael Rungef5d9f892014-05-06 16:54:42 -0700666 if (dataLen == 0) {
667 return true;
668 }
Doug Zongker596271f2009-04-29 16:52:04 -0700669 ssize_t soFar = 0;
Doug Zongker683c4622009-05-05 17:50:21 -0700670 while (true) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700671 ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar));
Doug Zongker683c4622009-05-05 17:50:21 -0700672 if (n <= 0) {
Doug Zongkera9300302014-02-10 12:35:19 -0800673 LOGE("Error writing %zd bytes from zip file from %p: %s\n",
Doug Zongker683c4622009-05-05 17:50:21 -0700674 dataLen-soFar, data+soFar, strerror(errno));
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700675 return false;
Doug Zongker596271f2009-04-29 16:52:04 -0700676 } else if (n > 0) {
677 soFar += n;
678 if (soFar == dataLen) return true;
679 if (soFar > dataLen) {
Doug Zongkera9300302014-02-10 12:35:19 -0800680 LOGE("write overrun? (%zd bytes instead of %d)\n",
Doug Zongker596271f2009-04-29 16:52:04 -0700681 soFar, dataLen);
682 return false;
683 }
Doug Zongker596271f2009-04-29 16:52:04 -0700684 }
Doug Zongker683c4622009-05-05 17:50:21 -0700685 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800686}
687
688/*
689 * Uncompress "pEntry" in "pArchive" to "fd" at the current offset.
690 */
691bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
Doug Zongker1c4ceae2009-05-08 09:43:28 -0700692 const ZipEntry *pEntry, int fd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800693{
694 bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
Colin Cross92cdf9c2014-02-05 17:30:31 -0800695 (void*)(intptr_t)fd);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800696 if (!ret) {
697 LOGE("Can't extract entry to file.\n");
698 return false;
699 }
700 return true;
701}
702
Doug Zongker6aece332010-02-01 14:40:12 -0800703typedef struct {
704 unsigned char* buffer;
705 long len;
706} BufferExtractCookie;
707
708static bool bufferProcessFunction(const unsigned char *data, int dataLen,
709 void *cookie) {
710 BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
711
712 memmove(bec->buffer, data, dataLen);
713 bec->buffer += dataLen;
714 bec->len -= dataLen;
715
716 return true;
717}
718
719/*
720 * Uncompress "pEntry" in "pArchive" to buffer, which must be large
721 * enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
722 */
723bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
724 const ZipEntry *pEntry, unsigned char *buffer)
725{
726 BufferExtractCookie bec;
727 bec.buffer = buffer;
728 bec.len = mzGetZipEntryUncompLen(pEntry);
729
730 bool ret = mzProcessZipEntryContents(pArchive, pEntry,
731 bufferProcessFunction, (void*)&bec);
732 if (!ret || bec.len != 0) {
733 LOGE("Can't extract entry to memory buffer.\n");
734 return false;
735 }
736 return true;
737}
738
739
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800740/* Helper state to make path translation easier and less malloc-happy.
741 */
742typedef struct {
743 const char *targetDir;
744 const char *zipDir;
745 char *buf;
746 int targetDirLen;
747 int zipDirLen;
748 int bufLen;
749} MzPathHelper;
750
751/* Given the values of targetDir and zipDir in the helper,
752 * return the target filename of the provided entry.
753 * The helper must be initialized first.
754 */
755static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry)
756{
757 int needLen;
758 bool firstTime = (helper->buf == NULL);
759
760 /* target file <-- targetDir + / + entry[zipDirLen:]
761 */
762 needLen = helper->targetDirLen + 1 +
763 pEntry->fileNameLen - helper->zipDirLen + 1;
Tianjie Xu66296552016-02-22 19:49:38 -0800764 if (firstTime || needLen > helper->bufLen) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800765 char *newBuf;
766
767 needLen *= 2;
768 newBuf = (char *)realloc(helper->buf, needLen);
769 if (newBuf == NULL) {
770 return NULL;
771 }
772 helper->buf = newBuf;
773 helper->bufLen = needLen;
774 }
775
776 /* Every path will start with the target path and a slash.
777 */
778 if (firstTime) {
779 char *p = helper->buf;
780 memcpy(p, helper->targetDir, helper->targetDirLen);
781 p += helper->targetDirLen;
782 if (p == helper->buf || p[-1] != '/') {
783 helper->targetDirLen += 1;
784 *p++ = '/';
785 }
786 }
787
788 /* Replace the custom part of the path with the appropriate
789 * part of the entry's path.
790 */
791 char *epath = helper->buf + helper->targetDirLen;
792 memcpy(epath, pEntry->fileName + helper->zipDirLen,
793 pEntry->fileNameLen - helper->zipDirLen);
794 epath += pEntry->fileNameLen - helper->zipDirLen;
795 *epath = '\0';
796
797 return helper->buf;
798}
799
800/*
801 * Inflate all entries under zipDir to the directory specified by
802 * targetDir, which must exist and be a writable directory.
803 *
804 * The immediate children of zipDir will become the immediate
805 * children of targetDir; e.g., if the archive contains the entries
806 *
807 * a/b/c/one
808 * a/b/c/two
809 * a/b/c/d/three
810 *
811 * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting
812 * files will be
813 *
814 * /tmp/one
815 * /tmp/two
816 * /tmp/d/three
817 *
818 * Returns true on success, false on failure.
819 */
820bool mzExtractRecursive(const ZipArchive *pArchive,
821 const char *zipDir, const char *targetDir,
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000822 const struct utimbuf *timestamp,
Stephen Smalley779701d2012-02-09 14:13:23 -0500823 void (*callback)(const char *fn, void *), void *cookie,
824 struct selabel_handle *sehnd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800825{
826 if (zipDir[0] == '/') {
827 LOGE("mzExtractRecursive(): zipDir must be a relative path.\n");
828 return false;
829 }
830 if (targetDir[0] != '/') {
831 LOGE("mzExtractRecursive(): targetDir must be an absolute path.\n");
832 return false;
833 }
834
835 unsigned int zipDirLen;
836 char *zpath;
837
838 zipDirLen = strlen(zipDir);
839 zpath = (char *)malloc(zipDirLen + 2);
840 if (zpath == NULL) {
841 LOGE("Can't allocate %d bytes for zip path\n", zipDirLen + 2);
842 return false;
843 }
844 /* If zipDir is empty, we'll extract the entire zip file.
845 * Otherwise, canonicalize the path.
846 */
847 if (zipDirLen > 0) {
848 /* Make sure there's (hopefully, exactly one) slash at the
849 * end of the path. This way we don't need to worry about
850 * accidentally extracting "one/twothree" when a path like
851 * "one/two" is specified.
852 */
853 memcpy(zpath, zipDir, zipDirLen);
854 if (zpath[zipDirLen-1] != '/') {
855 zpath[zipDirLen++] = '/';
856 }
857 }
858 zpath[zipDirLen] = '\0';
859
860 /* Set up the helper structure that we'll use to assemble paths.
861 */
862 MzPathHelper helper;
863 helper.targetDir = targetDir;
864 helper.targetDirLen = strlen(helper.targetDir);
865 helper.zipDir = zpath;
866 helper.zipDirLen = strlen(helper.zipDir);
867 helper.buf = NULL;
868 helper.bufLen = 0;
869
870 /* Walk through the entries and extract anything whose path begins
871 * with zpath.
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100872 //TODO: since the entries are sorted, binary search for the first match
873 // and stop after the first non-match.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800874 */
875 unsigned int i;
876 bool seenMatch = false;
877 int ok = true;
Doug Zongkerbf80f492012-10-19 12:24:26 -0700878 int extractCount = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800879 for (i = 0; i < pArchive->numEntries; i++) {
880 ZipEntry *pEntry = pArchive->pEntries + i;
881 if (pEntry->fileNameLen < zipDirLen) {
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100882 //TODO: look out for a single empty directory entry that matches zpath, but
883 // missing the trailing slash. Most zip files seem to include
884 // the trailing slash, but I think it's legal to leave it off.
885 // e.g., zpath "a/b/", entry "a/b", with no children of the entry.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800886 /* No chance of matching.
887 */
888#if SORT_ENTRIES
889 if (seenMatch) {
890 /* Since the entries are sorted, we can give up
891 * on the first mismatch after the first match.
892 */
893 break;
894 }
895#endif
896 continue;
897 }
898 /* If zpath is empty, this strncmp() will match everything,
899 * which is what we want.
900 */
901 if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) {
902#if SORT_ENTRIES
903 if (seenMatch) {
904 /* Since the entries are sorted, we can give up
905 * on the first mismatch after the first match.
906 */
907 break;
908 }
909#endif
910 continue;
911 }
912 /* This entry begins with zipDir, so we'll extract it.
913 */
914 seenMatch = true;
915
916 /* Find the target location of the entry.
917 */
918 const char *targetFile = targetEntryPath(&helper, pEntry);
919 if (targetFile == NULL) {
920 LOGE("Can't assemble target path for \"%.*s\"\n",
921 pEntry->fileNameLen, pEntry->fileName);
922 ok = false;
923 break;
924 }
925
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800926#define UNZIP_DIRMODE 0755
927#define UNZIP_FILEMODE 0644
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000928 /*
929 * Create the file or directory. We ignore directory entries
930 * because we recursively create paths to each file entry we encounter
931 * in the zip archive anyway.
932 *
933 * NOTE: A "directory entry" in a zip archive is just a zero length
934 * entry that ends in a "/". They're not mandatory and many tools get
935 * rid of them. We need to process them only if we want to preserve
936 * empty directories from the archive.
937 */
938 if (pEntry->fileName[pEntry->fileNameLen-1] != '/') {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800939 /* This is not a directory. First, make sure that
940 * the containing directory exists.
941 */
942 int ret = dirCreateHierarchy(
Stephen Smalley779701d2012-02-09 14:13:23 -0500943 targetFile, UNZIP_DIRMODE, timestamp, true, sehnd);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800944 if (ret != 0) {
945 LOGE("Can't create containing directory for \"%s\": %s\n",
946 targetFile, strerror(errno));
947 ok = false;
948 break;
949 }
950
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000951 /*
952 * The entry is a regular file or a symlink. Open the target for writing.
953 *
954 * TODO: This behavior for symlinks seems rather bizarre. For a
955 * symlink foo/bar/baz -> foo/tar/taz, we will create a file called
956 * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We
957 * warn about this for now and preserve older behavior.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800958 */
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000959 if (mzIsZipEntrySymlink(pEntry)) {
960 LOGE("Symlink entry \"%.*s\" will be output as a regular file.",
961 pEntry->fileNameLen, pEntry->fileName);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800962 }
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000963
964 char *secontext = NULL;
965
966 if (sehnd) {
967 selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
968 setfscreatecon(secontext);
969 }
970
Alistair Strachan733285f2016-05-05 16:04:32 -0700971 int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC,
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000972 UNZIP_FILEMODE);
973
974 if (secontext) {
975 freecon(secontext);
976 setfscreatecon(NULL);
977 }
978
979 if (fd < 0) {
980 LOGE("Can't create target file \"%s\": %s\n",
981 targetFile, strerror(errno));
982 ok = false;
983 break;
984 }
985
986 bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
987 if (ok) {
988 ok = (fsync(fd) == 0);
989 }
990 if (close(fd) != 0) {
991 ok = false;
992 }
993 if (!ok) {
994 LOGE("Error extracting \"%s\"\n", targetFile);
995 ok = false;
996 break;
997 }
998
999 if (timestamp != NULL && utime(targetFile, timestamp)) {
1000 LOGE("Error touching \"%s\"\n", targetFile);
1001 ok = false;
1002 break;
1003 }
1004
1005 LOGV("Extracted file \"%s\"\n", targetFile);
1006 ++extractCount;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001007 }
1008
1009 if (callback != NULL) callback(targetFile, cookie);
1010 }
1011
Tao Baodd4d9812015-07-15 14:13:06 -07001012 LOGV("Extracted %d file(s)\n", extractCount);
Doug Zongkerbf80f492012-10-19 12:24:26 -07001013
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001014 free(helper.buf);
1015 free(zpath);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001016
1017 return ok;
1018}