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(incfs::IncFsFileMap && dataMap,AccessMode mode,base::unique_fd fd)301  /*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
302                                                                     AccessMode mode,
303                                                                     base::unique_fd fd)
304  {
305      auto pAsset = util::make_unique<_FileAsset>();
306  
307      status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
308      if (result != NO_ERROR) {
309          return NULL;
310      }
311  
312      // We succeeded, so relinquish control of dataMap
313      pAsset->mAccessMode = mode;
314      return std::move(pAsset);
315  }
316  
317  /*
318   * Create a new Asset from compressed data in a memory mapping.
319   */
createFromCompressedMap(incfs::IncFsFileMap && dataMap,size_t uncompressedLen,AccessMode mode)320  /*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
321                                                                   size_t uncompressedLen,
322                                                                   AccessMode mode)
323  {
324    auto pAsset = util::make_unique<_CompressedAsset>();
325  
326    status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
327    if (result != NO_ERROR) {
328        return NULL;
329    }
330  
331    // We succeeded, so relinquish control of dataMap
332    pAsset->mAccessMode = mode;
333    return std::move(pAsset);
334  }
335  
336  /*
337   * Do generic seek() housekeeping.  Pass in the offset/whence values from
338   * the seek request, along with the current chunk offset and the chunk
339   * length.
340   *
341   * Returns the new chunk offset, or -1 if the seek is illegal.
342   */
handleSeek(off64_t offset,int whence,off64_t curPosn,off64_t maxPosn)343  off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
344  {
345      off64_t newOffset;
346  
347      switch (whence) {
348      case SEEK_SET:
349          newOffset = offset;
350          break;
351      case SEEK_CUR:
352          newOffset = curPosn + offset;
353          break;
354      case SEEK_END:
355          newOffset = maxPosn + offset;
356          break;
357      default:
358          ALOGW("unexpected whence %d\n", whence);
359          // this was happening due to an off64_t size mismatch
360          assert(false);
361          return (off64_t) -1;
362      }
363  
364      if (newOffset < 0 || newOffset > maxPosn) {
365          ALOGW("seek out of range: want %ld, end=%ld\n",
366              (long) newOffset, (long) maxPosn);
367          return (off64_t) -1;
368      }
369  
370      return newOffset;
371  }
372  
373  
374  /*
375   * ===========================================================================
376   *      _FileAsset
377   * ===========================================================================
378   */
379  
380  /*
381   * Constructor.
382   */
_FileAsset(void)383  _FileAsset::_FileAsset(void)
384      : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL)
385  {
386      // Register the Asset with the global list here after it is fully constructed and its
387      // vtable pointer points to this concrete type. b/31113965
388      registerAsset(this);
389  }
390  
391  /*
392   * Destructor.  Release resources.
393   */
~_FileAsset(void)394  _FileAsset::~_FileAsset(void)
395  {
396      close();
397  
398      // Unregister the Asset from the global list here before it is destructed and while its vtable
399      // pointer still points to this concrete type. b/31113965
400      unregisterAsset(this);
401  }
402  
403  /*
404   * Operate on a chunk of an uncompressed file.
405   *
406   * Zero-length chunks are allowed.
407   */
openChunk(const char * fileName,int fd,off64_t offset,size_t length)408  status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
409  {
410      assert(mFp == NULL);    // no reopen
411      assert(!mMap.has_value());
412      assert(fd >= 0);
413      assert(offset >= 0);
414  
415      /*
416       * Seek to end to get file length.
417       */
418      off64_t fileLength;
419      fileLength = lseek64(fd, 0, SEEK_END);
420      if (fileLength == (off64_t) -1) {
421          // probably a bad file descriptor
422          ALOGD("failed lseek (errno=%d)\n", errno);
423          return UNKNOWN_ERROR;
424      }
425  
426      if ((off64_t) (offset + length) > fileLength) {
427          ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
428              (long) offset, (long) length, (long) fileLength);
429          return BAD_INDEX;
430      }
431  
432      /* after fdopen, the fd will be closed on fclose() */
433      mFp = fdopen(fd, "rb");
434      if (mFp == NULL)
435          return UNKNOWN_ERROR;
436  
437      mStart = offset;
438      mLength = length;
439      assert(mOffset == 0);
440  
441      /* seek the FILE* to the start of chunk */
442      if (fseek(mFp, mStart, SEEK_SET) != 0) {
443          assert(false);
444      }
445  
446      mFileName = fileName != NULL ? strdup(fileName) : NULL;
447  
448      return NO_ERROR;
449  }
450  
451  /*
452   * Create the chunk from the map.
453   */
openChunk(incfs::IncFsFileMap && dataMap,base::unique_fd fd)454  status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd)
455  {
456      assert(mFp == NULL);    // no reopen
457      assert(!mMap.has_value());
458      assert(dataMap != NULL);
459  
460      mMap = std::move(dataMap);
461      mStart = -1;            // not used
462      mLength = mMap->length();
463      mFd = std::move(fd);
464      assert(mOffset == 0);
465  
466      return NO_ERROR;
467  }
468  
469  /*
470   * Read a chunk of data.
471   */
read(void * buf,size_t count)472  ssize_t _FileAsset::read(void* buf, size_t count)
473  {
474      size_t maxLen;
475      size_t actual;
476  
477      assert(mOffset >= 0 && mOffset <= mLength);
478  
479      if (getAccessMode() == ACCESS_BUFFER) {
480          /*
481           * On first access, read or map the entire file.  The caller has
482           * requested buffer access, either because they're going to be
483           * using the buffer or because what they're doing has appropriate
484           * performance needs and access patterns.
485           */
486          if (mBuf == NULL)
487              getBuffer(false);
488      }
489  
490      /* adjust count if we're near EOF */
491      maxLen = mLength - mOffset;
492      if (count > maxLen)
493          count = maxLen;
494  
495      if (!count)
496          return 0;
497  
498      if (mMap.has_value()) {
499          /* copy from mapped area */
500          //printf("map read\n");
501          const auto readPos = mMap->data().offset(mOffset).convert<char>();
502          if (!readPos.verify(count)) {
503              return -1;
504          }
505  
506          memcpy(buf, readPos.unsafe_ptr(), count);
507          actual = count;
508      } else if (mBuf != NULL) {
509          /* copy from buffer */
510          //printf("buf read\n");
511          memcpy(buf, (char*)mBuf + mOffset, count);
512          actual = count;
513      } else {
514          /* read from the file */
515          //printf("file read\n");
516          if (ftell(mFp) != mStart + mOffset) {
517              ALOGE("Hosed: %ld != %ld+%ld\n",
518                  ftell(mFp), (long) mStart, (long) mOffset);
519              assert(false);
520          }
521  
522          /*
523           * This returns 0 on error or eof.  We need to use ferror() or feof()
524           * to tell the difference, but we don't currently have those on the
525           * device.  However, we know how much data is *supposed* to be in the
526           * file, so if we don't read the full amount we know something is
527           * hosed.
528           */
529          actual = fread(buf, 1, count, mFp);
530          if (actual == 0)        // something failed -- I/O error?
531              return -1;
532  
533          assert(actual == count);
534      }
535  
536      mOffset += actual;
537      return actual;
538  }
539  
540  /*
541   * Seek to a new position.
542   */
seek(off64_t offset,int whence)543  off64_t _FileAsset::seek(off64_t offset, int whence)
544  {
545      off64_t newPosn;
546      off64_t actualOffset;
547  
548      // compute new position within chunk
549      newPosn = handleSeek(offset, whence, mOffset, mLength);
550      if (newPosn == (off64_t) -1)
551          return newPosn;
552  
553      actualOffset = mStart + newPosn;
554  
555      if (mFp != NULL) {
556          if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
557              return (off64_t) -1;
558      }
559  
560      mOffset = actualOffset - mStart;
561      return mOffset;
562  }
563  
564  /*
565   * Close the asset.
566   */
close(void)567  void _FileAsset::close(void)
568  {
569      if (mBuf != NULL) {
570          delete[] mBuf;
571          mBuf = NULL;
572      }
573  
574      if (mFileName != NULL) {
575          free(mFileName);
576          mFileName = NULL;
577      }
578  
579      if (mFp != NULL) {
580          // can only be NULL when called from destructor
581          // (otherwise we would never return this object)
582          fclose(mFp);
583          mFp = NULL;
584      }
585  }
586  
587  /*
588   * Return a read-only pointer to a buffer.
589   *
590   * We can either read the whole thing in or map the relevant piece of
591   * the source file.  Ideally a map would be established at a higher
592   * level and we'd be using a different object, but we didn't, so we
593   * deal with it here.
594   */
getBuffer(bool aligned)595  const void* _FileAsset::getBuffer(bool aligned)
596  {
597      auto buffer = getIncFsBuffer(aligned);
598      if (mBuf != NULL)
599          return mBuf;
600      if (!buffer.convert<uint8_t>().verify(mLength))
601          return NULL;
602      return buffer.unsafe_ptr();
603  }
604  
getIncFsBuffer(bool aligned)605  incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
606  {
607      /* subsequent requests just use what we did previously */
608      if (mBuf != NULL)
609          return mBuf;
610      if (mMap.has_value()) {
611          if (!aligned) {
612              return mMap->data();
613          }
614          return ensureAlignment(*mMap);
615      }
616  
617      assert(mFp != NULL);
618  
619      if (mLength < kReadVsMapThreshold) {
620          unsigned char* buf;
621          long allocLen;
622  
623          /* zero-length files are allowed; not sure about zero-len allocs */
624          /* (works fine with gcc + x86linux) */
625          allocLen = mLength;
626          if (mLength == 0)
627              allocLen = 1;
628  
629          buf = new unsigned char[allocLen];
630          if (buf == NULL) {
631              ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
632              return NULL;
633          }
634  
635          ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
636          if (mLength > 0) {
637              long oldPosn = ftell(mFp);
638              fseek(mFp, mStart, SEEK_SET);
639              if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
640                  ALOGE("failed reading %ld bytes\n", (long) mLength);
641                  delete[] buf;
642                  return NULL;
643              }
644              fseek(mFp, oldPosn, SEEK_SET);
645          }
646  
647          ALOGV(" getBuffer: loaded into buffer\n");
648  
649          mBuf = buf;
650          return mBuf;
651      } else {
652          incfs::IncFsFileMap map;
653          if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) {
654              return NULL;
655          }
656  
657          ALOGV(" getBuffer: mapped\n");
658  
659          mMap = std::move(map);
660          if (!aligned) {
661              return mMap->data();
662          }
663          return ensureAlignment(*mMap);
664      }
665  }
666  
openFileDescriptor(off64_t * outStart,off64_t * outLength) const667  int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
668  {
669      if (mMap.has_value()) {
670          if (mFd.ok()) {
671              *outStart = mMap->offset();
672              *outLength = mMap->length();
673              const int fd = dup(mFd);
674              if (fd < 0) {
675                  ALOGE("Unable to dup fd (%d).", mFd.get());
676                  return -1;
677              }
678              lseek64(fd, 0, SEEK_SET);
679              return fd;
680          }
681          const char* fname = mMap->file_name();
682          if (fname == NULL) {
683              fname = mFileName;
684          }
685          if (fname == NULL) {
686              return -1;
687          }
688          *outStart = mMap->offset();
689          *outLength = mMap->length();
690          return open(fname, O_RDONLY | O_BINARY);
691      }
692      if (mFileName == NULL) {
693          return -1;
694      }
695      *outStart = mStart;
696      *outLength = mLength;
697      return open(mFileName, O_RDONLY | O_BINARY);
698  }
699  
ensureAlignment(const incfs::IncFsFileMap & map)700  incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map)
701  {
702      const auto data = map.data();
703      if (util::IsFourByteAligned(data)) {
704          // We can return this directly if it is aligned on a word
705          // boundary.
706          ALOGV("Returning aligned FileAsset %p (%s).", this,
707                  getAssetSource());
708          return data;
709      }
710  
711       if (!data.convert<uint8_t>().verify(mLength)) {
712          return NULL;
713      }
714  
715      // If not aligned on a word boundary, then we need to copy it into
716      // our own buffer.
717      ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
718              getAssetSource(), (int)mLength);
719      unsigned char* buf = new unsigned char[mLength];
720      if (buf == NULL) {
721          ALOGE("alloc of %ld bytes failed\n", (long) mLength);
722          return NULL;
723      }
724  
725      memcpy(buf, data.unsafe_ptr(), mLength);
726      mBuf = buf;
727      return buf;
728  }
729  
730  /*
731   * ===========================================================================
732   *      _CompressedAsset
733   * ===========================================================================
734   */
735  
736  /*
737   * Constructor.
738   */
_CompressedAsset(void)739  _CompressedAsset::_CompressedAsset(void)
740      : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
741        mFd(-1), mZipInflater(NULL), mBuf(NULL)
742  {
743      // Register the Asset with the global list here after it is fully constructed and its
744      // vtable pointer points to this concrete type. b/31113965
745      registerAsset(this);
746  }
747  
748  /*
749   * Destructor.  Release resources.
750   */
~_CompressedAsset(void)751  _CompressedAsset::~_CompressedAsset(void)
752  {
753      close();
754  
755      // Unregister the Asset from the global list here before it is destructed and while its vtable
756      // pointer still points to this concrete type. b/31113965
757      unregisterAsset(this);
758  }
759  
760  /*
761   * Open a chunk of compressed data inside a file.
762   *
763   * This currently just sets up some values and returns.  On the first
764   * read, we expand the entire file into a buffer and return data from it.
765   */
openChunk(int fd,off64_t offset,int compressionMethod,size_t uncompressedLen,size_t compressedLen)766  status_t _CompressedAsset::openChunk(int fd, off64_t offset,
767      int compressionMethod, size_t uncompressedLen, size_t compressedLen)
768  {
769      assert(mFd < 0);        // no re-open
770      assert(!mMap.has_value());
771      assert(fd >= 0);
772      assert(offset >= 0);
773      assert(compressedLen > 0);
774  
775      if (compressionMethod != ZipFileRO::kCompressDeflated) {
776          assert(false);
777          return UNKNOWN_ERROR;
778      }
779  
780      mStart = offset;
781      mCompressedLen = compressedLen;
782      mUncompressedLen = uncompressedLen;
783      assert(mOffset == 0);
784      mFd = fd;
785      assert(mBuf == NULL);
786  
787      if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
788          mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
789      }
790  
791      return NO_ERROR;
792  }
793  
794  /*
795   * Open a chunk of compressed data in a mapped region.
796   *
797   * Nothing is expanded until the first read call.
798   */
openChunk(incfs::IncFsFileMap && dataMap,size_t uncompressedLen)799  status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen)
800  {
801      assert(mFd < 0);        // no re-open
802      assert(!mMap.has_value());
803      assert(dataMap != NULL);
804  
805      mMap = std::move(dataMap);
806      mStart = -1;        // not used
807      mCompressedLen = mMap->length();
808      mUncompressedLen = uncompressedLen;
809      assert(mOffset == 0);
810  
811      if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
812          mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen);
813      }
814      return NO_ERROR;
815  }
816  
817  /*
818   * Read data from a chunk of compressed data.
819   *
820   * [For now, that's just copying data out of a buffer.]
821   */
read(void * buf,size_t count)822  ssize_t _CompressedAsset::read(void* buf, size_t count)
823  {
824      size_t maxLen;
825      size_t actual;
826  
827      assert(mOffset >= 0 && mOffset <= mUncompressedLen);
828  
829      /* If we're relying on a streaming inflater, go through that */
830      if (mZipInflater) {
831          actual = mZipInflater->read(buf, count);
832      } else {
833          if (mBuf == NULL) {
834              if (getBuffer(false) == NULL)
835                  return -1;
836          }
837          assert(mBuf != NULL);
838  
839          /* adjust count if we're near EOF */
840          maxLen = mUncompressedLen - mOffset;
841          if (count > maxLen)
842              count = maxLen;
843  
844          if (!count)
845              return 0;
846  
847          /* copy from buffer */
848          //printf("comp buf read\n");
849          memcpy(buf, (char*)mBuf + mOffset, count);
850          actual = count;
851      }
852  
853      mOffset += actual;
854      return actual;
855  }
856  
857  /*
858   * Handle a seek request.
859   *
860   * If we're working in a streaming mode, this is going to be fairly
861   * expensive, because it requires plowing through a bunch of compressed
862   * data.
863   */
seek(off64_t offset,int whence)864  off64_t _CompressedAsset::seek(off64_t offset, int whence)
865  {
866      off64_t newPosn;
867  
868      // compute new position within chunk
869      newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
870      if (newPosn == (off64_t) -1)
871          return newPosn;
872  
873      if (mZipInflater) {
874          mZipInflater->seekAbsolute(newPosn);
875      }
876      mOffset = newPosn;
877      return mOffset;
878  }
879  
880  /*
881   * Close the asset.
882   */
close(void)883  void _CompressedAsset::close(void)
884  {
885      delete[] mBuf;
886      mBuf = NULL;
887  
888      delete mZipInflater;
889      mZipInflater = NULL;
890  
891      if (mFd > 0) {
892          ::close(mFd);
893          mFd = -1;
894      }
895  }
896  
897  /*
898   * Get a pointer to a read-only buffer of data.
899   *
900   * The first time this is called, we expand the compressed data into a
901   * buffer.
902   */
getBuffer(bool)903  const void* _CompressedAsset::getBuffer(bool)
904  {
905      unsigned char* buf = NULL;
906  
907      if (mBuf != NULL)
908          return mBuf;
909  
910      /*
911       * Allocate a buffer and read the file into it.
912       */
913      buf = new unsigned char[mUncompressedLen];
914      if (buf == NULL) {
915          ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
916          goto bail;
917      }
918  
919      if (mMap.has_value()) {
920          if (!ZipUtils::inflateToBuffer(mMap->data(), buf,
921                  mUncompressedLen, mCompressedLen))
922              goto bail;
923      } else {
924          assert(mFd >= 0);
925  
926          /*
927           * Seek to the start of the compressed data.
928           */
929          if (lseek(mFd, mStart, SEEK_SET) != mStart)
930              goto bail;
931  
932          /*
933           * Expand the data into it.
934           */
935          if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
936                  mCompressedLen))
937              goto bail;
938      }
939  
940      /*
941       * Success - now that we have the full asset in RAM we
942       * no longer need the streaming inflater
943       */
944      delete mZipInflater;
945      mZipInflater = NULL;
946  
947      mBuf = buf;
948      buf = NULL;
949  
950  bail:
951      delete[] buf;
952      return mBuf;
953  }
954  
getIncFsBuffer(bool aligned)955  incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) {
956      return incfs::map_ptr<void>(getBuffer(aligned));
957  }
958