blob: 42951237d6274506a659a4895e2516745d80a546 [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
24#include <utils/Asset.h>
25#include <utils/Atomic.h>
26#include <utils/FileMap.h>
27#include <utils/ZipUtils.h>
28#include <utils/ZipFileRO.h>
29#include <utils/Log.h>
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070030#include <utils/threads.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
32#include <string.h>
33#include <memory.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <assert.h>
37
38using namespace android;
39
40#ifndef O_BINARY
41# define O_BINARY 0
42#endif
43
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070044static Mutex gAssetLock;
45static int32_t gCount = 0;
46static Asset* gHead = NULL;
47static Asset* gTail = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49int32_t Asset::getGlobalCount()
50{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070051 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 return gCount;
53}
54
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070055String8 Asset::getAssetAllocations()
56{
57 AutoMutex _l(gAssetLock);
58 String8 res;
59 Asset* cur = gHead;
60 while (cur != NULL) {
61 if (cur->isAllocated()) {
62 res.append(" ");
63 res.append(cur->getAssetSource());
64 off_t size = (cur->getLength()+512)/1024;
65 char buf[64];
66 sprintf(buf, ": %dK\n", (int)size);
67 res.append(buf);
68 }
69 cur = cur->mNext;
70 }
71
72 return res;
73}
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075Asset::Asset(void)
76 : mAccessMode(ACCESS_UNKNOWN)
77{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070078 AutoMutex _l(gAssetLock);
79 gCount++;
80 mNext = mPrev = NULL;
81 if (gTail == NULL) {
82 gHead = gTail = this;
83 } else {
84 mPrev = gTail;
85 gTail->mNext = this;
86 gTail = this;
87 }
88 //LOGI("Creating Asset %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089}
90
91Asset::~Asset(void)
92{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070093 AutoMutex _l(gAssetLock);
94 gCount--;
95 if (gHead == this) {
96 gHead = mNext;
97 }
98 if (gTail == this) {
99 gTail = mPrev;
100 }
101 if (mNext != NULL) {
102 mNext->mPrev = mPrev;
103 }
104 if (mPrev != NULL) {
105 mPrev->mNext = mNext;
106 }
107 mNext = mPrev = NULL;
108 //LOGI("Destroying Asset in %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109}
110
111/*
112 * Create a new Asset from a file on disk. There is a fair chance that
113 * the file doesn't actually exist.
114 *
115 * We can use "mode" to decide how we want to go about it.
116 */
117/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
118{
119 _FileAsset* pAsset;
120 status_t result;
121 off_t length;
122 int fd;
123
124 fd = open(fileName, O_RDONLY | O_BINARY);
125 if (fd < 0)
126 return NULL;
127
128 /*
129 * Under Linux, the lseek fails if we actually opened a directory. To
130 * be correct we should test the file type explicitly, but since we
131 * always open things read-only it doesn't really matter, so there's
132 * no value in incurring the extra overhead of an fstat() call.
133 */
134 length = lseek(fd, 0, SEEK_END);
135 if (length < 0) {
136 ::close(fd);
137 return NULL;
138 }
139 (void) lseek(fd, 0, SEEK_SET);
140
141 pAsset = new _FileAsset;
142 result = pAsset->openChunk(fileName, fd, 0, length);
143 if (result != NO_ERROR) {
144 delete pAsset;
145 return NULL;
146 }
147
148 pAsset->mAccessMode = mode;
149 return pAsset;
150}
151
152
153/*
154 * Create a new Asset from a compressed file on disk. There is a fair chance
155 * that the file doesn't actually exist.
156 *
157 * We currently support gzip files. We might want to handle .bz2 someday.
158 */
159/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
160 AccessMode mode)
161{
162 _CompressedAsset* pAsset;
163 status_t result;
164 off_t fileLen;
165 bool scanResult;
166 long offset;
167 int method;
168 long uncompressedLen, compressedLen;
169 int fd;
170
171 fd = open(fileName, O_RDONLY | O_BINARY);
172 if (fd < 0)
173 return NULL;
174
175 fileLen = lseek(fd, 0, SEEK_END);
176 if (fileLen < 0) {
177 ::close(fd);
178 return NULL;
179 }
180 (void) lseek(fd, 0, SEEK_SET);
181
182 /* want buffered I/O for the file scan; must dup so fclose() is safe */
183 FILE* fp = fdopen(dup(fd), "rb");
184 if (fp == NULL) {
185 ::close(fd);
186 return NULL;
187 }
188
189 unsigned long crc32;
190 scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
191 &compressedLen, &crc32);
192 offset = ftell(fp);
193 fclose(fp);
194 if (!scanResult) {
195 LOGD("File '%s' is not in gzip format\n", fileName);
196 ::close(fd);
197 return NULL;
198 }
199
200 pAsset = new _CompressedAsset;
201 result = pAsset->openChunk(fd, offset, method, uncompressedLen,
202 compressedLen);
203 if (result != NO_ERROR) {
204 delete pAsset;
205 return NULL;
206 }
207
208 pAsset->mAccessMode = mode;
209 return pAsset;
210}
211
212
213#if 0
214/*
215 * Create a new Asset from part of an open file.
216 */
217/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset,
218 size_t length, AccessMode mode)
219{
220 _FileAsset* pAsset;
221 status_t result;
222
223 pAsset = new _FileAsset;
224 result = pAsset->openChunk(NULL, fd, offset, length);
225 if (result != NO_ERROR)
226 return NULL;
227
228 pAsset->mAccessMode = mode;
229 return pAsset;
230}
231
232/*
233 * Create a new Asset from compressed data in an open file.
234 */
235/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset,
236 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
237 AccessMode mode)
238{
239 _CompressedAsset* pAsset;
240 status_t result;
241
242 pAsset = new _CompressedAsset;
243 result = pAsset->openChunk(fd, offset, compressionMethod,
244 uncompressedLen, compressedLen);
245 if (result != NO_ERROR)
246 return NULL;
247
248 pAsset->mAccessMode = mode;
249 return pAsset;
250}
251#endif
252
253/*
254 * Create a new Asset from a memory mapping.
255 */
256/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
257 AccessMode mode)
258{
259 _FileAsset* pAsset;
260 status_t result;
261
262 pAsset = new _FileAsset;
263 result = pAsset->openChunk(dataMap);
264 if (result != NO_ERROR)
265 return NULL;
266
267 pAsset->mAccessMode = mode;
268 return pAsset;
269}
270
271/*
272 * Create a new Asset from compressed data in a memory mapping.
273 */
274/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
275 int method, size_t uncompressedLen, AccessMode mode)
276{
277 _CompressedAsset* pAsset;
278 status_t result;
279
280 pAsset = new _CompressedAsset;
281 result = pAsset->openChunk(dataMap, method, uncompressedLen);
282 if (result != NO_ERROR)
283 return NULL;
284
285 pAsset->mAccessMode = mode;
286 return pAsset;
287}
288
289
290/*
291 * Do generic seek() housekeeping. Pass in the offset/whence values from
292 * the seek request, along with the current chunk offset and the chunk
293 * length.
294 *
295 * Returns the new chunk offset, or -1 if the seek is illegal.
296 */
297off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn)
298{
299 off_t newOffset;
300
301 switch (whence) {
302 case SEEK_SET:
303 newOffset = offset;
304 break;
305 case SEEK_CUR:
306 newOffset = curPosn + offset;
307 break;
308 case SEEK_END:
309 newOffset = maxPosn + offset;
310 break;
311 default:
312 LOGW("unexpected whence %d\n", whence);
313 // this was happening due to an off_t size mismatch
314 assert(false);
315 return (off_t) -1;
316 }
317
318 if (newOffset < 0 || newOffset > maxPosn) {
319 LOGW("seek out of range: want %ld, end=%ld\n",
320 (long) newOffset, (long) maxPosn);
321 return (off_t) -1;
322 }
323
324 return newOffset;
325}
326
327
328/*
329 * ===========================================================================
330 * _FileAsset
331 * ===========================================================================
332 */
333
334/*
335 * Constructor.
336 */
337_FileAsset::_FileAsset(void)
338 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
339{
340}
341
342/*
343 * Destructor. Release resources.
344 */
345_FileAsset::~_FileAsset(void)
346{
347 close();
348}
349
350/*
351 * Operate on a chunk of an uncompressed file.
352 *
353 * Zero-length chunks are allowed.
354 */
355status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length)
356{
357 assert(mFp == NULL); // no reopen
358 assert(mMap == NULL);
359 assert(fd >= 0);
360 assert(offset >= 0);
361
362 /*
363 * Seek to end to get file length.
364 */
365 off_t fileLength;
366 fileLength = lseek(fd, 0, SEEK_END);
367 if (fileLength == (off_t) -1) {
368 // probably a bad file descriptor
369 LOGD("failed lseek (errno=%d)\n", errno);
370 return UNKNOWN_ERROR;
371 }
372
373 if ((off_t) (offset + length) > fileLength) {
374 LOGD("start (%ld) + len (%ld) > end (%ld)\n",
375 (long) offset, (long) length, (long) fileLength);
376 return BAD_INDEX;
377 }
378
379 /* after fdopen, the fd will be closed on fclose() */
380 mFp = fdopen(fd, "rb");
381 if (mFp == NULL)
382 return UNKNOWN_ERROR;
383
384 mStart = offset;
385 mLength = length;
386 assert(mOffset == 0);
387
388 /* seek the FILE* to the start of chunk */
389 if (fseek(mFp, mStart, SEEK_SET) != 0) {
390 assert(false);
391 }
392
393 mFileName = fileName != NULL ? strdup(fileName) : NULL;
394
395 return NO_ERROR;
396}
397
398/*
399 * Create the chunk from the map.
400 */
401status_t _FileAsset::openChunk(FileMap* dataMap)
402{
403 assert(mFp == NULL); // no reopen
404 assert(mMap == NULL);
405 assert(dataMap != NULL);
406
407 mMap = dataMap;
408 mStart = -1; // not used
409 mLength = dataMap->getDataLength();
410 assert(mOffset == 0);
411
412 return NO_ERROR;
413}
414
415/*
416 * Read a chunk of data.
417 */
418ssize_t _FileAsset::read(void* buf, size_t count)
419{
420 size_t maxLen;
421 size_t actual;
422
423 assert(mOffset >= 0 && mOffset <= mLength);
424
425 if (getAccessMode() == ACCESS_BUFFER) {
426 /*
427 * On first access, read or map the entire file. The caller has
428 * requested buffer access, either because they're going to be
429 * using the buffer or because what they're doing has appropriate
430 * performance needs and access patterns.
431 */
432 if (mBuf == NULL)
433 getBuffer(false);
434 }
435
436 /* adjust count if we're near EOF */
437 maxLen = mLength - mOffset;
438 if (count > maxLen)
439 count = maxLen;
440
441 if (!count)
442 return 0;
443
444 if (mMap != NULL) {
445 /* copy from mapped area */
446 //printf("map read\n");
447 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
448 actual = count;
449 } else if (mBuf != NULL) {
450 /* copy from buffer */
451 //printf("buf read\n");
452 memcpy(buf, (char*)mBuf + mOffset, count);
453 actual = count;
454 } else {
455 /* read from the file */
456 //printf("file read\n");
457 if (ftell(mFp) != mStart + mOffset) {
458 LOGE("Hosed: %ld != %ld+%ld\n",
459 ftell(mFp), (long) mStart, (long) mOffset);
460 assert(false);
461 }
462
463 /*
464 * This returns 0 on error or eof. We need to use ferror() or feof()
465 * to tell the difference, but we don't currently have those on the
466 * device. However, we know how much data is *supposed* to be in the
467 * file, so if we don't read the full amount we know something is
468 * hosed.
469 */
470 actual = fread(buf, 1, count, mFp);
471 if (actual == 0) // something failed -- I/O error?
472 return -1;
473
474 assert(actual == count);
475 }
476
477 mOffset += actual;
478 return actual;
479}
480
481/*
482 * Seek to a new position.
483 */
484off_t _FileAsset::seek(off_t offset, int whence)
485{
486 off_t newPosn;
487 long actualOffset;
488
489 // compute new position within chunk
490 newPosn = handleSeek(offset, whence, mOffset, mLength);
491 if (newPosn == (off_t) -1)
492 return newPosn;
493
494 actualOffset = (long) (mStart + newPosn);
495
496 if (mFp != NULL) {
497 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
498 return (off_t) -1;
499 }
500
501 mOffset = actualOffset - mStart;
502 return mOffset;
503}
504
505/*
506 * Close the asset.
507 */
508void _FileAsset::close(void)
509{
510 if (mMap != NULL) {
511 mMap->release();
512 mMap = NULL;
513 }
514 if (mBuf != NULL) {
515 delete[] mBuf;
516 mBuf = NULL;
517 }
518
519 if (mFileName != NULL) {
520 free(mFileName);
521 mFileName = NULL;
522 }
523
524 if (mFp != NULL) {
525 // can only be NULL when called from destructor
526 // (otherwise we would never return this object)
527 fclose(mFp);
528 mFp = NULL;
529 }
530}
531
532/*
533 * Return a read-only pointer to a buffer.
534 *
535 * We can either read the whole thing in or map the relevant piece of
536 * the source file. Ideally a map would be established at a higher
537 * level and we'd be using a different object, but we didn't, so we
538 * deal with it here.
539 */
540const void* _FileAsset::getBuffer(bool wordAligned)
541{
542 /* subsequent requests just use what we did previously */
543 if (mBuf != NULL)
544 return mBuf;
545 if (mMap != NULL) {
546 if (!wordAligned) {
547 return mMap->getDataPtr();
548 }
549 return ensureAlignment(mMap);
550 }
551
552 assert(mFp != NULL);
553
554 if (mLength < kReadVsMapThreshold) {
555 unsigned char* buf;
556 long allocLen;
557
558 /* zero-length files are allowed; not sure about zero-len allocs */
559 /* (works fine with gcc + x86linux) */
560 allocLen = mLength;
561 if (mLength == 0)
562 allocLen = 1;
563
564 buf = new unsigned char[allocLen];
565 if (buf == NULL) {
566 LOGE("alloc of %ld bytes failed\n", (long) allocLen);
567 return NULL;
568 }
569
570 LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
571 if (mLength > 0) {
572 long oldPosn = ftell(mFp);
573 fseek(mFp, mStart, SEEK_SET);
574 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
575 LOGE("failed reading %ld bytes\n", (long) mLength);
576 delete[] buf;
577 return NULL;
578 }
579 fseek(mFp, oldPosn, SEEK_SET);
580 }
581
582 LOGV(" getBuffer: loaded into buffer\n");
583
584 mBuf = buf;
585 return mBuf;
586 } else {
587 FileMap* map;
588
589 map = new FileMap;
590 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
591 map->release();
592 return NULL;
593 }
594
595 LOGV(" getBuffer: mapped\n");
596
597 mMap = map;
598 if (!wordAligned) {
599 return mMap->getDataPtr();
600 }
601 return ensureAlignment(mMap);
602 }
603}
604
605int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const
606{
607 if (mMap != NULL) {
608 const char* fname = mMap->getFileName();
609 if (fname == NULL) {
610 fname = mFileName;
611 }
612 if (fname == NULL) {
613 return -1;
614 }
615 *outStart = mMap->getDataOffset();
616 *outLength = mMap->getDataLength();
617 return open(fname, O_RDONLY | O_BINARY);
618 }
619 if (mFileName == NULL) {
620 return -1;
621 }
622 *outStart = mStart;
623 *outLength = mLength;
624 return open(mFileName, O_RDONLY | O_BINARY);
625}
626
627const void* _FileAsset::ensureAlignment(FileMap* map)
628{
629 void* data = map->getDataPtr();
630 if ((((size_t)data)&0x3) == 0) {
631 // We can return this directly if it is aligned on a word
632 // boundary.
Dianne Hackborn78c40512009-07-06 11:07:40 -0700633 LOGV("Returning aligned FileAsset %p (%s).", this,
634 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 return data;
636 }
637 // If not aligned on a word boundary, then we need to copy it into
638 // our own buffer.
Dianne Hackborn78c40512009-07-06 11:07:40 -0700639 LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
640 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 unsigned char* buf = new unsigned char[mLength];
642 if (buf == NULL) {
643 LOGE("alloc of %ld bytes failed\n", (long) mLength);
644 return NULL;
645 }
646 memcpy(buf, data, mLength);
647 mBuf = buf;
648 return buf;
649}
650
651/*
652 * ===========================================================================
653 * _CompressedAsset
654 * ===========================================================================
655 */
656
657/*
658 * Constructor.
659 */
660_CompressedAsset::_CompressedAsset(void)
661 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
662 mMap(NULL), mFd(-1), mBuf(NULL)
663{
664}
665
666/*
667 * Destructor. Release resources.
668 */
669_CompressedAsset::~_CompressedAsset(void)
670{
671 close();
672}
673
674/*
675 * Open a chunk of compressed data inside a file.
676 *
677 * This currently just sets up some values and returns. On the first
678 * read, we expand the entire file into a buffer and return data from it.
679 */
680status_t _CompressedAsset::openChunk(int fd, off_t offset,
681 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
682{
683 assert(mFd < 0); // no re-open
684 assert(mMap == NULL);
685 assert(fd >= 0);
686 assert(offset >= 0);
687 assert(compressedLen > 0);
688
689 if (compressionMethod != ZipFileRO::kCompressDeflated) {
690 assert(false);
691 return UNKNOWN_ERROR;
692 }
693
694 mStart = offset;
695 mCompressedLen = compressedLen;
696 mUncompressedLen = uncompressedLen;
697 assert(mOffset == 0);
698 mFd = fd;
699 assert(mBuf == NULL);
700
701 return NO_ERROR;
702}
703
704/*
705 * Open a chunk of compressed data in a mapped region.
706 *
707 * Nothing is expanded until the first read call.
708 */
709status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
710 size_t uncompressedLen)
711{
712 assert(mFd < 0); // no re-open
713 assert(mMap == NULL);
714 assert(dataMap != NULL);
715
716 if (compressionMethod != ZipFileRO::kCompressDeflated) {
717 assert(false);
718 return UNKNOWN_ERROR;
719 }
720
721 mMap = dataMap;
722 mStart = -1; // not used
723 mCompressedLen = dataMap->getDataLength();
724 mUncompressedLen = uncompressedLen;
725 assert(mOffset == 0);
726
727 return NO_ERROR;
728}
729
730/*
731 * Read data from a chunk of compressed data.
732 *
733 * [For now, that's just copying data out of a buffer.]
734 */
735ssize_t _CompressedAsset::read(void* buf, size_t count)
736{
737 size_t maxLen;
738 size_t actual;
739
740 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
741
742 // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly
743
744 if (mBuf == NULL) {
745 if (getBuffer(false) == NULL)
746 return -1;
747 }
748 assert(mBuf != NULL);
749
750 /* adjust count if we're near EOF */
751 maxLen = mUncompressedLen - mOffset;
752 if (count > maxLen)
753 count = maxLen;
754
755 if (!count)
756 return 0;
757
758 /* copy from buffer */
759 //printf("comp buf read\n");
760 memcpy(buf, (char*)mBuf + mOffset, count);
761 actual = count;
762
763 mOffset += actual;
764 return actual;
765}
766
767/*
768 * Handle a seek request.
769 *
770 * If we're working in a streaming mode, this is going to be fairly
771 * expensive, because it requires plowing through a bunch of compressed
772 * data.
773 */
774off_t _CompressedAsset::seek(off_t offset, int whence)
775{
776 off_t newPosn;
777
778 // compute new position within chunk
779 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
780 if (newPosn == (off_t) -1)
781 return newPosn;
782
783 mOffset = newPosn;
784 return mOffset;
785}
786
787/*
788 * Close the asset.
789 */
790void _CompressedAsset::close(void)
791{
792 if (mMap != NULL) {
793 mMap->release();
794 mMap = NULL;
795 }
796 if (mBuf != NULL) {
797 delete[] mBuf;
798 mBuf = NULL;
799 }
800
801 if (mFd > 0) {
802 ::close(mFd);
803 mFd = -1;
804 }
805}
806
807/*
808 * Get a pointer to a read-only buffer of data.
809 *
810 * The first time this is called, we expand the compressed data into a
811 * buffer.
812 */
813const void* _CompressedAsset::getBuffer(bool wordAligned)
814{
815 unsigned char* buf = NULL;
816
817 if (mBuf != NULL)
818 return mBuf;
819
820 if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
821 LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
822 (long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
823 goto bail;
824 }
825
826 /*
827 * Allocate a buffer and read the file into it.
828 */
829 buf = new unsigned char[mUncompressedLen];
830 if (buf == NULL) {
831 LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
832 goto bail;
833 }
834
835 if (mMap != NULL) {
836 if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
837 mUncompressedLen, mCompressedLen))
838 goto bail;
839 } else {
840 assert(mFd >= 0);
841
842 /*
843 * Seek to the start of the compressed data.
844 */
845 if (lseek(mFd, mStart, SEEK_SET) != mStart)
846 goto bail;
847
848 /*
849 * Expand the data into it.
850 */
851 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
852 mCompressedLen))
853 goto bail;
854 }
855
856 /* success! */
857 mBuf = buf;
858 buf = NULL;
859
860bail:
861 delete[] buf;
862 return mBuf;
863}
864