blob: 8e8c6a2e25a2d6e4c937eeddddfac4d97ef700a4 [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>
Mathias Agopian1f5762e2013-05-06 20:20:34 -070026#include <androidfw/ZipFileRO.h>
27#include <androidfw/ZipUtils.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028#include <utils/Atomic.h>
29#include <utils/FileMap.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030#include <utils/Log.h>
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070031#include <utils/threads.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033#include <assert.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080034#include <errno.h>
35#include <fcntl.h>
36#include <memory.h>
37#include <string.h>
Kenny Rootddb76c42010-11-24 12:56:06 -080038#include <sys/stat.h>
39#include <sys/types.h>
Mathias Agopianb13b9bd2012-02-17 18:27:36 -080040#include <unistd.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42using namespace android;
43
44#ifndef O_BINARY
45# define O_BINARY 0
46#endif
47
Andreas Gampe2204f0b2014-10-21 23:04:54 -070048static const bool kIsDebug = false;
49
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070050static Mutex gAssetLock;
51static int32_t gCount = 0;
52static Asset* gHead = NULL;
53static Asset* gTail = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Adam Lesinski0358efe2016-10-17 13:50:56 -070055void Asset::registerAsset(Asset* asset)
56{
57 AutoMutex _l(gAssetLock);
58 gCount++;
59 asset->mNext = asset->mPrev = NULL;
60 if (gTail == NULL) {
61 gHead = gTail = asset;
62 } else {
63 asset->mPrev = gTail;
64 gTail->mNext = asset;
65 gTail = asset;
66 }
67
68 if (kIsDebug) {
69 ALOGI("Creating Asset %p #%d\n", asset, gCount);
70 }
71}
72
73void Asset::unregisterAsset(Asset* asset)
74{
75 AutoMutex _l(gAssetLock);
76 gCount--;
77 if (gHead == asset) {
78 gHead = asset->mNext;
79 }
80 if (gTail == asset) {
81 gTail = asset->mPrev;
82 }
83 if (asset->mNext != NULL) {
84 asset->mNext->mPrev = asset->mPrev;
85 }
86 if (asset->mPrev != NULL) {
87 asset->mPrev->mNext = asset->mNext;
88 }
89 asset->mNext = asset->mPrev = NULL;
90
91 if (kIsDebug) {
92 ALOGI("Destroying Asset in %p #%d\n", asset, gCount);
93 }
94}
95
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096int32_t Asset::getGlobalCount()
97{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070098 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 return gCount;
100}
101
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700102String8 Asset::getAssetAllocations()
103{
104 AutoMutex _l(gAssetLock);
105 String8 res;
106 Asset* cur = gHead;
107 while (cur != NULL) {
108 if (cur->isAllocated()) {
109 res.append(" ");
110 res.append(cur->getAssetSource());
Kenny Rootddb76c42010-11-24 12:56:06 -0800111 off64_t size = (cur->getLength()+512)/1024;
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700112 char buf[64];
George Burgess IVa346f542016-03-02 13:34:44 -0800113 snprintf(buf, sizeof(buf), ": %dK\n", (int)size);
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700114 res.append(buf);
115 }
116 cur = cur->mNext;
117 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700118
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700119 return res;
120}
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122Asset::Asset(void)
Adam Lesinski0358efe2016-10-17 13:50:56 -0700123 : mAccessMode(ACCESS_UNKNOWN), mNext(NULL), mPrev(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125}
126
127/*
128 * Create a new Asset from a file on disk. There is a fair chance that
129 * the file doesn't actually exist.
130 *
131 * We can use "mode" to decide how we want to go about it.
132 */
133/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
134{
135 _FileAsset* pAsset;
136 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800137 off64_t length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 int fd;
139
140 fd = open(fileName, O_RDONLY | O_BINARY);
141 if (fd < 0)
142 return NULL;
143
144 /*
145 * Under Linux, the lseek fails if we actually opened a directory. To
146 * be correct we should test the file type explicitly, but since we
147 * always open things read-only it doesn't really matter, so there's
148 * no value in incurring the extra overhead of an fstat() call.
149 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800150 // TODO(kroot): replace this with fstat despite the plea above.
151#if 1
152 length = lseek64(fd, 0, SEEK_END);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 if (length < 0) {
154 ::close(fd);
155 return NULL;
156 }
Kenny Rootddb76c42010-11-24 12:56:06 -0800157 (void) lseek64(fd, 0, SEEK_SET);
158#else
159 struct stat st;
160 if (fstat(fd, &st) < 0) {
161 ::close(fd);
162 return NULL;
163 }
164
165 if (!S_ISREG(st.st_mode)) {
166 ::close(fd);
167 return NULL;
168 }
169#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170
171 pAsset = new _FileAsset;
172 result = pAsset->openChunk(fileName, fd, 0, length);
173 if (result != NO_ERROR) {
174 delete pAsset;
175 return NULL;
176 }
177
178 pAsset->mAccessMode = mode;
179 return pAsset;
180}
181
182
183/*
184 * Create a new Asset from a compressed file on disk. There is a fair chance
185 * that the file doesn't actually exist.
186 *
187 * We currently support gzip files. We might want to handle .bz2 someday.
188 */
189/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
190 AccessMode mode)
191{
192 _CompressedAsset* pAsset;
193 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800194 off64_t fileLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 bool scanResult;
196 long offset;
197 int method;
198 long uncompressedLen, compressedLen;
199 int fd;
200
201 fd = open(fileName, O_RDONLY | O_BINARY);
202 if (fd < 0)
203 return NULL;
204
205 fileLen = lseek(fd, 0, SEEK_END);
206 if (fileLen < 0) {
207 ::close(fd);
208 return NULL;
209 }
210 (void) lseek(fd, 0, SEEK_SET);
211
212 /* want buffered I/O for the file scan; must dup so fclose() is safe */
213 FILE* fp = fdopen(dup(fd), "rb");
214 if (fp == NULL) {
215 ::close(fd);
216 return NULL;
217 }
218
219 unsigned long crc32;
220 scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
221 &compressedLen, &crc32);
222 offset = ftell(fp);
223 fclose(fp);
224 if (!scanResult) {
Steve Block5baa3a62011-12-20 16:23:08 +0000225 ALOGD("File '%s' is not in gzip format\n", fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 ::close(fd);
227 return NULL;
228 }
229
230 pAsset = new _CompressedAsset;
231 result = pAsset->openChunk(fd, offset, method, uncompressedLen,
232 compressedLen);
233 if (result != NO_ERROR) {
234 delete pAsset;
235 return NULL;
236 }
237
238 pAsset->mAccessMode = mode;
239 return pAsset;
240}
241
242
243#if 0
244/*
245 * Create a new Asset from part of an open file.
246 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800247/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 size_t length, AccessMode mode)
249{
250 _FileAsset* pAsset;
251 status_t result;
252
253 pAsset = new _FileAsset;
254 result = pAsset->openChunk(NULL, fd, offset, length);
255 if (result != NO_ERROR)
256 return NULL;
257
258 pAsset->mAccessMode = mode;
259 return pAsset;
260}
261
262/*
263 * Create a new Asset from compressed data in an open file.
264 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800265/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
267 AccessMode mode)
268{
269 _CompressedAsset* pAsset;
270 status_t result;
271
272 pAsset = new _CompressedAsset;
273 result = pAsset->openChunk(fd, offset, compressionMethod,
274 uncompressedLen, compressedLen);
275 if (result != NO_ERROR)
276 return NULL;
277
278 pAsset->mAccessMode = mode;
279 return pAsset;
280}
281#endif
282
283/*
284 * Create a new Asset from a memory mapping.
285 */
286/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
287 AccessMode mode)
288{
289 _FileAsset* pAsset;
290 status_t result;
291
292 pAsset = new _FileAsset;
293 result = pAsset->openChunk(dataMap);
294 if (result != NO_ERROR)
295 return NULL;
296
297 pAsset->mAccessMode = mode;
298 return pAsset;
299}
300
301/*
302 * Create a new Asset from compressed data in a memory mapping.
303 */
304/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
Narayan Kamath4600dd02015-06-16 12:02:57 +0100305 size_t uncompressedLen, AccessMode mode)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306{
307 _CompressedAsset* pAsset;
308 status_t result;
309
310 pAsset = new _CompressedAsset;
Narayan Kamath4600dd02015-06-16 12:02:57 +0100311 result = pAsset->openChunk(dataMap, uncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 if (result != NO_ERROR)
313 return NULL;
314
315 pAsset->mAccessMode = mode;
316 return pAsset;
317}
318
319
320/*
321 * Do generic seek() housekeeping. Pass in the offset/whence values from
322 * the seek request, along with the current chunk offset and the chunk
323 * length.
324 *
325 * Returns the new chunk offset, or -1 if the seek is illegal.
326 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800327off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328{
Kenny Rootddb76c42010-11-24 12:56:06 -0800329 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
331 switch (whence) {
332 case SEEK_SET:
333 newOffset = offset;
334 break;
335 case SEEK_CUR:
336 newOffset = curPosn + offset;
337 break;
338 case SEEK_END:
339 newOffset = maxPosn + offset;
340 break;
341 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000342 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800343 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800345 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
347
348 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000349 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800351 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353
354 return newOffset;
355}
356
357
358/*
359 * ===========================================================================
360 * _FileAsset
361 * ===========================================================================
362 */
363
364/*
365 * Constructor.
366 */
367_FileAsset::_FileAsset(void)
368 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
369{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700370 // Register the Asset with the global list here after it is fully constructed and its
371 // vtable pointer points to this concrete type. b/31113965
372 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373}
374
375/*
376 * Destructor. Release resources.
377 */
378_FileAsset::~_FileAsset(void)
379{
380 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700381
382 // Unregister the Asset from the global list here before it is destructed and while its vtable
383 // pointer still points to this concrete type. b/31113965
384 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385}
386
387/*
388 * Operate on a chunk of an uncompressed file.
389 *
390 * Zero-length chunks are allowed.
391 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800392status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393{
394 assert(mFp == NULL); // no reopen
395 assert(mMap == NULL);
396 assert(fd >= 0);
397 assert(offset >= 0);
398
399 /*
400 * Seek to end to get file length.
401 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800402 off64_t fileLength;
403 fileLength = lseek64(fd, 0, SEEK_END);
404 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000406 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 return UNKNOWN_ERROR;
408 }
409
Kenny Rootddb76c42010-11-24 12:56:06 -0800410 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000411 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 (long) offset, (long) length, (long) fileLength);
413 return BAD_INDEX;
414 }
415
416 /* after fdopen, the fd will be closed on fclose() */
417 mFp = fdopen(fd, "rb");
418 if (mFp == NULL)
419 return UNKNOWN_ERROR;
420
421 mStart = offset;
422 mLength = length;
423 assert(mOffset == 0);
424
425 /* seek the FILE* to the start of chunk */
426 if (fseek(mFp, mStart, SEEK_SET) != 0) {
427 assert(false);
428 }
429
430 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 return NO_ERROR;
433}
434
435/*
436 * Create the chunk from the map.
437 */
438status_t _FileAsset::openChunk(FileMap* dataMap)
439{
440 assert(mFp == NULL); // no reopen
441 assert(mMap == NULL);
442 assert(dataMap != NULL);
443
444 mMap = dataMap;
445 mStart = -1; // not used
446 mLength = dataMap->getDataLength();
447 assert(mOffset == 0);
448
449 return NO_ERROR;
450}
451
452/*
453 * Read a chunk of data.
454 */
455ssize_t _FileAsset::read(void* buf, size_t count)
456{
457 size_t maxLen;
458 size_t actual;
459
460 assert(mOffset >= 0 && mOffset <= mLength);
461
462 if (getAccessMode() == ACCESS_BUFFER) {
463 /*
464 * On first access, read or map the entire file. The caller has
465 * requested buffer access, either because they're going to be
466 * using the buffer or because what they're doing has appropriate
467 * performance needs and access patterns.
468 */
469 if (mBuf == NULL)
470 getBuffer(false);
471 }
472
473 /* adjust count if we're near EOF */
474 maxLen = mLength - mOffset;
475 if (count > maxLen)
476 count = maxLen;
477
478 if (!count)
479 return 0;
480
481 if (mMap != NULL) {
482 /* copy from mapped area */
483 //printf("map read\n");
484 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
485 actual = count;
486 } else if (mBuf != NULL) {
487 /* copy from buffer */
488 //printf("buf read\n");
489 memcpy(buf, (char*)mBuf + mOffset, count);
490 actual = count;
491 } else {
492 /* read from the file */
493 //printf("file read\n");
494 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000495 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 ftell(mFp), (long) mStart, (long) mOffset);
497 assert(false);
498 }
499
500 /*
501 * This returns 0 on error or eof. We need to use ferror() or feof()
502 * to tell the difference, but we don't currently have those on the
503 * device. However, we know how much data is *supposed* to be in the
504 * file, so if we don't read the full amount we know something is
505 * hosed.
506 */
507 actual = fread(buf, 1, count, mFp);
508 if (actual == 0) // something failed -- I/O error?
509 return -1;
510
511 assert(actual == count);
512 }
513
514 mOffset += actual;
515 return actual;
516}
517
518/*
519 * Seek to a new position.
520 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800521off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522{
Kenny Rootddb76c42010-11-24 12:56:06 -0800523 off64_t newPosn;
524 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525
526 // compute new position within chunk
527 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800528 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 return newPosn;
530
Kenny Rootddb76c42010-11-24 12:56:06 -0800531 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532
533 if (mFp != NULL) {
534 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800535 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 }
537
538 mOffset = actualOffset - mStart;
539 return mOffset;
540}
541
542/*
543 * Close the asset.
544 */
545void _FileAsset::close(void)
546{
547 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000548 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 mMap = NULL;
550 }
551 if (mBuf != NULL) {
552 delete[] mBuf;
553 mBuf = NULL;
554 }
555
556 if (mFileName != NULL) {
557 free(mFileName);
558 mFileName = NULL;
559 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 if (mFp != NULL) {
562 // can only be NULL when called from destructor
563 // (otherwise we would never return this object)
564 fclose(mFp);
565 mFp = NULL;
566 }
567}
568
569/*
570 * Return a read-only pointer to a buffer.
571 *
572 * We can either read the whole thing in or map the relevant piece of
573 * the source file. Ideally a map would be established at a higher
574 * level and we'd be using a different object, but we didn't, so we
575 * deal with it here.
576 */
577const void* _FileAsset::getBuffer(bool wordAligned)
578{
579 /* subsequent requests just use what we did previously */
580 if (mBuf != NULL)
581 return mBuf;
582 if (mMap != NULL) {
583 if (!wordAligned) {
584 return mMap->getDataPtr();
585 }
586 return ensureAlignment(mMap);
587 }
588
589 assert(mFp != NULL);
590
591 if (mLength < kReadVsMapThreshold) {
592 unsigned char* buf;
593 long allocLen;
594
595 /* zero-length files are allowed; not sure about zero-len allocs */
596 /* (works fine with gcc + x86linux) */
597 allocLen = mLength;
598 if (mLength == 0)
599 allocLen = 1;
600
601 buf = new unsigned char[allocLen];
602 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000603 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 return NULL;
605 }
606
Steve Block71f2cf12011-10-20 11:56:00 +0100607 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 if (mLength > 0) {
609 long oldPosn = ftell(mFp);
610 fseek(mFp, mStart, SEEK_SET);
611 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000612 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 delete[] buf;
614 return NULL;
615 }
616 fseek(mFp, oldPosn, SEEK_SET);
617 }
618
Steve Block71f2cf12011-10-20 11:56:00 +0100619 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620
621 mBuf = buf;
622 return mBuf;
623 } else {
624 FileMap* map;
625
626 map = new FileMap;
627 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000628 delete map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 return NULL;
630 }
631
Steve Block71f2cf12011-10-20 11:56:00 +0100632 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633
634 mMap = map;
635 if (!wordAligned) {
636 return mMap->getDataPtr();
637 }
638 return ensureAlignment(mMap);
639 }
640}
641
Kenny Rootddb76c42010-11-24 12:56:06 -0800642int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643{
644 if (mMap != NULL) {
645 const char* fname = mMap->getFileName();
646 if (fname == NULL) {
647 fname = mFileName;
648 }
649 if (fname == NULL) {
650 return -1;
651 }
652 *outStart = mMap->getDataOffset();
653 *outLength = mMap->getDataLength();
654 return open(fname, O_RDONLY | O_BINARY);
655 }
656 if (mFileName == NULL) {
657 return -1;
658 }
659 *outStart = mStart;
660 *outLength = mLength;
661 return open(mFileName, O_RDONLY | O_BINARY);
662}
663
664const void* _FileAsset::ensureAlignment(FileMap* map)
665{
666 void* data = map->getDataPtr();
667 if ((((size_t)data)&0x3) == 0) {
668 // We can return this directly if it is aligned on a word
669 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100670 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700671 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 return data;
673 }
674 // If not aligned on a word boundary, then we need to copy it into
675 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100676 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700677 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 unsigned char* buf = new unsigned char[mLength];
679 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000680 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 return NULL;
682 }
683 memcpy(buf, data, mLength);
684 mBuf = buf;
685 return buf;
686}
687
688/*
689 * ===========================================================================
690 * _CompressedAsset
691 * ===========================================================================
692 */
693
694/*
695 * Constructor.
696 */
697_CompressedAsset::_CompressedAsset(void)
698 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700699 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700701 // Register the Asset with the global list here after it is fully constructed and its
702 // vtable pointer points to this concrete type. b/31113965
703 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704}
705
706/*
707 * Destructor. Release resources.
708 */
709_CompressedAsset::~_CompressedAsset(void)
710{
711 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700712
713 // Unregister the Asset from the global list here before it is destructed and while its vtable
714 // pointer still points to this concrete type. b/31113965
715 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716}
717
718/*
719 * Open a chunk of compressed data inside a file.
720 *
721 * This currently just sets up some values and returns. On the first
722 * read, we expand the entire file into a buffer and return data from it.
723 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800724status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
726{
727 assert(mFd < 0); // no re-open
728 assert(mMap == NULL);
729 assert(fd >= 0);
730 assert(offset >= 0);
731 assert(compressedLen > 0);
732
733 if (compressionMethod != ZipFileRO::kCompressDeflated) {
734 assert(false);
735 return UNKNOWN_ERROR;
736 }
737
738 mStart = offset;
739 mCompressedLen = compressedLen;
740 mUncompressedLen = uncompressedLen;
741 assert(mOffset == 0);
742 mFd = fd;
743 assert(mBuf == NULL);
744
Christopher Tateb100cbf2010-07-26 11:24:18 -0700745 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
746 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
747 }
748
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 return NO_ERROR;
750}
751
752/*
753 * Open a chunk of compressed data in a mapped region.
754 *
755 * Nothing is expanded until the first read call.
756 */
Narayan Kamath4600dd02015-06-16 12:02:57 +0100757status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758{
759 assert(mFd < 0); // no re-open
760 assert(mMap == NULL);
761 assert(dataMap != NULL);
762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 mMap = dataMap;
764 mStart = -1; // not used
765 mCompressedLen = dataMap->getDataLength();
766 mUncompressedLen = uncompressedLen;
767 assert(mOffset == 0);
768
Christopher Tateb100cbf2010-07-26 11:24:18 -0700769 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
770 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 return NO_ERROR;
773}
774
775/*
776 * Read data from a chunk of compressed data.
777 *
778 * [For now, that's just copying data out of a buffer.]
779 */
780ssize_t _CompressedAsset::read(void* buf, size_t count)
781{
782 size_t maxLen;
783 size_t actual;
784
785 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
786
Christopher Tateb100cbf2010-07-26 11:24:18 -0700787 /* If we're relying on a streaming inflater, go through that */
788 if (mZipInflater) {
789 actual = mZipInflater->read(buf, count);
790 } else {
791 if (mBuf == NULL) {
792 if (getBuffer(false) == NULL)
793 return -1;
794 }
795 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796
Christopher Tateb100cbf2010-07-26 11:24:18 -0700797 /* adjust count if we're near EOF */
798 maxLen = mUncompressedLen - mOffset;
799 if (count > maxLen)
800 count = maxLen;
801
802 if (!count)
803 return 0;
804
805 /* copy from buffer */
806 //printf("comp buf read\n");
807 memcpy(buf, (char*)mBuf + mOffset, count);
808 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810
811 mOffset += actual;
812 return actual;
813}
814
815/*
816 * Handle a seek request.
817 *
818 * If we're working in a streaming mode, this is going to be fairly
819 * expensive, because it requires plowing through a bunch of compressed
820 * data.
821 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800822off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823{
Kenny Rootddb76c42010-11-24 12:56:06 -0800824 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825
826 // compute new position within chunk
827 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800828 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 return newPosn;
830
Christopher Tateb100cbf2010-07-26 11:24:18 -0700831 if (mZipInflater) {
832 mZipInflater->seekAbsolute(newPosn);
833 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 mOffset = newPosn;
835 return mOffset;
836}
837
838/*
839 * Close the asset.
840 */
841void _CompressedAsset::close(void)
842{
843 if (mMap != NULL) {
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000844 delete mMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 mMap = NULL;
846 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700847
848 delete[] mBuf;
849 mBuf = NULL;
850
851 delete mZipInflater;
852 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853
854 if (mFd > 0) {
855 ::close(mFd);
856 mFd = -1;
857 }
858}
859
860/*
861 * Get a pointer to a read-only buffer of data.
862 *
863 * The first time this is called, we expand the compressed data into a
864 * buffer.
865 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000866const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867{
868 unsigned char* buf = NULL;
869
870 if (mBuf != NULL)
871 return mBuf;
872
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 /*
874 * Allocate a buffer and read the file into it.
875 */
876 buf = new unsigned char[mUncompressedLen];
877 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000878 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 goto bail;
880 }
881
882 if (mMap != NULL) {
Narayan Kamathafd31e02013-12-03 13:16:03 +0000883 if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 mUncompressedLen, mCompressedLen))
885 goto bail;
886 } else {
887 assert(mFd >= 0);
888
889 /*
890 * Seek to the start of the compressed data.
891 */
892 if (lseek(mFd, mStart, SEEK_SET) != mStart)
893 goto bail;
894
895 /*
896 * Expand the data into it.
897 */
898 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
899 mCompressedLen))
900 goto bail;
901 }
902
Christopher Tateb100cbf2010-07-26 11:24:18 -0700903 /*
904 * Success - now that we have the full asset in RAM we
905 * no longer need the streaming inflater
906 */
907 delete mZipInflater;
908 mZipInflater = NULL;
909
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 mBuf = buf;
911 buf = NULL;
912
913bail:
914 delete[] buf;
915 return mBuf;
916}
917