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