OSDN Git Service

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