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