80094e39f9
55ef6167a2c235bd88c7216238b2001b46795b79 Change-Id: I02d4890d181655dfd0a14c188468db512559d27b Merged-In: I02d4890d181655dfd0a14c188468db512559d27b
293 lines
7.6 KiB
C++
293 lines
7.6 KiB
C++
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
//
|
|
// Read-only access to Zip archives, with minimal heap allocation.
|
|
//
|
|
#define LOG_TAG "zipro"
|
|
//#define LOG_NDEBUG 0
|
|
#include <androidfw/ZipFileRO.h>
|
|
#include <utils/Log.h>
|
|
#include <utils/Compat.h>
|
|
#include <utils/misc.h>
|
|
#include <utils/threads.h>
|
|
#include <ziparchive/zip_archive.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
|
|
using namespace android;
|
|
|
|
class _ZipEntryRO {
|
|
public:
|
|
ZipEntry entry;
|
|
std::string_view name;
|
|
void *cookie;
|
|
|
|
_ZipEntryRO() : cookie(NULL) {}
|
|
|
|
~_ZipEntryRO() {
|
|
EndIteration(cookie);
|
|
}
|
|
|
|
private:
|
|
_ZipEntryRO(const _ZipEntryRO& other);
|
|
_ZipEntryRO& operator=(const _ZipEntryRO& other);
|
|
};
|
|
|
|
ZipFileRO::~ZipFileRO() {
|
|
CloseArchive(mHandle);
|
|
if (mFileName != NULL) {
|
|
free(mFileName);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open the specified file read-only. We memory-map the entire thing and
|
|
* close the file before returning.
|
|
*/
|
|
/* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName)
|
|
{
|
|
ZipArchiveHandle handle;
|
|
const int32_t error = OpenArchive(zipFileName, &handle);
|
|
if (error) {
|
|
ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error));
|
|
CloseArchive(handle);
|
|
return NULL;
|
|
}
|
|
|
|
return new ZipFileRO(handle, strdup(zipFileName));
|
|
}
|
|
|
|
|
|
/* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName,
|
|
bool assume_ownership)
|
|
{
|
|
ZipArchiveHandle handle;
|
|
const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership);
|
|
if (error) {
|
|
ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error));
|
|
CloseArchive(handle);
|
|
return NULL;
|
|
}
|
|
|
|
return new ZipFileRO(handle, strdup(debugFileName));
|
|
}
|
|
|
|
ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
|
|
{
|
|
_ZipEntryRO* data = new _ZipEntryRO;
|
|
|
|
data->name = entryName;
|
|
|
|
const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
|
|
if (error) {
|
|
delete data;
|
|
return NULL;
|
|
}
|
|
|
|
return (ZipEntryRO) data;
|
|
}
|
|
|
|
/*
|
|
* Get the useful fields from the zip entry.
|
|
*
|
|
* Returns "false" if the offsets to the fields or the contents of the fields
|
|
* appear to be bogus.
|
|
*/
|
|
bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
|
|
uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
|
|
uint32_t* pModWhen, uint32_t* pCrc32) const
|
|
{
|
|
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const ZipEntry& ze = zipEntry->entry;
|
|
|
|
if (pMethod != NULL) {
|
|
*pMethod = ze.method;
|
|
}
|
|
if (pUncompLen != NULL) {
|
|
*pUncompLen = ze.uncompressed_length;
|
|
}
|
|
if (pCompLen != NULL) {
|
|
*pCompLen = ze.compressed_length;
|
|
}
|
|
if (pOffset != NULL) {
|
|
*pOffset = ze.offset;
|
|
}
|
|
if (pModWhen != NULL) {
|
|
*pModWhen = ze.mod_time;
|
|
}
|
|
if (pCrc32 != NULL) {
|
|
*pCrc32 = ze.crc32;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZipFileRO::startIteration(void** cookie) {
|
|
return startIteration(cookie, NULL, NULL);
|
|
}
|
|
|
|
bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix)
|
|
{
|
|
_ZipEntryRO* ze = new _ZipEntryRO;
|
|
int32_t error = StartIteration(mHandle, &(ze->cookie),
|
|
prefix ? prefix : "", suffix ? suffix : "");
|
|
if (error) {
|
|
ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
|
|
ErrorCodeString(error));
|
|
delete ze;
|
|
return false;
|
|
}
|
|
|
|
*cookie = ze;
|
|
return true;
|
|
}
|
|
|
|
ZipEntryRO ZipFileRO::nextEntry(void* cookie)
|
|
{
|
|
_ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie);
|
|
int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
|
|
if (error) {
|
|
if (error != -1) {
|
|
ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
|
|
ErrorCodeString(error));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return &(ze->entry);
|
|
}
|
|
|
|
void ZipFileRO::endIteration(void* cookie)
|
|
{
|
|
delete reinterpret_cast<_ZipEntryRO*>(cookie);
|
|
}
|
|
|
|
void ZipFileRO::releaseEntry(ZipEntryRO entry) const
|
|
{
|
|
delete reinterpret_cast<_ZipEntryRO*>(entry);
|
|
}
|
|
|
|
/*
|
|
* Copy the entry's filename to the buffer.
|
|
*/
|
|
int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, size_t bufLen)
|
|
const
|
|
{
|
|
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const uint16_t requiredSize = zipEntry->name.length() + 1;
|
|
|
|
if (bufLen < requiredSize) {
|
|
ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize);
|
|
return requiredSize;
|
|
}
|
|
|
|
memcpy(buffer, zipEntry->name.data(), requiredSize - 1);
|
|
buffer[requiredSize - 1] = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create a new FileMap object that spans the data in "entry".
|
|
*/
|
|
FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
|
|
{
|
|
const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const ZipEntry& ze = zipEntry->entry;
|
|
int fd = GetFileDescriptor(mHandle);
|
|
size_t actualLen = 0;
|
|
|
|
if (ze.method == kCompressStored) {
|
|
actualLen = ze.uncompressed_length;
|
|
} else {
|
|
actualLen = ze.compressed_length;
|
|
}
|
|
|
|
FileMap* newMap = new FileMap();
|
|
if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) {
|
|
delete newMap;
|
|
return NULL;
|
|
}
|
|
|
|
return newMap;
|
|
}
|
|
|
|
/*
|
|
* Create a new incfs::IncFsFileMap object that spans the data in "entry".
|
|
*/
|
|
std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const
|
|
{
|
|
const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const ZipEntry& ze = zipEntry->entry;
|
|
int fd = GetFileDescriptor(mHandle);
|
|
size_t actualLen = 0;
|
|
|
|
if (ze.method == kCompressStored) {
|
|
actualLen = ze.uncompressed_length;
|
|
} else {
|
|
actualLen = ze.compressed_length;
|
|
}
|
|
|
|
incfs::IncFsFileMap newMap;
|
|
if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) {
|
|
return std::nullopt;
|
|
}
|
|
return std::move(newMap);
|
|
}
|
|
|
|
/*
|
|
* Uncompress an entry, in its entirety, into the provided output buffer.
|
|
*
|
|
* This doesn't verify the data's CRC, which might be useful for
|
|
* uncompressed data. The caller should be able to manage it.
|
|
*/
|
|
bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const
|
|
{
|
|
_ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry),
|
|
(uint8_t*) buffer, size);
|
|
if (error) {
|
|
ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Uncompress an entry, in its entirety, to an open file descriptor.
|
|
*
|
|
* This doesn't verify the data's CRC, but probably should.
|
|
*/
|
|
bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
|
|
{
|
|
_ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
|
const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd);
|
|
if (error) {
|
|
ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|