Home | History | Annotate | Download | only in androidfw
      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 
     55 int32_t Asset::getGlobalCount()
     56 {
     57     AutoMutex _l(gAssetLock);
     58     return gCount;
     59 }
     60 
     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 
     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 
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    629 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 
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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