• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 // Provide access to a read-only asset.
19 //
20 
21 #define LOG_TAG "asset"
22 //#define NDEBUG 0
23 
24 #include <utils/Asset.h>
25 #include <utils/Atomic.h>
26 #include <utils/FileMap.h>
27 #include <utils/ZipUtils.h>
28 #include <utils/ZipFileRO.h>
29 #include <utils/Log.h>
30 #include <utils/threads.h>
31 
32 #include <string.h>
33 #include <memory.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <assert.h>
37 
38 using namespace android;
39 
40 #ifndef O_BINARY
41 # define O_BINARY 0
42 #endif
43 
44 static Mutex gAssetLock;
45 static int32_t gCount = 0;
46 static Asset* gHead = NULL;
47 static Asset* gTail = NULL;
48 
getGlobalCount()49 int32_t Asset::getGlobalCount()
50 {
51     AutoMutex _l(gAssetLock);
52     return gCount;
53 }
54 
getAssetAllocations()55 String8 Asset::getAssetAllocations()
56 {
57     AutoMutex _l(gAssetLock);
58     String8 res;
59     Asset* cur = gHead;
60     while (cur != NULL) {
61         if (cur->isAllocated()) {
62             res.append("    ");
63             res.append(cur->getAssetSource());
64             off_t size = (cur->getLength()+512)/1024;
65             char buf[64];
66             sprintf(buf, ": %dK\n", (int)size);
67             res.append(buf);
68         }
69         cur = cur->mNext;
70     }
71 
72     return res;
73 }
74 
Asset(void)75 Asset::Asset(void)
76     : mAccessMode(ACCESS_UNKNOWN)
77 {
78     AutoMutex _l(gAssetLock);
79     gCount++;
80     mNext = mPrev = NULL;
81     if (gTail == NULL) {
82         gHead = gTail = this;
83   	} else {
84   	    mPrev = gTail;
85   	    gTail->mNext = this;
86   	    gTail = this;
87   	}
88     //LOGI("Creating Asset %p #%d\n", this, gCount);
89 }
90 
~Asset(void)91 Asset::~Asset(void)
92 {
93     AutoMutex _l(gAssetLock);
94 	gCount--;
95     if (gHead == this) {
96         gHead = mNext;
97     }
98     if (gTail == this) {
99         gTail = mPrev;
100     }
101     if (mNext != NULL) {
102         mNext->mPrev = mPrev;
103     }
104     if (mPrev != NULL) {
105         mPrev->mNext = mNext;
106     }
107     mNext = mPrev = NULL;
108     //LOGI("Destroying Asset in %p #%d\n", this, gCount);
109 }
110 
111 /*
112  * Create a new Asset from a file on disk.  There is a fair chance that
113  * the file doesn't actually exist.
114  *
115  * We can use "mode" to decide how we want to go about it.
116  */
createFromFile(const char * fileName,AccessMode mode)117 /*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
118 {
119     _FileAsset* pAsset;
120     status_t result;
121     off_t length;
122     int fd;
123 
124     fd = open(fileName, O_RDONLY | O_BINARY);
125     if (fd < 0)
126         return NULL;
127 
128     /*
129      * Under Linux, the lseek fails if we actually opened a directory.  To
130      * be correct we should test the file type explicitly, but since we
131      * always open things read-only it doesn't really matter, so there's
132      * no value in incurring the extra overhead of an fstat() call.
133      */
134     length = lseek(fd, 0, SEEK_END);
135     if (length < 0) {
136         ::close(fd);
137         return NULL;
138     }
139     (void) lseek(fd, 0, SEEK_SET);
140 
141     pAsset = new _FileAsset;
142     result = pAsset->openChunk(fileName, fd, 0, length);
143     if (result != NO_ERROR) {
144         delete pAsset;
145         return NULL;
146     }
147 
148     pAsset->mAccessMode = mode;
149     return pAsset;
150 }
151 
152 
153 /*
154  * Create a new Asset from a compressed file on disk.  There is a fair chance
155  * that the file doesn't actually exist.
156  *
157  * We currently support gzip files.  We might want to handle .bz2 someday.
158  */
createFromCompressedFile(const char * fileName,AccessMode mode)159 /*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
160     AccessMode mode)
161 {
162     _CompressedAsset* pAsset;
163     status_t result;
164     off_t fileLen;
165     bool scanResult;
166     long offset;
167     int method;
168     long uncompressedLen, compressedLen;
169     int fd;
170 
171     fd = open(fileName, O_RDONLY | O_BINARY);
172     if (fd < 0)
173         return NULL;
174 
175     fileLen = lseek(fd, 0, SEEK_END);
176     if (fileLen < 0) {
177         ::close(fd);
178         return NULL;
179     }
180     (void) lseek(fd, 0, SEEK_SET);
181 
182     /* want buffered I/O for the file scan; must dup so fclose() is safe */
183     FILE* fp = fdopen(dup(fd), "rb");
184     if (fp == NULL) {
185         ::close(fd);
186         return NULL;
187     }
188 
189     unsigned long crc32;
190     scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
191                     &compressedLen, &crc32);
192     offset = ftell(fp);
193     fclose(fp);
194     if (!scanResult) {
195         LOGD("File '%s' is not in gzip format\n", fileName);
196         ::close(fd);
197         return NULL;
198     }
199 
200     pAsset = new _CompressedAsset;
201     result = pAsset->openChunk(fd, offset, method, uncompressedLen,
202                 compressedLen);
203     if (result != NO_ERROR) {
204         delete pAsset;
205         return NULL;
206     }
207 
208     pAsset->mAccessMode = mode;
209     return pAsset;
210 }
211 
212 
213 #if 0
214 /*
215  * Create a new Asset from part of an open file.
216  */
217 /*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset,
218     size_t length, AccessMode mode)
219 {
220     _FileAsset* pAsset;
221     status_t result;
222 
223     pAsset = new _FileAsset;
224     result = pAsset->openChunk(NULL, fd, offset, length);
225     if (result != NO_ERROR)
226         return NULL;
227 
228     pAsset->mAccessMode = mode;
229     return pAsset;
230 }
231 
232 /*
233  * Create a new Asset from compressed data in an open file.
234  */
235 /*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset,
236     int compressionMethod, size_t uncompressedLen, size_t compressedLen,
237     AccessMode mode)
238 {
239     _CompressedAsset* pAsset;
240     status_t result;
241 
242     pAsset = new _CompressedAsset;
243     result = pAsset->openChunk(fd, offset, compressionMethod,
244                 uncompressedLen, compressedLen);
245     if (result != NO_ERROR)
246         return NULL;
247 
248     pAsset->mAccessMode = mode;
249     return pAsset;
250 }
251 #endif
252 
253 /*
254  * Create a new Asset from a memory mapping.
255  */
createFromUncompressedMap(FileMap * dataMap,AccessMode mode)256 /*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
257     AccessMode mode)
258 {
259     _FileAsset* pAsset;
260     status_t result;
261 
262     pAsset = new _FileAsset;
263     result = pAsset->openChunk(dataMap);
264     if (result != NO_ERROR)
265         return NULL;
266 
267     pAsset->mAccessMode = mode;
268     return pAsset;
269 }
270 
271 /*
272  * Create a new Asset from compressed data in a memory mapping.
273  */
createFromCompressedMap(FileMap * dataMap,int method,size_t uncompressedLen,AccessMode mode)274 /*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
275     int method, size_t uncompressedLen, AccessMode mode)
276 {
277     _CompressedAsset* pAsset;
278     status_t result;
279 
280     pAsset = new _CompressedAsset;
281     result = pAsset->openChunk(dataMap, method, uncompressedLen);
282     if (result != NO_ERROR)
283         return NULL;
284 
285     pAsset->mAccessMode = mode;
286     return pAsset;
287 }
288 
289 
290 /*
291  * Do generic seek() housekeeping.  Pass in the offset/whence values from
292  * the seek request, along with the current chunk offset and the chunk
293  * length.
294  *
295  * Returns the new chunk offset, or -1 if the seek is illegal.
296  */
handleSeek(off_t offset,int whence,off_t curPosn,off_t maxPosn)297 off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn)
298 {
299     off_t newOffset;
300 
301     switch (whence) {
302     case SEEK_SET:
303         newOffset = offset;
304         break;
305     case SEEK_CUR:
306         newOffset = curPosn + offset;
307         break;
308     case SEEK_END:
309         newOffset = maxPosn + offset;
310         break;
311     default:
312         LOGW("unexpected whence %d\n", whence);
313         // this was happening due to an off_t size mismatch
314         assert(false);
315         return (off_t) -1;
316     }
317 
318     if (newOffset < 0 || newOffset > maxPosn) {
319         LOGW("seek out of range: want %ld, end=%ld\n",
320             (long) newOffset, (long) maxPosn);
321         return (off_t) -1;
322     }
323 
324     return newOffset;
325 }
326 
327 
328 /*
329  * ===========================================================================
330  *      _FileAsset
331  * ===========================================================================
332  */
333 
334 /*
335  * Constructor.
336  */
_FileAsset(void)337 _FileAsset::_FileAsset(void)
338     : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
339 {
340 }
341 
342 /*
343  * Destructor.  Release resources.
344  */
~_FileAsset(void)345 _FileAsset::~_FileAsset(void)
346 {
347     close();
348 }
349 
350 /*
351  * Operate on a chunk of an uncompressed file.
352  *
353  * Zero-length chunks are allowed.
354  */
openChunk(const char * fileName,int fd,off_t offset,size_t length)355 status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length)
356 {
357     assert(mFp == NULL);    // no reopen
358     assert(mMap == NULL);
359     assert(fd >= 0);
360     assert(offset >= 0);
361 
362     /*
363      * Seek to end to get file length.
364      */
365     off_t fileLength;
366     fileLength = lseek(fd, 0, SEEK_END);
367     if (fileLength == (off_t) -1) {
368         // probably a bad file descriptor
369         LOGD("failed lseek (errno=%d)\n", errno);
370         return UNKNOWN_ERROR;
371     }
372 
373     if ((off_t) (offset + length) > fileLength) {
374         LOGD("start (%ld) + len (%ld) > end (%ld)\n",
375             (long) offset, (long) length, (long) fileLength);
376         return BAD_INDEX;
377     }
378 
379     /* after fdopen, the fd will be closed on fclose() */
380     mFp = fdopen(fd, "rb");
381     if (mFp == NULL)
382         return UNKNOWN_ERROR;
383 
384     mStart = offset;
385     mLength = length;
386     assert(mOffset == 0);
387 
388     /* seek the FILE* to the start of chunk */
389     if (fseek(mFp, mStart, SEEK_SET) != 0) {
390         assert(false);
391     }
392 
393     mFileName = fileName != NULL ? strdup(fileName) : NULL;
394 
395     return NO_ERROR;
396 }
397 
398 /*
399  * Create the chunk from the map.
400  */
openChunk(FileMap * dataMap)401 status_t _FileAsset::openChunk(FileMap* dataMap)
402 {
403     assert(mFp == NULL);    // no reopen
404     assert(mMap == NULL);
405     assert(dataMap != NULL);
406 
407     mMap = dataMap;
408     mStart = -1;            // not used
409     mLength = dataMap->getDataLength();
410     assert(mOffset == 0);
411 
412     return NO_ERROR;
413 }
414 
415 /*
416  * Read a chunk of data.
417  */
read(void * buf,size_t count)418 ssize_t _FileAsset::read(void* buf, size_t count)
419 {
420     size_t maxLen;
421     size_t actual;
422 
423     assert(mOffset >= 0 && mOffset <= mLength);
424 
425     if (getAccessMode() == ACCESS_BUFFER) {
426         /*
427          * On first access, read or map the entire file.  The caller has
428          * requested buffer access, either because they're going to be
429          * using the buffer or because what they're doing has appropriate
430          * performance needs and access patterns.
431          */
432         if (mBuf == NULL)
433             getBuffer(false);
434     }
435 
436     /* adjust count if we're near EOF */
437     maxLen = mLength - mOffset;
438     if (count > maxLen)
439         count = maxLen;
440 
441     if (!count)
442         return 0;
443 
444     if (mMap != NULL) {
445         /* copy from mapped area */
446         //printf("map read\n");
447         memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
448         actual = count;
449     } else if (mBuf != NULL) {
450         /* copy from buffer */
451         //printf("buf read\n");
452         memcpy(buf, (char*)mBuf + mOffset, count);
453         actual = count;
454     } else {
455         /* read from the file */
456         //printf("file read\n");
457         if (ftell(mFp) != mStart + mOffset) {
458             LOGE("Hosed: %ld != %ld+%ld\n",
459                 ftell(mFp), (long) mStart, (long) mOffset);
460             assert(false);
461         }
462 
463         /*
464          * This returns 0 on error or eof.  We need to use ferror() or feof()
465          * to tell the difference, but we don't currently have those on the
466          * device.  However, we know how much data is *supposed* to be in the
467          * file, so if we don't read the full amount we know something is
468          * hosed.
469          */
470         actual = fread(buf, 1, count, mFp);
471         if (actual == 0)        // something failed -- I/O error?
472             return -1;
473 
474         assert(actual == count);
475     }
476 
477     mOffset += actual;
478     return actual;
479 }
480 
481 /*
482  * Seek to a new position.
483  */
seek(off_t offset,int whence)484 off_t _FileAsset::seek(off_t offset, int whence)
485 {
486     off_t newPosn;
487     long actualOffset;
488 
489     // compute new position within chunk
490     newPosn = handleSeek(offset, whence, mOffset, mLength);
491     if (newPosn == (off_t) -1)
492         return newPosn;
493 
494     actualOffset = (long) (mStart + newPosn);
495 
496     if (mFp != NULL) {
497         if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
498             return (off_t) -1;
499     }
500 
501     mOffset = actualOffset - mStart;
502     return mOffset;
503 }
504 
505 /*
506  * Close the asset.
507  */
close(void)508 void _FileAsset::close(void)
509 {
510     if (mMap != NULL) {
511         mMap->release();
512         mMap = NULL;
513     }
514     if (mBuf != NULL) {
515         delete[] mBuf;
516         mBuf = NULL;
517     }
518 
519     if (mFileName != NULL) {
520         free(mFileName);
521         mFileName = NULL;
522     }
523 
524     if (mFp != NULL) {
525         // can only be NULL when called from destructor
526         // (otherwise we would never return this object)
527         fclose(mFp);
528         mFp = NULL;
529     }
530 }
531 
532 /*
533  * Return a read-only pointer to a buffer.
534  *
535  * We can either read the whole thing in or map the relevant piece of
536  * the source file.  Ideally a map would be established at a higher
537  * level and we'd be using a different object, but we didn't, so we
538  * deal with it here.
539  */
getBuffer(bool wordAligned)540 const void* _FileAsset::getBuffer(bool wordAligned)
541 {
542     /* subsequent requests just use what we did previously */
543     if (mBuf != NULL)
544         return mBuf;
545     if (mMap != NULL) {
546         if (!wordAligned) {
547             return  mMap->getDataPtr();
548         }
549         return ensureAlignment(mMap);
550     }
551 
552     assert(mFp != NULL);
553 
554     if (mLength < kReadVsMapThreshold) {
555         unsigned char* buf;
556         long allocLen;
557 
558         /* zero-length files are allowed; not sure about zero-len allocs */
559         /* (works fine with gcc + x86linux) */
560         allocLen = mLength;
561         if (mLength == 0)
562             allocLen = 1;
563 
564         buf = new unsigned char[allocLen];
565         if (buf == NULL) {
566             LOGE("alloc of %ld bytes failed\n", (long) allocLen);
567             return NULL;
568         }
569 
570         LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
571         if (mLength > 0) {
572             long oldPosn = ftell(mFp);
573             fseek(mFp, mStart, SEEK_SET);
574             if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
575                 LOGE("failed reading %ld bytes\n", (long) mLength);
576                 delete[] buf;
577                 return NULL;
578             }
579             fseek(mFp, oldPosn, SEEK_SET);
580         }
581 
582         LOGV(" getBuffer: loaded into buffer\n");
583 
584         mBuf = buf;
585         return mBuf;
586     } else {
587         FileMap* map;
588 
589         map = new FileMap;
590         if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
591             map->release();
592             return NULL;
593         }
594 
595         LOGV(" getBuffer: mapped\n");
596 
597         mMap = map;
598         if (!wordAligned) {
599             return  mMap->getDataPtr();
600         }
601         return ensureAlignment(mMap);
602     }
603 }
604 
openFileDescriptor(off_t * outStart,off_t * outLength) const605 int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const
606 {
607     if (mMap != NULL) {
608         const char* fname = mMap->getFileName();
609         if (fname == NULL) {
610             fname = mFileName;
611         }
612         if (fname == NULL) {
613             return -1;
614         }
615         *outStart = mMap->getDataOffset();
616         *outLength = mMap->getDataLength();
617         return open(fname, O_RDONLY | O_BINARY);
618     }
619     if (mFileName == NULL) {
620         return -1;
621     }
622     *outStart = mStart;
623     *outLength = mLength;
624     return open(mFileName, O_RDONLY | O_BINARY);
625 }
626 
ensureAlignment(FileMap * map)627 const void* _FileAsset::ensureAlignment(FileMap* map)
628 {
629     void* data = map->getDataPtr();
630     if ((((size_t)data)&0x3) == 0) {
631         // We can return this directly if it is aligned on a word
632         // boundary.
633         LOGV("Returning aligned FileAsset %p (%s).", this,
634                 getAssetSource());
635         return data;
636     }
637     // If not aligned on a word boundary, then we need to copy it into
638     // our own buffer.
639     LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
640             getAssetSource(), (int)mLength);
641     unsigned char* buf = new unsigned char[mLength];
642     if (buf == NULL) {
643         LOGE("alloc of %ld bytes failed\n", (long) mLength);
644         return NULL;
645     }
646     memcpy(buf, data, mLength);
647     mBuf = buf;
648     return buf;
649 }
650 
651 /*
652  * ===========================================================================
653  *      _CompressedAsset
654  * ===========================================================================
655  */
656 
657 /*
658  * Constructor.
659  */
_CompressedAsset(void)660 _CompressedAsset::_CompressedAsset(void)
661     : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
662       mMap(NULL), mFd(-1), mBuf(NULL)
663 {
664 }
665 
666 /*
667  * Destructor.  Release resources.
668  */
~_CompressedAsset(void)669 _CompressedAsset::~_CompressedAsset(void)
670 {
671     close();
672 }
673 
674 /*
675  * Open a chunk of compressed data inside a file.
676  *
677  * This currently just sets up some values and returns.  On the first
678  * read, we expand the entire file into a buffer and return data from it.
679  */
openChunk(int fd,off_t offset,int compressionMethod,size_t uncompressedLen,size_t compressedLen)680 status_t _CompressedAsset::openChunk(int fd, off_t offset,
681     int compressionMethod, size_t uncompressedLen, size_t compressedLen)
682 {
683     assert(mFd < 0);        // no re-open
684     assert(mMap == NULL);
685     assert(fd >= 0);
686     assert(offset >= 0);
687     assert(compressedLen > 0);
688 
689     if (compressionMethod != ZipFileRO::kCompressDeflated) {
690         assert(false);
691         return UNKNOWN_ERROR;
692     }
693 
694     mStart = offset;
695     mCompressedLen = compressedLen;
696     mUncompressedLen = uncompressedLen;
697     assert(mOffset == 0);
698     mFd = fd;
699     assert(mBuf == NULL);
700 
701     return NO_ERROR;
702 }
703 
704 /*
705  * Open a chunk of compressed data in a mapped region.
706  *
707  * Nothing is expanded until the first read call.
708  */
openChunk(FileMap * dataMap,int compressionMethod,size_t uncompressedLen)709 status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
710     size_t uncompressedLen)
711 {
712     assert(mFd < 0);        // no re-open
713     assert(mMap == NULL);
714     assert(dataMap != NULL);
715 
716     if (compressionMethod != ZipFileRO::kCompressDeflated) {
717         assert(false);
718         return UNKNOWN_ERROR;
719     }
720 
721     mMap = dataMap;
722     mStart = -1;        // not used
723     mCompressedLen = dataMap->getDataLength();
724     mUncompressedLen = uncompressedLen;
725     assert(mOffset == 0);
726 
727     return NO_ERROR;
728 }
729 
730 /*
731  * Read data from a chunk of compressed data.
732  *
733  * [For now, that's just copying data out of a buffer.]
734  */
read(void * buf,size_t count)735 ssize_t _CompressedAsset::read(void* buf, size_t count)
736 {
737     size_t maxLen;
738     size_t actual;
739 
740     assert(mOffset >= 0 && mOffset <= mUncompressedLen);
741 
742     // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly
743 
744     if (mBuf == NULL) {
745         if (getBuffer(false) == NULL)
746             return -1;
747     }
748     assert(mBuf != NULL);
749 
750     /* adjust count if we're near EOF */
751     maxLen = mUncompressedLen - mOffset;
752     if (count > maxLen)
753         count = maxLen;
754 
755     if (!count)
756         return 0;
757 
758     /* copy from buffer */
759     //printf("comp buf read\n");
760     memcpy(buf, (char*)mBuf + mOffset, count);
761     actual = count;
762 
763     mOffset += actual;
764     return actual;
765 }
766 
767 /*
768  * Handle a seek request.
769  *
770  * If we're working in a streaming mode, this is going to be fairly
771  * expensive, because it requires plowing through a bunch of compressed
772  * data.
773  */
seek(off_t offset,int whence)774 off_t _CompressedAsset::seek(off_t offset, int whence)
775 {
776     off_t newPosn;
777 
778     // compute new position within chunk
779     newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
780     if (newPosn == (off_t) -1)
781         return newPosn;
782 
783     mOffset = newPosn;
784     return mOffset;
785 }
786 
787 /*
788  * Close the asset.
789  */
close(void)790 void _CompressedAsset::close(void)
791 {
792     if (mMap != NULL) {
793         mMap->release();
794         mMap = NULL;
795     }
796     if (mBuf != NULL) {
797         delete[] mBuf;
798         mBuf = NULL;
799     }
800 
801     if (mFd > 0) {
802         ::close(mFd);
803         mFd = -1;
804     }
805 }
806 
807 /*
808  * Get a pointer to a read-only buffer of data.
809  *
810  * The first time this is called, we expand the compressed data into a
811  * buffer.
812  */
getBuffer(bool wordAligned)813 const void* _CompressedAsset::getBuffer(bool wordAligned)
814 {
815     unsigned char* buf = NULL;
816 
817     if (mBuf != NULL)
818         return mBuf;
819 
820     if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
821         LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
822             (long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
823         goto bail;
824     }
825 
826     /*
827      * Allocate a buffer and read the file into it.
828      */
829     buf = new unsigned char[mUncompressedLen];
830     if (buf == NULL) {
831         LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
832         goto bail;
833     }
834 
835     if (mMap != NULL) {
836         if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
837                 mUncompressedLen, mCompressedLen))
838             goto bail;
839     } else {
840         assert(mFd >= 0);
841 
842         /*
843          * Seek to the start of the compressed data.
844          */
845         if (lseek(mFd, mStart, SEEK_SET) != mStart)
846             goto bail;
847 
848         /*
849          * Expand the data into it.
850          */
851         if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
852                 mCompressedLen))
853             goto bail;
854     }
855 
856     /* success! */
857     mBuf = buf;
858     buf = NULL;
859 
860 bail:
861     delete[] buf;
862     return mBuf;
863 }
864 
865