am 336bf2fb: am d21752df: Merge "Reimplement ZipFileRO in terms of libziparchive."

* commit '336bf2fb497fece49a874da2ca2e11572446d1d1':
  Reimplement ZipFileRO in terms of libziparchive.
This commit is contained in:
Narayan Kamath
2013-12-11 21:02:08 +00:00
committed by Android Git Automerger
13 changed files with 402 additions and 1209 deletions

View File

@ -63,14 +63,19 @@ extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
namespace android {
static const int ANIM_ENTRY_NAME_MAX = 256;
// ---------------------------------------------------------------------------
BootAnimation::BootAnimation() : Thread(false)
BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
{
mSession = new SurfaceComposerClient();
}
BootAnimation::~BootAnimation() {
if (mZip != NULL) {
delete mZip;
}
}
void BootAnimation::onFirstRef() {
@ -86,7 +91,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
}
void BootAnimation::binderDied(const wp<IBinder>& who)
void BootAnimation::binderDied(const wp<IBinder>&)
{
// woah, surfaceflinger died!
ALOGD("SurfaceFlinger died, exiting...");
@ -268,8 +273,6 @@ status_t BootAnimation::readyToRun() {
mFlingerSurfaceControl = control;
mFlingerSurface = s;
mAndroidAnimation = true;
// If the device has encryption turned on or is in process
// of being encrypted we show the encrypted boot animation.
char decrypt[PROPERTY_VALUE_MAX];
@ -277,16 +280,17 @@ status_t BootAnimation::readyToRun() {
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
ZipFileRO* zipFile = NULL;
if ((encryptedAnimation &&
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
(mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||
((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
(mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) ||
((zipFile = ZipFileRO::open(USER_BOOTANIMATION_FILE)) != NULL)) ||
((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
(mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
mAndroidAnimation = false;
((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
mZip = zipFile;
}
return NO_ERROR;
@ -295,7 +299,9 @@ status_t BootAnimation::readyToRun() {
bool BootAnimation::threadLoop()
{
bool r;
if (mAndroidAnimation) {
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZip == NULL) {
r = android();
} else {
r = movie();
@ -392,11 +398,14 @@ void BootAnimation::checkExit() {
bool BootAnimation::movie()
{
ZipFileRO& zip(mZip);
ZipEntryRO desc = mZip->findEntryByName("desc.txt");
ALOGE_IF(!desc, "couldn't find desc.txt");
if (!desc) {
return false;
}
size_t numEntries = zip.getNumEntries();
ZipEntryRO desc = zip.findEntryByName("desc.txt");
FileMap* descMap = zip.createEntryFileMap(desc);
FileMap* descMap = mZip->createEntryFileMap(desc);
mZip->releaseEntry(desc);
ALOGE_IF(!descMap, "descMap is null");
if (!descMap) {
return false;
@ -415,7 +424,7 @@ bool BootAnimation::movie()
String8 line(s, endl - s);
const char* l = line.string();
int fps, width, height, count, pause;
char path[256];
char path[ANIM_ENTRY_NAME_MAX];
char pathType;
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
//LOGD("> w=%d, h=%d, fps=%d", width, height, fps);
@ -438,28 +447,37 @@ bool BootAnimation::movie()
// read all the data structures
const size_t pcount = animation.parts.size();
for (size_t i=0 ; i<numEntries ; i++) {
char name[256];
ZipEntryRO entry = zip.findEntryByIndex(i);
if (zip.getEntryFileName(entry, name, 256) == 0) {
const String8 entryName(name);
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
for (int j=0 ; j<pcount ; j++) {
if (path == animation.parts[j].path) {
int method;
// supports only stored png files
if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {
if (method == ZipFileRO::kCompressStored) {
FileMap* map = zip.createEntryFileMap(entry);
if (map) {
Animation::Frame frame;
frame.name = leaf;
frame.map = map;
Animation::Part& part(animation.parts.editItemAt(j));
part.frames.add(frame);
}
void *cookie = NULL;
if (!mZip->startIteration(&cookie)) {
return false;
}
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
while ((entry = mZip->nextEntry(cookie)) != NULL) {
const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
ALOGE("Error fetching entry file name");
continue;
}
const String8 entryName(name);
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
for (size_t j=0 ; j<pcount ; j++) {
if (path == animation.parts[j].path) {
int method;
// supports only stored png files
if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
if (method == ZipFileRO::kCompressStored) {
FileMap* map = mZip->createEntryFileMap(entry);
if (map) {
Animation::Frame frame;
frame.name = leaf;
frame.map = map;
Animation::Part& part(animation.parts.editItemAt(j));
part.frames.add(frame);
}
}
}
@ -468,6 +486,8 @@ bool BootAnimation::movie()
}
}
mZip->endIteration(cookie);
// clear screen
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
@ -494,7 +514,7 @@ bool BootAnimation::movie()
Region clearReg(Rect(mWidth, mHeight));
clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
for (int i=0 ; i<pcount ; i++) {
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
@ -504,7 +524,7 @@ bool BootAnimation::movie()
if(exitPending() && !part.playUntilComplete)
break;
for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
@ -564,7 +584,7 @@ bool BootAnimation::movie()
// free the textures for this part
if (part.count != 1) {
for (int j=0 ; j<fcount ; j++) {
for (size_t j=0 ; j<fcount ; j++) {
const Animation::Frame& frame(part.frames[j]);
glDeleteTextures(1, &frame.tid);
}

View File

@ -95,8 +95,7 @@ private:
EGLDisplay mSurface;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
bool mAndroidAnimation;
ZipFileRO mZip;
ZipFileRO *mZip;
};
// ---------------------------------------------------------------------------

View File

@ -21,7 +21,9 @@
#include <utils/Log.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
#include <ScopedUtfChars.h>
#include <UniquePtr.h>
#include <zlib.h>
@ -143,7 +145,7 @@ isFileDifferent(const char* filePath, size_t fileSize, time_t modifiedTime,
}
static install_status_t
sumFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char*)
{
size_t* total = (size_t*) arg;
size_t uncompLen;
@ -178,7 +180,7 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
return INSTALL_FAILED_INVALID_APK;
} else {
struct tm t;
ZipFileRO::zipTimeToTimespec(when, &t);
ZipUtils::zipTimeToTimespec(when, &t);
modTime = mktime(&t);
}
@ -273,26 +275,25 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js
ScopedUtfChars cpuAbi(env, javaCpuAbi);
ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
ZipFileRO zipFile;
if (zipFile.open(filePath.c_str()) != NO_ERROR) {
UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str()));
if (zipFile.get() == NULL) {
ALOGI("Couldn't open APK %s\n", filePath.c_str());
return INSTALL_FAILED_INVALID_APK;
}
const int N = zipFile.getNumEntries();
char fileName[PATH_MAX];
bool hasPrimaryAbi = false;
for (int i = 0; i < N; i++) {
const ZipEntryRO entry = zipFile.findEntryByIndex(i);
if (entry == NULL) {
continue;
}
void* cookie = NULL;
if (!zipFile->startIteration(&cookie)) {
ALOGI("Couldn't iterate over APK%s\n", filePath.c_str());
return INSTALL_FAILED_INVALID_APK;
}
ZipEntryRO entry = NULL;
while ((entry = zipFile->nextEntry(cookie)) != NULL) {
// Make sure this entry has a filename.
if (zipFile.getEntryFileName(entry, fileName, sizeof(fileName))) {
if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
continue;
}
@ -346,15 +347,18 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js
&& isFilenameSafe(lastSlash + 1))
|| !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
install_status_t ret = callFunc(env, callArg, &zipFile, entry, lastSlash + 1);
install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1);
if (ret != INSTALL_SUCCEEDED) {
ALOGV("Failure for entry %s", lastSlash + 1);
zipFile->endIteration(cookie);
return ret;
}
}
}
zipFile->endIteration(cookie);
return INSTALL_SUCCEEDED;
}

View File

@ -40,6 +40,8 @@
#include <unistd.h>
#include <time.h>
typedef void* ZipArchiveHandle;
namespace android {
/*
@ -51,18 +53,13 @@ typedef void* ZipEntryRO;
/*
* Open a Zip archive for reading.
*
* We want "open" and "find entry by name" to be fast operations, and we
* want to use as little memory as possible. We memory-map the file,
* and load a hash table with pointers to the filenames (which aren't
* null-terminated). The other fields are at a fixed offset from the
* filename, so we don't need to extract those (but we do need to byte-read
* and endian-swap them every time we want them).
* Implemented as a thin wrapper over system/core/libziparchive.
*
* To speed comparisons when doing a lookup by name, we could make the mapping
* "private" (copy-on-write) and null-terminate the filenames after verifying
* the record structure. However, this requires a private mapping of
* every page that the Central Directory touches. Easier to tuck a copy
* of the string length into the hash table entry.
* "open" and "find entry by name" are fast operations and use as little
* memory as possible.
*
* We also support fast iteration over all entries in the file (with a
* stable, but unspecified iteration order).
*
* NOTE: If this is used on file descriptors inherited from a fork() operation,
* you must be on a platform that implements pread() to guarantee correctness
@ -70,48 +67,44 @@ typedef void* ZipEntryRO;
*/
class ZipFileRO {
public:
ZipFileRO()
: mFd(-1), mFileName(NULL), mFileLength(-1),
mDirectoryMap(NULL),
mNumEntries(-1), mDirectoryOffset(-1),
mHashTableSize(-1), mHashTable(NULL)
{}
~ZipFileRO();
/* Zip compression methods we support */
enum {
kCompressStored = 0, // no compression
kCompressDeflated = 8, // standard deflate
};
/*
* Open an archive.
*/
status_t open(const char* zipFileName);
static ZipFileRO* open(const char* zipFileName);
/*
* Find an entry, by name. Returns the entry identifier, or NULL if
* not found.
*
* If two entries have the same name, one will be chosen at semi-random.
*/
ZipEntryRO findEntryByName(const char* fileName) const;
ZipEntryRO findEntryByName(const char* entryName) const;
/*
* Start iterating over the list of entries in the zip file. Requires
* a matching call to endIteration with the same cookie.
*/
bool startIteration(void** cookie);
/**
* Return the next entry in iteration order, or NULL if there are no more
* entries in this archive.
*/
ZipEntryRO nextEntry(void* cookie);
void endIteration(void* cookie);
void releaseEntry(ZipEntryRO entry) const;
/*
* Return the #of entries in the Zip archive.
*/
int getNumEntries(void) const {
return mNumEntries;
}
/*
* Return the Nth entry. Zip file entries are not stored in sorted
* order, and updated entries may appear at the end, so anyone walking
* the archive needs to avoid making ordering assumptions. We take
* that further by returning the Nth non-empty entry in the hash table
* rather than the Nth entry in the archive.
*
* Valid values are [0..numEntries).
*
* [This is currently O(n). If it needs to be fast we can allocate an
* additional data structure or provide an iterator interface.]
*/
ZipEntryRO findEntryByIndex(int idx) const;
int getNumEntries();
/*
* Copy the filename into the supplied buffer. Returns 0 on success,
@ -149,112 +142,27 @@ public:
*
* Returns "true" on success.
*/
bool uncompressEntry(ZipEntryRO entry, void* buffer) const;
bool uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const;
/*
* Uncompress the data to an open file descriptor.
*/
bool uncompressEntry(ZipEntryRO entry, int fd) const;
/* Zip compression methods we support */
enum {
kCompressStored = 0, // no compression
kCompressDeflated = 8, // standard deflate
};
/*
* Utility function: uncompress deflated data, buffer to buffer.
*/
static bool inflateBuffer(void* outBuf, const void* inBuf,
size_t uncompLen, size_t compLen);
/*
* Utility function: uncompress deflated data, buffer to fd.
*/
static bool inflateBuffer(int fd, const void* inBuf,
size_t uncompLen, size_t compLen);
/*
* Utility function to convert ZIP's time format to a timespec struct.
*/
static inline void zipTimeToTimespec(long when, struct tm* timespec) {
const long date = when >> 16;
timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
timespec->tm_mon = (date >> 5) & 0x0F;
timespec->tm_mday = date & 0x1F;
timespec->tm_hour = (when >> 11) & 0x1F;
timespec->tm_min = (when >> 5) & 0x3F;
timespec->tm_sec = (when & 0x1F) << 1;
}
/*
* Some basic functions for raw data manipulation. "LE" means
* Little Endian.
*/
static inline unsigned short get2LE(const unsigned char* buf) {
return buf[0] | (buf[1] << 8);
}
static inline unsigned long get4LE(const unsigned char* buf) {
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
~ZipFileRO();
private:
/* these are private and not defined */
/* these are private and not defined */
ZipFileRO(const ZipFileRO& src);
ZipFileRO& operator=(const ZipFileRO& src);
/* locate and parse the central directory */
bool mapCentralDirectory(void);
ZipFileRO(ZipArchiveHandle handle, char* fileName) : mHandle(handle),
mFileName(fileName)
{
}
/* parse the archive, prepping internal structures */
bool parseZipArchive(void);
/* add a new entry to the hash table */
void addToHash(const char* str, int strLen, unsigned int hash);
/* compute string hash code */
static unsigned int computeHash(const char* str, int len);
/* convert a ZipEntryRO back to a hash table index */
int entryToIndex(const ZipEntryRO entry) const;
/*
* One entry in the hash table.
*/
typedef struct HashEntry {
const char* name;
unsigned short nameLen;
//unsigned int hash;
} HashEntry;
/* open Zip archive */
int mFd;
/* Lock for handling the file descriptor (seeks, etc) */
mutable Mutex mFdLock;
/* zip file name */
char* mFileName;
/* length of file */
size_t mFileLength;
/* mapped file */
FileMap* mDirectoryMap;
/* number of entries in the Zip archive */
int mNumEntries;
/* CD directory offset in the Zip archive */
off64_t mDirectoryOffset;
/*
* We know how many entries are in the Zip archive, so we have a
* fixed-size hash table. We probe for an empty slot.
*/
int mHashTableSize;
HashEntry* mHashTable;
const ZipArchiveHandle mHandle;
char* mFileName;
};
}; // namespace android

View File

@ -21,6 +21,7 @@
#define __LIBS_ZIPUTILS_H
#include <stdio.h>
#include <time.h>
namespace android {
@ -33,9 +34,11 @@ public:
* General utility function for uncompressing "deflate" data from a file
* to a buffer.
*/
static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
long compressedLen);
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
long compressedLen);
static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
static bool inflateToBuffer(void *in, void* buf, long uncompressedLen,
long compressedLen);
/*
@ -57,6 +60,19 @@ public:
static bool examineGzip(FILE* fp, int* pCompressionMethod,
long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32);
/*
* Utility function to convert ZIP's time format to a timespec struct.
*/
static inline void zipTimeToTimespec(long when, struct tm* timespec) {
const long date = when >> 16;
timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
timespec->tm_mon = (date >> 5) & 0x0F;
timespec->tm_mday = date & 0x1F;
timespec->tm_hour = (when >> 11) & 0x1F;
timespec->tm_min = (when >> 5) & 0x3F;
timespec->tm_sec = (when & 0x1F) << 1;
}
private:
ZipUtils() {}
~ZipUtils() {}

View File

@ -54,6 +54,7 @@ LOCAL_C_INCLUDES := \
external/zlib
LOCAL_STATIC_LIBRARIES := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive-host
include $(BUILD_HOST_STATIC_LIBRARY)
@ -72,9 +73,12 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libz
LOCAL_STATIC_LIBRARIES := libziparchive
LOCAL_C_INCLUDES := \
external/icu4c/common \
external/zlib
external/zlib \
system/core/include
LOCAL_MODULE:= libandroidfw

View File

@ -843,7 +843,7 @@ void _CompressedAsset::close(void)
* The first time this is called, we expand the compressed data into a
* buffer.
*/
const void* _CompressedAsset::getBuffer(bool wordAligned)
const void* _CompressedAsset::getBuffer(bool)
{
unsigned char* buf = NULL;
@ -860,7 +860,7 @@ const void* _CompressedAsset::getBuffer(bool wordAligned)
}
if (mMap != NULL) {
if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
mUncompressedLen, mCompressedLen))
goto bail;
} else {

View File

@ -305,10 +305,11 @@ bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entr
if (entry == NULL) {
return false;
}
if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) {
return false;
}
return true;
const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc);
zip->releaseEntry(entry);
return gotInfo;
}
bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
@ -821,16 +822,14 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m
String8 path(fileName);
/* check the appropriate Zip file */
ZipFileRO* pZip;
ZipEntryRO entry;
pZip = getZipFileLocked(ap);
ZipFileRO* pZip = getZipFileLocked(ap);
if (pZip != NULL) {
//printf("GOT zip, checking NA '%s'\n", (const char*) path);
entry = pZip->findEntryByName(path.string());
ZipEntryRO entry = pZip->findEntryByName(path.string());
if (entry != NULL) {
//printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
pZip->releaseEntry(entry);
}
}
@ -975,17 +974,15 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m
path.appendPath(fileName);
/* check the appropriate Zip file */
ZipFileRO* pZip;
ZipEntryRO entry;
pZip = getZipFileLocked(ap);
ZipFileRO* pZip = getZipFileLocked(ap);
if (pZip != NULL) {
//printf("GOT zip, checking '%s'\n", (const char*) path);
entry = pZip->findEntryByName(path.string());
ZipEntryRO entry = pZip->findEntryByName(path.string());
if (entry != NULL) {
//printf("FOUND in Zip file for %s/%s-%s\n",
// appName, locale, vendor);
pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
pZip->releaseEntry(entry);
}
}
@ -1487,11 +1484,16 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg
* semantics.
*/
int dirNameLen = dirName.length();
for (int i = 0; i < pZip->getNumEntries(); i++) {
ZipEntryRO entry;
void *iterationCookie;
if (!pZip->startIteration(&iterationCookie)) {
ALOGW("ZipFileRO::startIteration returned false");
return false;
}
ZipEntryRO entry;
while ((entry = pZip->nextEntry(iterationCookie)) != NULL) {
char nameBuf[256];
entry = pZip->findEntryByIndex(i);
if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) {
// TODO: fix this if we expect to have long names
ALOGE("ARGH: name too long?\n");
@ -1541,6 +1543,8 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMerg
}
}
pZip->endIteration(iterationCookie);
/*
* Add the set of unique directories.
*/
@ -1814,12 +1818,10 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
mResourceTableAsset(NULL), mResourceTable(NULL)
{
//ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
mZipFile = new ZipFileRO;
ALOGV("+++ opening zip '%s'\n", mPath.string());
if (mZipFile->open(mPath.string()) != NO_ERROR) {
mZipFile = ZipFileRO::open(mPath.string());
if (mZipFile == NULL) {
ALOGD("failed to open Zip archive '%s'\n", mPath.string());
delete mZipFile;
mZipFile = NULL;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -33,115 +33,13 @@
using namespace android;
/*
* Utility function that expands zip/gzip "deflate" compressed data
* into a buffer.
*
* "fd" is an open file positioned at the start of the "deflate" data
* "buf" must hold at least "uncompressedLen" bytes.
*/
/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
long uncompressedLen, long compressedLen)
{
bool result = false;
const unsigned long kReadBufSize = 32768;
unsigned char* readBuf = NULL;
z_stream zstream;
int zerr;
unsigned long compRemaining;
assert(uncompressedLen >= 0);
assert(compressedLen >= 0);
readBuf = new unsigned char[kReadBufSize];
if (readBuf == NULL)
goto bail;
compRemaining = compressedLen;
/*
* Initialize the zlib stream.
*/
memset(&zstream, 0, sizeof(zstream));
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
zstream.next_in = NULL;
zstream.avail_in = 0;
zstream.next_out = (Bytef*) buf;
zstream.avail_out = uncompressedLen;
zstream.data_type = Z_UNKNOWN;
/*
* Use the undocumented "negative window bits" feature to tell zlib
* that there's no zlib header waiting for it.
*/
zerr = inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
ALOGE("Installed zlib is not compatible with linked version (%s)\n",
ZLIB_VERSION);
} else {
ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
}
goto bail;
}
/*
* Loop while we have data.
*/
do {
unsigned long getSize;
/* read as much as we can */
if (zstream.avail_in == 0) {
getSize = (compRemaining > kReadBufSize) ?
kReadBufSize : compRemaining;
ALOGV("+++ reading %ld bytes (%ld left)\n",
getSize, compRemaining);
int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize));
if (cc < 0) {
ALOGW("inflate read failed: %s", strerror(errno));
} else if (cc != (int) getSize) {
ALOGW("inflate read failed (%d vs %ld)", cc, getSize);
goto z_bail;
}
compRemaining -= getSize;
zstream.next_in = readBuf;
zstream.avail_in = getSize;
}
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
goto z_bail;
}
/* output buffer holds all, so no need to write the output */
} while (zerr == Z_OK);
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
if ((long) zstream.total_out != uncompressedLen) {
ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
zstream.total_out, uncompressedLen);
goto z_bail;
}
// success!
result = true;
z_bail:
inflateEnd(&zstream); /* free up any allocated structures */
bail:
delete[] readBuf;
return result;
static inline unsigned long get4LE(const unsigned char* buf) {
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
static const unsigned long kReadBufSize = 32768;
/*
* Utility function that expands zip/gzip "deflate" compressed data
* into a buffer.
@ -153,12 +51,11 @@ bail:
* "fp" is an open file positioned at the start of the "deflate" data
* "buf" must hold at least "uncompressedLen" bytes.
*/
/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
long uncompressedLen, long compressedLen)
{
bool result = false;
const unsigned long kReadBufSize = 32768;
unsigned char* readBuf = NULL;
z_stream zstream;
int zerr;
unsigned long compRemaining;
@ -166,15 +63,12 @@ bail:
assert(uncompressedLen >= 0);
assert(compressedLen >= 0);
readBuf = new unsigned char[kReadBufSize];
if (readBuf == NULL)
goto bail;
compRemaining = compressedLen;
/*
* Initialize the zlib stream.
*/
memset(&zstream, 0, sizeof(zstream));
memset(&zstream, 0, sizeof(zstream));
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
@ -184,10 +78,10 @@ bail:
zstream.avail_out = uncompressedLen;
zstream.data_type = Z_UNKNOWN;
/*
* Use the undocumented "negative window bits" feature to tell zlib
* that there's no zlib header waiting for it.
*/
/*
* Use the undocumented "negative window bits" feature to tell zlib
* that there's no zlib header waiting for it.
*/
zerr = inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
@ -212,17 +106,18 @@ bail:
ALOGV("+++ reading %ld bytes (%ld left)\n",
getSize, compRemaining);
int cc = fread(readBuf, 1, getSize, fp);
if (cc != (int) getSize) {
ALOGD("inflate read failed (%d vs %ld)\n",
cc, getSize);
unsigned char* nextBuffer = NULL;
const unsigned long nextSize = reader.read(&nextBuffer, getSize);
if (nextSize < getSize || nextBuffer == NULL) {
ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
goto z_bail;
}
compRemaining -= getSize;
compRemaining -= nextSize;
zstream.next_in = readBuf;
zstream.avail_in = getSize;
zstream.next_in = nextBuffer;
zstream.avail_in = nextSize;
}
/* uncompress the data */
@ -250,10 +145,100 @@ z_bail:
inflateEnd(&zstream); /* free up any allocated structures */
bail:
delete[] readBuf;
return result;
}
class FileReader {
public:
FileReader(FILE* fp) :
mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
{
}
~FileReader() {
delete[] mReadBuf;
}
long read(unsigned char** nextBuffer, long readSize) const {
*nextBuffer = mReadBuf;
return fread(mReadBuf, 1, readSize, mFp);
}
FILE* mFp;
unsigned char* mReadBuf;
};
class FdReader {
public:
FdReader(int fd) :
mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
{
}
~FdReader() {
delete[] mReadBuf;
}
long read(unsigned char** nextBuffer, long readSize) const {
*nextBuffer = mReadBuf;
return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
}
int mFd;
unsigned char* mReadBuf;
};
class BufferReader {
public:
BufferReader(void* input, size_t inputSize) :
mInput(reinterpret_cast<unsigned char*>(input)),
mInputSize(inputSize),
mBufferReturned(false)
{
}
long read(unsigned char** nextBuffer, long readSize) {
if (!mBufferReturned) {
mBufferReturned = true;
*nextBuffer = mInput;
return mInputSize;
}
*nextBuffer = NULL;
return 0;
}
unsigned char* mInput;
const size_t mInputSize;
bool mBufferReturned;
};
/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
long uncompressedLen, long compressedLen)
{
FileReader reader(fp);
return ::inflateToBuffer<FileReader>(reader, buf,
uncompressedLen, compressedLen);
}
/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
long uncompressedLen, long compressedLen)
{
FdReader reader(fd);
return ::inflateToBuffer<FdReader>(reader, buf,
uncompressedLen, compressedLen);
}
/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
long uncompressedLen, long compressedLen)
{
BufferReader reader(in, compressedLen);
return ::inflateToBuffer<BufferReader>(reader, buf,
uncompressedLen, compressedLen);
}
/*
* Look at the contents of a gzip archive. We want to know where the
* data starts, and how long it will be after it is uncompressed.
@ -338,8 +323,8 @@ bail:
fseek(fp, curPosn, SEEK_SET);
*pCompressionMethod = method;
*pCRC32 = ZipFileRO::get4LE(&buf[0]);
*pUncompressedLen = ZipFileRO::get4LE(&buf[4]);
*pCRC32 = get4LE(&buf[0]);
*pUncompressedLen = get4LE(&buf[4]);
return true;
}

View File

@ -5,7 +5,7 @@ include $(CLEAR_VARS)
# Build the unit tests.
test_src_files := \
ObbFile_test.cpp \
ZipFileRO_test.cpp
ZipUtils_test.cpp
shared_libraries := \
libandroidfw \

View File

@ -14,9 +14,9 @@
* limitations under the License.
*/
#define LOG_TAG "ZipFileRO_test"
#define LOG_TAG "ZipUtils_test"
#include <utils/Log.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
#include <gtest/gtest.h>
@ -25,7 +25,7 @@
namespace android {
class ZipFileROTest : public testing::Test {
class ZipUtilsTest : public testing::Test {
protected:
virtual void SetUp() {
}
@ -34,13 +34,13 @@ protected:
}
};
TEST_F(ZipFileROTest, ZipTimeConvertSuccess) {
TEST_F(ZipUtilsTest, ZipTimeConvertSuccess) {
struct tm t;
// 2011-06-29 14:40:40
long when = 0x3EDD7514;
ZipFileRO::zipTimeToTimespec(when, &t);
ZipUtils::zipTimeToTimespec(when, &t);
EXPECT_EQ(2011, t.tm_year + 1900)
<< "Year was improperly converted.";

View File

@ -50,7 +50,8 @@ LOCAL_STATIC_LIBRARIES := \
libcutils \
libexpat \
libpng \
liblog
liblog \
libziparchive-host
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -ldl -lpthread