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