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