blob: c132f343713f5be948718624fa7fc7b2e0d40578 [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{
Winson9947f1e2019-08-16 10:20:39 -0700136 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 */
145/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode)
146{
147 if (fd < 0) {
148 return NULL;
149 }
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 _FileAsset* pAsset;
152 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800153 off64_t length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154
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 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800161 // TODO(kroot): replace this with fstat despite the plea above.
162#if 1
163 length = lseek64(fd, 0, SEEK_END);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 if (length < 0) {
165 ::close(fd);
166 return NULL;
167 }
Kenny Rootddb76c42010-11-24 12:56:06 -0800168 (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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
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 */
200/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
201 AccessMode mode)
202{
203 _CompressedAsset* pAsset;
204 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800205 off64_t fileLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 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) {
Steve Block5baa3a62011-12-20 16:23:08 +0000236 ALOGD("File '%s' is not in gzip format\n", fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 ::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 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800258/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 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);
Winson89bb24e2019-04-03 15:50:36 -0700266 if (result != NO_ERROR) {
267 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
271 pAsset->mAccessMode = mode;
272 return pAsset;
273}
274
275/*
276 * Create a new Asset from compressed data in an open file.
277 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800278/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 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);
Winson89bb24e2019-04-03 15:50:36 -0700288 if (result != NO_ERROR) {
289 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292
293 pAsset->mAccessMode = mode;
294 return pAsset;
295}
296#endif
297
298/*
299 * Create a new Asset from a memory mapping.
300 */
301/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
302 AccessMode mode)
303{
304 _FileAsset* pAsset;
305 status_t result;
306
307 pAsset = new _FileAsset;
308 result = pAsset->openChunk(dataMap);
Winson40e0d2a2019-03-18 13:46:54 -0700309 if (result != NO_ERROR) {
310 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 return NULL;
Winson40e0d2a2019-03-18 13:46:54 -0700312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313
314 pAsset->mAccessMode = mode;
315 return pAsset;
316}
317
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800318/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
319 AccessMode mode)
320{
321 std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
322
323 status_t result = pAsset->openChunk(dataMap.get());
324 if (result != NO_ERROR) {
325 return NULL;
326 }
327
328 // We succeeded, so relinquish control of dataMap
329 (void) dataMap.release();
330 pAsset->mAccessMode = mode;
331 return std::move(pAsset);
332}
333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334/*
335 * Create a new Asset from compressed data in a memory mapping.
336 */
337/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
Narayan Kamath4600dd02015-06-16 12:02:57 +0100338 size_t uncompressedLen, AccessMode mode)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339{
340 _CompressedAsset* pAsset;
341 status_t result;
342
343 pAsset = new _CompressedAsset;
Narayan Kamath4600dd02015-06-16 12:02:57 +0100344 result = pAsset->openChunk(dataMap, uncompressedLen);
Winson89bb24e2019-04-03 15:50:36 -0700345 if (result != NO_ERROR) {
346 delete pAsset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 return NULL;
Winson89bb24e2019-04-03 15:50:36 -0700348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349
350 pAsset->mAccessMode = mode;
351 return pAsset;
352}
353
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800354/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
355 size_t uncompressedLen, AccessMode mode)
356{
357 std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
358
359 status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
360 if (result != NO_ERROR) {
361 return NULL;
362 }
363
364 // We succeeded, so relinquish control of dataMap
365 (void) dataMap.release();
366 pAsset->mAccessMode = mode;
367 return std::move(pAsset);
368}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
370/*
371 * Do generic seek() housekeeping. Pass in the offset/whence values from
372 * the seek request, along with the current chunk offset and the chunk
373 * length.
374 *
375 * Returns the new chunk offset, or -1 if the seek is illegal.
376 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800377off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378{
Kenny Rootddb76c42010-11-24 12:56:06 -0800379 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380
381 switch (whence) {
382 case SEEK_SET:
383 newOffset = offset;
384 break;
385 case SEEK_CUR:
386 newOffset = curPosn + offset;
387 break;
388 case SEEK_END:
389 newOffset = maxPosn + offset;
390 break;
391 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000392 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800393 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800395 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 }
397
398 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000399 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800401 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 }
403
404 return newOffset;
405}
406
407
408/*
409 * ===========================================================================
410 * _FileAsset
411 * ===========================================================================
412 */
413
414/*
415 * Constructor.
416 */
417_FileAsset::_FileAsset(void)
418 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
419{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700420 // Register the Asset with the global list here after it is fully constructed and its
421 // vtable pointer points to this concrete type. b/31113965
422 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423}
424
425/*
426 * Destructor. Release resources.
427 */
428_FileAsset::~_FileAsset(void)
429{
430 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700431
432 // Unregister the Asset from the global list here before it is destructed and while its vtable
433 // pointer still points to this concrete type. b/31113965
434 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435}
436
437/*
438 * Operate on a chunk of an uncompressed file.
439 *
440 * Zero-length chunks are allowed.
441 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800442status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443{
444 assert(mFp == NULL); // no reopen
445 assert(mMap == NULL);
446 assert(fd >= 0);
447 assert(offset >= 0);
448
449 /*
450 * Seek to end to get file length.
451 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800452 off64_t fileLength;
453 fileLength = lseek64(fd, 0, SEEK_END);
454 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000456 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 return UNKNOWN_ERROR;
458 }
459
Kenny Rootddb76c42010-11-24 12:56:06 -0800460 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000461 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 (long) offset, (long) length, (long) fileLength);
463 return BAD_INDEX;
464 }
465
466 /* after fdopen, the fd will be closed on fclose() */
467 mFp = fdopen(fd, "rb");
468 if (mFp == NULL)
469 return UNKNOWN_ERROR;
470
471 mStart = offset;
472 mLength = length;
473 assert(mOffset == 0);
474
475 /* seek the FILE* to the start of chunk */
476 if (fseek(mFp, mStart, SEEK_SET) != 0) {
477 assert(false);
478 }
479
480 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700481
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 return NO_ERROR;
483}
484
485/*
486 * Create the chunk from the map.
487 */
488status_t _FileAsset::openChunk(FileMap* dataMap)
489{
490 assert(mFp == NULL); // no reopen
491 assert(mMap == NULL);
492 assert(dataMap != NULL);
493
494 mMap = dataMap;
495 mStart = -1; // not used
496 mLength = dataMap->getDataLength();
497 assert(mOffset == 0);
498
499 return NO_ERROR;
500}
501
502/*
503 * Read a chunk of data.
504 */
505ssize_t _FileAsset::read(void* buf, size_t count)
506{
507 size_t maxLen;
508 size_t actual;
509
510 assert(mOffset >= 0 && mOffset <= mLength);
511
512 if (getAccessMode() == ACCESS_BUFFER) {
513 /*
514 * On first access, read or map the entire file. The caller has
515 * requested buffer access, either because they're going to be
516 * using the buffer or because what they're doing has appropriate
517 * performance needs and access patterns.
518 */
519 if (mBuf == NULL)
520 getBuffer(false);
521 }
522
523 /* adjust count if we're near EOF */
524 maxLen = mLength - mOffset;
525 if (count > maxLen)
526 count = maxLen;
527
528 if (!count)
529 return 0;
530
531 if (mMap != NULL) {
532 /* copy from mapped area */
533 //printf("map read\n");
534 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
535 actual = count;
536 } else if (mBuf != NULL) {
537 /* copy from buffer */
538 //printf("buf read\n");
539 memcpy(buf, (char*)mBuf + mOffset, count);
540 actual = count;
541 } else {
542 /* read from the file */
543 //printf("file read\n");
544 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000545 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 ftell(mFp), (long) mStart, (long) mOffset);
547 assert(false);
548 }
549
550 /*
551 * This returns 0 on error or eof. We need to use ferror() or feof()
552 * to tell the difference, but we don't currently have those on the
553 * device. However, we know how much data is *supposed* to be in the
554 * file, so if we don't read the full amount we know something is
555 * hosed.
556 */
557 actual = fread(buf, 1, count, mFp);
558 if (actual == 0) // something failed -- I/O error?
559 return -1;
560
561 assert(actual == count);
562 }
563
564 mOffset += actual;
565 return actual;
566}
567
568/*
569 * Seek to a new position.
570 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800571off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572{
Kenny Rootddb76c42010-11-24 12:56:06 -0800573 off64_t newPosn;
574 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575
576 // compute new position within chunk
577 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800578 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 return newPosn;
580
Kenny Rootddb76c42010-11-24 12:56:06 -0800581 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582
583 if (mFp != NULL) {
584 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800585 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 }
587
588 mOffset = actualOffset - mStart;
589 return mOffset;
590}
591
592/*
593 * Close the asset.
594 */
595void _FileAsset::close(void)
596{
597 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000598 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 mMap = NULL;
600 }
601 if (mBuf != NULL) {
602 delete[] mBuf;
603 mBuf = NULL;
604 }
605
606 if (mFileName != NULL) {
607 free(mFileName);
608 mFileName = NULL;
609 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700610
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 if (mFp != NULL) {
612 // can only be NULL when called from destructor
613 // (otherwise we would never return this object)
614 fclose(mFp);
615 mFp = NULL;
616 }
617}
618
619/*
620 * Return a read-only pointer to a buffer.
621 *
622 * We can either read the whole thing in or map the relevant piece of
623 * the source file. Ideally a map would be established at a higher
624 * level and we'd be using a different object, but we didn't, so we
625 * deal with it here.
626 */
627const void* _FileAsset::getBuffer(bool wordAligned)
628{
629 /* subsequent requests just use what we did previously */
630 if (mBuf != NULL)
631 return mBuf;
632 if (mMap != NULL) {
633 if (!wordAligned) {
634 return mMap->getDataPtr();
635 }
636 return ensureAlignment(mMap);
637 }
638
639 assert(mFp != NULL);
640
641 if (mLength < kReadVsMapThreshold) {
642 unsigned char* buf;
643 long allocLen;
644
645 /* zero-length files are allowed; not sure about zero-len allocs */
646 /* (works fine with gcc + x86linux) */
647 allocLen = mLength;
648 if (mLength == 0)
649 allocLen = 1;
650
651 buf = new unsigned char[allocLen];
652 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000653 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 return NULL;
655 }
656
Steve Block71f2cf12011-10-20 11:56:00 +0100657 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 if (mLength > 0) {
659 long oldPosn = ftell(mFp);
660 fseek(mFp, mStart, SEEK_SET);
661 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000662 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 delete[] buf;
664 return NULL;
665 }
666 fseek(mFp, oldPosn, SEEK_SET);
667 }
668
Steve Block71f2cf12011-10-20 11:56:00 +0100669 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670
671 mBuf = buf;
672 return mBuf;
673 } else {
674 FileMap* map;
675
676 map = new FileMap;
677 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000678 delete map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 return NULL;
680 }
681
Steve Block71f2cf12011-10-20 11:56:00 +0100682 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683
684 mMap = map;
685 if (!wordAligned) {
686 return mMap->getDataPtr();
687 }
688 return ensureAlignment(mMap);
689 }
690}
691
Kenny Rootddb76c42010-11-24 12:56:06 -0800692int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693{
694 if (mMap != NULL) {
695 const char* fname = mMap->getFileName();
696 if (fname == NULL) {
697 fname = mFileName;
698 }
699 if (fname == NULL) {
700 return -1;
701 }
702 *outStart = mMap->getDataOffset();
703 *outLength = mMap->getDataLength();
704 return open(fname, O_RDONLY | O_BINARY);
705 }
706 if (mFileName == NULL) {
707 return -1;
708 }
709 *outStart = mStart;
710 *outLength = mLength;
711 return open(mFileName, O_RDONLY | O_BINARY);
712}
713
714const void* _FileAsset::ensureAlignment(FileMap* map)
715{
716 void* data = map->getDataPtr();
717 if ((((size_t)data)&0x3) == 0) {
718 // We can return this directly if it is aligned on a word
719 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100720 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700721 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 return data;
723 }
724 // If not aligned on a word boundary, then we need to copy it into
725 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100726 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700727 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 unsigned char* buf = new unsigned char[mLength];
729 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000730 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 return NULL;
732 }
733 memcpy(buf, data, mLength);
734 mBuf = buf;
735 return buf;
736}
737
738/*
739 * ===========================================================================
740 * _CompressedAsset
741 * ===========================================================================
742 */
743
744/*
745 * Constructor.
746 */
747_CompressedAsset::_CompressedAsset(void)
748 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700749 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700751 // Register the Asset with the global list here after it is fully constructed and its
752 // vtable pointer points to this concrete type. b/31113965
753 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754}
755
756/*
757 * Destructor. Release resources.
758 */
759_CompressedAsset::~_CompressedAsset(void)
760{
761 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700762
763 // Unregister the Asset from the global list here before it is destructed and while its vtable
764 // pointer still points to this concrete type. b/31113965
765 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766}
767
768/*
769 * Open a chunk of compressed data inside a file.
770 *
771 * This currently just sets up some values and returns. On the first
772 * read, we expand the entire file into a buffer and return data from it.
773 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800774status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
776{
777 assert(mFd < 0); // no re-open
778 assert(mMap == NULL);
779 assert(fd >= 0);
780 assert(offset >= 0);
781 assert(compressedLen > 0);
782
783 if (compressionMethod != ZipFileRO::kCompressDeflated) {
784 assert(false);
785 return UNKNOWN_ERROR;
786 }
787
788 mStart = offset;
789 mCompressedLen = compressedLen;
790 mUncompressedLen = uncompressedLen;
791 assert(mOffset == 0);
792 mFd = fd;
793 assert(mBuf == NULL);
794
Christopher Tateb100cbf2010-07-26 11:24:18 -0700795 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
796 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
797 }
798
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 return NO_ERROR;
800}
801
802/*
803 * Open a chunk of compressed data in a mapped region.
804 *
805 * Nothing is expanded until the first read call.
806 */
Narayan Kamath4600dd02015-06-16 12:02:57 +0100807status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808{
809 assert(mFd < 0); // no re-open
810 assert(mMap == NULL);
811 assert(dataMap != NULL);
812
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 mMap = dataMap;
814 mStart = -1; // not used
815 mCompressedLen = dataMap->getDataLength();
816 mUncompressedLen = uncompressedLen;
817 assert(mOffset == 0);
818
Christopher Tateb100cbf2010-07-26 11:24:18 -0700819 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
820 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 return NO_ERROR;
823}
824
825/*
826 * Read data from a chunk of compressed data.
827 *
828 * [For now, that's just copying data out of a buffer.]
829 */
830ssize_t _CompressedAsset::read(void* buf, size_t count)
831{
832 size_t maxLen;
833 size_t actual;
834
835 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
836
Christopher Tateb100cbf2010-07-26 11:24:18 -0700837 /* If we're relying on a streaming inflater, go through that */
838 if (mZipInflater) {
839 actual = mZipInflater->read(buf, count);
840 } else {
841 if (mBuf == NULL) {
842 if (getBuffer(false) == NULL)
843 return -1;
844 }
845 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846
Christopher Tateb100cbf2010-07-26 11:24:18 -0700847 /* adjust count if we're near EOF */
848 maxLen = mUncompressedLen - mOffset;
849 if (count > maxLen)
850 count = maxLen;
851
852 if (!count)
853 return 0;
854
855 /* copy from buffer */
856 //printf("comp buf read\n");
857 memcpy(buf, (char*)mBuf + mOffset, count);
858 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860
861 mOffset += actual;
862 return actual;
863}
864
865/*
866 * Handle a seek request.
867 *
868 * If we're working in a streaming mode, this is going to be fairly
869 * expensive, because it requires plowing through a bunch of compressed
870 * data.
871 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800872off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873{
Kenny Rootddb76c42010-11-24 12:56:06 -0800874 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875
876 // compute new position within chunk
877 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800878 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 return newPosn;
880
Christopher Tateb100cbf2010-07-26 11:24:18 -0700881 if (mZipInflater) {
882 mZipInflater->seekAbsolute(newPosn);
883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 mOffset = newPosn;
885 return mOffset;
886}
887
888/*
889 * Close the asset.
890 */
891void _CompressedAsset::close(void)
892{
893 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000894 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 mMap = NULL;
896 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700897
898 delete[] mBuf;
899 mBuf = NULL;
900
901 delete mZipInflater;
902 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903
904 if (mFd > 0) {
905 ::close(mFd);
906 mFd = -1;
907 }
908}
909
910/*
911 * Get a pointer to a read-only buffer of data.
912 *
913 * The first time this is called, we expand the compressed data into a
914 * buffer.
915 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000916const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917{
918 unsigned char* buf = NULL;
919
920 if (mBuf != NULL)
921 return mBuf;
922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 /*
924 * Allocate a buffer and read the file into it.
925 */
926 buf = new unsigned char[mUncompressedLen];
927 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000928 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 goto bail;
930 }
931
932 if (mMap != NULL) {
Narayan Kamathafd31e02013-12-03 13:16:03 +0000933 if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 mUncompressedLen, mCompressedLen))
935 goto bail;
936 } else {
937 assert(mFd >= 0);
938
939 /*
940 * Seek to the start of the compressed data.
941 */
942 if (lseek(mFd, mStart, SEEK_SET) != mStart)
943 goto bail;
944
945 /*
946 * Expand the data into it.
947 */
948 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
949 mCompressedLen))
950 goto bail;
951 }
952
Christopher Tateb100cbf2010-07-26 11:24:18 -0700953 /*
954 * Success - now that we have the full asset in RAM we
955 * no longer need the streaming inflater
956 */
957 delete mZipInflater;
958 mZipInflater = NULL;
959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 mBuf = buf;
961 buf = NULL;
962
963bail:
964 delete[] buf;
965 return mBuf;
966}
967