1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // 18 // Provide access to a read-only asset. 19 // 20 21 #define LOG_TAG "asset" 22 //#define NDEBUG 0 23 24 #include <androidfw/Asset.h> 25 #include <androidfw/StreamingZipInflater.h> 26 #include <androidfw/ZipFileRO.h> 27 #include <androidfw/ZipUtils.h> 28 #include <utils/Atomic.h> 29 #include <utils/FileMap.h> 30 #include <utils/Log.h> 31 #include <utils/threads.h> 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <memory.h> 37 #include <string.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <unistd.h> 41 42 using namespace android; 43 44 #ifndef O_BINARY 45 # define O_BINARY 0 46 #endif 47 48 static const bool kIsDebug = false; 49 50 static Mutex gAssetLock; 51 static int32_t gCount = 0; 52 static Asset* gHead = NULL; 53 static Asset* gTail = NULL; 54 55 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 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 96 int32_t Asset::getGlobalCount() 97 { 98 AutoMutex _l(gAssetLock); 99 return gCount; 100 } 101 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 642 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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