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