blob: 9a95fdf80cb57796211b3641d1bea1de9f0e39a6 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080024#include <androidfw/Asset.h>
25#include <androidfw/StreamingZipInflater.h>
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080026#include <androidfw/Util.h>
Mathias Agopian1f5762e2013-05-06 20:20:34 -070027#include <androidfw/ZipFileRO.h>
28#include <androidfw/ZipUtils.h>
Steven Morelandfb7952f2018-02-23 14:58:50 -080029#include <cutils/atomic.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030#include <utils/FileMap.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031#include <utils/Log.h>
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070032#include <utils/threads.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034#include <assert.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080035#include <errno.h>
36#include <fcntl.h>
37#include <memory.h>
38#include <string.h>
Kenny Rootddb76c42010-11-24 12:56:06 -080039#include <sys/stat.h>
40#include <sys/types.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080041#include <unistd.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43using namespace android;
44
45#ifndef O_BINARY
46# define O_BINARY 0
47#endif
48
Andreas Gampe2204f0b2014-10-21 23:04:54 -070049static const bool kIsDebug = false;
50
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070051static Mutex gAssetLock;
52static int32_t gCount = 0;
53static Asset* gHead = NULL;
54static Asset* gTail = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
Adam Lesinski0358efe2016-10-17 13:50:56 -070056void 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
74void 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097int32_t Asset::getGlobalCount()
98{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070099 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 return gCount;
101}
102
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700103String8 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());
Kenny Rootddb76c42010-11-24 12:56:06 -0800112 off64_t size = (cur->getLength()+512)/1024;
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700113 char buf[64];
George Burgess IVa346f542016-03-02 13:34:44 -0800114 snprintf(buf, sizeof(buf), ": %dK\n", (int)size);
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700115 res.append(buf);
116 }
117 cur = cur->mNext;
118 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700119
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700120 return res;
121}
122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123Asset::Asset(void)
Adam Lesinski0358efe2016-10-17 13:50:56 -0700124 : mAccessMode(ACCESS_UNKNOWN), mNext(NULL), mPrev(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126}
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;
Kenny Rootddb76c42010-11-24 12:56:06 -0800138 off64_t length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 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 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800151 // TODO(kroot): replace this with fstat despite the plea above.
152#if 1
153 length = lseek64(fd, 0, SEEK_END);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 if (length < 0) {
155 ::close(fd);
156 return NULL;
157 }
Kenny Rootddb76c42010-11-24 12:56:06 -0800158 (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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171
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;
Kenny Rootddb76c42010-11-24 12:56:06 -0800195 off64_t fileLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 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) {
Steve Block5baa3a62011-12-20 16:23:08 +0000226 ALOGD("File '%s' is not in gzip format\n", fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 ::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 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800248/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 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 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800266/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 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);
Winson40e0d2a2019-03-18 13:46:54 -0700295 if (result != NO_ERROR) {
296 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 return NULL;
Winson40e0d2a2019-03-18 13:46:54 -0700298 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299
300 pAsset->mAccessMode = mode;
301 return pAsset;
302}
303
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800304/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
305 AccessMode mode)
306{
307 std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
308
309 status_t result = pAsset->openChunk(dataMap.get());
310 if (result != NO_ERROR) {
311 return NULL;
312 }
313
314 // We succeeded, so relinquish control of dataMap
315 (void) dataMap.release();
316 pAsset->mAccessMode = mode;
317 return std::move(pAsset);
318}
319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320/*
321 * Create a new Asset from compressed data in a memory mapping.
322 */
323/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
Narayan Kamath4600dd02015-06-16 12:02:57 +0100324 size_t uncompressedLen, AccessMode mode)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325{
326 _CompressedAsset* pAsset;
327 status_t result;
328
329 pAsset = new _CompressedAsset;
Narayan Kamath4600dd02015-06-16 12:02:57 +0100330 result = pAsset->openChunk(dataMap, uncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 if (result != NO_ERROR)
332 return NULL;
333
334 pAsset->mAccessMode = mode;
335 return pAsset;
336}
337
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800338/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
339 size_t uncompressedLen, AccessMode mode)
340{
341 std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
342
343 status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
344 if (result != NO_ERROR) {
345 return NULL;
346 }
347
348 // We succeeded, so relinquish control of dataMap
349 (void) dataMap.release();
350 pAsset->mAccessMode = mode;
351 return std::move(pAsset);
352}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353
354/*
355 * Do generic seek() housekeeping. Pass in the offset/whence values from
356 * the seek request, along with the current chunk offset and the chunk
357 * length.
358 *
359 * Returns the new chunk offset, or -1 if the seek is illegal.
360 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800361off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362{
Kenny Rootddb76c42010-11-24 12:56:06 -0800363 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364
365 switch (whence) {
366 case SEEK_SET:
367 newOffset = offset;
368 break;
369 case SEEK_CUR:
370 newOffset = curPosn + offset;
371 break;
372 case SEEK_END:
373 newOffset = maxPosn + offset;
374 break;
375 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000376 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800377 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800379 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 }
381
382 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000383 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800385 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 }
387
388 return newOffset;
389}
390
391
392/*
393 * ===========================================================================
394 * _FileAsset
395 * ===========================================================================
396 */
397
398/*
399 * Constructor.
400 */
401_FileAsset::_FileAsset(void)
402 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
403{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700404 // Register the Asset with the global list here after it is fully constructed and its
405 // vtable pointer points to this concrete type. b/31113965
406 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407}
408
409/*
410 * Destructor. Release resources.
411 */
412_FileAsset::~_FileAsset(void)
413{
414 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700415
416 // Unregister the Asset from the global list here before it is destructed and while its vtable
417 // pointer still points to this concrete type. b/31113965
418 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419}
420
421/*
422 * Operate on a chunk of an uncompressed file.
423 *
424 * Zero-length chunks are allowed.
425 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800426status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427{
428 assert(mFp == NULL); // no reopen
429 assert(mMap == NULL);
430 assert(fd >= 0);
431 assert(offset >= 0);
432
433 /*
434 * Seek to end to get file length.
435 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800436 off64_t fileLength;
437 fileLength = lseek64(fd, 0, SEEK_END);
438 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000440 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 return UNKNOWN_ERROR;
442 }
443
Kenny Rootddb76c42010-11-24 12:56:06 -0800444 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000445 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 (long) offset, (long) length, (long) fileLength);
447 return BAD_INDEX;
448 }
449
450 /* after fdopen, the fd will be closed on fclose() */
451 mFp = fdopen(fd, "rb");
452 if (mFp == NULL)
453 return UNKNOWN_ERROR;
454
455 mStart = offset;
456 mLength = length;
457 assert(mOffset == 0);
458
459 /* seek the FILE* to the start of chunk */
460 if (fseek(mFp, mStart, SEEK_SET) != 0) {
461 assert(false);
462 }
463
464 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 return NO_ERROR;
467}
468
469/*
470 * Create the chunk from the map.
471 */
472status_t _FileAsset::openChunk(FileMap* dataMap)
473{
474 assert(mFp == NULL); // no reopen
475 assert(mMap == NULL);
476 assert(dataMap != NULL);
477
478 mMap = dataMap;
479 mStart = -1; // not used
480 mLength = dataMap->getDataLength();
481 assert(mOffset == 0);
482
483 return NO_ERROR;
484}
485
486/*
487 * Read a chunk of data.
488 */
489ssize_t _FileAsset::read(void* buf, size_t count)
490{
491 size_t maxLen;
492 size_t actual;
493
494 assert(mOffset >= 0 && mOffset <= mLength);
495
496 if (getAccessMode() == ACCESS_BUFFER) {
497 /*
498 * On first access, read or map the entire file. The caller has
499 * requested buffer access, either because they're going to be
500 * using the buffer or because what they're doing has appropriate
501 * performance needs and access patterns.
502 */
503 if (mBuf == NULL)
504 getBuffer(false);
505 }
506
507 /* adjust count if we're near EOF */
508 maxLen = mLength - mOffset;
509 if (count > maxLen)
510 count = maxLen;
511
512 if (!count)
513 return 0;
514
515 if (mMap != NULL) {
516 /* copy from mapped area */
517 //printf("map read\n");
518 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
519 actual = count;
520 } else if (mBuf != NULL) {
521 /* copy from buffer */
522 //printf("buf read\n");
523 memcpy(buf, (char*)mBuf + mOffset, count);
524 actual = count;
525 } else {
526 /* read from the file */
527 //printf("file read\n");
528 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000529 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 ftell(mFp), (long) mStart, (long) mOffset);
531 assert(false);
532 }
533
534 /*
535 * This returns 0 on error or eof. We need to use ferror() or feof()
536 * to tell the difference, but we don't currently have those on the
537 * device. However, we know how much data is *supposed* to be in the
538 * file, so if we don't read the full amount we know something is
539 * hosed.
540 */
541 actual = fread(buf, 1, count, mFp);
542 if (actual == 0) // something failed -- I/O error?
543 return -1;
544
545 assert(actual == count);
546 }
547
548 mOffset += actual;
549 return actual;
550}
551
552/*
553 * Seek to a new position.
554 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800555off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556{
Kenny Rootddb76c42010-11-24 12:56:06 -0800557 off64_t newPosn;
558 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559
560 // compute new position within chunk
561 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800562 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 return newPosn;
564
Kenny Rootddb76c42010-11-24 12:56:06 -0800565 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566
567 if (mFp != NULL) {
568 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800569 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 }
571
572 mOffset = actualOffset - mStart;
573 return mOffset;
574}
575
576/*
577 * Close the asset.
578 */
579void _FileAsset::close(void)
580{
581 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000582 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 mMap = NULL;
584 }
585 if (mBuf != NULL) {
586 delete[] mBuf;
587 mBuf = NULL;
588 }
589
590 if (mFileName != NULL) {
591 free(mFileName);
592 mFileName = NULL;
593 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 if (mFp != NULL) {
596 // can only be NULL when called from destructor
597 // (otherwise we would never return this object)
598 fclose(mFp);
599 mFp = NULL;
600 }
601}
602
603/*
604 * Return a read-only pointer to a buffer.
605 *
606 * We can either read the whole thing in or map the relevant piece of
607 * the source file. Ideally a map would be established at a higher
608 * level and we'd be using a different object, but we didn't, so we
609 * deal with it here.
610 */
611const void* _FileAsset::getBuffer(bool wordAligned)
612{
613 /* subsequent requests just use what we did previously */
614 if (mBuf != NULL)
615 return mBuf;
616 if (mMap != NULL) {
617 if (!wordAligned) {
618 return mMap->getDataPtr();
619 }
620 return ensureAlignment(mMap);
621 }
622
623 assert(mFp != NULL);
624
625 if (mLength < kReadVsMapThreshold) {
626 unsigned char* buf;
627 long allocLen;
628
629 /* zero-length files are allowed; not sure about zero-len allocs */
630 /* (works fine with gcc + x86linux) */
631 allocLen = mLength;
632 if (mLength == 0)
633 allocLen = 1;
634
635 buf = new unsigned char[allocLen];
636 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000637 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 return NULL;
639 }
640
Steve Block71f2cf12011-10-20 11:56:00 +0100641 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 if (mLength > 0) {
643 long oldPosn = ftell(mFp);
644 fseek(mFp, mStart, SEEK_SET);
645 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000646 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 delete[] buf;
648 return NULL;
649 }
650 fseek(mFp, oldPosn, SEEK_SET);
651 }
652
Steve Block71f2cf12011-10-20 11:56:00 +0100653 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654
655 mBuf = buf;
656 return mBuf;
657 } else {
658 FileMap* map;
659
660 map = new FileMap;
661 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000662 delete map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 return NULL;
664 }
665
Steve Block71f2cf12011-10-20 11:56:00 +0100666 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667
668 mMap = map;
669 if (!wordAligned) {
670 return mMap->getDataPtr();
671 }
672 return ensureAlignment(mMap);
673 }
674}
675
Kenny Rootddb76c42010-11-24 12:56:06 -0800676int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677{
678 if (mMap != NULL) {
679 const char* fname = mMap->getFileName();
680 if (fname == NULL) {
681 fname = mFileName;
682 }
683 if (fname == NULL) {
684 return -1;
685 }
686 *outStart = mMap->getDataOffset();
687 *outLength = mMap->getDataLength();
688 return open(fname, O_RDONLY | O_BINARY);
689 }
690 if (mFileName == NULL) {
691 return -1;
692 }
693 *outStart = mStart;
694 *outLength = mLength;
695 return open(mFileName, O_RDONLY | O_BINARY);
696}
697
698const void* _FileAsset::ensureAlignment(FileMap* map)
699{
700 void* data = map->getDataPtr();
701 if ((((size_t)data)&0x3) == 0) {
702 // We can return this directly if it is aligned on a word
703 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100704 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700705 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 return data;
707 }
708 // If not aligned on a word boundary, then we need to copy it into
709 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100710 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700711 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 unsigned char* buf = new unsigned char[mLength];
713 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000714 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 return NULL;
716 }
717 memcpy(buf, data, mLength);
718 mBuf = buf;
719 return buf;
720}
721
722/*
723 * ===========================================================================
724 * _CompressedAsset
725 * ===========================================================================
726 */
727
728/*
729 * Constructor.
730 */
731_CompressedAsset::_CompressedAsset(void)
732 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700733 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700735 // Register the Asset with the global list here after it is fully constructed and its
736 // vtable pointer points to this concrete type. b/31113965
737 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738}
739
740/*
741 * Destructor. Release resources.
742 */
743_CompressedAsset::~_CompressedAsset(void)
744{
745 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700746
747 // Unregister the Asset from the global list here before it is destructed and while its vtable
748 // pointer still points to this concrete type. b/31113965
749 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750}
751
752/*
753 * Open a chunk of compressed data inside a file.
754 *
755 * This currently just sets up some values and returns. On the first
756 * read, we expand the entire file into a buffer and return data from it.
757 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800758status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
760{
761 assert(mFd < 0); // no re-open
762 assert(mMap == NULL);
763 assert(fd >= 0);
764 assert(offset >= 0);
765 assert(compressedLen > 0);
766
767 if (compressionMethod != ZipFileRO::kCompressDeflated) {
768 assert(false);
769 return UNKNOWN_ERROR;
770 }
771
772 mStart = offset;
773 mCompressedLen = compressedLen;
774 mUncompressedLen = uncompressedLen;
775 assert(mOffset == 0);
776 mFd = fd;
777 assert(mBuf == NULL);
778
Christopher Tateb100cbf2010-07-26 11:24:18 -0700779 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
780 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
781 }
782
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 return NO_ERROR;
784}
785
786/*
787 * Open a chunk of compressed data in a mapped region.
788 *
789 * Nothing is expanded until the first read call.
790 */
Narayan Kamath4600dd02015-06-16 12:02:57 +0100791status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792{
793 assert(mFd < 0); // no re-open
794 assert(mMap == NULL);
795 assert(dataMap != NULL);
796
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 mMap = dataMap;
798 mStart = -1; // not used
799 mCompressedLen = dataMap->getDataLength();
800 mUncompressedLen = uncompressedLen;
801 assert(mOffset == 0);
802
Christopher Tateb100cbf2010-07-26 11:24:18 -0700803 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
804 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
805 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 return NO_ERROR;
807}
808
809/*
810 * Read data from a chunk of compressed data.
811 *
812 * [For now, that's just copying data out of a buffer.]
813 */
814ssize_t _CompressedAsset::read(void* buf, size_t count)
815{
816 size_t maxLen;
817 size_t actual;
818
819 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
820
Christopher Tateb100cbf2010-07-26 11:24:18 -0700821 /* If we're relying on a streaming inflater, go through that */
822 if (mZipInflater) {
823 actual = mZipInflater->read(buf, count);
824 } else {
825 if (mBuf == NULL) {
826 if (getBuffer(false) == NULL)
827 return -1;
828 }
829 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
Christopher Tateb100cbf2010-07-26 11:24:18 -0700831 /* adjust count if we're near EOF */
832 maxLen = mUncompressedLen - mOffset;
833 if (count > maxLen)
834 count = maxLen;
835
836 if (!count)
837 return 0;
838
839 /* copy from buffer */
840 //printf("comp buf read\n");
841 memcpy(buf, (char*)mBuf + mOffset, count);
842 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844
845 mOffset += actual;
846 return actual;
847}
848
849/*
850 * Handle a seek request.
851 *
852 * If we're working in a streaming mode, this is going to be fairly
853 * expensive, because it requires plowing through a bunch of compressed
854 * data.
855 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800856off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857{
Kenny Rootddb76c42010-11-24 12:56:06 -0800858 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859
860 // compute new position within chunk
861 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800862 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 return newPosn;
864
Christopher Tateb100cbf2010-07-26 11:24:18 -0700865 if (mZipInflater) {
866 mZipInflater->seekAbsolute(newPosn);
867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 mOffset = newPosn;
869 return mOffset;
870}
871
872/*
873 * Close the asset.
874 */
875void _CompressedAsset::close(void)
876{
877 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000878 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 mMap = NULL;
880 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700881
882 delete[] mBuf;
883 mBuf = NULL;
884
885 delete mZipInflater;
886 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887
888 if (mFd > 0) {
889 ::close(mFd);
890 mFd = -1;
891 }
892}
893
894/*
895 * Get a pointer to a read-only buffer of data.
896 *
897 * The first time this is called, we expand the compressed data into a
898 * buffer.
899 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000900const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901{
902 unsigned char* buf = NULL;
903
904 if (mBuf != NULL)
905 return mBuf;
906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 /*
908 * Allocate a buffer and read the file into it.
909 */
910 buf = new unsigned char[mUncompressedLen];
911 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000912 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 goto bail;
914 }
915
916 if (mMap != NULL) {
Narayan Kamathafd31e02013-12-03 13:16:03 +0000917 if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 mUncompressedLen, mCompressedLen))
919 goto bail;
920 } else {
921 assert(mFd >= 0);
922
923 /*
924 * Seek to the start of the compressed data.
925 */
926 if (lseek(mFd, mStart, SEEK_SET) != mStart)
927 goto bail;
928
929 /*
930 * Expand the data into it.
931 */
932 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
933 mCompressedLen))
934 goto bail;
935 }
936
Christopher Tateb100cbf2010-07-26 11:24:18 -0700937 /*
938 * Success - now that we have the full asset in RAM we
939 * no longer need the streaming inflater
940 */
941 delete mZipInflater;
942 mZipInflater = NULL;
943
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 mBuf = buf;
945 buf = NULL;
946
947bail:
948 delete[] buf;
949 return mBuf;
950}
951