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