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