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