static int mapCentralDirectory0(int fd, const char* debugFileName, ZipArchive* pArchive, off_t fileLength, size_t readAmount, u1* scanBuf) { off_t searchStart = fileLength - readAmount; if (lseek(fd, searchStart, SEEK_SET) != searchStart) { LOGW("Zip: seek %ld failed: %s", (long) searchStart, strerror(errno)); return -1; } ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { LOGW("Zip: read %zd failed: %s", readAmount, strerror(errno)); return -1; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { LOGV("+++ Found EOCD at buf+%d", i); break; } } if (i < 0) { LOGD("Zip: EOCD not found, %s is not zip", debugFileName); return -1; } off_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 dirSize = get4LE(eocdPtr + kEOCDSize); u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset); if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)", (long) dirOffset, dirSize, (long) eocdOffset); return -1; } if (numEntries == 0) { LOGW("Zip: empty archive?"); return -1; } LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d", numEntries, dirSize, dirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize, &pArchive->mDirectoryMap) != 0) { LOGW("Zip: cd map failed"); return -1; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = dirOffset; return 0; }
/* * Find the zip Central Directory and memory-map it. * * On success, returns 0 after populating fields from the EOCD area: * mDirectoryOffset * mDirectoryMap * mNumEntries */ static int mapCentralDirectory(int fd, const char* debugFileName, ZipArchive* pArchive) { u1* scanBuf = NULL; int result = -1; /* * Get and test file length. */ off_t fileLength = lseek(fd, 0, SEEK_END); if (fileLength < kEOCDLen) { LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength); goto bail; } /* * Perform the traditional EOCD snipe hunt. * * We're searching for the End of Central Directory magic number, * which appears at the start of the EOCD block. It's followed by * 18 bytes of EOCD stuff and up to 64KB of archive comment. We * need to read the last part of the file into a buffer, dig through * it to find the magic number, parse some values out, and use those * to determine the extent of the CD. * * We start by pulling in the last part of the file. */ size_t readAmount = kMaxEOCDSearch; if (readAmount > (size_t) fileLength) readAmount = fileLength; off_t searchStart = fileLength - readAmount; scanBuf = (u1*) malloc(readAmount); if (lseek(fd, searchStart, SEEK_SET) != searchStart) { LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno)); goto bail; } ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); goto bail; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { LOGV("+++ Found EOCD at buf+%d\n", i); break; } } if (i < 0) { LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName); goto bail; } off_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 dirSize = get4LE(eocdPtr + kEOCDSize); u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset); if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n", (long) dirOffset, dirSize, (long) eocdOffset); goto bail; } if (numEntries == 0) { LOGW("Zip: empty archive?\n"); goto bail; } LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", numEntries, dirSize, dirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize, &pArchive->mDirectoryMap) != 0) { LOGW("Zip: cd map failed\n"); goto bail; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = dirOffset; result = 0; bail: free(scanBuf); return result; }
static int mapCentralDirectory0(int fd, const char* debugFileName, ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf) { /* * Make sure this is a Zip archive. */ if (lseek64(pArchive->mFd, 0, SEEK_SET) != 0) { ALOGW("seek to start failed: %s", strerror(errno)); return false; } ssize_t actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, sizeof(int32_t))); if (actual != (ssize_t) sizeof(int32_t)) { ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); return false; } unsigned int header = get4LE(scanBuf); if (header != kLFHSignature) { ALOGV("Not a Zip archive (found 0x%08x)\n", header); return false; } /* * Perform the traditional EOCD snipe hunt. * * We're searching for the End of Central Directory magic number, * which appears at the start of the EOCD block. It's followed by * 18 bytes of EOCD stuff and up to 64KB of archive comment. We * need to read the last part of the file into a buffer, dig through * it to find the magic number, parse some values out, and use those * to determine the extent of the CD. * * We start by pulling in the last part of the file. */ off64_t searchStart = fileLength - readAmount; if (lseek64(pArchive->mFd, searchStart, SEEK_SET) != searchStart) { ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); return false; } actual = TEMP_FAILURE_RETRY(read(pArchive->mFd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { ALOGW("Zip: read %zd, expected %zd. Failed: %s\n", actual, readAmount, strerror(errno)); return false; } /* * Scan backward for the EOCD magic. In an archive without a trailing * comment, we'll find it on the first try. (We may want to consider * doing an initial minimal read; if we don't find it, retry with a * second read as above.) */ int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { ALOGV("+++ Found EOCD at buf+%d", i); break; } } if (i < 0) { ALOGD("Zip: EOCD not found, %s is not zip", debugFileName); return -1; } off64_t eocdOffset = searchStart + i; const u1* eocdPtr = scanBuf + i; assert(eocdOffset < fileLength); /* * Grab the CD offset and size, and the number of entries in the * archive. Verify that they look reasonable. */ u4 diskNumber = get2LE(eocdPtr + kEOCDDiskNumber); u4 diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD); u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries); u4 totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries); u4 centralDirSize = get4LE(eocdPtr + kEOCDSize); u4 centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset); u4 commentSize = get2LE(eocdPtr + kEOCDCommentSize); // Verify that they look reasonable. if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) { ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", (long) centralDirOffset, centralDirSize, (long) eocdOffset); return false; } if (numEntries == 0) { ALOGW("empty archive?\n"); return false; } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { ALOGW("spanned archives not supported"); return false; } // Check to see if comment is a sane size if (((size_t) commentSize > (fileLength - kEOCDLen)) || (eocdOffset > (fileLength - kEOCDLen) - commentSize)) { ALOGW("comment size runs off end of file"); return false; } ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", numEntries, centralDirSize, centralDirOffset); /* * It all looks good. Create a mapping for the CD, and set the fields * in pArchive. */ if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize, &pArchive->mDirectoryMap) != 0) { ALOGW("Zip: cd map failed"); return -1; } pArchive->mNumEntries = numEntries; pArchive->mDirectoryOffset = centralDirOffset; return 0; }