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