blob: 4b3382e4fc5df4655c622997cb06f401e4f61b3a [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
55int32_t Asset::getGlobalCount()
56{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070057 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 return gCount;
59}
60
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070061String8 Asset::getAssetAllocations()
62{
63 AutoMutex _l(gAssetLock);
64 String8 res;
65 Asset* cur = gHead;
66 while (cur != NULL) {
67 if (cur->isAllocated()) {
68 res.append(" ");
69 res.append(cur->getAssetSource());
Kenny Rootddb76c42010-11-24 12:56:06 -080070 off64_t size = (cur->getLength()+512)/1024;
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070071 char buf[64];
72 sprintf(buf, ": %dK\n", (int)size);
73 res.append(buf);
74 }
75 cur = cur->mNext;
76 }
Mark Salyzyn00adb862014-03-19 11:00:06 -070077
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070078 return res;
79}
80
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081Asset::Asset(void)
82 : mAccessMode(ACCESS_UNKNOWN)
83{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070084 AutoMutex _l(gAssetLock);
85 gCount++;
86 mNext = mPrev = NULL;
87 if (gTail == NULL) {
88 gHead = gTail = this;
Mark Salyzyn00adb862014-03-19 11:00:06 -070089 } else {
90 mPrev = gTail;
91 gTail->mNext = this;
92 gTail = this;
93 }
Andreas Gampe2204f0b2014-10-21 23:04:54 -070094 if (kIsDebug) {
95 ALOGI("Creating Asset %p #%d\n", this, gCount);
96 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097}
98
99Asset::~Asset(void)
100{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700101 AutoMutex _l(gAssetLock);
Mark Salyzyn00adb862014-03-19 11:00:06 -0700102 gCount--;
Dianne Hackborn82e1ee92009-08-11 18:56:41 -0700103 if (gHead == this) {
104 gHead = mNext;
105 }
106 if (gTail == this) {
107 gTail = mPrev;
108 }
109 if (mNext != NULL) {
110 mNext->mPrev = mPrev;
111 }
112 if (mPrev != NULL) {
113 mPrev->mNext = mNext;
114 }
115 mNext = mPrev = NULL;
Andreas Gampe2204f0b2014-10-21 23:04:54 -0700116 if (kIsDebug) {
117 ALOGI("Destroying Asset in %p #%d\n", this, gCount);
118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119}
120
121/*
122 * Create a new Asset from a file on disk. There is a fair chance that
123 * the file doesn't actually exist.
124 *
125 * We can use "mode" to decide how we want to go about it.
126 */
127/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
128{
129 _FileAsset* pAsset;
130 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800131 off64_t length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 int fd;
133
134 fd = open(fileName, O_RDONLY | O_BINARY);
135 if (fd < 0)
136 return NULL;
137
138 /*
139 * Under Linux, the lseek fails if we actually opened a directory. To
140 * be correct we should test the file type explicitly, but since we
141 * always open things read-only it doesn't really matter, so there's
142 * no value in incurring the extra overhead of an fstat() call.
143 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800144 // TODO(kroot): replace this with fstat despite the plea above.
145#if 1
146 length = lseek64(fd, 0, SEEK_END);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 if (length < 0) {
148 ::close(fd);
149 return NULL;
150 }
Kenny Rootddb76c42010-11-24 12:56:06 -0800151 (void) lseek64(fd, 0, SEEK_SET);
152#else
153 struct stat st;
154 if (fstat(fd, &st) < 0) {
155 ::close(fd);
156 return NULL;
157 }
158
159 if (!S_ISREG(st.st_mode)) {
160 ::close(fd);
161 return NULL;
162 }
163#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
165 pAsset = new _FileAsset;
166 result = pAsset->openChunk(fileName, fd, 0, length);
167 if (result != NO_ERROR) {
168 delete pAsset;
169 return NULL;
170 }
171
172 pAsset->mAccessMode = mode;
173 return pAsset;
174}
175
176
177/*
178 * Create a new Asset from a compressed file on disk. There is a fair chance
179 * that the file doesn't actually exist.
180 *
181 * We currently support gzip files. We might want to handle .bz2 someday.
182 */
183/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
184 AccessMode mode)
185{
186 _CompressedAsset* pAsset;
187 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800188 off64_t fileLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 bool scanResult;
190 long offset;
191 int method;
192 long uncompressedLen, compressedLen;
193 int fd;
194
195 fd = open(fileName, O_RDONLY | O_BINARY);
196 if (fd < 0)
197 return NULL;
198
199 fileLen = lseek(fd, 0, SEEK_END);
200 if (fileLen < 0) {
201 ::close(fd);
202 return NULL;
203 }
204 (void) lseek(fd, 0, SEEK_SET);
205
206 /* want buffered I/O for the file scan; must dup so fclose() is safe */
207 FILE* fp = fdopen(dup(fd), "rb");
208 if (fp == NULL) {
209 ::close(fd);
210 return NULL;
211 }
212
213 unsigned long crc32;
214 scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
215 &compressedLen, &crc32);
216 offset = ftell(fp);
217 fclose(fp);
218 if (!scanResult) {
Steve Block5baa3a62011-12-20 16:23:08 +0000219 ALOGD("File '%s' is not in gzip format\n", fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 ::close(fd);
221 return NULL;
222 }
223
224 pAsset = new _CompressedAsset;
225 result = pAsset->openChunk(fd, offset, method, uncompressedLen,
226 compressedLen);
227 if (result != NO_ERROR) {
228 delete pAsset;
229 return NULL;
230 }
231
232 pAsset->mAccessMode = mode;
233 return pAsset;
234}
235
236
237#if 0
238/*
239 * Create a new Asset from part of an open file.
240 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800241/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 size_t length, AccessMode mode)
243{
244 _FileAsset* pAsset;
245 status_t result;
246
247 pAsset = new _FileAsset;
248 result = pAsset->openChunk(NULL, fd, offset, length);
249 if (result != NO_ERROR)
250 return NULL;
251
252 pAsset->mAccessMode = mode;
253 return pAsset;
254}
255
256/*
257 * Create a new Asset from compressed data in an open file.
258 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800259/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
261 AccessMode mode)
262{
263 _CompressedAsset* pAsset;
264 status_t result;
265
266 pAsset = new _CompressedAsset;
267 result = pAsset->openChunk(fd, offset, compressionMethod,
268 uncompressedLen, compressedLen);
269 if (result != NO_ERROR)
270 return NULL;
271
272 pAsset->mAccessMode = mode;
273 return pAsset;
274}
275#endif
276
277/*
278 * Create a new Asset from a memory mapping.
279 */
280/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
281 AccessMode mode)
282{
283 _FileAsset* pAsset;
284 status_t result;
285
286 pAsset = new _FileAsset;
287 result = pAsset->openChunk(dataMap);
288 if (result != NO_ERROR)
289 return NULL;
290
291 pAsset->mAccessMode = mode;
292 return pAsset;
293}
294
295/*
296 * Create a new Asset from compressed data in a memory mapping.
297 */
298/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
299 int method, size_t uncompressedLen, AccessMode mode)
300{
301 _CompressedAsset* pAsset;
302 status_t result;
303
304 pAsset = new _CompressedAsset;
305 result = pAsset->openChunk(dataMap, method, uncompressedLen);
306 if (result != NO_ERROR)
307 return NULL;
308
309 pAsset->mAccessMode = mode;
310 return pAsset;
311}
312
313
314/*
315 * Do generic seek() housekeeping. Pass in the offset/whence values from
316 * the seek request, along with the current chunk offset and the chunk
317 * length.
318 *
319 * Returns the new chunk offset, or -1 if the seek is illegal.
320 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800321off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322{
Kenny Rootddb76c42010-11-24 12:56:06 -0800323 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324
325 switch (whence) {
326 case SEEK_SET:
327 newOffset = offset;
328 break;
329 case SEEK_CUR:
330 newOffset = curPosn + offset;
331 break;
332 case SEEK_END:
333 newOffset = maxPosn + offset;
334 break;
335 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000336 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800337 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800339 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341
342 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000343 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 (long) newOffset, (long) maxPosn);
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 return newOffset;
349}
350
351
352/*
353 * ===========================================================================
354 * _FileAsset
355 * ===========================================================================
356 */
357
358/*
359 * Constructor.
360 */
361_FileAsset::_FileAsset(void)
362 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
363{
364}
365
366/*
367 * Destructor. Release resources.
368 */
369_FileAsset::~_FileAsset(void)
370{
371 close();
372}
373
374/*
375 * Operate on a chunk of an uncompressed file.
376 *
377 * Zero-length chunks are allowed.
378 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800379status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380{
381 assert(mFp == NULL); // no reopen
382 assert(mMap == NULL);
383 assert(fd >= 0);
384 assert(offset >= 0);
385
386 /*
387 * Seek to end to get file length.
388 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800389 off64_t fileLength;
390 fileLength = lseek64(fd, 0, SEEK_END);
391 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000393 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return UNKNOWN_ERROR;
395 }
396
Kenny Rootddb76c42010-11-24 12:56:06 -0800397 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000398 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 (long) offset, (long) length, (long) fileLength);
400 return BAD_INDEX;
401 }
402
403 /* after fdopen, the fd will be closed on fclose() */
404 mFp = fdopen(fd, "rb");
405 if (mFp == NULL)
406 return UNKNOWN_ERROR;
407
408 mStart = offset;
409 mLength = length;
410 assert(mOffset == 0);
411
412 /* seek the FILE* to the start of chunk */
413 if (fseek(mFp, mStart, SEEK_SET) != 0) {
414 assert(false);
415 }
416
417 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 return NO_ERROR;
420}
421
422/*
423 * Create the chunk from the map.
424 */
425status_t _FileAsset::openChunk(FileMap* dataMap)
426{
427 assert(mFp == NULL); // no reopen
428 assert(mMap == NULL);
429 assert(dataMap != NULL);
430
431 mMap = dataMap;
432 mStart = -1; // not used
433 mLength = dataMap->getDataLength();
434 assert(mOffset == 0);
435
436 return NO_ERROR;
437}
438
439/*
440 * Read a chunk of data.
441 */
442ssize_t _FileAsset::read(void* buf, size_t count)
443{
444 size_t maxLen;
445 size_t actual;
446
447 assert(mOffset >= 0 && mOffset <= mLength);
448
449 if (getAccessMode() == ACCESS_BUFFER) {
450 /*
451 * On first access, read or map the entire file. The caller has
452 * requested buffer access, either because they're going to be
453 * using the buffer or because what they're doing has appropriate
454 * performance needs and access patterns.
455 */
456 if (mBuf == NULL)
457 getBuffer(false);
458 }
459
460 /* adjust count if we're near EOF */
461 maxLen = mLength - mOffset;
462 if (count > maxLen)
463 count = maxLen;
464
465 if (!count)
466 return 0;
467
468 if (mMap != NULL) {
469 /* copy from mapped area */
470 //printf("map read\n");
471 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
472 actual = count;
473 } else if (mBuf != NULL) {
474 /* copy from buffer */
475 //printf("buf read\n");
476 memcpy(buf, (char*)mBuf + mOffset, count);
477 actual = count;
478 } else {
479 /* read from the file */
480 //printf("file read\n");
481 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000482 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 ftell(mFp), (long) mStart, (long) mOffset);
484 assert(false);
485 }
486
487 /*
488 * This returns 0 on error or eof. We need to use ferror() or feof()
489 * to tell the difference, but we don't currently have those on the
490 * device. However, we know how much data is *supposed* to be in the
491 * file, so if we don't read the full amount we know something is
492 * hosed.
493 */
494 actual = fread(buf, 1, count, mFp);
495 if (actual == 0) // something failed -- I/O error?
496 return -1;
497
498 assert(actual == count);
499 }
500
501 mOffset += actual;
502 return actual;
503}
504
505/*
506 * Seek to a new position.
507 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800508off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509{
Kenny Rootddb76c42010-11-24 12:56:06 -0800510 off64_t newPosn;
511 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512
513 // compute new position within chunk
514 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800515 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 return newPosn;
517
Kenny Rootddb76c42010-11-24 12:56:06 -0800518 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519
520 if (mFp != NULL) {
521 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800522 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 }
524
525 mOffset = actualOffset - mStart;
526 return mOffset;
527}
528
529/*
530 * Close the asset.
531 */
532void _FileAsset::close(void)
533{
534 if (mMap != NULL) {
535 mMap->release();
536 mMap = NULL;
537 }
538 if (mBuf != NULL) {
539 delete[] mBuf;
540 mBuf = NULL;
541 }
542
543 if (mFileName != NULL) {
544 free(mFileName);
545 mFileName = NULL;
546 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 if (mFp != NULL) {
549 // can only be NULL when called from destructor
550 // (otherwise we would never return this object)
551 fclose(mFp);
552 mFp = NULL;
553 }
554}
555
556/*
557 * Return a read-only pointer to a buffer.
558 *
559 * We can either read the whole thing in or map the relevant piece of
560 * the source file. Ideally a map would be established at a higher
561 * level and we'd be using a different object, but we didn't, so we
562 * deal with it here.
563 */
564const void* _FileAsset::getBuffer(bool wordAligned)
565{
566 /* subsequent requests just use what we did previously */
567 if (mBuf != NULL)
568 return mBuf;
569 if (mMap != NULL) {
570 if (!wordAligned) {
571 return mMap->getDataPtr();
572 }
573 return ensureAlignment(mMap);
574 }
575
576 assert(mFp != NULL);
577
578 if (mLength < kReadVsMapThreshold) {
579 unsigned char* buf;
580 long allocLen;
581
582 /* zero-length files are allowed; not sure about zero-len allocs */
583 /* (works fine with gcc + x86linux) */
584 allocLen = mLength;
585 if (mLength == 0)
586 allocLen = 1;
587
588 buf = new unsigned char[allocLen];
589 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000590 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 return NULL;
592 }
593
Steve Block71f2cf12011-10-20 11:56:00 +0100594 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 if (mLength > 0) {
596 long oldPosn = ftell(mFp);
597 fseek(mFp, mStart, SEEK_SET);
598 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000599 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 delete[] buf;
601 return NULL;
602 }
603 fseek(mFp, oldPosn, SEEK_SET);
604 }
605
Steve Block71f2cf12011-10-20 11:56:00 +0100606 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607
608 mBuf = buf;
609 return mBuf;
610 } else {
611 FileMap* map;
612
613 map = new FileMap;
614 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
615 map->release();
616 return NULL;
617 }
618
Steve Block71f2cf12011-10-20 11:56:00 +0100619 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620
621 mMap = map;
622 if (!wordAligned) {
623 return mMap->getDataPtr();
624 }
625 return ensureAlignment(mMap);
626 }
627}
628
Kenny Rootddb76c42010-11-24 12:56:06 -0800629int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630{
631 if (mMap != NULL) {
632 const char* fname = mMap->getFileName();
633 if (fname == NULL) {
634 fname = mFileName;
635 }
636 if (fname == NULL) {
637 return -1;
638 }
639 *outStart = mMap->getDataOffset();
640 *outLength = mMap->getDataLength();
641 return open(fname, O_RDONLY | O_BINARY);
642 }
643 if (mFileName == NULL) {
644 return -1;
645 }
646 *outStart = mStart;
647 *outLength = mLength;
648 return open(mFileName, O_RDONLY | O_BINARY);
649}
650
651const void* _FileAsset::ensureAlignment(FileMap* map)
652{
653 void* data = map->getDataPtr();
654 if ((((size_t)data)&0x3) == 0) {
655 // We can return this directly if it is aligned on a word
656 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100657 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700658 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 return data;
660 }
661 // If not aligned on a word boundary, then we need to copy it into
662 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100663 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700664 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 unsigned char* buf = new unsigned char[mLength];
666 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000667 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 return NULL;
669 }
670 memcpy(buf, data, mLength);
671 mBuf = buf;
672 return buf;
673}
674
675/*
676 * ===========================================================================
677 * _CompressedAsset
678 * ===========================================================================
679 */
680
681/*
682 * Constructor.
683 */
684_CompressedAsset::_CompressedAsset(void)
685 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700686 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687{
688}
689
690/*
691 * Destructor. Release resources.
692 */
693_CompressedAsset::~_CompressedAsset(void)
694{
695 close();
696}
697
698/*
699 * Open a chunk of compressed data inside a file.
700 *
701 * This currently just sets up some values and returns. On the first
702 * read, we expand the entire file into a buffer and return data from it.
703 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800704status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
706{
707 assert(mFd < 0); // no re-open
708 assert(mMap == NULL);
709 assert(fd >= 0);
710 assert(offset >= 0);
711 assert(compressedLen > 0);
712
713 if (compressionMethod != ZipFileRO::kCompressDeflated) {
714 assert(false);
715 return UNKNOWN_ERROR;
716 }
717
718 mStart = offset;
719 mCompressedLen = compressedLen;
720 mUncompressedLen = uncompressedLen;
721 assert(mOffset == 0);
722 mFd = fd;
723 assert(mBuf == NULL);
724
Christopher Tateb100cbf2010-07-26 11:24:18 -0700725 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
726 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
727 }
728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 return NO_ERROR;
730}
731
732/*
733 * Open a chunk of compressed data in a mapped region.
734 *
735 * Nothing is expanded until the first read call.
736 */
737status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
738 size_t uncompressedLen)
739{
740 assert(mFd < 0); // no re-open
741 assert(mMap == NULL);
742 assert(dataMap != NULL);
743
744 if (compressionMethod != ZipFileRO::kCompressDeflated) {
745 assert(false);
746 return UNKNOWN_ERROR;
747 }
748
749 mMap = dataMap;
750 mStart = -1; // not used
751 mCompressedLen = dataMap->getDataLength();
752 mUncompressedLen = uncompressedLen;
753 assert(mOffset == 0);
754
Christopher Tateb100cbf2010-07-26 11:24:18 -0700755 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
756 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
757 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 return NO_ERROR;
759}
760
761/*
762 * Read data from a chunk of compressed data.
763 *
764 * [For now, that's just copying data out of a buffer.]
765 */
766ssize_t _CompressedAsset::read(void* buf, size_t count)
767{
768 size_t maxLen;
769 size_t actual;
770
771 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
772
Christopher Tateb100cbf2010-07-26 11:24:18 -0700773 /* If we're relying on a streaming inflater, go through that */
774 if (mZipInflater) {
775 actual = mZipInflater->read(buf, count);
776 } else {
777 if (mBuf == NULL) {
778 if (getBuffer(false) == NULL)
779 return -1;
780 }
781 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782
Christopher Tateb100cbf2010-07-26 11:24:18 -0700783 /* adjust count if we're near EOF */
784 maxLen = mUncompressedLen - mOffset;
785 if (count > maxLen)
786 count = maxLen;
787
788 if (!count)
789 return 0;
790
791 /* copy from buffer */
792 //printf("comp buf read\n");
793 memcpy(buf, (char*)mBuf + mOffset, count);
794 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796
797 mOffset += actual;
798 return actual;
799}
800
801/*
802 * Handle a seek request.
803 *
804 * If we're working in a streaming mode, this is going to be fairly
805 * expensive, because it requires plowing through a bunch of compressed
806 * data.
807 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800808off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809{
Kenny Rootddb76c42010-11-24 12:56:06 -0800810 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811
812 // compute new position within chunk
813 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800814 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 return newPosn;
816
Christopher Tateb100cbf2010-07-26 11:24:18 -0700817 if (mZipInflater) {
818 mZipInflater->seekAbsolute(newPosn);
819 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 mOffset = newPosn;
821 return mOffset;
822}
823
824/*
825 * Close the asset.
826 */
827void _CompressedAsset::close(void)
828{
829 if (mMap != NULL) {
830 mMap->release();
831 mMap = NULL;
832 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700833
834 delete[] mBuf;
835 mBuf = NULL;
836
837 delete mZipInflater;
838 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839
840 if (mFd > 0) {
841 ::close(mFd);
842 mFd = -1;
843 }
844}
845
846/*
847 * Get a pointer to a read-only buffer of data.
848 *
849 * The first time this is called, we expand the compressed data into a
850 * buffer.
851 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000852const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853{
854 unsigned char* buf = NULL;
855
856 if (mBuf != NULL)
857 return mBuf;
858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 /*
860 * Allocate a buffer and read the file into it.
861 */
862 buf = new unsigned char[mUncompressedLen];
863 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000864 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 goto bail;
866 }
867
868 if (mMap != NULL) {
Narayan Kamathafd31e02013-12-03 13:16:03 +0000869 if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 mUncompressedLen, mCompressedLen))
871 goto bail;
872 } else {
873 assert(mFd >= 0);
874
875 /*
876 * Seek to the start of the compressed data.
877 */
878 if (lseek(mFd, mStart, SEEK_SET) != mStart)
879 goto bail;
880
881 /*
882 * Expand the data into it.
883 */
884 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
885 mCompressedLen))
886 goto bail;
887 }
888
Christopher Tateb100cbf2010-07-26 11:24:18 -0700889 /*
890 * Success - now that we have the full asset in RAM we
891 * no longer need the streaming inflater
892 */
893 delete mZipInflater;
894 mZipInflater = NULL;
895
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 mBuf = buf;
897 buf = NULL;
898
899bail:
900 delete[] buf;
901 return mBuf;
902}
903