blob: 43a70c176a839d957c0b84fdd8836545da02fa19 [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 */
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000301/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
302 AccessMode mode,
303 base::unique_fd fd)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304{
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000305 auto pAsset = util::make_unique<_FileAsset>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000307 status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800308 if (result != NO_ERROR) {
309 return NULL;
310 }
311
312 // We succeeded, so relinquish control of dataMap
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800313 pAsset->mAccessMode = mode;
314 return std::move(pAsset);
315}
316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317/*
318 * Create a new Asset from compressed data in a memory mapping.
319 */
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000320/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
321 size_t uncompressedLen,
322 AccessMode mode)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323{
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000324 auto pAsset = util::make_unique<_CompressedAsset>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000326 status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800327 if (result != NO_ERROR) {
328 return NULL;
329 }
330
331 // We succeeded, so relinquish control of dataMap
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800332 pAsset->mAccessMode = mode;
333 return std::move(pAsset);
334}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335
336/*
337 * Do generic seek() housekeeping. Pass in the offset/whence values from
338 * the seek request, along with the current chunk offset and the chunk
339 * length.
340 *
341 * Returns the new chunk offset, or -1 if the seek is illegal.
342 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800343off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344{
Kenny Rootddb76c42010-11-24 12:56:06 -0800345 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346
347 switch (whence) {
348 case SEEK_SET:
349 newOffset = offset;
350 break;
351 case SEEK_CUR:
352 newOffset = curPosn + offset;
353 break;
354 case SEEK_END:
355 newOffset = maxPosn + offset;
356 break;
357 default:
Steve Block8564c8d2012-01-05 23:22:43 +0000358 ALOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800359 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800361 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363
364 if (newOffset < 0 || newOffset > maxPosn) {
Steve Block8564c8d2012-01-05 23:22:43 +0000365 ALOGW("seek out of range: want %ld, end=%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800367 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 }
369
370 return newOffset;
371}
372
373
374/*
375 * ===========================================================================
376 * _FileAsset
377 * ===========================================================================
378 */
379
380/*
381 * Constructor.
382 */
383_FileAsset::_FileAsset(void)
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000384 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700386 // Register the Asset with the global list here after it is fully constructed and its
387 // vtable pointer points to this concrete type. b/31113965
388 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389}
390
391/*
392 * Destructor. Release resources.
393 */
394_FileAsset::~_FileAsset(void)
395{
396 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700397
398 // Unregister the Asset from the global list here before it is destructed and while its vtable
399 // pointer still points to this concrete type. b/31113965
400 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401}
402
403/*
404 * Operate on a chunk of an uncompressed file.
405 *
406 * Zero-length chunks are allowed.
407 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800408status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409{
410 assert(mFp == NULL); // no reopen
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000411 assert(!mMap.has_value());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 assert(fd >= 0);
413 assert(offset >= 0);
414
415 /*
416 * Seek to end to get file length.
417 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800418 off64_t fileLength;
419 fileLength = lseek64(fd, 0, SEEK_END);
420 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 // probably a bad file descriptor
Steve Block5baa3a62011-12-20 16:23:08 +0000422 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 return UNKNOWN_ERROR;
424 }
425
Kenny Rootddb76c42010-11-24 12:56:06 -0800426 if ((off64_t) (offset + length) > fileLength) {
Steve Block5baa3a62011-12-20 16:23:08 +0000427 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 (long) offset, (long) length, (long) fileLength);
429 return BAD_INDEX;
430 }
431
432 /* after fdopen, the fd will be closed on fclose() */
433 mFp = fdopen(fd, "rb");
434 if (mFp == NULL)
435 return UNKNOWN_ERROR;
436
437 mStart = offset;
438 mLength = length;
439 assert(mOffset == 0);
440
441 /* seek the FILE* to the start of chunk */
442 if (fseek(mFp, mStart, SEEK_SET) != 0) {
443 assert(false);
444 }
445
446 mFileName = fileName != NULL ? strdup(fileName) : NULL;
Mark Salyzyn00adb862014-03-19 11:00:06 -0700447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 return NO_ERROR;
449}
450
451/*
452 * Create the chunk from the map.
453 */
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000454status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455{
456 assert(mFp == NULL); // no reopen
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000457 assert(!mMap.has_value());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 assert(dataMap != NULL);
459
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000460 mMap = std::move(dataMap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 mStart = -1; // not used
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000462 mLength = mMap->length();
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700463 mFd = std::move(fd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 assert(mOffset == 0);
465
466 return NO_ERROR;
467}
468
469/*
470 * Read a chunk of data.
471 */
472ssize_t _FileAsset::read(void* buf, size_t count)
473{
474 size_t maxLen;
475 size_t actual;
476
477 assert(mOffset >= 0 && mOffset <= mLength);
478
479 if (getAccessMode() == ACCESS_BUFFER) {
480 /*
481 * On first access, read or map the entire file. The caller has
482 * requested buffer access, either because they're going to be
483 * using the buffer or because what they're doing has appropriate
484 * performance needs and access patterns.
485 */
486 if (mBuf == NULL)
487 getBuffer(false);
488 }
489
490 /* adjust count if we're near EOF */
491 maxLen = mLength - mOffset;
492 if (count > maxLen)
493 count = maxLen;
494
495 if (!count)
496 return 0;
497
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000498 if (mMap.has_value()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 /* copy from mapped area */
500 //printf("map read\n");
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000501 const auto readPos = mMap->data().offset(mOffset).convert<char>();
502 if (!readPos.verify(count)) {
503 return -1;
504 }
505
506 memcpy(buf, readPos.unsafe_ptr(), count);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 actual = count;
508 } else if (mBuf != NULL) {
509 /* copy from buffer */
510 //printf("buf read\n");
511 memcpy(buf, (char*)mBuf + mOffset, count);
512 actual = count;
513 } else {
514 /* read from the file */
515 //printf("file read\n");
516 if (ftell(mFp) != mStart + mOffset) {
Steve Block3762c312012-01-06 19:20:56 +0000517 ALOGE("Hosed: %ld != %ld+%ld\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 ftell(mFp), (long) mStart, (long) mOffset);
519 assert(false);
520 }
521
522 /*
523 * This returns 0 on error or eof. We need to use ferror() or feof()
524 * to tell the difference, but we don't currently have those on the
525 * device. However, we know how much data is *supposed* to be in the
526 * file, so if we don't read the full amount we know something is
527 * hosed.
528 */
529 actual = fread(buf, 1, count, mFp);
530 if (actual == 0) // something failed -- I/O error?
531 return -1;
532
533 assert(actual == count);
534 }
535
536 mOffset += actual;
537 return actual;
538}
539
540/*
541 * Seek to a new position.
542 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800543off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544{
Kenny Rootddb76c42010-11-24 12:56:06 -0800545 off64_t newPosn;
546 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547
548 // compute new position within chunk
549 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800550 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 return newPosn;
552
Kenny Rootddb76c42010-11-24 12:56:06 -0800553 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554
555 if (mFp != NULL) {
556 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800557 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 }
559
560 mOffset = actualOffset - mStart;
561 return mOffset;
562}
563
564/*
565 * Close the asset.
566 */
567void _FileAsset::close(void)
568{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 if (mBuf != NULL) {
570 delete[] mBuf;
571 mBuf = NULL;
572 }
573
574 if (mFileName != NULL) {
575 free(mFileName);
576 mFileName = NULL;
577 }
Mark Salyzyn00adb862014-03-19 11:00:06 -0700578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 if (mFp != NULL) {
580 // can only be NULL when called from destructor
581 // (otherwise we would never return this object)
582 fclose(mFp);
583 mFp = NULL;
584 }
585}
586
587/*
588 * Return a read-only pointer to a buffer.
589 *
590 * We can either read the whole thing in or map the relevant piece of
591 * the source file. Ideally a map would be established at a higher
592 * level and we'd be using a different object, but we didn't, so we
593 * deal with it here.
594 */
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000595const void* _FileAsset::getBuffer(bool aligned)
596{
Ryan Mitchellcfb916e2021-05-27 12:34:52 -0700597 auto buffer = getIncFsBuffer(aligned);
598 if (mBuf != NULL)
599 return mBuf;
600 if (!buffer.convert<uint8_t>().verify(mLength))
601 return NULL;
602 return buffer.unsafe_ptr();
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000603}
604
605incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606{
607 /* subsequent requests just use what we did previously */
608 if (mBuf != NULL)
609 return mBuf;
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000610 if (mMap.has_value()) {
611 if (!aligned) {
612 return mMap->data();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000614 return ensureAlignment(*mMap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 }
616
617 assert(mFp != NULL);
618
619 if (mLength < kReadVsMapThreshold) {
620 unsigned char* buf;
621 long allocLen;
622
623 /* zero-length files are allowed; not sure about zero-len allocs */
624 /* (works fine with gcc + x86linux) */
625 allocLen = mLength;
626 if (mLength == 0)
627 allocLen = 1;
628
629 buf = new unsigned char[allocLen];
630 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000631 ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 return NULL;
633 }
634
Steve Block71f2cf12011-10-20 11:56:00 +0100635 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 if (mLength > 0) {
637 long oldPosn = ftell(mFp);
638 fseek(mFp, mStart, SEEK_SET);
639 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
Steve Block3762c312012-01-06 19:20:56 +0000640 ALOGE("failed reading %ld bytes\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 delete[] buf;
642 return NULL;
643 }
644 fseek(mFp, oldPosn, SEEK_SET);
645 }
646
Steve Block71f2cf12011-10-20 11:56:00 +0100647 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648
649 mBuf = buf;
650 return mBuf;
651 } else {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000652 incfs::IncFsFileMap map;
653 if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 return NULL;
655 }
656
Steve Block71f2cf12011-10-20 11:56:00 +0100657 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000659 mMap = std::move(map);
660 if (!aligned) {
661 return mMap->data();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000663 return ensureAlignment(*mMap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
665}
666
Kenny Rootddb76c42010-11-24 12:56:06 -0800667int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668{
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000669 if (mMap.has_value()) {
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700670 if (mFd.ok()) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000671 *outStart = mMap->offset();
672 *outLength = mMap->length();
673 const int fd = dup(mFd);
674 if (fd < 0) {
675 ALOGE("Unable to dup fd (%d).", mFd.get());
676 return -1;
677 }
678 lseek64(fd, 0, SEEK_SET);
679 return fd;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700680 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000681 const char* fname = mMap->file_name();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 if (fname == NULL) {
683 fname = mFileName;
684 }
685 if (fname == NULL) {
686 return -1;
687 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000688 *outStart = mMap->offset();
689 *outLength = mMap->length();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 return open(fname, O_RDONLY | O_BINARY);
691 }
692 if (mFileName == NULL) {
693 return -1;
694 }
695 *outStart = mStart;
696 *outLength = mLength;
697 return open(mFileName, O_RDONLY | O_BINARY);
698}
699
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000700incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701{
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000702 const auto data = map.data();
703 if (util::IsFourByteAligned(data)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 // We can return this directly if it is aligned on a word
705 // boundary.
Steve Block71f2cf12011-10-20 11:56:00 +0100706 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700707 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 return data;
709 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000710
711 if (!data.convert<uint8_t>().verify(mLength)) {
712 return NULL;
713 }
714
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 // If not aligned on a word boundary, then we need to copy it into
716 // our own buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100717 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700718 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 unsigned char* buf = new unsigned char[mLength];
720 if (buf == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000721 ALOGE("alloc of %ld bytes failed\n", (long) mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 return NULL;
723 }
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000724
725 memcpy(buf, data.unsafe_ptr(), mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 mBuf = buf;
727 return buf;
728}
729
730/*
731 * ===========================================================================
732 * _CompressedAsset
733 * ===========================================================================
734 */
735
736/*
737 * Constructor.
738 */
739_CompressedAsset::_CompressedAsset(void)
740 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000741 mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742{
Adam Lesinski0358efe2016-10-17 13:50:56 -0700743 // Register the Asset with the global list here after it is fully constructed and its
744 // vtable pointer points to this concrete type. b/31113965
745 registerAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746}
747
748/*
749 * Destructor. Release resources.
750 */
751_CompressedAsset::~_CompressedAsset(void)
752{
753 close();
Adam Lesinski0358efe2016-10-17 13:50:56 -0700754
755 // Unregister the Asset from the global list here before it is destructed and while its vtable
756 // pointer still points to this concrete type. b/31113965
757 unregisterAsset(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758}
759
760/*
761 * Open a chunk of compressed data inside a file.
762 *
763 * This currently just sets up some values and returns. On the first
764 * read, we expand the entire file into a buffer and return data from it.
765 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800766status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
768{
769 assert(mFd < 0); // no re-open
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000770 assert(!mMap.has_value());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 assert(fd >= 0);
772 assert(offset >= 0);
773 assert(compressedLen > 0);
774
775 if (compressionMethod != ZipFileRO::kCompressDeflated) {
776 assert(false);
777 return UNKNOWN_ERROR;
778 }
779
780 mStart = offset;
781 mCompressedLen = compressedLen;
782 mUncompressedLen = uncompressedLen;
783 assert(mOffset == 0);
784 mFd = fd;
785 assert(mBuf == NULL);
786
Christopher Tateb100cbf2010-07-26 11:24:18 -0700787 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
788 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
789 }
790
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 return NO_ERROR;
792}
793
794/*
795 * Open a chunk of compressed data in a mapped region.
796 *
797 * Nothing is expanded until the first read call.
798 */
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000799status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800{
801 assert(mFd < 0); // no re-open
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000802 assert(!mMap.has_value());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 assert(dataMap != NULL);
804
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000805 mMap = std::move(dataMap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 mStart = -1; // not used
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000807 mCompressedLen = mMap->length();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 mUncompressedLen = uncompressedLen;
809 assert(mOffset == 0);
810
Christopher Tateb100cbf2010-07-26 11:24:18 -0700811 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000812 mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen);
Christopher Tateb100cbf2010-07-26 11:24:18 -0700813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 return NO_ERROR;
815}
816
817/*
818 * Read data from a chunk of compressed data.
819 *
820 * [For now, that's just copying data out of a buffer.]
821 */
822ssize_t _CompressedAsset::read(void* buf, size_t count)
823{
824 size_t maxLen;
825 size_t actual;
826
827 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
828
Christopher Tateb100cbf2010-07-26 11:24:18 -0700829 /* If we're relying on a streaming inflater, go through that */
830 if (mZipInflater) {
831 actual = mZipInflater->read(buf, count);
832 } else {
833 if (mBuf == NULL) {
834 if (getBuffer(false) == NULL)
835 return -1;
836 }
837 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838
Christopher Tateb100cbf2010-07-26 11:24:18 -0700839 /* adjust count if we're near EOF */
840 maxLen = mUncompressedLen - mOffset;
841 if (count > maxLen)
842 count = maxLen;
843
844 if (!count)
845 return 0;
846
847 /* copy from buffer */
848 //printf("comp buf read\n");
849 memcpy(buf, (char*)mBuf + mOffset, count);
850 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852
853 mOffset += actual;
854 return actual;
855}
856
857/*
858 * Handle a seek request.
859 *
860 * If we're working in a streaming mode, this is going to be fairly
861 * expensive, because it requires plowing through a bunch of compressed
862 * data.
863 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800864off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865{
Kenny Rootddb76c42010-11-24 12:56:06 -0800866 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867
868 // compute new position within chunk
869 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800870 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 return newPosn;
872
Christopher Tateb100cbf2010-07-26 11:24:18 -0700873 if (mZipInflater) {
874 mZipInflater->seekAbsolute(newPosn);
875 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 mOffset = newPosn;
877 return mOffset;
878}
879
880/*
881 * Close the asset.
882 */
883void _CompressedAsset::close(void)
884{
Christopher Tateb100cbf2010-07-26 11:24:18 -0700885 delete[] mBuf;
886 mBuf = NULL;
887
888 delete mZipInflater;
889 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890
891 if (mFd > 0) {
892 ::close(mFd);
893 mFd = -1;
894 }
895}
896
897/*
898 * Get a pointer to a read-only buffer of data.
899 *
900 * The first time this is called, we expand the compressed data into a
901 * buffer.
902 */
Narayan Kamathafd31e02013-12-03 13:16:03 +0000903const void* _CompressedAsset::getBuffer(bool)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904{
905 unsigned char* buf = NULL;
906
907 if (mBuf != NULL)
908 return mBuf;
909
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 /*
911 * Allocate a buffer and read the file into it.
912 */
913 buf = new unsigned char[mUncompressedLen];
914 if (buf == NULL) {
Steve Block8564c8d2012-01-05 23:22:43 +0000915 ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 goto bail;
917 }
918
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000919 if (mMap.has_value()) {
920 if (!ZipUtils::inflateToBuffer(mMap->data(), buf,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 mUncompressedLen, mCompressedLen))
922 goto bail;
923 } else {
924 assert(mFd >= 0);
925
926 /*
927 * Seek to the start of the compressed data.
928 */
929 if (lseek(mFd, mStart, SEEK_SET) != mStart)
930 goto bail;
931
932 /*
933 * Expand the data into it.
934 */
935 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
936 mCompressedLen))
937 goto bail;
938 }
939
Christopher Tateb100cbf2010-07-26 11:24:18 -0700940 /*
941 * Success - now that we have the full asset in RAM we
942 * no longer need the streaming inflater
943 */
944 delete mZipInflater;
945 mZipInflater = NULL;
946
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 mBuf = buf;
948 buf = NULL;
949
950bail:
951 delete[] buf;
952 return mBuf;
953}
954
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000955incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) {
956 return incfs::map_ptr<void>(getBuffer(aligned));
957}