OSDN Git Service

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