blob: 92125c9da8bb8e6de03edf99852d985b7a224870 [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);
Winson89bb24e2019-04-03 15:50:36 -0700256 if (result != NO_ERROR) {
257 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260
261 pAsset->mAccessMode = mode;
262 return pAsset;
263}
264
265/*
266 * Create a new Asset from compressed data in an open file.
267 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800268/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
270 AccessMode mode)
271{
272 _CompressedAsset* pAsset;
273 status_t result;
274
275 pAsset = new _CompressedAsset;
276 result = pAsset->openChunk(fd, offset, compressionMethod,
277 uncompressedLen, compressedLen);
Winson89bb24e2019-04-03 15:50:36 -0700278 if (result != NO_ERROR) {
279 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700281 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
283 pAsset->mAccessMode = mode;
284 return pAsset;
285}
286#endif
287
288/*
289 * Create a new Asset from a memory mapping.
290 */
291/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
292 AccessMode mode)
293{
294 _FileAsset* pAsset;
295 status_t result;
296
297 pAsset = new _FileAsset;
298 result = pAsset->openChunk(dataMap);
Winson40e0d2a2019-03-18 13:46:54 -0700299 if (result != NO_ERROR) {
300 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 return NULL;
Winson40e0d2a2019-03-18 13:46:54 -0700302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303
304 pAsset->mAccessMode = mode;
305 return pAsset;
306}
307
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800308/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
309 AccessMode mode)
310{
311 std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
312
313 status_t result = pAsset->openChunk(dataMap.get());
314 if (result != NO_ERROR) {
315 return NULL;
316 }
317
318 // We succeeded, so relinquish control of dataMap
319 (void) dataMap.release();
320 pAsset->mAccessMode = mode;
321 return std::move(pAsset);
322}
323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324/*
325 * Create a new Asset from compressed data in a memory mapping.
326 */
327/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
Narayan Kamath4600dd02015-06-16 12:02:57 +0100328 size_t uncompressedLen, AccessMode mode)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329{
330 _CompressedAsset* pAsset;
331 status_t result;
332
333 pAsset = new _CompressedAsset;
Narayan Kamath4600dd02015-06-16 12:02:57 +0100334 result = pAsset->openChunk(dataMap, uncompressedLen);
Winson89bb24e2019-04-03 15:50:36 -0700335 if (result != NO_ERROR) {
336 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700338 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339
340 pAsset->mAccessMode = mode;
341 return pAsset;
342}
343
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800344/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
345 size_t uncompressedLen, AccessMode mode)
346{
347 std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
348
349 status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
350 if (result != NO_ERROR) {
351 return NULL;
352 }
353
354 // We succeeded, so relinquish control of dataMap
355 (void) dataMap.release();
356 pAsset->mAccessMode = mode;
357 return std::move(pAsset);
358}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359
360/*
361 * Do generic seek() housekeeping. Pass in the offset/whence values from
362 * the seek request, along with the current chunk offset and the chunk
363 * length.
364 *
365 * Returns the new chunk offset, or -1 if the seek is illegal.
366 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800367off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368{
Kenny Rootddb76c42010-11-24 12:56:06 -0800369 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370
371 switch (whence) {
372 case SEEK_SET:
373 newOffset = offset;
374 break;
375 case SEEK_CUR:
376 newOffset = curPosn + offset;
377 break;
378 case SEEK_END:
379 newOffset = maxPosn + offset;
380 break;
381 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000382 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800383 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 assert(false);
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 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000389 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800391 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
393
394 return newOffset;
395}
396
397
398/*
399 * ===========================================================================
400 * _FileAsset
401 * ===========================================================================
402 */
403
404/*
405 * Constructor.
406 */
407_FileAsset::_FileAsset(void)
408 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
409{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700410 // Register the Asset with the global list here after it is fully constructed and its
411 // vtable pointer points to this concrete type. b/31113965
412 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413}
414
415/*
416 * Destructor. Release resources.
417 */
418_FileAsset::~_FileAsset(void)
419{
420 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700421
422 // Unregister the Asset from the global list here before it is destructed and while its vtable
423 // pointer still points to this concrete type. b/31113965
424 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425}
426
427/*
428 * Operate on a chunk of an uncompressed file.
429 *
430 * Zero-length chunks are allowed.
431 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800432status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433{
434 assert(mFp == NULL); // no reopen
435 assert(mMap == NULL);
436 assert(fd >= 0);
437 assert(offset >= 0);
438
439 /*
440 * Seek to end to get file length.
441 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800442 off64_t fileLength;
443 fileLength = lseek64(fd, 0, SEEK_END);
444 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000446 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 return UNKNOWN_ERROR;
448 }
449
Kenny Rootddb76c42010-11-24 12:56:06 -0800450 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000451 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 (long) offset, (long) length, (long) fileLength);
453 return BAD_INDEX;
454 }
455
456 /* after fdopen, the fd will be closed on fclose() */
457 mFp = fdopen(fd, "rb");
458 if (mFp == NULL)
459 return UNKNOWN_ERROR;
460
461 mStart = offset;
462 mLength = length;
463 assert(mOffset == 0);
464
465 /* seek the FILE* to the start of chunk */
466 if (fseek(mFp, mStart, SEEK_SET) != 0) {
467 assert(false);
468 }
469
470 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 return NO_ERROR;
473}
474
475/*
476 * Create the chunk from the map.
477 */
478status_t _FileAsset::openChunk(FileMap* dataMap)
479{
480 assert(mFp == NULL); // no reopen
481 assert(mMap == NULL);
482 assert(dataMap != NULL);
483
484 mMap = dataMap;
485 mStart = -1; // not used
486 mLength = dataMap->getDataLength();
487 assert(mOffset == 0);
488
489 return NO_ERROR;
490}
491
492/*
493 * Read a chunk of data.
494 */
495ssize_t _FileAsset::read(void* buf, size_t count)
496{
497 size_t maxLen;
498 size_t actual;
499
500 assert(mOffset >= 0 && mOffset <= mLength);
501
502 if (getAccessMode() == ACCESS_BUFFER) {
503 /*
504 * On first access, read or map the entire file. The caller has
505 * requested buffer access, either because they're going to be
506 * using the buffer or because what they're doing has appropriate
507 * performance needs and access patterns.
508 */
509 if (mBuf == NULL)
510 getBuffer(false);
511 }
512
513 /* adjust count if we're near EOF */
514 maxLen = mLength - mOffset;
515 if (count > maxLen)
516 count = maxLen;
517
518 if (!count)
519 return 0;
520
521 if (mMap != NULL) {
522 /* copy from mapped area */
523 //printf("map read\n");
524 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
525 actual = count;
526 } else if (mBuf != NULL) {
527 /* copy from buffer */
528 //printf("buf read\n");
529 memcpy(buf, (char*)mBuf + mOffset, count);
530 actual = count;
531 } else {
532 /* read from the file */
533 //printf("file read\n");
534 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000535 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 ftell(mFp), (long) mStart, (long) mOffset);
537 assert(false);
538 }
539
540 /*
541 * This returns 0 on error or eof. We need to use ferror() or feof()
542 * to tell the difference, but we don't currently have those on the
543 * device. However, we know how much data is *supposed* to be in the
544 * file, so if we don't read the full amount we know something is
545 * hosed.
546 */
547 actual = fread(buf, 1, count, mFp);
548 if (actual == 0) // something failed -- I/O error?
549 return -1;
550
551 assert(actual == count);
552 }
553
554 mOffset += actual;
555 return actual;
556}
557
558/*
559 * Seek to a new position.
560 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800561off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562{
Kenny Rootddb76c42010-11-24 12:56:06 -0800563 off64_t newPosn;
564 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565
566 // compute new position within chunk
567 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800568 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 return newPosn;
570
Kenny Rootddb76c42010-11-24 12:56:06 -0800571 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572
573 if (mFp != NULL) {
574 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800575 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 }
577
578 mOffset = actualOffset - mStart;
579 return mOffset;
580}
581
582/*
583 * Close the asset.
584 */
585void _FileAsset::close(void)
586{
587 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000588 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 mMap = NULL;
590 }
591 if (mBuf != NULL) {
592 delete[] mBuf;
593 mBuf = NULL;
594 }
595
596 if (mFileName != NULL) {
597 free(mFileName);
598 mFileName = NULL;
599 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700600
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 if (mFp != NULL) {
602 // can only be NULL when called from destructor
603 // (otherwise we would never return this object)
604 fclose(mFp);
605 mFp = NULL;
606 }
607}
608
609/*
610 * Return a read-only pointer to a buffer.
611 *
612 * We can either read the whole thing in or map the relevant piece of
613 * the source file. Ideally a map would be established at a higher
614 * level and we'd be using a different object, but we didn't, so we
615 * deal with it here.
616 */
617const void* _FileAsset::getBuffer(bool wordAligned)
618{
619 /* subsequent requests just use what we did previously */
620 if (mBuf != NULL)
621 return mBuf;
622 if (mMap != NULL) {
623 if (!wordAligned) {
624 return mMap->getDataPtr();
625 }
626 return ensureAlignment(mMap);
627 }
628
629 assert(mFp != NULL);
630
631 if (mLength < kReadVsMapThreshold) {
632 unsigned char* buf;
633 long allocLen;
634
635 /* zero-length files are allowed; not sure about zero-len allocs */
636 /* (works fine with gcc + x86linux) */
637 allocLen = mLength;
638 if (mLength == 0)
639 allocLen = 1;
640
641 buf = new unsigned char[allocLen];
642 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000643 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 return NULL;
645 }
646
Steve Block71f2cf12011-10-20 11:56:00 +0100647 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 if (mLength > 0) {
649 long oldPosn = ftell(mFp);
650 fseek(mFp, mStart, SEEK_SET);
651 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000652 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 delete[] buf;
654 return NULL;
655 }
656 fseek(mFp, oldPosn, SEEK_SET);
657 }
658
Steve Block71f2cf12011-10-20 11:56:00 +0100659 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660
661 mBuf = buf;
662 return mBuf;
663 } else {
664 FileMap* map;
665
666 map = new FileMap;
667 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000668 delete map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 return NULL;
670 }
671
Steve Block71f2cf12011-10-20 11:56:00 +0100672 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673
674 mMap = map;
675 if (!wordAligned) {
676 return mMap->getDataPtr();
677 }
678 return ensureAlignment(mMap);
679 }
680}
681
Kenny Rootddb76c42010-11-24 12:56:06 -0800682int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683{
684 if (mMap != NULL) {
685 const char* fname = mMap->getFileName();
686 if (fname == NULL) {
687 fname = mFileName;
688 }
689 if (fname == NULL) {
690 return -1;
691 }
692 *outStart = mMap->getDataOffset();
693 *outLength = mMap->getDataLength();
694 return open(fname, O_RDONLY | O_BINARY);
695 }
696 if (mFileName == NULL) {
697 return -1;
698 }
699 *outStart = mStart;
700 *outLength = mLength;
701 return open(mFileName, O_RDONLY | O_BINARY);
702}
703
704const void* _FileAsset::ensureAlignment(FileMap* map)
705{
706 void* data = map->getDataPtr();
707 if ((((size_t)data)&0x3) == 0) {
708 // We can return this directly if it is aligned on a word
709 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100710 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700711 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 return data;
713 }
714 // If not aligned on a word boundary, then we need to copy it into
715 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100716 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700717 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 unsigned char* buf = new unsigned char[mLength];
719 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000720 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 return NULL;
722 }
723 memcpy(buf, data, mLength);
724 mBuf = buf;
725 return buf;
726}
727
728/*
729 * ===========================================================================
730 * _CompressedAsset
731 * ===========================================================================
732 */
733
734/*
735 * Constructor.
736 */
737_CompressedAsset::_CompressedAsset(void)
738 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700739 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700741 // Register the Asset with the global list here after it is fully constructed and its
742 // vtable pointer points to this concrete type. b/31113965
743 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744}
745
746/*
747 * Destructor. Release resources.
748 */
749_CompressedAsset::~_CompressedAsset(void)
750{
751 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700752
753 // Unregister the Asset from the global list here before it is destructed and while its vtable
754 // pointer still points to this concrete type. b/31113965
755 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756}
757
758/*
759 * Open a chunk of compressed data inside a file.
760 *
761 * This currently just sets up some values and returns. On the first
762 * read, we expand the entire file into a buffer and return data from it.
763 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800764status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
766{
767 assert(mFd < 0); // no re-open
768 assert(mMap == NULL);
769 assert(fd >= 0);
770 assert(offset >= 0);
771 assert(compressedLen > 0);
772
773 if (compressionMethod != ZipFileRO::kCompressDeflated) {
774 assert(false);
775 return UNKNOWN_ERROR;
776 }
777
778 mStart = offset;
779 mCompressedLen = compressedLen;
780 mUncompressedLen = uncompressedLen;
781 assert(mOffset == 0);
782 mFd = fd;
783 assert(mBuf == NULL);
784
Christopher Tateb100cbf2010-07-26 11:24:18 -0700785 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
786 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
787 }
788
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 return NO_ERROR;
790}
791
792/*
793 * Open a chunk of compressed data in a mapped region.
794 *
795 * Nothing is expanded until the first read call.
796 */
Narayan Kamath4600dd02015-06-16 12:02:57 +0100797status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798{
799 assert(mFd < 0); // no re-open
800 assert(mMap == NULL);
801 assert(dataMap != NULL);
802
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 mMap = dataMap;
804 mStart = -1; // not used
805 mCompressedLen = dataMap->getDataLength();
806 mUncompressedLen = uncompressedLen;
807 assert(mOffset == 0);
808
Christopher Tateb100cbf2010-07-26 11:24:18 -0700809 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
810 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 return NO_ERROR;
813}
814
815/*
816 * Read data from a chunk of compressed data.
817 *
818 * [For now, that's just copying data out of a buffer.]
819 */
820ssize_t _CompressedAsset::read(void* buf, size_t count)
821{
822 size_t maxLen;
823 size_t actual;
824
825 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
826
Christopher Tateb100cbf2010-07-26 11:24:18 -0700827 /* If we're relying on a streaming inflater, go through that */
828 if (mZipInflater) {
829 actual = mZipInflater->read(buf, count);
830 } else {
831 if (mBuf == NULL) {
832 if (getBuffer(false) == NULL)
833 return -1;
834 }
835 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836
Christopher Tateb100cbf2010-07-26 11:24:18 -0700837 /* adjust count if we're near EOF */
838 maxLen = mUncompressedLen - mOffset;
839 if (count > maxLen)
840 count = maxLen;
841
842 if (!count)
843 return 0;
844
845 /* copy from buffer */
846 //printf("comp buf read\n");
847 memcpy(buf, (char*)mBuf + mOffset, count);
848 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850
851 mOffset += actual;
852 return actual;
853}
854
855/*
856 * Handle a seek request.
857 *
858 * If we're working in a streaming mode, this is going to be fairly
859 * expensive, because it requires plowing through a bunch of compressed
860 * data.
861 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800862off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863{
Kenny Rootddb76c42010-11-24 12:56:06 -0800864 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865
866 // compute new position within chunk
867 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800868 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 return newPosn;
870
Christopher Tateb100cbf2010-07-26 11:24:18 -0700871 if (mZipInflater) {
872 mZipInflater->seekAbsolute(newPosn);
873 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 mOffset = newPosn;
875 return mOffset;
876}
877
878/*
879 * Close the asset.
880 */
881void _CompressedAsset::close(void)
882{
883 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000884 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 mMap = NULL;
886 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700887
888 delete[] mBuf;
889 mBuf = NULL;
890
891 delete mZipInflater;
892 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893
894 if (mFd > 0) {
895 ::close(mFd);
896 mFd = -1;
897 }
898}
899
900/*
901 * Get a pointer to a read-only buffer of data.
902 *
903 * The first time this is called, we expand the compressed data into a
904 * buffer.
905 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000906const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907{
908 unsigned char* buf = NULL;
909
910 if (mBuf != NULL)
911 return mBuf;
912
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 /*
914 * Allocate a buffer and read the file into it.
915 */
916 buf = new unsigned char[mUncompressedLen];
917 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000918 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 goto bail;
920 }
921
922 if (mMap != NULL) {
Narayan Kamathafd31e02013-12-03 13:16:03 +0000923 if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 mUncompressedLen, mCompressedLen))
925 goto bail;
926 } else {
927 assert(mFd >= 0);
928
929 /*
930 * Seek to the start of the compressed data.
931 */
932 if (lseek(mFd, mStart, SEEK_SET) != mStart)
933 goto bail;
934
935 /*
936 * Expand the data into it.
937 */
938 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
939 mCompressedLen))
940 goto bail;
941 }
942
Christopher Tateb100cbf2010-07-26 11:24:18 -0700943 /*
944 * Success - now that we have the full asset in RAM we
945 * no longer need the streaming inflater
946 */
947 delete mZipInflater;
948 mZipInflater = NULL;
949
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 mBuf = buf;
951 buf = NULL;
952
953bail:
954 delete[] buf;
955 return mBuf;
956}
957