blob: b887b8466c3eb37163a942ebff9293c5b286c5e3 [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
Tianjie Xu74778142016-08-05 18:00:04 -070017#include <string>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080018
Tianjie Xu74778142016-08-05 18:00:04 -070019#include <android-base/logging.h>
20#include <android-base/stringprintf.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080021#include <assert.h>
Elliott Hughes4bbd5bf2016-04-01 18:24:39 -070022#include <selinux/label.h>
23#include <selinux/selinux.h>
24
Tianjie Xu74778142016-08-05 18:00:04 -070025#include "Zip.h"
26#include "Bits.h"
27#include "DirUtil.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) {
Tianjie Xu74778142016-08-05 18:00:04 -0700157 LOG(WARNING) << "WARNING: duplicate entry '" << std::string(found->fileName,
158 found->fileNameLen) << "' in Zip";
159
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800160 /* keep going */
161 }
162}
163
164static int validFilename(const char *fileName, unsigned int fileNameLen)
165{
166 // Forbid super long filenames.
167 if (fileNameLen >= PATH_MAX) {
Tianjie Xu74778142016-08-05 18:00:04 -0700168 LOG(WARNING) << "Filename too long (" << fileNameLen << " chatacters)";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800169 return 0;
170 }
171
172 // Require all characters to be printable ASCII (no NUL, no UTF-8, etc).
173 unsigned int i;
174 for (i = 0; i < fileNameLen; ++i) {
175 if (fileName[i] < 32 || fileName[i] >= 127) {
Tianjie Xu74778142016-08-05 18:00:04 -0700176 LOG(WARNING) << android::base::StringPrintf(
177 "Filename contains invalid character '\%02x'\n", fileName[i]);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800178 return 0;
179 }
180 }
181
182 return 1;
183}
184
185/*
186 * Parse the contents of a Zip archive. After confirming that the file
187 * is in fact a Zip, we scan out the contents of the central directory and
188 * store it in a hash table.
189 *
190 * Returns "true" on success.
191 */
Doug Zongker99916f02014-01-13 14:16:58 -0800192static bool parseZipArchive(ZipArchive* pArchive)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800193{
194 bool result = false;
195 const unsigned char* ptr;
196 unsigned int i, numEntries, cdOffset;
197 unsigned int val;
198
199 /*
200 * The first 4 bytes of the file will either be the local header
201 * signature for the first file (LOCSIG) or, if the archive doesn't
202 * have any files in it, the end-of-central-directory signature (ENDSIG).
203 */
Doug Zongker99916f02014-01-13 14:16:58 -0800204 val = get4LE(pArchive->addr);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800205 if (val == ENDSIG) {
Tianjie Xu74778142016-08-05 18:00:04 -0700206 LOG(WARNING) << "Found Zip archive, but it looks empty";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800207 goto bail;
208 } else if (val != LOCSIG) {
Tianjie Xu74778142016-08-05 18:00:04 -0700209 LOG(WARNING) << android::base::StringPrintf("Not a Zip archive (found 0x%08x)\n", val);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800210 goto bail;
211 }
212
213 /*
214 * Find the EOCD. We'll find it immediately unless they have a file
215 * comment.
216 */
Doug Zongker99916f02014-01-13 14:16:58 -0800217 ptr = pArchive->addr + pArchive->length - ENDHDR;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800218
Doug Zongker99916f02014-01-13 14:16:58 -0800219 while (ptr >= (const unsigned char*) pArchive->addr) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800220 if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
221 break;
222 ptr--;
223 }
Doug Zongker99916f02014-01-13 14:16:58 -0800224 if (ptr < (const unsigned char*) pArchive->addr) {
Tianjie Xu74778142016-08-05 18:00:04 -0700225 LOG(WARNING) << "Could not find end-of-central-directory in Zip";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800226 goto bail;
227 }
228
229 /*
230 * There are two interesting items in the EOCD block: the number of
231 * entries in the file, and the file offset of the start of the
232 * central directory.
233 */
234 numEntries = get2LE(ptr + ENDSUB);
235 cdOffset = get4LE(ptr + ENDOFF);
236
Tianjie Xu74778142016-08-05 18:00:04 -0700237 LOG(VERBOSE) << "numEntries=" << numEntries << " cdOffset=" << cdOffset;
Doug Zongker99916f02014-01-13 14:16:58 -0800238 if (numEntries == 0 || cdOffset >= pArchive->length) {
Tianjie Xu74778142016-08-05 18:00:04 -0700239 LOG(WARNING) << "Invalid entries=" << numEntries << " offset=" << cdOffset
240 << " (len=" << pArchive->length << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800241 goto bail;
242 }
243
244 /*
245 * Create data structures to hold entries.
246 */
247 pArchive->numEntries = numEntries;
248 pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry));
249 pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL);
250 if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
251 goto bail;
252
Doug Zongker99916f02014-01-13 14:16:58 -0800253 ptr = pArchive->addr + cdOffset;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800254 for (i = 0; i < numEntries; i++) {
255 ZipEntry* pEntry;
256 unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
257 const unsigned char* localHdr;
258 const char *fileName;
259
Doug Zongker99916f02014-01-13 14:16:58 -0800260 if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) {
Tianjie Xu74778142016-08-05 18:00:04 -0700261 LOG(WARNING) << "Ran off the end (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800262 goto bail;
263 }
264 if (get4LE(ptr) != CENSIG) {
Tianjie Xu74778142016-08-05 18:00:04 -0700265 LOG(WARNING) << "Missed a central dir sig (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800266 goto bail;
267 }
268
269 localHdrOffset = get4LE(ptr + CENOFF);
270 fileNameLen = get2LE(ptr + CENNAM);
271 extraLen = get2LE(ptr + CENEXT);
272 commentLen = get2LE(ptr + CENCOM);
273 fileName = (const char*)ptr + CENHDR;
Doug Zongker99916f02014-01-13 14:16:58 -0800274 if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) {
Tianjie Xu74778142016-08-05 18:00:04 -0700275 LOG(WARNING) << "Filename ran off the end (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800276 goto bail;
277 }
278 if (!validFilename(fileName, fileNameLen)) {
Tianjie Xu74778142016-08-05 18:00:04 -0700279 LOG(WARNING) << "Invalid filename (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800280 goto bail;
281 }
282
283#if SORT_ENTRIES
284 /* Figure out where this entry should go (binary search).
285 */
286 if (i > 0) {
287 int low, high;
288
289 low = 0;
290 high = i - 1;
291 while (low <= high) {
292 int mid;
293 int diff;
294 int diffLen;
295
296 mid = low + ((high - low) / 2); // avoid overflow
297
298 if (pArchive->pEntries[mid].fileNameLen < fileNameLen) {
299 diffLen = pArchive->pEntries[mid].fileNameLen;
300 } else {
301 diffLen = fileNameLen;
302 }
303 diff = strncmp(pArchive->pEntries[mid].fileName, fileName,
304 diffLen);
305 if (diff == 0) {
306 diff = pArchive->pEntries[mid].fileNameLen - fileNameLen;
307 }
308 if (diff < 0) {
309 low = mid + 1;
310 } else if (diff > 0) {
311 high = mid - 1;
312 } else {
313 high = mid;
314 break;
315 }
316 }
317
318 unsigned int target = high + 1;
319 assert(target <= i);
320 if (target != i) {
321 /* It belongs somewhere other than at the end of
322 * the list. Make some room at [target].
323 */
324 memmove(pArchive->pEntries + target + 1,
325 pArchive->pEntries + target,
326 (i - target) * sizeof(ZipEntry));
327 }
328 pEntry = &pArchive->pEntries[target];
329 } else {
330 pEntry = &pArchive->pEntries[0];
331 }
332#else
333 pEntry = &pArchive->pEntries[i];
334#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800335 pEntry->fileNameLen = fileNameLen;
336 pEntry->fileName = fileName;
337
338 pEntry->compLen = get4LE(ptr + CENSIZ);
339 pEntry->uncompLen = get4LE(ptr + CENLEN);
340 pEntry->compression = get2LE(ptr + CENHOW);
341 pEntry->modTime = get4LE(ptr + CENTIM);
342 pEntry->crc32 = get4LE(ptr + CENCRC);
343
344 /* These two are necessary for finding the mode of the file.
345 */
346 pEntry->versionMadeBy = get2LE(ptr + CENVEM);
347 if ((pEntry->versionMadeBy & 0xff00) != 0 &&
348 (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX)
349 {
Tianjie Xu74778142016-08-05 18:00:04 -0700350 LOG(WARNING) << android::base::StringPrintf(
351 "Incompatible \"version made by\": 0x%02x (at %d)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800352 pEntry->versionMadeBy >> 8, i);
353 goto bail;
354 }
355 pEntry->externalFileAttributes = get4LE(ptr + CENATX);
356
Doug Zongker99916f02014-01-13 14:16:58 -0800357 // Perform pArchive->addr + localHdrOffset, ensuring that it won't
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800358 // overflow. This is needed because localHdrOffset is untrusted.
Doug Zongker99916f02014-01-13 14:16:58 -0800359 if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr,
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800360 (uintptr_t)localHdrOffset)) {
Tianjie Xu74778142016-08-05 18:00:04 -0700361 LOG(WARNING) << "Integer overflow adding in parseZipArchive";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800362 goto bail;
363 }
364 if ((uintptr_t)localHdr + LOCHDR >
Doug Zongker99916f02014-01-13 14:16:58 -0800365 (uintptr_t)pArchive->addr + pArchive->length) {
Tianjie Xu74778142016-08-05 18:00:04 -0700366 LOG(WARNING) << "Bad offset to local header: " << localHdrOffset
367 << " (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800368 goto bail;
369 }
370 if (get4LE(localHdr) != LOCSIG) {
Tianjie Xu74778142016-08-05 18:00:04 -0700371 LOG(WARNING) << "Missed a local header sig (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800372 goto bail;
373 }
374 pEntry->offset = localHdrOffset + LOCHDR
375 + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT);
376 if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) {
Tianjie Xu74778142016-08-05 18:00:04 -0700377 LOG(WARNING) << "Integer overflow adding in parseZipArchive";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800378 goto bail;
379 }
Doug Zongker99916f02014-01-13 14:16:58 -0800380 if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) {
Tianjie Xu74778142016-08-05 18:00:04 -0700381 LOG(WARNING) << "Data ran off the end (at " << i << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800382 goto bail;
383 }
384
385#if !SORT_ENTRIES
386 /* Add to hash table; no need to lock here.
387 * Can't do this now if we're sorting, because entries
388 * will move around.
389 */
390 addEntryToHashTable(pArchive->pHash, pEntry);
391#endif
392
393 //dumpEntry(pEntry);
394 ptr += CENHDR + fileNameLen + extraLen + commentLen;
395 }
396
397#if SORT_ENTRIES
398 /* If we're sorting, we have to wait until all entries
399 * are in their final places, otherwise the pointers will
400 * probably point to the wrong things.
401 */
402 for (i = 0; i < numEntries; i++) {
403 /* Add to hash table; no need to lock here.
404 */
405 addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]);
406 }
407#endif
408
409 result = true;
410
411bail:
412 if (!result) {
413 mzHashTableFree(pArchive->pHash);
414 pArchive->pHash = NULL;
415 }
416 return result;
417}
418
419/*
420 * Open a Zip archive and scan out the contents.
421 *
422 * The easiest way to do this is to mmap() the whole thing and do the
423 * traditional backward scan for central directory. Since the EOCD is
424 * a relatively small bit at the end, we should end up only touching a
425 * small set of pages.
426 *
427 * This will be called on non-Zip files, especially during startup, so
428 * we don't want to be too noisy about failures. (Do we want a "quiet"
429 * flag?)
430 *
431 * On success, we fill out the contents of "pArchive".
432 */
Doug Zongker99916f02014-01-13 14:16:58 -0800433int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800434{
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800435 int err;
436
Doug Zongker99916f02014-01-13 14:16:58 -0800437 if (length < ENDHDR) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800438 err = -1;
Tianjie Xu74778142016-08-05 18:00:04 -0700439 LOG(WARNING) << "Archive " << pArchive << " is too small to be zip ("
440 << length << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800441 goto bail;
442 }
443
Doug Zongker99916f02014-01-13 14:16:58 -0800444 pArchive->addr = addr;
445 pArchive->length = length;
446
447 if (!parseZipArchive(pArchive)) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800448 err = -1;
Tianjie Xu74778142016-08-05 18:00:04 -0700449 LOG(WARNING) << "Parsing archive " << pArchive << " failed";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800450 goto bail;
451 }
452
453 err = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800454
455bail:
456 if (err != 0)
457 mzCloseZipArchive(pArchive);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800458 return err;
459}
460
461/*
462 * Close a ZipArchive, closing the file and freeing the contents.
463 *
464 * NOTE: the ZipArchive may not have been fully created.
465 */
466void mzCloseZipArchive(ZipArchive* pArchive)
467{
Tianjie Xu74778142016-08-05 18:00:04 -0700468 LOG(VERBOSE) << "Closing archive " << pArchive;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800469
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800470 free(pArchive->pEntries);
471
472 mzHashTableFree(pArchive->pHash);
473
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800474 pArchive->pHash = NULL;
475 pArchive->pEntries = NULL;
476}
477
478/*
479 * Find a matching entry.
480 *
481 * Returns NULL if no matching entry found.
482 */
483const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
484 const char* entryName)
485{
486 unsigned int itemHash = computeHash(entryName, strlen(entryName));
487
488 return (const ZipEntry*)mzHashTableLookup(pArchive->pHash,
489 itemHash, (char*) entryName, hashcmpZipName, false);
490}
491
492/*
493 * Return true if the entry is a symbolic link.
494 */
Narayan Kamath3e700cf2015-02-23 13:29:16 +0000495static bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800496{
497 if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) {
498 return S_ISLNK(pEntry->externalFileAttributes >> 16);
499 }
500 return false;
501}
502
503/* Call processFunction on the uncompressed data of a STORED entry.
504 */
505static bool processStoredEntry(const ZipArchive *pArchive,
506 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
507 void *cookie)
508{
Doug Zongker99916f02014-01-13 14:16:58 -0800509 return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800510}
511
512static bool processDeflatedEntry(const ZipArchive *pArchive,
513 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
514 void *cookie)
515{
caozhiyuan2584f9c2016-03-11 15:18:29 +0800516 bool success = false;
517 unsigned long totalOut = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800518 unsigned char procBuf[32 * 1024];
519 z_stream zstream;
520 int zerr;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800521
522 /*
523 * Initialize the zlib stream.
524 */
525 memset(&zstream, 0, sizeof(zstream));
526 zstream.zalloc = Z_NULL;
527 zstream.zfree = Z_NULL;
528 zstream.opaque = Z_NULL;
Doug Zongker99916f02014-01-13 14:16:58 -0800529 zstream.next_in = pArchive->addr + pEntry->offset;
530 zstream.avail_in = pEntry->compLen;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800531 zstream.next_out = (Bytef*) procBuf;
532 zstream.avail_out = sizeof(procBuf);
533 zstream.data_type = Z_UNKNOWN;
534
535 /*
536 * Use the undocumented "negative window bits" feature to tell zlib
537 * that there's no zlib header waiting for it.
538 */
539 zerr = inflateInit2(&zstream, -MAX_WBITS);
540 if (zerr != Z_OK) {
541 if (zerr == Z_VERSION_ERROR) {
Tianjie Xu74778142016-08-05 18:00:04 -0700542 LOG(ERROR) << "Installed zlib is not compatible with linked version ("
543 << ZLIB_VERSION << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800544 } else {
Tianjie Xu74778142016-08-05 18:00:04 -0700545 LOG(ERROR) << "Call to inflateInit2 failed (zerr=" << zerr << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800546 }
547 goto bail;
548 }
549
550 /*
551 * Loop while we have data.
552 */
553 do {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800554 /* uncompress the data */
555 zerr = inflate(&zstream, Z_NO_FLUSH);
556 if (zerr != Z_OK && zerr != Z_STREAM_END) {
Tianjie Xu74778142016-08-05 18:00:04 -0700557 LOG(WARNING) << "zlib inflate call failed (zerr=" << zerr << ")";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800558 goto z_bail;
559 }
560
561 /* write when we're full or when we're done */
562 if (zstream.avail_out == 0 ||
563 (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf)))
564 {
565 long procSize = zstream.next_out - procBuf;
Tianjie Xu74778142016-08-05 18:00:04 -0700566 LOG(VERBOSE) << "+++ processing " << procSize << " bytes";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800567 bool ret = processFunction(procBuf, procSize, cookie);
568 if (!ret) {
Tianjie Xu74778142016-08-05 18:00:04 -0700569 LOG(WARNING) << "Process function elected to fail (in inflate)";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800570 goto z_bail;
571 }
572
573 zstream.next_out = procBuf;
574 zstream.avail_out = sizeof(procBuf);
575 }
576 } while (zerr == Z_OK);
577
578 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
579
580 // success!
caozhiyuan2584f9c2016-03-11 15:18:29 +0800581 totalOut = zstream.total_out;
582 success = true;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800583
584z_bail:
585 inflateEnd(&zstream); /* free up any allocated structures */
586
587bail:
caozhiyuan2584f9c2016-03-11 15:18:29 +0800588 if (totalOut != pEntry->uncompLen) {
589 if (success) { // error already shown?
Tianjie Xu74778142016-08-05 18:00:04 -0700590 LOG(WARNING) << "Size mismatch on inflated file (" << totalOut << " vs "
591 << pEntry->uncompLen << ")";
caozhiyuan2584f9c2016-03-11 15:18:29 +0800592 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800593 return false;
594 }
595 return true;
596}
597
598/*
599 * Stream the uncompressed data through the supplied function,
600 * passing cookie to it each time it gets called. processFunction
601 * may be called more than once.
602 *
603 * If processFunction returns false, the operation is abandoned and
604 * mzProcessZipEntryContents() immediately returns false.
605 *
606 * This is useful for calculating the hash of an entry's uncompressed contents.
607 */
608bool mzProcessZipEntryContents(const ZipArchive *pArchive,
609 const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
610 void *cookie)
611{
612 bool ret = false;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800613
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800614 switch (pEntry->compression) {
615 case STORED:
616 ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
617 break;
618 case DEFLATED:
619 ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie);
620 break;
621 default:
Tianjie Xu74778142016-08-05 18:00:04 -0700622 LOG(ERROR) << "Unsupported compression type " << pEntry->compression
623 << " for entry '" << pEntry->fileName << "'";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800624 break;
625 }
626
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800627 return ret;
628}
629
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800630typedef struct {
631 char *buf;
632 int bufLen;
633} CopyProcessArgs;
634
635static bool copyProcessFunction(const unsigned char *data, int dataLen,
636 void *cookie)
637{
638 CopyProcessArgs *args = (CopyProcessArgs *)cookie;
639 if (dataLen <= args->bufLen) {
640 memcpy(args->buf, data, dataLen);
641 args->buf += dataLen;
642 args->bufLen -= dataLen;
643 return true;
644 }
645 return false;
646}
647
648/*
649 * Read an entry into a buffer allocated by the caller.
650 */
651bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
652 char *buf, int bufLen)
653{
654 CopyProcessArgs args;
655 bool ret;
Doug Zongker596271f2009-04-29 16:52:04 -0700656
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800657 args.buf = buf;
658 args.bufLen = bufLen;
659 ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
660 (void *)&args);
661 if (!ret) {
Tianjie Xu74778142016-08-05 18:00:04 -0700662 LOG(ERROR) << "Can't extract entry to buffer";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800663 return false;
664 }
665 return true;
666}
667
668static bool writeProcessFunction(const unsigned char *data, int dataLen,
Doug Zongker683c4622009-05-05 17:50:21 -0700669 void *cookie)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800670{
Colin Cross92cdf9c2014-02-05 17:30:31 -0800671 int fd = (int)(intptr_t)cookie;
Michael Rungef5d9f892014-05-06 16:54:42 -0700672 if (dataLen == 0) {
673 return true;
674 }
Doug Zongker596271f2009-04-29 16:52:04 -0700675 ssize_t soFar = 0;
Doug Zongker683c4622009-05-05 17:50:21 -0700676 while (true) {
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700677 ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar));
Doug Zongker683c4622009-05-05 17:50:21 -0700678 if (n <= 0) {
Tianjie Xu74778142016-08-05 18:00:04 -0700679 PLOG(ERROR) << "Error writing " << dataLen-soFar << " bytes from zip file from "
680 << data+soFar;
Elliott Hughes7bad7c42015-04-28 17:24:24 -0700681 return false;
Doug Zongker596271f2009-04-29 16:52:04 -0700682 } else if (n > 0) {
683 soFar += n;
684 if (soFar == dataLen) return true;
685 if (soFar > dataLen) {
Tianjie Xu74778142016-08-05 18:00:04 -0700686 LOG(ERROR) << "write overrun? (" << soFar << " bytes instead of "
687 << dataLen << ")";
Doug Zongker596271f2009-04-29 16:52:04 -0700688 return false;
689 }
Doug Zongker596271f2009-04-29 16:52:04 -0700690 }
Doug Zongker683c4622009-05-05 17:50:21 -0700691 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800692}
693
694/*
695 * Uncompress "pEntry" in "pArchive" to "fd" at the current offset.
696 */
697bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
Doug Zongker1c4ceae2009-05-08 09:43:28 -0700698 const ZipEntry *pEntry, int fd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800699{
700 bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
Colin Cross92cdf9c2014-02-05 17:30:31 -0800701 (void*)(intptr_t)fd);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800702 if (!ret) {
Tianjie Xu74778142016-08-05 18:00:04 -0700703 LOG(ERROR) << "Can't extract entry to file.";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800704 return false;
705 }
706 return true;
707}
708
Doug Zongker6aece332010-02-01 14:40:12 -0800709typedef struct {
710 unsigned char* buffer;
711 long len;
712} BufferExtractCookie;
713
714static bool bufferProcessFunction(const unsigned char *data, int dataLen,
715 void *cookie) {
716 BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
717
718 memmove(bec->buffer, data, dataLen);
719 bec->buffer += dataLen;
720 bec->len -= dataLen;
721
722 return true;
723}
724
725/*
726 * Uncompress "pEntry" in "pArchive" to buffer, which must be large
727 * enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
728 */
729bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
730 const ZipEntry *pEntry, unsigned char *buffer)
731{
732 BufferExtractCookie bec;
733 bec.buffer = buffer;
734 bec.len = mzGetZipEntryUncompLen(pEntry);
735
736 bool ret = mzProcessZipEntryContents(pArchive, pEntry,
737 bufferProcessFunction, (void*)&bec);
738 if (!ret || bec.len != 0) {
Tianjie Xu74778142016-08-05 18:00:04 -0700739 LOG(ERROR) << "Can't extract entry to memory buffer.";
Doug Zongker6aece332010-02-01 14:40:12 -0800740 return false;
741 }
742 return true;
743}
744
745
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800746/* Helper state to make path translation easier and less malloc-happy.
747 */
748typedef struct {
749 const char *targetDir;
750 const char *zipDir;
751 char *buf;
752 int targetDirLen;
753 int zipDirLen;
754 int bufLen;
755} MzPathHelper;
756
757/* Given the values of targetDir and zipDir in the helper,
758 * return the target filename of the provided entry.
759 * The helper must be initialized first.
760 */
761static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry)
762{
763 int needLen;
764 bool firstTime = (helper->buf == NULL);
765
766 /* target file <-- targetDir + / + entry[zipDirLen:]
767 */
768 needLen = helper->targetDirLen + 1 +
769 pEntry->fileNameLen - helper->zipDirLen + 1;
Tianjie Xu66296552016-02-22 19:49:38 -0800770 if (firstTime || needLen > helper->bufLen) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800771 char *newBuf;
772
773 needLen *= 2;
774 newBuf = (char *)realloc(helper->buf, needLen);
775 if (newBuf == NULL) {
776 return NULL;
777 }
778 helper->buf = newBuf;
779 helper->bufLen = needLen;
780 }
781
782 /* Every path will start with the target path and a slash.
783 */
784 if (firstTime) {
785 char *p = helper->buf;
786 memcpy(p, helper->targetDir, helper->targetDirLen);
787 p += helper->targetDirLen;
788 if (p == helper->buf || p[-1] != '/') {
789 helper->targetDirLen += 1;
790 *p++ = '/';
791 }
792 }
793
794 /* Replace the custom part of the path with the appropriate
795 * part of the entry's path.
796 */
797 char *epath = helper->buf + helper->targetDirLen;
798 memcpy(epath, pEntry->fileName + helper->zipDirLen,
799 pEntry->fileNameLen - helper->zipDirLen);
800 epath += pEntry->fileNameLen - helper->zipDirLen;
801 *epath = '\0';
802
803 return helper->buf;
804}
805
806/*
807 * Inflate all entries under zipDir to the directory specified by
808 * targetDir, which must exist and be a writable directory.
809 *
810 * The immediate children of zipDir will become the immediate
811 * children of targetDir; e.g., if the archive contains the entries
812 *
813 * a/b/c/one
814 * a/b/c/two
815 * a/b/c/d/three
816 *
817 * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting
818 * files will be
819 *
820 * /tmp/one
821 * /tmp/two
822 * /tmp/d/three
823 *
824 * Returns true on success, false on failure.
825 */
826bool mzExtractRecursive(const ZipArchive *pArchive,
827 const char *zipDir, const char *targetDir,
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000828 const struct utimbuf *timestamp,
Stephen Smalley779701d2012-02-09 14:13:23 -0500829 void (*callback)(const char *fn, void *), void *cookie,
830 struct selabel_handle *sehnd)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800831{
832 if (zipDir[0] == '/') {
Tianjie Xu74778142016-08-05 18:00:04 -0700833 LOG(ERROR) << "mzExtractRecursive(): zipDir must be a relative path.";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800834 return false;
835 }
836 if (targetDir[0] != '/') {
Tianjie Xu74778142016-08-05 18:00:04 -0700837 LOG(ERROR) << "mzExtractRecursive(): targetDir must be an absolute path.\n";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800838 return false;
839 }
840
841 unsigned int zipDirLen;
842 char *zpath;
843
844 zipDirLen = strlen(zipDir);
845 zpath = (char *)malloc(zipDirLen + 2);
846 if (zpath == NULL) {
Tianjie Xu74778142016-08-05 18:00:04 -0700847 LOG(ERROR) << "Can't allocate " << (zipDirLen + 2) << " bytes for zip path";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800848 return false;
849 }
850 /* If zipDir is empty, we'll extract the entire zip file.
851 * Otherwise, canonicalize the path.
852 */
853 if (zipDirLen > 0) {
854 /* Make sure there's (hopefully, exactly one) slash at the
855 * end of the path. This way we don't need to worry about
856 * accidentally extracting "one/twothree" when a path like
857 * "one/two" is specified.
858 */
859 memcpy(zpath, zipDir, zipDirLen);
860 if (zpath[zipDirLen-1] != '/') {
861 zpath[zipDirLen++] = '/';
862 }
863 }
864 zpath[zipDirLen] = '\0';
865
866 /* Set up the helper structure that we'll use to assemble paths.
867 */
868 MzPathHelper helper;
869 helper.targetDir = targetDir;
870 helper.targetDirLen = strlen(helper.targetDir);
871 helper.zipDir = zpath;
872 helper.zipDirLen = strlen(helper.zipDir);
873 helper.buf = NULL;
874 helper.bufLen = 0;
875
876 /* Walk through the entries and extract anything whose path begins
877 * with zpath.
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100878 //TODO: since the entries are sorted, binary search for the first match
879 // and stop after the first non-match.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800880 */
881 unsigned int i;
882 bool seenMatch = false;
883 int ok = true;
Doug Zongkerbf80f492012-10-19 12:24:26 -0700884 int extractCount = 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800885 for (i = 0; i < pArchive->numEntries; i++) {
886 ZipEntry *pEntry = pArchive->pEntries + i;
887 if (pEntry->fileNameLen < zipDirLen) {
Nanik Tolaram4e8e93b2015-02-08 22:31:14 +1100888 //TODO: look out for a single empty directory entry that matches zpath, but
889 // missing the trailing slash. Most zip files seem to include
890 // the trailing slash, but I think it's legal to leave it off.
891 // e.g., zpath "a/b/", entry "a/b", with no children of the entry.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800892 /* No chance of matching.
893 */
894#if SORT_ENTRIES
895 if (seenMatch) {
896 /* Since the entries are sorted, we can give up
897 * on the first mismatch after the first match.
898 */
899 break;
900 }
901#endif
902 continue;
903 }
904 /* If zpath is empty, this strncmp() will match everything,
905 * which is what we want.
906 */
907 if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) {
908#if SORT_ENTRIES
909 if (seenMatch) {
910 /* Since the entries are sorted, we can give up
911 * on the first mismatch after the first match.
912 */
913 break;
914 }
915#endif
916 continue;
917 }
918 /* This entry begins with zipDir, so we'll extract it.
919 */
920 seenMatch = true;
921
922 /* Find the target location of the entry.
923 */
924 const char *targetFile = targetEntryPath(&helper, pEntry);
925 if (targetFile == NULL) {
Tianjie Xu74778142016-08-05 18:00:04 -0700926 LOG(ERROR) << "Can't assemble target path for \"" << std::string(pEntry->fileName,
927 pEntry->fileNameLen) << "\"";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800928 ok = false;
929 break;
930 }
931
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800932#define UNZIP_DIRMODE 0755
933#define UNZIP_FILEMODE 0644
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000934 /*
935 * Create the file or directory. We ignore directory entries
936 * because we recursively create paths to each file entry we encounter
937 * in the zip archive anyway.
938 *
939 * NOTE: A "directory entry" in a zip archive is just a zero length
940 * entry that ends in a "/". They're not mandatory and many tools get
941 * rid of them. We need to process them only if we want to preserve
942 * empty directories from the archive.
943 */
944 if (pEntry->fileName[pEntry->fileNameLen-1] != '/') {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800945 /* This is not a directory. First, make sure that
946 * the containing directory exists.
947 */
948 int ret = dirCreateHierarchy(
Stephen Smalley779701d2012-02-09 14:13:23 -0500949 targetFile, UNZIP_DIRMODE, timestamp, true, sehnd);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800950 if (ret != 0) {
Tianjie Xu74778142016-08-05 18:00:04 -0700951 PLOG(ERROR) << "Can't create containing directory for \"" << targetFile << "\"";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800952 ok = false;
953 break;
954 }
955
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000956 /*
957 * The entry is a regular file or a symlink. Open the target for writing.
958 *
959 * TODO: This behavior for symlinks seems rather bizarre. For a
960 * symlink foo/bar/baz -> foo/tar/taz, we will create a file called
961 * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We
962 * warn about this for now and preserve older behavior.
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800963 */
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000964 if (mzIsZipEntrySymlink(pEntry)) {
Tianjie Xu74778142016-08-05 18:00:04 -0700965 LOG(ERROR) << "Symlink entry \"" << std::string(pEntry->fileName,
966 pEntry->fileNameLen) << "\" will be output as a regular file.";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800967 }
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000968
969 char *secontext = NULL;
970
971 if (sehnd) {
972 selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
973 setfscreatecon(secontext);
974 }
975
Alistair Strachan733285f2016-05-05 16:04:32 -0700976 int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC,
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000977 UNZIP_FILEMODE);
978
979 if (secontext) {
980 freecon(secontext);
981 setfscreatecon(NULL);
982 }
983
984 if (fd < 0) {
Tianjie Xu74778142016-08-05 18:00:04 -0700985 PLOG(ERROR) << "Can't create target file \"" << targetFile << "\"";
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000986 ok = false;
987 break;
988 }
989
990 bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
991 if (ok) {
992 ok = (fsync(fd) == 0);
993 }
994 if (close(fd) != 0) {
995 ok = false;
996 }
997 if (!ok) {
Tianjie Xu74778142016-08-05 18:00:04 -0700998 LOG(ERROR) << "Error extracting \"" << targetFile << "\"";
Narayan Kamath9c0f5d62015-02-23 14:09:31 +0000999 ok = false;
1000 break;
1001 }
1002
1003 if (timestamp != NULL && utime(targetFile, timestamp)) {
Tianjie Xu74778142016-08-05 18:00:04 -07001004 LOG(ERROR) << "Error touching \"" << targetFile << "\"";
Narayan Kamath9c0f5d62015-02-23 14:09:31 +00001005 ok = false;
1006 break;
1007 }
1008
Tianjie Xu74778142016-08-05 18:00:04 -07001009 LOG(VERBOSE) <<"Extracted file \"" << targetFile << "\"";
Narayan Kamath9c0f5d62015-02-23 14:09:31 +00001010 ++extractCount;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001011 }
1012
1013 if (callback != NULL) callback(targetFile, cookie);
1014 }
1015
Tianjie Xu74778142016-08-05 18:00:04 -07001016 LOG(VERBOSE) << "Extracted " << extractCount << " file(s)";
Doug Zongkerbf80f492012-10-19 12:24:26 -07001017
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001018 free(helper.buf);
1019 free(zpath);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001020
1021 return ok;
1022}