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