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