OSDN Git Service

TypeRef, not ClassRef.
[android-x86/dalvik.git] / libdex / ZipArchive.c
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 }