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