1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Read-only access to Zip archives, with minimal heap allocation.
19 */
20 #include "ZipArchive.h"
21
22 #include <zlib.h>
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <errno.h>
29
30 #include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
31
32
33 /*
34 * Zip file constants.
35 */
36 #define kEOCDSignature 0x06054b50
37 #define kEOCDLen 22
38 #define kEOCDNumEntries 8 // offset to #of entries in file
39 #define kEOCDSize 12 // size of the central directory
40 #define kEOCDFileOffset 16 // offset to central directory
41
42 #define kMaxCommentLen 65535 // longest possible in ushort
43 #define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
44
45 #define kLFHSignature 0x04034b50
46 #define kLFHLen 30 // excluding variable-len fields
47 #define kLFHNameLen 26 // offset to filename length
48 #define kLFHExtraLen 28 // offset to extra length
49
50 #define kCDESignature 0x02014b50
51 #define kCDELen 46 // excluding variable-len fields
52 #define kCDEMethod 10 // offset to compression method
53 #define kCDEModWhen 12 // offset to modification timestamp
54 #define kCDECRC 16 // offset to entry CRC
55 #define kCDECompLen 20 // offset to compressed length
56 #define kCDEUncompLen 24 // offset to uncompressed length
57 #define kCDENameLen 28 // offset to filename length
58 #define kCDEExtraLen 30 // offset to extra length
59 #define kCDECommentLen 32 // offset to comment length
60 #define kCDELocalOffset 42 // offset to local hdr
61
62 /*
63 * The values we return for ZipEntry use 0 as an invalid value, so we
64 * want to adjust the hash table index by a fixed amount. Using a large
65 * value helps insure that people don't mix & match arguments, e.g. with
66 * entry indices.
67 */
68 #define kZipEntryAdj 10000
69
70 /*
71 * Convert a ZipEntry to a hash table index, verifying that it's in a
72 * valid range.
73 */
entryToIndex(const ZipArchive * pArchive,const ZipEntry entry)74 static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry)
75 {
76 long ent = ((long) entry) - kZipEntryAdj;
77 if (ent < 0 || ent >= pArchive->mHashTableSize ||
78 pArchive->mHashTable[ent].name == NULL)
79 {
80 LOGW("Zip: invalid ZipEntry %p (%ld)\n", entry, ent);
81 return -1;
82 }
83 return ent;
84 }
85
86 /*
87 * Simple string hash function for non-null-terminated strings.
88 */
computeHash(const char * str,int len)89 static unsigned int computeHash(const char* str, int len)
90 {
91 unsigned int hash = 0;
92
93 while (len--)
94 hash = hash * 31 + *str++;
95
96 return hash;
97 }
98
99 /*
100 * Add a new entry to the hash table.
101 */
addToHash(ZipArchive * pArchive,const char * str,int strLen,unsigned int hash)102 static void addToHash(ZipArchive* pArchive, const char* str, int strLen,
103 unsigned int hash)
104 {
105 const int hashTableSize = pArchive->mHashTableSize;
106 int ent = hash & (hashTableSize - 1);
107
108 /*
109 * We over-allocated the table, so we're guaranteed to find an empty slot.
110 */
111 while (pArchive->mHashTable[ent].name != NULL)
112 ent = (ent + 1) & (hashTableSize-1);
113
114 pArchive->mHashTable[ent].name = str;
115 pArchive->mHashTable[ent].nameLen = strLen;
116 }
117
118 /*
119 * Get 2 little-endian bytes.
120 */
get2LE(unsigned char const * pSrc)121 static u2 get2LE(unsigned char const* pSrc)
122 {
123 return pSrc[0] | (pSrc[1] << 8);
124 }
125
126 /*
127 * Get 4 little-endian bytes.
128 */
get4LE(unsigned char const * pSrc)129 static u4 get4LE(unsigned char const* pSrc)
130 {
131 u4 result;
132
133 result = pSrc[0];
134 result |= pSrc[1] << 8;
135 result |= pSrc[2] << 16;
136 result |= pSrc[3] << 24;
137
138 return result;
139 }
140
141 /*
142 * Find the zip Central Directory and memory-map it.
143 *
144 * On success, returns 0 after populating fields from the EOCD area:
145 * mDirectoryOffset
146 * mDirectoryMap
147 * mNumEntries
148 */
mapCentralDirectory(int fd,const char * debugFileName,ZipArchive * pArchive)149 static int mapCentralDirectory(int fd, const char* debugFileName,
150 ZipArchive* pArchive)
151 {
152 u1* scanBuf = NULL;
153 int result = -1;
154
155 /*
156 * Get and test file length.
157 */
158 off_t fileLength = lseek(fd, 0, SEEK_END);
159 if (fileLength < kEOCDLen) {
160 LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength);
161 goto bail;
162 }
163
164 /*
165 * Perform the traditional EOCD snipe hunt.
166 *
167 * We're searching for the End of Central Directory magic number,
168 * which appears at the start of the EOCD block. It's followed by
169 * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
170 * need to read the last part of the file into a buffer, dig through
171 * it to find the magic number, parse some values out, and use those
172 * to determine the extent of the CD.
173 *
174 * We start by pulling in the last part of the file.
175 */
176 size_t readAmount = kMaxEOCDSearch;
177 if (readAmount > (size_t) fileLength)
178 readAmount = fileLength;
179 off_t searchStart = fileLength - readAmount;
180
181 scanBuf = (u1*) malloc(readAmount);
182 if (lseek(fd, searchStart, SEEK_SET) != searchStart) {
183 LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno));
184 goto bail;
185 }
186 ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
187 if (actual != (ssize_t) readAmount) {
188 LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno));
189 goto bail;
190 }
191
192 /*
193 * Scan backward for the EOCD magic. In an archive without a trailing
194 * comment, we'll find it on the first try. (We may want to consider
195 * doing an initial minimal read; if we don't find it, retry with a
196 * second read as above.)
197 */
198 int i;
199 for (i = readAmount - kEOCDLen; i >= 0; i--) {
200 if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
201 LOGV("+++ Found EOCD at buf+%d\n", i);
202 break;
203 }
204 }
205 if (i < 0) {
206 LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName);
207 goto bail;
208 }
209
210 off_t eocdOffset = searchStart + i;
211 const u1* eocdPtr = scanBuf + i;
212
213 assert(eocdOffset < fileLength);
214
215 /*
216 * Grab the CD offset and size, and the number of entries in the
217 * archive. Verify that they look reasonable.
218 */
219 u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries);
220 u4 dirSize = get4LE(eocdPtr + kEOCDSize);
221 u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
222
223 if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
224 LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n",
225 (long) dirOffset, dirSize, (long) eocdOffset);
226 goto bail;
227 }
228 if (numEntries == 0) {
229 LOGW("Zip: empty archive?\n");
230 goto bail;
231 }
232
233 LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
234 numEntries, dirSize, dirOffset);
235
236 /*
237 * It all looks good. Create a mapping for the CD, and set the fields
238 * in pArchive.
239 */
240 if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize,
241 &pArchive->mDirectoryMap) != 0)
242 {
243 LOGW("Zip: cd map failed\n");
244 goto bail;
245 }
246
247 pArchive->mNumEntries = numEntries;
248 pArchive->mDirectoryOffset = dirOffset;
249
250 result = 0;
251
252 bail:
253 free(scanBuf);
254 return result;
255 }
256
257 /*
258 * Parses the Zip archive's Central Directory. Allocates and populates the
259 * hash table.
260 *
261 * Returns 0 on success.
262 */
parseZipArchive(ZipArchive * pArchive)263 static int parseZipArchive(ZipArchive* pArchive)
264 {
265 int result = -1;
266 const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr;
267 size_t cdLength = pArchive->mDirectoryMap.length;
268 int numEntries = pArchive->mNumEntries;
269
270 /*
271 * Create hash table. We have a minimum 75% load factor, possibly as
272 * low as 50% after we round off to a power of 2. There must be at
273 * least one unused entry to avoid an infinite loop during creation.
274 */
275 pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3);
276 pArchive->mHashTable = (ZipHashEntry*)
277 calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry));
278
279 /*
280 * Walk through the central directory, adding entries to the hash
281 * table and verifying values.
282 */
283 const u1* ptr = cdPtr;
284 int i;
285 for (i = 0; i < numEntries; i++) {
286 if (get4LE(ptr) != kCDESignature) {
287 LOGW("Zip: missed a central dir sig (at %d)\n", i);
288 goto bail;
289 }
290 if (ptr + kCDELen > cdPtr + cdLength) {
291 LOGW("Zip: ran off the end (at %d)\n", i);
292 goto bail;
293 }
294
295 long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
296 if (localHdrOffset >= pArchive->mDirectoryOffset) {
297 LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i);
298 goto bail;
299 }
300
301 unsigned int fileNameLen, extraLen, commentLen, hash;
302 fileNameLen = get2LE(ptr + kCDENameLen);
303 extraLen = get2LE(ptr + kCDEExtraLen);
304 commentLen = get2LE(ptr + kCDECommentLen);
305
306 /* add the CDE filename to the hash table */
307 hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
308 addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash);
309
310 ptr += kCDELen + fileNameLen + extraLen + commentLen;
311 if ((size_t)(ptr - cdPtr) > cdLength) {
312 LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n",
313 (int) (ptr - cdPtr), cdLength, i);
314 goto bail;
315 }
316 }
317 LOGV("+++ zip good scan %d entries\n", numEntries);
318
319 result = 0;
320
321 bail:
322 return result;
323 }
324
325 /*
326 * Open the specified file read-only. We examine the contents and verify
327 * that it appears to be a valid zip file.
328 *
329 * This will be called on non-Zip files, especially during VM startup, so
330 * we don't want to be too noisy about certain types of failure. (Do
331 * we want a "quiet" flag?)
332 *
333 * On success, we fill out the contents of "pArchive" and return 0. On
334 * failure we return the errno value.
335 */
dexZipOpenArchive(const char * fileName,ZipArchive * pArchive)336 int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
337 {
338 int fd, err;
339
340 LOGV("Opening as zip '%s' %p\n", fileName, pArchive);
341
342 memset(pArchive, 0, sizeof(ZipArchive));
343
344 fd = open(fileName, O_RDONLY, 0);
345 if (fd < 0) {
346 err = errno ? errno : -1;
347 LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
348 return err;
349 }
350
351 return dexZipPrepArchive(fd, fileName, pArchive);
352 }
353
354 /*
355 * Prepare to access a ZipArchive through an open file descriptor.
356 *
357 * On success, we fill out the contents of "pArchive" and return 0.
358 */
dexZipPrepArchive(int fd,const char * debugFileName,ZipArchive * pArchive)359 int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
360 {
361 int result = -1;
362
363 memset(pArchive, 0, sizeof(*pArchive));
364 pArchive->mFd = fd;
365
366 if (mapCentralDirectory(fd, debugFileName, pArchive) != 0)
367 goto bail;
368
369 if (parseZipArchive(pArchive) != 0) {
370 LOGV("Zip: parsing '%s' failed\n", debugFileName);
371 goto bail;
372 }
373
374 /* success */
375 result = 0;
376
377 bail:
378 if (result != 0)
379 dexZipCloseArchive(pArchive);
380 return result;
381 }
382
383
384 /*
385 * Close a ZipArchive, closing the file and freeing the contents.
386 *
387 * NOTE: the ZipArchive may not have been fully created.
388 */
dexZipCloseArchive(ZipArchive * pArchive)389 void dexZipCloseArchive(ZipArchive* pArchive)
390 {
391 LOGV("Closing archive %p\n", pArchive);
392
393 if (pArchive->mFd >= 0)
394 close(pArchive->mFd);
395
396 sysReleaseShmem(&pArchive->mDirectoryMap);
397
398 free(pArchive->mHashTable);
399
400 /* ensure nobody tries to use the ZipArchive after it's closed */
401 pArchive->mDirectoryOffset = -1;
402 pArchive->mFd = -1;
403 pArchive->mNumEntries = -1;
404 pArchive->mHashTableSize = -1;
405 pArchive->mHashTable = NULL;
406 }
407
408
409 /*
410 * Find a matching entry.
411 *
412 * Returns 0 if not found.
413 */
dexZipFindEntry(const ZipArchive * pArchive,const char * entryName)414 ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName)
415 {
416 int nameLen = strlen(entryName);
417 unsigned int hash = computeHash(entryName, nameLen);
418 const int hashTableSize = pArchive->mHashTableSize;
419 int ent = hash & (hashTableSize-1);
420
421 while (pArchive->mHashTable[ent].name != NULL) {
422 if (pArchive->mHashTable[ent].nameLen == nameLen &&
423 memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0)
424 {
425 /* match */
426 return (ZipEntry)(long)(ent + kZipEntryAdj);
427 }
428
429 ent = (ent + 1) & (hashTableSize-1);
430 }
431
432 return NULL;
433 }
434
435 #if 0
436 /*
437 * Find the Nth entry.
438 *
439 * This currently involves walking through the sparse hash table, counting
440 * non-empty entries. If we need to speed this up we can either allocate
441 * a parallel lookup table or (perhaps better) provide an iterator interface.
442 */
443 ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx)
444 {
445 if (idx < 0 || idx >= pArchive->mNumEntries) {
446 LOGW("Invalid index %d\n", idx);
447 return NULL;
448 }
449
450 int ent;
451 for (ent = 0; ent < pArchive->mHashTableSize; ent++) {
452 if (pArchive->mHashTable[ent].name != NULL) {
453 if (idx-- == 0)
454 return (ZipEntry) (ent + kZipEntryAdj);
455 }
456 }
457
458 return NULL;
459 }
460 #endif
461
462 /*
463 * Get the useful fields from the zip entry.
464 *
465 * Returns non-zero if the contents of the fields (particularly the data
466 * offset) appear to be bogus.
467 */
dexZipGetEntryInfo(const ZipArchive * pArchive,ZipEntry entry,int * pMethod,size_t * pUncompLen,size_t * pCompLen,off_t * pOffset,long * pModWhen,long * pCrc32)468 int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
469 int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
470 long* pModWhen, long* pCrc32)
471 {
472 int ent = entryToIndex(pArchive, entry);
473 if (ent < 0)
474 return -1;
475
476 /*
477 * Recover the start of the central directory entry from the filename
478 * pointer. The filename is the first entry past the fixed-size data,
479 * so we can just subtract back from that.
480 */
481 const unsigned char* basePtr = (const unsigned char*)
482 pArchive->mDirectoryMap.addr;
483 const unsigned char* ptr = (const unsigned char*)
484 pArchive->mHashTable[ent].name;
485 off_t cdOffset = pArchive->mDirectoryOffset;
486
487 ptr -= kCDELen;
488
489 int method = get2LE(ptr + kCDEMethod);
490 if (pMethod != NULL)
491 *pMethod = method;
492
493 if (pModWhen != NULL)
494 *pModWhen = get4LE(ptr + kCDEModWhen);
495 if (pCrc32 != NULL)
496 *pCrc32 = get4LE(ptr + kCDECRC);
497
498 size_t compLen = get4LE(ptr + kCDECompLen);
499 if (pCompLen != NULL)
500 *pCompLen = compLen;
501 size_t uncompLen = get4LE(ptr + kCDEUncompLen);
502 if (pUncompLen != NULL)
503 *pUncompLen = uncompLen;
504
505 /*
506 * If requested, determine the offset of the start of the data. All we
507 * have is the offset to the Local File Header, which is variable size,
508 * so we have to read the contents of the struct to figure out where
509 * the actual data starts.
510 *
511 * We also need to make sure that the lengths are not so large that
512 * somebody trying to map the compressed or uncompressed data runs
513 * off the end of the mapped region.
514 *
515 * Note we don't verify compLen/uncompLen if they don't request the
516 * dataOffset, because dataOffset is expensive to determine. However,
517 * if they don't have the file offset, they're not likely to be doing
518 * anything with the contents.
519 */
520 if (pOffset != NULL) {
521 long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
522 if (localHdrOffset + kLFHLen >= cdOffset) {
523 LOGW("Zip: bad local hdr offset in zip\n");
524 return -1;
525 }
526
527 u1 lfhBuf[kLFHLen];
528 if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
529 LOGW("Zip: failed seeking to lfh at offset %ld\n", localHdrOffset);
530 return -1;
531 }
532 ssize_t actual =
533 TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf)));
534 if (actual != sizeof(lfhBuf)) {
535 LOGW("Zip: failed reading lfh from offset %ld\n", localHdrOffset);
536 return -1;
537 }
538
539 if (get4LE(lfhBuf) != kLFHSignature) {
540 LOGW("Zip: didn't find signature at start of lfh, offset=%ld\n",
541 localHdrOffset);
542 return -1;
543 }
544
545 off_t dataOffset = localHdrOffset + kLFHLen
546 + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
547 if (dataOffset >= cdOffset) {
548 LOGW("Zip: bad data offset %ld in zip\n", (long) dataOffset);
549 return -1;
550 }
551
552 /* check lengths */
553 if ((off_t)(dataOffset + compLen) > cdOffset) {
554 LOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)\n",
555 (long) dataOffset, compLen, (long) cdOffset);
556 return -1;
557 }
558
559 if (method == kCompressStored &&
560 (off_t)(dataOffset + uncompLen) > cdOffset)
561 {
562 LOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)\n",
563 (long) dataOffset, uncompLen, (long) cdOffset);
564 return -1;
565 }
566
567 *pOffset = dataOffset;
568 }
569 return 0;
570 }
571
572 /*
573 * Uncompress "deflate" data from the archive's file to an open file
574 * descriptor.
575 */
inflateToFile(int inFd,int outFd,size_t uncompLen,size_t compLen)576 static int inflateToFile(int inFd, int outFd, size_t uncompLen, size_t compLen)
577 {
578 int result = -1;
579 const size_t kBufSize = 32768;
580 unsigned char* readBuf = (unsigned char*) malloc(kBufSize);
581 unsigned char* writeBuf = (unsigned char*) malloc(kBufSize);
582 z_stream zstream;
583 int zerr;
584
585 if (readBuf == NULL || writeBuf == NULL)
586 goto bail;
587
588 /*
589 * Initialize the zlib stream struct.
590 */
591 memset(&zstream, 0, sizeof(zstream));
592 zstream.zalloc = Z_NULL;
593 zstream.zfree = Z_NULL;
594 zstream.opaque = Z_NULL;
595 zstream.next_in = NULL;
596 zstream.avail_in = 0;
597 zstream.next_out = (Bytef*) writeBuf;
598 zstream.avail_out = kBufSize;
599 zstream.data_type = Z_UNKNOWN;
600
601 /*
602 * Use the undocumented "negative window bits" feature to tell zlib
603 * that there's no zlib header waiting for it.
604 */
605 zerr = inflateInit2(&zstream, -MAX_WBITS);
606 if (zerr != Z_OK) {
607 if (zerr == Z_VERSION_ERROR) {
608 LOGE("Installed zlib is not compatible with linked version (%s)\n",
609 ZLIB_VERSION);
610 } else {
611 LOGW("Call to inflateInit2 failed (zerr=%d)\n", zerr);
612 }
613 goto bail;
614 }
615
616 /*
617 * Loop while we have more to do.
618 */
619 do {
620 /* read as much as we can */
621 if (zstream.avail_in == 0) {
622 size_t getSize = (compLen > kBufSize) ? kBufSize : compLen;
623
624 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize));
625 if (actual != (ssize_t) getSize) {
626 LOGW("Zip: inflate read failed (%d vs %zd)\n",
627 (int)actual, getSize);
628 goto z_bail;
629 }
630
631 compLen -= getSize;
632
633 zstream.next_in = readBuf;
634 zstream.avail_in = getSize;
635 }
636
637 /* uncompress the data */
638 zerr = inflate(&zstream, Z_NO_FLUSH);
639 if (zerr != Z_OK && zerr != Z_STREAM_END) {
640 LOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
641 zerr, zstream.next_in, zstream.avail_in,
642 zstream.next_out, zstream.avail_out);
643 goto z_bail;
644 }
645
646 /* write when we're full or when we're done */
647 if (zstream.avail_out == 0 ||
648 (zerr == Z_STREAM_END && zstream.avail_out != kBufSize))
649 {
650 size_t writeSize = zstream.next_out - writeBuf;
651 if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0)
652 goto z_bail;
653
654 zstream.next_out = writeBuf;
655 zstream.avail_out = kBufSize;
656 }
657 } while (zerr == Z_OK);
658
659 assert(zerr == Z_STREAM_END); /* other errors should've been caught */
660
661 /* paranoia */
662 if (zstream.total_out != uncompLen) {
663 LOGW("Zip: size mismatch on inflated file (%ld vs %zd)\n",
664 zstream.total_out, uncompLen);
665 goto z_bail;
666 }
667
668 result = 0;
669
670 z_bail:
671 inflateEnd(&zstream); /* free up any allocated structures */
672
673 bail:
674 free(readBuf);
675 free(writeBuf);
676 return result;
677 }
678
679 /*
680 * Copy bytes from input to output.
681 */
copyFileToFile(int inFd,int outFd,size_t uncompLen)682 static int copyFileToFile(int inFd, int outFd, size_t uncompLen)
683 {
684 const size_t kBufSize = 32768;
685 unsigned char buf[kBufSize];
686
687 while (uncompLen != 0) {
688 size_t getSize = (uncompLen > kBufSize) ? kBufSize : uncompLen;
689
690 ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
691 if (actual != (ssize_t) getSize) {
692 LOGW("Zip: copy read failed (%d vs %zd)\n", (int)actual, getSize);
693 return -1;
694 }
695
696 if (sysWriteFully(outFd, buf, getSize, "Zip copy") != 0)
697 return -1;
698
699 uncompLen -= getSize;
700 }
701
702 return 0;
703 }
704
705 /*
706 * Uncompress an entry, in its entirety, to an open file descriptor.
707 *
708 * TODO: this doesn't verify the data's CRC, but probably should (especially
709 * for uncompressed data).
710 */
dexZipExtractEntryToFile(const ZipArchive * pArchive,const ZipEntry entry,int fd)711 int dexZipExtractEntryToFile(const ZipArchive* pArchive,
712 const ZipEntry entry, int fd)
713 {
714 int result = -1;
715 int ent = entryToIndex(pArchive, entry);
716 if (ent < 0) {
717 LOGW("Zip: extract can't find entry %p\n", entry);
718 goto bail;
719 }
720
721 int method;
722 size_t uncompLen, compLen;
723 off_t dataOffset;
724
725 if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen,
726 &dataOffset, NULL, NULL) != 0)
727 {
728 goto bail;
729 }
730 if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) {
731 LOGW("Zip: lseek to data at %ld failed\n", (long) dataOffset);
732 goto bail;
733 }
734
735 if (method == kCompressStored) {
736 if (copyFileToFile(pArchive->mFd, fd, uncompLen) != 0)
737 goto bail;
738 } else {
739 if (inflateToFile(pArchive->mFd, fd, uncompLen, compLen) != 0)
740 goto bail;
741 }
742
743 result = 0;
744
745 bail:
746 return result;
747 }
748