From 7b0ad9c638176dc364dabb65b363536055a0ea9c Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 5 Aug 2016 18:00:04 -0700 Subject: Switch recovery to libbase logging Clean up the recovery image and switch to libbase logging. Bug: 28191554 Change-Id: Icd999c3cc832f0639f204b5c36cea8afe303ad35 Merged-In: Icd999c3cc832f0639f204b5c36cea8afe303ad35 --- minzip/Android.mk | 8 +- minzip/Hash.c | 379 ------------------- minzip/Hash.cpp | 378 +++++++++++++++++++ minzip/Log.h | 207 ----------- minzip/SysUtil.c | 212 ----------- minzip/SysUtil.cpp | 212 +++++++++++ minzip/Zip.c | 1018 --------------------------------------------------- minzip/Zip.cpp | 1022 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1616 insertions(+), 1820 deletions(-) delete mode 100644 minzip/Hash.c create mode 100644 minzip/Hash.cpp delete mode 100644 minzip/Log.h delete mode 100644 minzip/SysUtil.c create mode 100644 minzip/SysUtil.cpp delete mode 100644 minzip/Zip.c create mode 100644 minzip/Zip.cpp (limited to 'minzip') diff --git a/minzip/Android.mk b/minzip/Android.mk index 3d36fd64e..6dbfee993 100644 --- a/minzip/Android.mk +++ b/minzip/Android.mk @@ -2,17 +2,17 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - Hash.c \ - SysUtil.c \ + Hash.cpp \ + SysUtil.cpp \ DirUtil.cpp \ Inlines.c \ - Zip.c + Zip.cpp LOCAL_C_INCLUDES := \ external/zlib \ external/safe-iop/include -LOCAL_STATIC_LIBRARIES := libselinux +LOCAL_STATIC_LIBRARIES := libselinux libbase LOCAL_MODULE := libminzip diff --git a/minzip/Hash.c b/minzip/Hash.c deleted file mode 100644 index 49bcb3161..000000000 --- a/minzip/Hash.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Hash table. The dominant calls are add and lookup, with removals - * happening very infrequently. We use probing, and don't worry much - * about tombstone removal. - */ -#include -#include - -#define LOG_TAG "minzip" -#include "Log.h" -#include "Hash.h" - -/* table load factor, i.e. how full can it get before we resize */ -//#define LOAD_NUMER 3 // 75% -//#define LOAD_DENOM 4 -#define LOAD_NUMER 5 // 62.5% -#define LOAD_DENOM 8 -//#define LOAD_NUMER 1 // 50% -//#define LOAD_DENOM 2 - -/* - * Compute the capacity needed for a table to hold "size" elements. - */ -size_t mzHashSize(size_t size) { - return (size * LOAD_DENOM) / LOAD_NUMER +1; -} - -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - -/* - * Create and initialize a hash table. - */ -HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc) -{ - HashTable* pHashTable; - - assert(initialSize > 0); - - pHashTable = (HashTable*) malloc(sizeof(*pHashTable)); - if (pHashTable == NULL) - return NULL; - - pHashTable->tableSize = roundUpPower2(initialSize); - pHashTable->numEntries = pHashTable->numDeadEntries = 0; - pHashTable->freeFunc = freeFunc; - pHashTable->pEntries = - (HashEntry*) calloc((size_t)pHashTable->tableSize, sizeof(HashTable)); - if (pHashTable->pEntries == NULL) { - free(pHashTable); - return NULL; - } - - return pHashTable; -} - -/* - * Clear out all entries. - */ -void mzHashTableClear(HashTable* pHashTable) -{ - HashEntry* pEnt; - int i; - - pEnt = pHashTable->pEntries; - for (i = 0; i < pHashTable->tableSize; i++, pEnt++) { - if (pEnt->data == HASH_TOMBSTONE) { - // nuke entry - pEnt->data = NULL; - } else if (pEnt->data != NULL) { - // call free func then nuke entry - if (pHashTable->freeFunc != NULL) - (*pHashTable->freeFunc)(pEnt->data); - pEnt->data = NULL; - } - } - - pHashTable->numEntries = 0; - pHashTable->numDeadEntries = 0; -} - -/* - * Free the table. - */ -void mzHashTableFree(HashTable* pHashTable) -{ - if (pHashTable == NULL) - return; - mzHashTableClear(pHashTable); - free(pHashTable->pEntries); - free(pHashTable); -} - -#ifndef NDEBUG -/* - * Count up the number of tombstone entries in the hash table. - */ -static int countTombStones(HashTable* pHashTable) -{ - int i, count; - - for (count = i = 0; i < pHashTable->tableSize; i++) { - if (pHashTable->pEntries[i].data == HASH_TOMBSTONE) - count++; - } - return count; -} -#endif - -/* - * Resize a hash table. We do this when adding an entry increased the - * size of the table beyond its comfy limit. - * - * This essentially requires re-inserting all elements into the new storage. - * - * If multiple threads can access the hash table, the table's lock should - * have been grabbed before issuing the "lookup+add" call that led to the - * resize, so we don't have a synchronization problem here. - */ -static bool resizeHash(HashTable* pHashTable, int newSize) -{ - HashEntry* pNewEntries; - int i; - - assert(countTombStones(pHashTable) == pHashTable->numDeadEntries); - - pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable)); - if (pNewEntries == NULL) - return false; - - for (i = 0; i < pHashTable->tableSize; i++) { - void* data = pHashTable->pEntries[i].data; - if (data != NULL && data != HASH_TOMBSTONE) { - int hashValue = pHashTable->pEntries[i].hashValue; - int newIdx; - - /* probe for new spot, wrapping around */ - newIdx = hashValue & (newSize-1); - while (pNewEntries[newIdx].data != NULL) - newIdx = (newIdx + 1) & (newSize-1); - - pNewEntries[newIdx].hashValue = hashValue; - pNewEntries[newIdx].data = data; - } - } - - free(pHashTable->pEntries); - pHashTable->pEntries = pNewEntries; - pHashTable->tableSize = newSize; - pHashTable->numDeadEntries = 0; - - assert(countTombStones(pHashTable) == 0); - return true; -} - -/* - * Look up an entry. - * - * We probe on collisions, wrapping around the table. - */ -void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item, - HashCompareFunc cmpFunc, bool doAdd) -{ - HashEntry* pEntry; - HashEntry* pEnd; - void* result = NULL; - - assert(pHashTable->tableSize > 0); - assert(item != HASH_TOMBSTONE); - assert(item != NULL); - - /* jump to the first entry and probe for a match */ - pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; - pEnd = &pHashTable->pEntries[pHashTable->tableSize]; - while (pEntry->data != NULL) { - if (pEntry->data != HASH_TOMBSTONE && - pEntry->hashValue == itemHash && - (*cmpFunc)(pEntry->data, item) == 0) - { - /* match */ - break; - } - - pEntry++; - if (pEntry == pEnd) { /* wrap around to start */ - if (pHashTable->tableSize == 1) - break; /* edge case - single-entry table */ - pEntry = pHashTable->pEntries; - } - } - - if (pEntry->data == NULL) { - if (doAdd) { - pEntry->hashValue = itemHash; - pEntry->data = item; - pHashTable->numEntries++; - - /* - * We've added an entry. See if this brings us too close to full. - */ - if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM - > pHashTable->tableSize * LOAD_NUMER) - { - if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) { - /* don't really have a way to indicate failure */ - LOGE("Dalvik hash resize failure\n"); - abort(); - } - /* note "pEntry" is now invalid */ - } - - /* full table is bad -- search for nonexistent never halts */ - assert(pHashTable->numEntries < pHashTable->tableSize); - result = item; - } else { - assert(result == NULL); - } - } else { - result = pEntry->data; - } - - return result; -} - -/* - * Remove an entry from the table. - * - * Does NOT invoke the "free" function on the item. - */ -bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item) -{ - HashEntry* pEntry; - HashEntry* pEnd; - - assert(pHashTable->tableSize > 0); - - /* jump to the first entry and probe for a match */ - pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; - pEnd = &pHashTable->pEntries[pHashTable->tableSize]; - while (pEntry->data != NULL) { - if (pEntry->data == item) { - pEntry->data = HASH_TOMBSTONE; - pHashTable->numEntries--; - pHashTable->numDeadEntries++; - return true; - } - - pEntry++; - if (pEntry == pEnd) { /* wrap around to start */ - if (pHashTable->tableSize == 1) - break; /* edge case - single-entry table */ - pEntry = pHashTable->pEntries; - } - } - - return false; -} - -/* - * Execute a function on every entry in the hash table. - * - * If "func" returns a nonzero value, terminate early and return the value. - */ -int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg) -{ - int i, val; - - for (i = 0; i < pHashTable->tableSize; i++) { - HashEntry* pEnt = &pHashTable->pEntries[i]; - - if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) { - val = (*func)(pEnt->data, arg); - if (val != 0) - return val; - } - } - - return 0; -} - - -/* - * Look up an entry, counting the number of times we have to probe. - * - * Returns -1 if the entry wasn't found. - */ -int countProbes(HashTable* pHashTable, unsigned int itemHash, const void* item, - HashCompareFunc cmpFunc) -{ - HashEntry* pEntry; - HashEntry* pEnd; - int count = 0; - - assert(pHashTable->tableSize > 0); - assert(item != HASH_TOMBSTONE); - assert(item != NULL); - - /* jump to the first entry and probe for a match */ - pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; - pEnd = &pHashTable->pEntries[pHashTable->tableSize]; - while (pEntry->data != NULL) { - if (pEntry->data != HASH_TOMBSTONE && - pEntry->hashValue == itemHash && - (*cmpFunc)(pEntry->data, item) == 0) - { - /* match */ - break; - } - - pEntry++; - if (pEntry == pEnd) { /* wrap around to start */ - if (pHashTable->tableSize == 1) - break; /* edge case - single-entry table */ - pEntry = pHashTable->pEntries; - } - - count++; - } - if (pEntry->data == NULL) - return -1; - - return count; -} - -/* - * Evaluate the amount of probing required for the specified hash table. - * - * We do this by running through all entries in the hash table, computing - * the hash value and then doing a lookup. - * - * The caller should lock the table before calling here. - */ -void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, - HashCompareFunc cmpFunc) -{ - int numEntries, minProbe, maxProbe, totalProbe; - HashIter iter; - - numEntries = maxProbe = totalProbe = 0; - minProbe = 65536*32767; - - for (mzHashIterBegin(pHashTable, &iter); !mzHashIterDone(&iter); - mzHashIterNext(&iter)) - { - const void* data = (const void*)mzHashIterData(&iter); - int count; - - count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc); - - numEntries++; - - if (count < minProbe) - minProbe = count; - if (count > maxProbe) - maxProbe = count; - totalProbe += count; - } - - LOGV("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n", - minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize, - (float) totalProbe / (float) numEntries); -} diff --git a/minzip/Hash.cpp b/minzip/Hash.cpp new file mode 100644 index 000000000..ac08935d4 --- /dev/null +++ b/minzip/Hash.cpp @@ -0,0 +1,378 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Hash table. The dominant calls are add and lookup, with removals + * happening very infrequently. We use probing, and don't worry much + * about tombstone removal. + */ +#include +#include + +#include + +#include "Hash.h" + +/* table load factor, i.e. how full can it get before we resize */ +//#define LOAD_NUMER 3 // 75% +//#define LOAD_DENOM 4 +#define LOAD_NUMER 5 // 62.5% +#define LOAD_DENOM 8 +//#define LOAD_NUMER 1 // 50% +//#define LOAD_DENOM 2 + +/* + * Compute the capacity needed for a table to hold "size" elements. + */ +size_t mzHashSize(size_t size) { + return (size * LOAD_DENOM) / LOAD_NUMER +1; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +/* + * Create and initialize a hash table. + */ +HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc) +{ + HashTable* pHashTable; + + assert(initialSize > 0); + + pHashTable = (HashTable*) malloc(sizeof(*pHashTable)); + if (pHashTable == NULL) + return NULL; + + pHashTable->tableSize = roundUpPower2(initialSize); + pHashTable->numEntries = pHashTable->numDeadEntries = 0; + pHashTable->freeFunc = freeFunc; + pHashTable->pEntries = + (HashEntry*) calloc((size_t)pHashTable->tableSize, sizeof(HashTable)); + if (pHashTable->pEntries == NULL) { + free(pHashTable); + return NULL; + } + + return pHashTable; +} + +/* + * Clear out all entries. + */ +void mzHashTableClear(HashTable* pHashTable) +{ + HashEntry* pEnt; + int i; + + pEnt = pHashTable->pEntries; + for (i = 0; i < pHashTable->tableSize; i++, pEnt++) { + if (pEnt->data == HASH_TOMBSTONE) { + // nuke entry + pEnt->data = NULL; + } else if (pEnt->data != NULL) { + // call free func then nuke entry + if (pHashTable->freeFunc != NULL) + (*pHashTable->freeFunc)(pEnt->data); + pEnt->data = NULL; + } + } + + pHashTable->numEntries = 0; + pHashTable->numDeadEntries = 0; +} + +/* + * Free the table. + */ +void mzHashTableFree(HashTable* pHashTable) +{ + if (pHashTable == NULL) + return; + mzHashTableClear(pHashTable); + free(pHashTable->pEntries); + free(pHashTable); +} + +#ifndef NDEBUG +/* + * Count up the number of tombstone entries in the hash table. + */ +static int countTombStones(HashTable* pHashTable) +{ + int i, count; + + for (count = i = 0; i < pHashTable->tableSize; i++) { + if (pHashTable->pEntries[i].data == HASH_TOMBSTONE) + count++; + } + return count; +} +#endif + +/* + * Resize a hash table. We do this when adding an entry increased the + * size of the table beyond its comfy limit. + * + * This essentially requires re-inserting all elements into the new storage. + * + * If multiple threads can access the hash table, the table's lock should + * have been grabbed before issuing the "lookup+add" call that led to the + * resize, so we don't have a synchronization problem here. + */ +static bool resizeHash(HashTable* pHashTable, int newSize) +{ + HashEntry* pNewEntries; + int i; + + assert(countTombStones(pHashTable) == pHashTable->numDeadEntries); + + pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable)); + if (pNewEntries == NULL) + return false; + + for (i = 0; i < pHashTable->tableSize; i++) { + void* data = pHashTable->pEntries[i].data; + if (data != NULL && data != HASH_TOMBSTONE) { + int hashValue = pHashTable->pEntries[i].hashValue; + int newIdx; + + /* probe for new spot, wrapping around */ + newIdx = hashValue & (newSize-1); + while (pNewEntries[newIdx].data != NULL) + newIdx = (newIdx + 1) & (newSize-1); + + pNewEntries[newIdx].hashValue = hashValue; + pNewEntries[newIdx].data = data; + } + } + + free(pHashTable->pEntries); + pHashTable->pEntries = pNewEntries; + pHashTable->tableSize = newSize; + pHashTable->numDeadEntries = 0; + + assert(countTombStones(pHashTable) == 0); + return true; +} + +/* + * Look up an entry. + * + * We probe on collisions, wrapping around the table. + */ +void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item, + HashCompareFunc cmpFunc, bool doAdd) +{ + HashEntry* pEntry; + HashEntry* pEnd; + void* result = NULL; + + assert(pHashTable->tableSize > 0); + assert(item != HASH_TOMBSTONE); + assert(item != NULL); + + /* jump to the first entry and probe for a match */ + pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; + pEnd = &pHashTable->pEntries[pHashTable->tableSize]; + while (pEntry->data != NULL) { + if (pEntry->data != HASH_TOMBSTONE && + pEntry->hashValue == itemHash && + (*cmpFunc)(pEntry->data, item) == 0) + { + /* match */ + break; + } + + pEntry++; + if (pEntry == pEnd) { /* wrap around to start */ + if (pHashTable->tableSize == 1) + break; /* edge case - single-entry table */ + pEntry = pHashTable->pEntries; + } + } + + if (pEntry->data == NULL) { + if (doAdd) { + pEntry->hashValue = itemHash; + pEntry->data = item; + pHashTable->numEntries++; + + /* + * We've added an entry. See if this brings us too close to full. + */ + if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM + > pHashTable->tableSize * LOAD_NUMER) + { + if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) { + /* don't really have a way to indicate failure */ + LOG(FATAL) << "Hash resize failure"; + } + /* note "pEntry" is now invalid */ + } + + /* full table is bad -- search for nonexistent never halts */ + assert(pHashTable->numEntries < pHashTable->tableSize); + result = item; + } else { + assert(result == NULL); + } + } else { + result = pEntry->data; + } + + return result; +} + +/* + * Remove an entry from the table. + * + * Does NOT invoke the "free" function on the item. + */ +bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item) +{ + HashEntry* pEntry; + HashEntry* pEnd; + + assert(pHashTable->tableSize > 0); + + /* jump to the first entry and probe for a match */ + pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; + pEnd = &pHashTable->pEntries[pHashTable->tableSize]; + while (pEntry->data != NULL) { + if (pEntry->data == item) { + pEntry->data = HASH_TOMBSTONE; + pHashTable->numEntries--; + pHashTable->numDeadEntries++; + return true; + } + + pEntry++; + if (pEntry == pEnd) { /* wrap around to start */ + if (pHashTable->tableSize == 1) + break; /* edge case - single-entry table */ + pEntry = pHashTable->pEntries; + } + } + + return false; +} + +/* + * Execute a function on every entry in the hash table. + * + * If "func" returns a nonzero value, terminate early and return the value. + */ +int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg) +{ + int i, val; + + for (i = 0; i < pHashTable->tableSize; i++) { + HashEntry* pEnt = &pHashTable->pEntries[i]; + + if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) { + val = (*func)(pEnt->data, arg); + if (val != 0) + return val; + } + } + + return 0; +} + + +/* + * Look up an entry, counting the number of times we have to probe. + * + * Returns -1 if the entry wasn't found. + */ +int countProbes(HashTable* pHashTable, unsigned int itemHash, const void* item, + HashCompareFunc cmpFunc) +{ + HashEntry* pEntry; + HashEntry* pEnd; + int count = 0; + + assert(pHashTable->tableSize > 0); + assert(item != HASH_TOMBSTONE); + assert(item != NULL); + + /* jump to the first entry and probe for a match */ + pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; + pEnd = &pHashTable->pEntries[pHashTable->tableSize]; + while (pEntry->data != NULL) { + if (pEntry->data != HASH_TOMBSTONE && + pEntry->hashValue == itemHash && + (*cmpFunc)(pEntry->data, item) == 0) + { + /* match */ + break; + } + + pEntry++; + if (pEntry == pEnd) { /* wrap around to start */ + if (pHashTable->tableSize == 1) + break; /* edge case - single-entry table */ + pEntry = pHashTable->pEntries; + } + + count++; + } + if (pEntry->data == NULL) + return -1; + + return count; +} + +/* + * Evaluate the amount of probing required for the specified hash table. + * + * We do this by running through all entries in the hash table, computing + * the hash value and then doing a lookup. + * + * The caller should lock the table before calling here. + */ +void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, + HashCompareFunc cmpFunc) +{ + int numEntries, minProbe, maxProbe, totalProbe; + HashIter iter; + + numEntries = maxProbe = totalProbe = 0; + minProbe = 65536*32767; + + for (mzHashIterBegin(pHashTable, &iter); !mzHashIterDone(&iter); + mzHashIterNext(&iter)) + { + const void* data = (const void*)mzHashIterData(&iter); + int count; + + count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc); + + numEntries++; + + if (count < minProbe) + minProbe = count; + if (count > maxProbe) + maxProbe = count; + totalProbe += count; + } + + LOG(VERBOSE) << "Probe: min=" << minProbe << ", max=" << maxProbe << ", total=" + << totalProbe <<" in " << numEntries << " (" << pHashTable->tableSize + << "), avg=" << (float) totalProbe / (float) numEntries; +} diff --git a/minzip/Log.h b/minzip/Log.h deleted file mode 100644 index 36e62f594..000000000 --- a/minzip/Log.h +++ /dev/null @@ -1,207 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _MINZIP_LOG_H -#define _MINZIP_LOG_H - -#include - -// --------------------------------------------------------------------- - -/* - * Normally we strip LOGV (VERBOSE messages) from release builds. - * You can modify this (for example with "#define LOG_NDEBUG 0" - * at the top of your source file) to change that behavior. - */ -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - -/* - * This is the local tag used for the following simplified - * logging macros. You can change this preprocessor definition - * before using the other macros to change the tag. - */ -#ifndef LOG_TAG -#define LOG_TAG NULL -#endif - -// --------------------------------------------------------------------- - -/* - * Simplified macro to send a verbose log message using the current LOG_TAG. - */ -#ifndef LOGV -#if LOG_NDEBUG -#define LOGV(...) ((void)0) -#else -#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) - -#ifndef LOGV_IF -#if LOG_NDEBUG -#define LOGV_IF(cond, ...) ((void)0) -#else -#define LOGV_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif -#endif - -#define LOGVV LOGV -#define LOGVV_IF LOGV_IF - -/* - * Simplified macro to send a debug log message using the current LOG_TAG. - */ -#ifndef LOGD -#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef LOGD_IF -#define LOGD_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an info log message using the current LOG_TAG. - */ -#ifndef LOGI -#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef LOGI_IF -#define LOGI_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send a warning log message using the current LOG_TAG. - */ -#ifndef LOGW -#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef LOGW_IF -#define LOGW_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an error log message using the current LOG_TAG. - */ -#ifndef LOGE -#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef LOGE_IF -#define LOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * verbose priority. - */ -#ifndef IF_LOGV -#if LOG_NDEBUG -#define IF_LOGV() if (false) -#else -#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG) -#endif -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * debug priority. - */ -#ifndef IF_LOGD -#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * info priority. - */ -#ifndef IF_LOGI -#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * warn priority. - */ -#ifndef IF_LOGW -#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * error priority. - */ -#ifndef IF_LOGE -#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG) -#endif - -// --------------------------------------------------------------------- - -/* - * Basic log message macro. - * - * Example: - * LOG(LOG_WARN, NULL, "Failed with error %d", errno); - * - * The second argument may be NULL or "" to indicate the "global" tag. - * - * Non-gcc probably won't have __FUNCTION__. It's not vital. gcc also - * offers __PRETTY_FUNCTION__, which is rather more than we need. - */ -#ifndef LOG -#define LOG(priority, tag, ...) \ - LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to specify a number for the priority. - */ -#ifndef LOG_PRI -#define LOG_PRI(priority, tag, ...) \ - printf(tag ": " __VA_ARGS__) -#endif - -/* - * Conditional given a desired logging priority and tag. - */ -#ifndef IF_LOG -#define IF_LOG(priority, tag) \ - if (1) -#endif - -#endif // _MINZIP_LOG_H diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c deleted file mode 100644 index e7dd17b51..000000000 --- a/minzip/SysUtil.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * System utilities. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_TAG "sysutil" -#include "Log.h" -#include "SysUtil.h" - -static bool sysMapFD(int fd, MemMapping* pMap) { - assert(pMap != NULL); - - struct stat sb; - if (fstat(fd, &sb) == -1) { - LOGE("fstat(%d) failed: %s\n", fd, strerror(errno)); - return false; - } - - void* memPtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (memPtr == MAP_FAILED) { - LOGE("mmap(%d, R, PRIVATE, %d, 0) failed: %s\n", (int) sb.st_size, fd, strerror(errno)); - return false; - } - - pMap->addr = memPtr; - pMap->length = sb.st_size; - pMap->range_count = 1; - pMap->ranges = malloc(sizeof(MappedRange)); - if (pMap->ranges == NULL) { - LOGE("malloc failed: %s\n", strerror(errno)); - munmap(memPtr, sb.st_size); - return false; - } - pMap->ranges[0].addr = memPtr; - pMap->ranges[0].length = sb.st_size; - - return true; -} - -static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) -{ - char block_dev[PATH_MAX+1]; - size_t size; - unsigned int blksize; - size_t blocks; - unsigned int range_count; - unsigned int i; - - if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { - LOGE("failed to read block device from header\n"); - return -1; - } - for (i = 0; i < sizeof(block_dev); ++i) { - if (block_dev[i] == '\n') { - block_dev[i] = 0; - break; - } - } - - if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { - LOGE("failed to parse block map header\n"); - return -1; - } - if (blksize != 0) { - blocks = ((size-1) / blksize) + 1; - } - if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) { - LOGE("invalid data in block map file: size %zu, blksize %u, range_count %u\n", - size, blksize, range_count); - return -1; - } - - pMap->range_count = range_count; - pMap->ranges = calloc(range_count, sizeof(MappedRange)); - if (pMap->ranges == NULL) { - LOGE("calloc(%u, %zu) failed: %s\n", range_count, sizeof(MappedRange), strerror(errno)); - return -1; - } - - // Reserve enough contiguous address space for the whole file. - unsigned char* reserve; - reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); - if (reserve == MAP_FAILED) { - LOGE("failed to reserve address space: %s\n", strerror(errno)); - free(pMap->ranges); - return -1; - } - - int fd = open(block_dev, O_RDONLY); - if (fd < 0) { - LOGE("failed to open block device %s: %s\n", block_dev, strerror(errno)); - munmap(reserve, blocks * blksize); - free(pMap->ranges); - return -1; - } - - unsigned char* next = reserve; - size_t remaining_size = blocks * blksize; - bool success = true; - for (i = 0; i < range_count; ++i) { - size_t start, end; - if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) { - LOGE("failed to parse range %d in block map\n", i); - success = false; - break; - } - size_t length = (end - start) * blksize; - if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { - LOGE("unexpected range in block map: %zu %zu\n", start, end); - success = false; - break; - } - - void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); - if (addr == MAP_FAILED) { - LOGE("failed to map block %d: %s\n", i, strerror(errno)); - success = false; - break; - } - pMap->ranges[i].addr = addr; - pMap->ranges[i].length = length; - - next += length; - remaining_size -= length; - } - if (success && remaining_size != 0) { - LOGE("ranges in block map are invalid: remaining_size = %zu\n", remaining_size); - success = false; - } - if (!success) { - close(fd); - munmap(reserve, blocks * blksize); - free(pMap->ranges); - return -1; - } - - close(fd); - pMap->addr = reserve; - pMap->length = size; - - LOGI("mmapped %d ranges\n", range_count); - - return 0; -} - -int sysMapFile(const char* fn, MemMapping* pMap) -{ - memset(pMap, 0, sizeof(*pMap)); - - if (fn && fn[0] == '@') { - // A map of blocks - FILE* mapf = fopen(fn+1, "r"); - if (mapf == NULL) { - LOGE("Unable to open '%s': %s\n", fn+1, strerror(errno)); - return -1; - } - - if (sysMapBlockFile(mapf, pMap) != 0) { - LOGE("Map of '%s' failed\n", fn); - fclose(mapf); - return -1; - } - - fclose(mapf); - } else { - // This is a regular file. - int fd = open(fn, O_RDONLY); - if (fd == -1) { - LOGE("Unable to open '%s': %s\n", fn, strerror(errno)); - return -1; - } - - if (!sysMapFD(fd, pMap)) { - LOGE("Map of '%s' failed\n", fn); - close(fd); - return -1; - } - - close(fd); - } - return 0; -} - -/* - * Release a memory mapping. - */ -void sysReleaseMap(MemMapping* pMap) -{ - int i; - for (i = 0; i < pMap->range_count; ++i) { - if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) { - LOGE("munmap(%p, %d) failed: %s\n", - pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno)); - } - } - free(pMap->ranges); - pMap->ranges = NULL; - pMap->range_count = 0; -} diff --git a/minzip/SysUtil.cpp b/minzip/SysUtil.cpp new file mode 100644 index 000000000..2936c5ca4 --- /dev/null +++ b/minzip/SysUtil.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * System utilities. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "SysUtil.h" + +static bool sysMapFD(int fd, MemMapping* pMap) { + assert(pMap != NULL); + + struct stat sb; + if (fstat(fd, &sb) == -1) { + PLOG(ERROR) << "fstat(" << fd << ") failed"; + return false; + } + + void* memPtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (memPtr == MAP_FAILED) { + PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed"; + return false; + } + + pMap->addr = reinterpret_cast(memPtr); + pMap->length = sb.st_size; + pMap->range_count = 1; + pMap->ranges = reinterpret_cast(malloc(sizeof(MappedRange))); + if (pMap->ranges == NULL) { + PLOG(ERROR) << "malloc failed"; + munmap(memPtr, sb.st_size); + return false; + } + pMap->ranges[0].addr = memPtr; + pMap->ranges[0].length = sb.st_size; + + return true; +} + +static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) +{ + char block_dev[PATH_MAX+1]; + size_t size; + unsigned int blksize; + size_t blocks; + unsigned int range_count; + unsigned int i; + + if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { + PLOG(ERROR) << "failed to read block device from header"; + return -1; + } + for (i = 0; i < sizeof(block_dev); ++i) { + if (block_dev[i] == '\n') { + block_dev[i] = 0; + break; + } + } + + if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { + LOG(ERROR) << "failed to parse block map header"; + return -1; + } + if (blksize != 0) { + blocks = ((size-1) / blksize) + 1; + } + if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) { + LOG(ERROR) << "invalid data in block map file: size " << size << ", blksize " << blksize + << ", range_count " << range_count; + return -1; + } + + pMap->range_count = range_count; + pMap->ranges = reinterpret_cast(calloc(range_count, sizeof(MappedRange))); + if (pMap->ranges == NULL) { + PLOG(ERROR) << "calloc(" << range_count << ", " << sizeof(MappedRange) << ") failed"; + return -1; + } + + // Reserve enough contiguous address space for the whole file. + unsigned char* reserve = reinterpret_cast(mmap64(NULL, blocks * blksize, + PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0)); + if (reserve == MAP_FAILED) { + PLOG(ERROR) << "failed to reserve address space"; + free(pMap->ranges); + return -1; + } + + int fd = open(block_dev, O_RDONLY); + if (fd < 0) { + PLOG(ERROR) << "failed to open block device " << block_dev; + munmap(reserve, blocks * blksize); + free(pMap->ranges); + return -1; + } + + unsigned char* next = reserve; + size_t remaining_size = blocks * blksize; + bool success = true; + for (i = 0; i < range_count; ++i) { + size_t start, end; + if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) { + LOG(ERROR) << "failed to parse range " << i << " in block map"; + success = false; + break; + } + size_t length = (end - start) * blksize; + if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { + LOG(ERROR) << "unexpected range in block map: " << start << " " << end; + success = false; + break; + } + + void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); + if (addr == MAP_FAILED) { + PLOG(ERROR) << "failed to map block " << i; + success = false; + break; + } + pMap->ranges[i].addr = addr; + pMap->ranges[i].length = length; + + next += length; + remaining_size -= length; + } + if (success && remaining_size != 0) { + LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size; + success = false; + } + if (!success) { + close(fd); + munmap(reserve, blocks * blksize); + free(pMap->ranges); + return -1; + } + + close(fd); + pMap->addr = reserve; + pMap->length = size; + + LOG(INFO) << "mmapped " << range_count << " ranges"; + + return 0; +} + +int sysMapFile(const char* fn, MemMapping* pMap) +{ + memset(pMap, 0, sizeof(*pMap)); + + if (fn && fn[0] == '@') { + // A map of blocks + FILE* mapf = fopen(fn+1, "r"); + if (mapf == NULL) { + PLOG(ERROR) << "Unable to open '" << (fn+1) << "'"; + return -1; + } + + if (sysMapBlockFile(mapf, pMap) != 0) { + LOG(ERROR) << "Map of '" << fn << "' failed"; + fclose(mapf); + return -1; + } + + fclose(mapf); + } else { + // This is a regular file. + int fd = open(fn, O_RDONLY); + if (fd == -1) { + PLOG(ERROR) << "Unable to open '" << fn << "'"; + return -1; + } + + if (!sysMapFD(fd, pMap)) { + LOG(ERROR) << "Map of '" << fn << "' failed"; + close(fd); + return -1; + } + + close(fd); + } + return 0; +} + +/* + * Release a memory mapping. + */ +void sysReleaseMap(MemMapping* pMap) +{ + int i; + for (i = 0; i < pMap->range_count; ++i) { + if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) { + PLOG(ERROR) << "munmap(" << pMap->ranges[i].addr << ", " << pMap->ranges[i].length + << ") failed"; + } + } + free(pMap->ranges); + pMap->ranges = NULL; + pMap->range_count = 0; +} diff --git a/minzip/Zip.c b/minzip/Zip.c deleted file mode 100644 index d557daa7f..000000000 --- a/minzip/Zip.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Simple Zip file support. - */ -#include "safe_iop.h" -#include "zlib.h" - -#include -#include -#include -#include // for uintptr_t -#include -#include // for S_ISLNK() -#include - -#define LOG_TAG "minzip" -#include "Zip.h" -#include "Bits.h" -#include "Log.h" -#include "DirUtil.h" - -#undef NDEBUG // do this after including Log.h -#include - -#include -#include - -#define SORT_ENTRIES 1 - -/* - * Offset and length constants (java.util.zip naming convention). - */ -enum { - CENSIG = 0x02014b50, // PK12 - CENHDR = 46, - - CENVEM = 4, - CENVER = 6, - CENFLG = 8, - CENHOW = 10, - CENTIM = 12, - CENCRC = 16, - CENSIZ = 20, - CENLEN = 24, - CENNAM = 28, - CENEXT = 30, - CENCOM = 32, - CENDSK = 34, - CENATT = 36, - CENATX = 38, - CENOFF = 42, - - ENDSIG = 0x06054b50, // PK56 - ENDHDR = 22, - - ENDSUB = 8, - ENDTOT = 10, - ENDSIZ = 12, - ENDOFF = 16, - ENDCOM = 20, - - EXTSIG = 0x08074b50, // PK78 - EXTHDR = 16, - - EXTCRC = 4, - EXTSIZ = 8, - EXTLEN = 12, - - LOCSIG = 0x04034b50, // PK34 - LOCHDR = 30, - - LOCVER = 4, - LOCFLG = 6, - LOCHOW = 8, - LOCTIM = 10, - LOCCRC = 14, - LOCSIZ = 18, - LOCLEN = 22, - LOCNAM = 26, - LOCEXT = 28, - - STORED = 0, - DEFLATED = 8, - - CENVEM_UNIX = 3 << 8, // the high byte of CENVEM -}; - - -/* - * For debugging, dump the contents of a ZipEntry. - */ -#if 0 -static void dumpEntry(const ZipEntry* pEntry) -{ - LOGI(" %p '%.*s'\n", pEntry->fileName,pEntry->fileNameLen,pEntry->fileName); - LOGI(" off=%u comp=%u uncomp=%u how=%d\n", pEntry->offset, - pEntry->compLen, pEntry->uncompLen, pEntry->compression); -} -#endif - -/* - * (This is a mzHashTableLookup callback.) - * - * Compare two ZipEntry structs, by name. - */ -static int hashcmpZipEntry(const void* ventry1, const void* ventry2) -{ - const ZipEntry* entry1 = (const ZipEntry*) ventry1; - const ZipEntry* entry2 = (const ZipEntry*) ventry2; - - if (entry1->fileNameLen != entry2->fileNameLen) - return entry1->fileNameLen - entry2->fileNameLen; - return memcmp(entry1->fileName, entry2->fileName, entry1->fileNameLen); -} - -/* - * (This is a mzHashTableLookup callback.) - * - * find a ZipEntry struct by name. - */ -static int hashcmpZipName(const void* ventry, const void* vname) -{ - const ZipEntry* entry = (const ZipEntry*) ventry; - const char* name = (const char*) vname; - unsigned int nameLen = strlen(name); - - if (entry->fileNameLen != nameLen) - return entry->fileNameLen - nameLen; - return memcmp(entry->fileName, name, nameLen); -} - -/* - * Compute the hash code for a ZipEntry filename. - * - * Not expected to be compatible with any other hash function, so we init - * to 2 to ensure it doesn't happen to match. - */ -static unsigned int computeHash(const char* name, int nameLen) -{ - unsigned int hash = 2; - - while (nameLen--) - hash = hash * 31 + *name++; - - return hash; -} - -static void addEntryToHashTable(HashTable* pHash, ZipEntry* pEntry) -{ - unsigned int itemHash = computeHash(pEntry->fileName, pEntry->fileNameLen); - const ZipEntry* found; - - found = (const ZipEntry*)mzHashTableLookup(pHash, - itemHash, pEntry, hashcmpZipEntry, true); - if (found != pEntry) { - LOGW("WARNING: duplicate entry '%.*s' in Zip\n", - found->fileNameLen, found->fileName); - /* keep going */ - } -} - -static int validFilename(const char *fileName, unsigned int fileNameLen) -{ - // Forbid super long filenames. - if (fileNameLen >= PATH_MAX) { - LOGW("Filename too long (%d chatacters)\n", fileNameLen); - return 0; - } - - // Require all characters to be printable ASCII (no NUL, no UTF-8, etc). - unsigned int i; - for (i = 0; i < fileNameLen; ++i) { - if (fileName[i] < 32 || fileName[i] >= 127) { - LOGW("Filename contains invalid character '\%03o'\n", fileName[i]); - return 0; - } - } - - return 1; -} - -/* - * Parse the contents of a Zip archive. After confirming that the file - * is in fact a Zip, we scan out the contents of the central directory and - * store it in a hash table. - * - * Returns "true" on success. - */ -static bool parseZipArchive(ZipArchive* pArchive) -{ - bool result = false; - const unsigned char* ptr; - unsigned int i, numEntries, cdOffset; - unsigned int val; - - /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (LOCSIG) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature (ENDSIG). - */ - val = get4LE(pArchive->addr); - if (val == ENDSIG) { - LOGW("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != LOCSIG) { - LOGW("Not a Zip archive (found 0x%08x)\n", val); - goto bail; - } - - /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. - */ - ptr = pArchive->addr + pArchive->length - ENDHDR; - - while (ptr >= (const unsigned char*) pArchive->addr) { - if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG) - break; - ptr--; - } - if (ptr < (const unsigned char*) pArchive->addr) { - LOGW("Could not find end-of-central-directory in Zip\n"); - goto bail; - } - - /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - */ - numEntries = get2LE(ptr + ENDSUB); - cdOffset = get4LE(ptr + ENDOFF); - - LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= pArchive->length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, pArchive->length); - goto bail; - } - - /* - * Create data structures to hold entries. - */ - pArchive->numEntries = numEntries; - pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry)); - pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL); - if (pArchive->pEntries == NULL || pArchive->pHash == NULL) - goto bail; - - ptr = pArchive->addr + cdOffset; - for (i = 0; i < numEntries; i++) { - ZipEntry* pEntry; - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - const char *fileName; - - if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) { - LOGW("Ran off the end (at %d)\n", i); - goto bail; - } - if (get4LE(ptr) != CENSIG) { - LOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - - localHdrOffset = get4LE(ptr + CENOFF); - fileNameLen = get2LE(ptr + CENNAM); - extraLen = get2LE(ptr + CENEXT); - commentLen = get2LE(ptr + CENCOM); - fileName = (const char*)ptr + CENHDR; - if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) { - LOGW("Filename ran off the end (at %d)\n", i); - goto bail; - } - if (!validFilename(fileName, fileNameLen)) { - LOGW("Invalid filename (at %d)\n", i); - goto bail; - } - -#if SORT_ENTRIES - /* Figure out where this entry should go (binary search). - */ - if (i > 0) { - int low, high; - - low = 0; - high = i - 1; - while (low <= high) { - int mid; - int diff; - int diffLen; - - mid = low + ((high - low) / 2); // avoid overflow - - if (pArchive->pEntries[mid].fileNameLen < fileNameLen) { - diffLen = pArchive->pEntries[mid].fileNameLen; - } else { - diffLen = fileNameLen; - } - diff = strncmp(pArchive->pEntries[mid].fileName, fileName, - diffLen); - if (diff == 0) { - diff = pArchive->pEntries[mid].fileNameLen - fileNameLen; - } - if (diff < 0) { - low = mid + 1; - } else if (diff > 0) { - high = mid - 1; - } else { - high = mid; - break; - } - } - - unsigned int target = high + 1; - assert(target <= i); - if (target != i) { - /* It belongs somewhere other than at the end of - * the list. Make some room at [target]. - */ - memmove(pArchive->pEntries + target + 1, - pArchive->pEntries + target, - (i - target) * sizeof(ZipEntry)); - } - pEntry = &pArchive->pEntries[target]; - } else { - pEntry = &pArchive->pEntries[0]; - } -#else - pEntry = &pArchive->pEntries[i]; -#endif - pEntry->fileNameLen = fileNameLen; - pEntry->fileName = fileName; - - pEntry->compLen = get4LE(ptr + CENSIZ); - pEntry->uncompLen = get4LE(ptr + CENLEN); - pEntry->compression = get2LE(ptr + CENHOW); - pEntry->modTime = get4LE(ptr + CENTIM); - pEntry->crc32 = get4LE(ptr + CENCRC); - - /* These two are necessary for finding the mode of the file. - */ - pEntry->versionMadeBy = get2LE(ptr + CENVEM); - if ((pEntry->versionMadeBy & 0xff00) != 0 && - (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX) - { - LOGW("Incompatible \"version made by\": 0x%02x (at %d)\n", - pEntry->versionMadeBy >> 8, i); - goto bail; - } - pEntry->externalFileAttributes = get4LE(ptr + CENATX); - - // Perform pArchive->addr + localHdrOffset, ensuring that it won't - // overflow. This is needed because localHdrOffset is untrusted. - if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr, - (uintptr_t)localHdrOffset)) { - LOGW("Integer overflow adding in parseZipArchive\n"); - goto bail; - } - if ((uintptr_t)localHdr + LOCHDR > - (uintptr_t)pArchive->addr + pArchive->length) { - LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i); - goto bail; - } - if (get4LE(localHdr) != LOCSIG) { - LOGW("Missed a local header sig (at %d)\n", i); - goto bail; - } - pEntry->offset = localHdrOffset + LOCHDR - + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT); - if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) { - LOGW("Integer overflow adding in parseZipArchive\n"); - goto bail; - } - if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) { - LOGW("Data ran off the end (at %d)\n", i); - goto bail; - } - -#if !SORT_ENTRIES - /* Add to hash table; no need to lock here. - * Can't do this now if we're sorting, because entries - * will move around. - */ - addEntryToHashTable(pArchive->pHash, pEntry); -#endif - - //dumpEntry(pEntry); - ptr += CENHDR + fileNameLen + extraLen + commentLen; - } - -#if SORT_ENTRIES - /* If we're sorting, we have to wait until all entries - * are in their final places, otherwise the pointers will - * probably point to the wrong things. - */ - for (i = 0; i < numEntries; i++) { - /* Add to hash table; no need to lock here. - */ - addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]); - } -#endif - - result = true; - -bail: - if (!result) { - mzHashTableFree(pArchive->pHash); - pArchive->pHash = NULL; - } - return result; -} - -/* - * Open a Zip archive and scan out the contents. - * - * The easiest way to do this is to mmap() the whole thing and do the - * traditional backward scan for central directory. Since the EOCD is - * a relatively small bit at the end, we should end up only touching a - * small set of pages. - * - * This will be called on non-Zip files, especially during startup, so - * we don't want to be too noisy about failures. (Do we want a "quiet" - * flag?) - * - * On success, we fill out the contents of "pArchive". - */ -int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive) -{ - int err; - - if (length < ENDHDR) { - err = -1; - LOGW("Archive %p is too small to be zip (%zd)\n", pArchive, length); - goto bail; - } - - pArchive->addr = addr; - pArchive->length = length; - - if (!parseZipArchive(pArchive)) { - err = -1; - LOGW("Parsing archive %p failed\n", pArchive); - goto bail; - } - - err = 0; - -bail: - if (err != 0) - mzCloseZipArchive(pArchive); - return err; -} - -/* - * Close a ZipArchive, closing the file and freeing the contents. - * - * NOTE: the ZipArchive may not have been fully created. - */ -void mzCloseZipArchive(ZipArchive* pArchive) -{ - LOGV("Closing archive %p\n", pArchive); - - free(pArchive->pEntries); - - mzHashTableFree(pArchive->pHash); - - pArchive->pHash = NULL; - pArchive->pEntries = NULL; -} - -/* - * Find a matching entry. - * - * Returns NULL if no matching entry found. - */ -const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive, - const char* entryName) -{ - unsigned int itemHash = computeHash(entryName, strlen(entryName)); - - return (const ZipEntry*)mzHashTableLookup(pArchive->pHash, - itemHash, (char*) entryName, hashcmpZipName, false); -} - -/* - * Return true if the entry is a symbolic link. - */ -static bool mzIsZipEntrySymlink(const ZipEntry* pEntry) -{ - if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) { - return S_ISLNK(pEntry->externalFileAttributes >> 16); - } - return false; -} - -/* Call processFunction on the uncompressed data of a STORED entry. - */ -static bool processStoredEntry(const ZipArchive *pArchive, - const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, - void *cookie) -{ - return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie); -} - -static bool processDeflatedEntry(const ZipArchive *pArchive, - const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, - void *cookie) -{ - bool success = false; - unsigned long totalOut = 0; - unsigned char procBuf[32 * 1024]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = pArchive->addr + pEntry->offset; - zstream.avail_in = pEntry->compLen; - zstream.next_out = (Bytef*) procBuf; - zstream.avail_out = sizeof(procBuf); - 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) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGW("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf))) - { - long procSize = zstream.next_out - procBuf; - LOGVV("+++ processing %d bytes\n", (int) procSize); - bool ret = processFunction(procBuf, procSize, cookie); - if (!ret) { - LOGW("Process function elected to fail (in inflate)\n"); - goto z_bail; - } - - zstream.next_out = procBuf; - zstream.avail_out = sizeof(procBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - // success! - totalOut = zstream.total_out; - success = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - if (totalOut != pEntry->uncompLen) { - if (success) { // error already shown? - LOGW("Size mismatch on inflated file (%lu vs %u)\n", totalOut, pEntry->uncompLen); - } - return false; - } - return true; -} - -/* - * Stream the uncompressed data through the supplied function, - * passing cookie to it each time it gets called. processFunction - * may be called more than once. - * - * If processFunction returns false, the operation is abandoned and - * mzProcessZipEntryContents() immediately returns false. - * - * This is useful for calculating the hash of an entry's uncompressed contents. - */ -bool mzProcessZipEntryContents(const ZipArchive *pArchive, - const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, - void *cookie) -{ - bool ret = false; - - switch (pEntry->compression) { - case STORED: - ret = processStoredEntry(pArchive, pEntry, processFunction, cookie); - break; - case DEFLATED: - ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie); - break; - default: - LOGE("Unsupported compression type %d for entry '%s'\n", - pEntry->compression, pEntry->fileName); - break; - } - - return ret; -} - -typedef struct { - char *buf; - int bufLen; -} CopyProcessArgs; - -static bool copyProcessFunction(const unsigned char *data, int dataLen, - void *cookie) -{ - CopyProcessArgs *args = (CopyProcessArgs *)cookie; - if (dataLen <= args->bufLen) { - memcpy(args->buf, data, dataLen); - args->buf += dataLen; - args->bufLen -= dataLen; - return true; - } - return false; -} - -/* - * Read an entry into a buffer allocated by the caller. - */ -bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, - char *buf, int bufLen) -{ - CopyProcessArgs args; - bool ret; - - args.buf = buf; - args.bufLen = bufLen; - ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction, - (void *)&args); - if (!ret) { - LOGE("Can't extract entry to buffer.\n"); - return false; - } - return true; -} - -static bool writeProcessFunction(const unsigned char *data, int dataLen, - void *cookie) -{ - int fd = (int)(intptr_t)cookie; - if (dataLen == 0) { - return true; - } - ssize_t soFar = 0; - while (true) { - ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar)); - if (n <= 0) { - LOGE("Error writing %zd bytes from zip file from %p: %s\n", - dataLen-soFar, data+soFar, strerror(errno)); - return false; - } else if (n > 0) { - soFar += n; - if (soFar == dataLen) return true; - if (soFar > dataLen) { - LOGE("write overrun? (%zd bytes instead of %d)\n", - soFar, dataLen); - return false; - } - } - } -} - -/* - * Uncompress "pEntry" in "pArchive" to "fd" at the current offset. - */ -bool mzExtractZipEntryToFile(const ZipArchive *pArchive, - const ZipEntry *pEntry, int fd) -{ - bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, - (void*)(intptr_t)fd); - if (!ret) { - LOGE("Can't extract entry to file.\n"); - return false; - } - return true; -} - -typedef struct { - unsigned char* buffer; - long len; -} BufferExtractCookie; - -static bool bufferProcessFunction(const unsigned char *data, int dataLen, - void *cookie) { - BufferExtractCookie *bec = (BufferExtractCookie*)cookie; - - memmove(bec->buffer, data, dataLen); - bec->buffer += dataLen; - bec->len -= dataLen; - - return true; -} - -/* - * Uncompress "pEntry" in "pArchive" to buffer, which must be large - * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. - */ -bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, - const ZipEntry *pEntry, unsigned char *buffer) -{ - BufferExtractCookie bec; - bec.buffer = buffer; - bec.len = mzGetZipEntryUncompLen(pEntry); - - bool ret = mzProcessZipEntryContents(pArchive, pEntry, - bufferProcessFunction, (void*)&bec); - if (!ret || bec.len != 0) { - LOGE("Can't extract entry to memory buffer.\n"); - return false; - } - return true; -} - - -/* Helper state to make path translation easier and less malloc-happy. - */ -typedef struct { - const char *targetDir; - const char *zipDir; - char *buf; - int targetDirLen; - int zipDirLen; - int bufLen; -} MzPathHelper; - -/* Given the values of targetDir and zipDir in the helper, - * return the target filename of the provided entry. - * The helper must be initialized first. - */ -static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry) -{ - int needLen; - bool firstTime = (helper->buf == NULL); - - /* target file <-- targetDir + / + entry[zipDirLen:] - */ - needLen = helper->targetDirLen + 1 + - pEntry->fileNameLen - helper->zipDirLen + 1; - if (firstTime || needLen > helper->bufLen) { - char *newBuf; - - needLen *= 2; - newBuf = (char *)realloc(helper->buf, needLen); - if (newBuf == NULL) { - return NULL; - } - helper->buf = newBuf; - helper->bufLen = needLen; - } - - /* Every path will start with the target path and a slash. - */ - if (firstTime) { - char *p = helper->buf; - memcpy(p, helper->targetDir, helper->targetDirLen); - p += helper->targetDirLen; - if (p == helper->buf || p[-1] != '/') { - helper->targetDirLen += 1; - *p++ = '/'; - } - } - - /* Replace the custom part of the path with the appropriate - * part of the entry's path. - */ - char *epath = helper->buf + helper->targetDirLen; - memcpy(epath, pEntry->fileName + helper->zipDirLen, - pEntry->fileNameLen - helper->zipDirLen); - epath += pEntry->fileNameLen - helper->zipDirLen; - *epath = '\0'; - - return helper->buf; -} - -/* - * Inflate all entries under zipDir to the directory specified by - * targetDir, which must exist and be a writable directory. - * - * The immediate children of zipDir will become the immediate - * children of targetDir; e.g., if the archive contains the entries - * - * a/b/c/one - * a/b/c/two - * a/b/c/d/three - * - * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting - * files will be - * - * /tmp/one - * /tmp/two - * /tmp/d/three - * - * Returns true on success, false on failure. - */ -bool mzExtractRecursive(const ZipArchive *pArchive, - const char *zipDir, const char *targetDir, - const struct utimbuf *timestamp, - void (*callback)(const char *fn, void *), void *cookie, - struct selabel_handle *sehnd) -{ - if (zipDir[0] == '/') { - LOGE("mzExtractRecursive(): zipDir must be a relative path.\n"); - return false; - } - if (targetDir[0] != '/') { - LOGE("mzExtractRecursive(): targetDir must be an absolute path.\n"); - return false; - } - - unsigned int zipDirLen; - char *zpath; - - zipDirLen = strlen(zipDir); - zpath = (char *)malloc(zipDirLen + 2); - if (zpath == NULL) { - LOGE("Can't allocate %d bytes for zip path\n", zipDirLen + 2); - return false; - } - /* If zipDir is empty, we'll extract the entire zip file. - * Otherwise, canonicalize the path. - */ - if (zipDirLen > 0) { - /* Make sure there's (hopefully, exactly one) slash at the - * end of the path. This way we don't need to worry about - * accidentally extracting "one/twothree" when a path like - * "one/two" is specified. - */ - memcpy(zpath, zipDir, zipDirLen); - if (zpath[zipDirLen-1] != '/') { - zpath[zipDirLen++] = '/'; - } - } - zpath[zipDirLen] = '\0'; - - /* Set up the helper structure that we'll use to assemble paths. - */ - MzPathHelper helper; - helper.targetDir = targetDir; - helper.targetDirLen = strlen(helper.targetDir); - helper.zipDir = zpath; - helper.zipDirLen = strlen(helper.zipDir); - helper.buf = NULL; - helper.bufLen = 0; - - /* Walk through the entries and extract anything whose path begins - * with zpath. - //TODO: since the entries are sorted, binary search for the first match - // and stop after the first non-match. - */ - unsigned int i; - bool seenMatch = false; - int ok = true; - int extractCount = 0; - for (i = 0; i < pArchive->numEntries; i++) { - ZipEntry *pEntry = pArchive->pEntries + i; - if (pEntry->fileNameLen < zipDirLen) { - //TODO: look out for a single empty directory entry that matches zpath, but - // missing the trailing slash. Most zip files seem to include - // the trailing slash, but I think it's legal to leave it off. - // e.g., zpath "a/b/", entry "a/b", with no children of the entry. - /* No chance of matching. - */ -#if SORT_ENTRIES - if (seenMatch) { - /* Since the entries are sorted, we can give up - * on the first mismatch after the first match. - */ - break; - } -#endif - continue; - } - /* If zpath is empty, this strncmp() will match everything, - * which is what we want. - */ - if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) { -#if SORT_ENTRIES - if (seenMatch) { - /* Since the entries are sorted, we can give up - * on the first mismatch after the first match. - */ - break; - } -#endif - continue; - } - /* This entry begins with zipDir, so we'll extract it. - */ - seenMatch = true; - - /* Find the target location of the entry. - */ - const char *targetFile = targetEntryPath(&helper, pEntry); - if (targetFile == NULL) { - LOGE("Can't assemble target path for \"%.*s\"\n", - pEntry->fileNameLen, pEntry->fileName); - ok = false; - break; - } - -#define UNZIP_DIRMODE 0755 -#define UNZIP_FILEMODE 0644 - /* - * Create the file or directory. We ignore directory entries - * because we recursively create paths to each file entry we encounter - * in the zip archive anyway. - * - * NOTE: A "directory entry" in a zip archive is just a zero length - * entry that ends in a "/". They're not mandatory and many tools get - * rid of them. We need to process them only if we want to preserve - * empty directories from the archive. - */ - if (pEntry->fileName[pEntry->fileNameLen-1] != '/') { - /* This is not a directory. First, make sure that - * the containing directory exists. - */ - int ret = dirCreateHierarchy( - targetFile, UNZIP_DIRMODE, timestamp, true, sehnd); - if (ret != 0) { - LOGE("Can't create containing directory for \"%s\": %s\n", - targetFile, strerror(errno)); - ok = false; - break; - } - - /* - * The entry is a regular file or a symlink. Open the target for writing. - * - * TODO: This behavior for symlinks seems rather bizarre. For a - * symlink foo/bar/baz -> foo/tar/taz, we will create a file called - * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We - * warn about this for now and preserve older behavior. - */ - if (mzIsZipEntrySymlink(pEntry)) { - LOGE("Symlink entry \"%.*s\" will be output as a regular file.", - pEntry->fileNameLen, pEntry->fileName); - } - - char *secontext = NULL; - - if (sehnd) { - selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); - setfscreatecon(secontext); - } - - int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC, - UNZIP_FILEMODE); - - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } - - if (fd < 0) { - LOGE("Can't create target file \"%s\": %s\n", - targetFile, strerror(errno)); - ok = false; - break; - } - - bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); - if (ok) { - ok = (fsync(fd) == 0); - } - if (close(fd) != 0) { - ok = false; - } - if (!ok) { - LOGE("Error extracting \"%s\"\n", targetFile); - ok = false; - break; - } - - if (timestamp != NULL && utime(targetFile, timestamp)) { - LOGE("Error touching \"%s\"\n", targetFile); - ok = false; - break; - } - - LOGV("Extracted file \"%s\"\n", targetFile); - ++extractCount; - } - - if (callback != NULL) callback(targetFile, cookie); - } - - LOGV("Extracted %d file(s)\n", extractCount); - - free(helper.buf); - free(zpath); - - return ok; -} diff --git a/minzip/Zip.cpp b/minzip/Zip.cpp new file mode 100644 index 000000000..b887b8466 --- /dev/null +++ b/minzip/Zip.cpp @@ -0,0 +1,1022 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Simple Zip file support. + */ +#include "safe_iop.h" +#include "zlib.h" + +#include +#include +#include +#include // for uintptr_t +#include +#include // for S_ISLNK() +#include + +#include + +#include +#include +#include +#include +#include + +#include "Zip.h" +#include "Bits.h" +#include "DirUtil.h" + +#define SORT_ENTRIES 1 + +/* + * Offset and length constants (java.util.zip naming convention). + */ +enum { + CENSIG = 0x02014b50, // PK12 + CENHDR = 46, + + CENVEM = 4, + CENVER = 6, + CENFLG = 8, + CENHOW = 10, + CENTIM = 12, + CENCRC = 16, + CENSIZ = 20, + CENLEN = 24, + CENNAM = 28, + CENEXT = 30, + CENCOM = 32, + CENDSK = 34, + CENATT = 36, + CENATX = 38, + CENOFF = 42, + + ENDSIG = 0x06054b50, // PK56 + ENDHDR = 22, + + ENDSUB = 8, + ENDTOT = 10, + ENDSIZ = 12, + ENDOFF = 16, + ENDCOM = 20, + + EXTSIG = 0x08074b50, // PK78 + EXTHDR = 16, + + EXTCRC = 4, + EXTSIZ = 8, + EXTLEN = 12, + + LOCSIG = 0x04034b50, // PK34 + LOCHDR = 30, + + LOCVER = 4, + LOCFLG = 6, + LOCHOW = 8, + LOCTIM = 10, + LOCCRC = 14, + LOCSIZ = 18, + LOCLEN = 22, + LOCNAM = 26, + LOCEXT = 28, + + STORED = 0, + DEFLATED = 8, + + CENVEM_UNIX = 3 << 8, // the high byte of CENVEM +}; + + +/* + * For debugging, dump the contents of a ZipEntry. + */ +#if 0 +static void dumpEntry(const ZipEntry* pEntry) +{ + LOGI(" %p '%.*s'\n", pEntry->fileName,pEntry->fileNameLen,pEntry->fileName); + LOGI(" off=%u comp=%u uncomp=%u how=%d\n", pEntry->offset, + pEntry->compLen, pEntry->uncompLen, pEntry->compression); +} +#endif + +/* + * (This is a mzHashTableLookup callback.) + * + * Compare two ZipEntry structs, by name. + */ +static int hashcmpZipEntry(const void* ventry1, const void* ventry2) +{ + const ZipEntry* entry1 = (const ZipEntry*) ventry1; + const ZipEntry* entry2 = (const ZipEntry*) ventry2; + + if (entry1->fileNameLen != entry2->fileNameLen) + return entry1->fileNameLen - entry2->fileNameLen; + return memcmp(entry1->fileName, entry2->fileName, entry1->fileNameLen); +} + +/* + * (This is a mzHashTableLookup callback.) + * + * find a ZipEntry struct by name. + */ +static int hashcmpZipName(const void* ventry, const void* vname) +{ + const ZipEntry* entry = (const ZipEntry*) ventry; + const char* name = (const char*) vname; + unsigned int nameLen = strlen(name); + + if (entry->fileNameLen != nameLen) + return entry->fileNameLen - nameLen; + return memcmp(entry->fileName, name, nameLen); +} + +/* + * Compute the hash code for a ZipEntry filename. + * + * Not expected to be compatible with any other hash function, so we init + * to 2 to ensure it doesn't happen to match. + */ +static unsigned int computeHash(const char* name, int nameLen) +{ + unsigned int hash = 2; + + while (nameLen--) + hash = hash * 31 + *name++; + + return hash; +} + +static void addEntryToHashTable(HashTable* pHash, ZipEntry* pEntry) +{ + unsigned int itemHash = computeHash(pEntry->fileName, pEntry->fileNameLen); + const ZipEntry* found; + + found = (const ZipEntry*)mzHashTableLookup(pHash, + itemHash, pEntry, hashcmpZipEntry, true); + if (found != pEntry) { + LOG(WARNING) << "WARNING: duplicate entry '" << std::string(found->fileName, + found->fileNameLen) << "' in Zip"; + + /* keep going */ + } +} + +static int validFilename(const char *fileName, unsigned int fileNameLen) +{ + // Forbid super long filenames. + if (fileNameLen >= PATH_MAX) { + LOG(WARNING) << "Filename too long (" << fileNameLen << " chatacters)"; + return 0; + } + + // Require all characters to be printable ASCII (no NUL, no UTF-8, etc). + unsigned int i; + for (i = 0; i < fileNameLen; ++i) { + if (fileName[i] < 32 || fileName[i] >= 127) { + LOG(WARNING) << android::base::StringPrintf( + "Filename contains invalid character '\%02x'\n", fileName[i]); + return 0; + } + } + + return 1; +} + +/* + * Parse the contents of a Zip archive. After confirming that the file + * is in fact a Zip, we scan out the contents of the central directory and + * store it in a hash table. + * + * Returns "true" on success. + */ +static bool parseZipArchive(ZipArchive* pArchive) +{ + bool result = false; + const unsigned char* ptr; + unsigned int i, numEntries, cdOffset; + unsigned int val; + + /* + * The first 4 bytes of the file will either be the local header + * signature for the first file (LOCSIG) or, if the archive doesn't + * have any files in it, the end-of-central-directory signature (ENDSIG). + */ + val = get4LE(pArchive->addr); + if (val == ENDSIG) { + LOG(WARNING) << "Found Zip archive, but it looks empty"; + goto bail; + } else if (val != LOCSIG) { + LOG(WARNING) << android::base::StringPrintf("Not a Zip archive (found 0x%08x)\n", val); + goto bail; + } + + /* + * Find the EOCD. We'll find it immediately unless they have a file + * comment. + */ + ptr = pArchive->addr + pArchive->length - ENDHDR; + + while (ptr >= (const unsigned char*) pArchive->addr) { + if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG) + break; + ptr--; + } + if (ptr < (const unsigned char*) pArchive->addr) { + LOG(WARNING) << "Could not find end-of-central-directory in Zip"; + goto bail; + } + + /* + * There are two interesting items in the EOCD block: the number of + * entries in the file, and the file offset of the start of the + * central directory. + */ + numEntries = get2LE(ptr + ENDSUB); + cdOffset = get4LE(ptr + ENDOFF); + + LOG(VERBOSE) << "numEntries=" << numEntries << " cdOffset=" << cdOffset; + if (numEntries == 0 || cdOffset >= pArchive->length) { + LOG(WARNING) << "Invalid entries=" << numEntries << " offset=" << cdOffset + << " (len=" << pArchive->length << ")"; + goto bail; + } + + /* + * Create data structures to hold entries. + */ + pArchive->numEntries = numEntries; + pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry)); + pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL); + if (pArchive->pEntries == NULL || pArchive->pHash == NULL) + goto bail; + + ptr = pArchive->addr + cdOffset; + for (i = 0; i < numEntries; i++) { + ZipEntry* pEntry; + unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; + const unsigned char* localHdr; + const char *fileName; + + if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) { + LOG(WARNING) << "Ran off the end (at " << i << ")"; + goto bail; + } + if (get4LE(ptr) != CENSIG) { + LOG(WARNING) << "Missed a central dir sig (at " << i << ")"; + goto bail; + } + + localHdrOffset = get4LE(ptr + CENOFF); + fileNameLen = get2LE(ptr + CENNAM); + extraLen = get2LE(ptr + CENEXT); + commentLen = get2LE(ptr + CENCOM); + fileName = (const char*)ptr + CENHDR; + if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) { + LOG(WARNING) << "Filename ran off the end (at " << i << ")"; + goto bail; + } + if (!validFilename(fileName, fileNameLen)) { + LOG(WARNING) << "Invalid filename (at " << i << ")"; + goto bail; + } + +#if SORT_ENTRIES + /* Figure out where this entry should go (binary search). + */ + if (i > 0) { + int low, high; + + low = 0; + high = i - 1; + while (low <= high) { + int mid; + int diff; + int diffLen; + + mid = low + ((high - low) / 2); // avoid overflow + + if (pArchive->pEntries[mid].fileNameLen < fileNameLen) { + diffLen = pArchive->pEntries[mid].fileNameLen; + } else { + diffLen = fileNameLen; + } + diff = strncmp(pArchive->pEntries[mid].fileName, fileName, + diffLen); + if (diff == 0) { + diff = pArchive->pEntries[mid].fileNameLen - fileNameLen; + } + if (diff < 0) { + low = mid + 1; + } else if (diff > 0) { + high = mid - 1; + } else { + high = mid; + break; + } + } + + unsigned int target = high + 1; + assert(target <= i); + if (target != i) { + /* It belongs somewhere other than at the end of + * the list. Make some room at [target]. + */ + memmove(pArchive->pEntries + target + 1, + pArchive->pEntries + target, + (i - target) * sizeof(ZipEntry)); + } + pEntry = &pArchive->pEntries[target]; + } else { + pEntry = &pArchive->pEntries[0]; + } +#else + pEntry = &pArchive->pEntries[i]; +#endif + pEntry->fileNameLen = fileNameLen; + pEntry->fileName = fileName; + + pEntry->compLen = get4LE(ptr + CENSIZ); + pEntry->uncompLen = get4LE(ptr + CENLEN); + pEntry->compression = get2LE(ptr + CENHOW); + pEntry->modTime = get4LE(ptr + CENTIM); + pEntry->crc32 = get4LE(ptr + CENCRC); + + /* These two are necessary for finding the mode of the file. + */ + pEntry->versionMadeBy = get2LE(ptr + CENVEM); + if ((pEntry->versionMadeBy & 0xff00) != 0 && + (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX) + { + LOG(WARNING) << android::base::StringPrintf( + "Incompatible \"version made by\": 0x%02x (at %d)\n", + pEntry->versionMadeBy >> 8, i); + goto bail; + } + pEntry->externalFileAttributes = get4LE(ptr + CENATX); + + // Perform pArchive->addr + localHdrOffset, ensuring that it won't + // overflow. This is needed because localHdrOffset is untrusted. + if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr, + (uintptr_t)localHdrOffset)) { + LOG(WARNING) << "Integer overflow adding in parseZipArchive"; + goto bail; + } + if ((uintptr_t)localHdr + LOCHDR > + (uintptr_t)pArchive->addr + pArchive->length) { + LOG(WARNING) << "Bad offset to local header: " << localHdrOffset + << " (at " << i << ")"; + goto bail; + } + if (get4LE(localHdr) != LOCSIG) { + LOG(WARNING) << "Missed a local header sig (at " << i << ")"; + goto bail; + } + pEntry->offset = localHdrOffset + LOCHDR + + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT); + if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) { + LOG(WARNING) << "Integer overflow adding in parseZipArchive"; + goto bail; + } + if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) { + LOG(WARNING) << "Data ran off the end (at " << i << ")"; + goto bail; + } + +#if !SORT_ENTRIES + /* Add to hash table; no need to lock here. + * Can't do this now if we're sorting, because entries + * will move around. + */ + addEntryToHashTable(pArchive->pHash, pEntry); +#endif + + //dumpEntry(pEntry); + ptr += CENHDR + fileNameLen + extraLen + commentLen; + } + +#if SORT_ENTRIES + /* If we're sorting, we have to wait until all entries + * are in their final places, otherwise the pointers will + * probably point to the wrong things. + */ + for (i = 0; i < numEntries; i++) { + /* Add to hash table; no need to lock here. + */ + addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]); + } +#endif + + result = true; + +bail: + if (!result) { + mzHashTableFree(pArchive->pHash); + pArchive->pHash = NULL; + } + return result; +} + +/* + * Open a Zip archive and scan out the contents. + * + * The easiest way to do this is to mmap() the whole thing and do the + * traditional backward scan for central directory. Since the EOCD is + * a relatively small bit at the end, we should end up only touching a + * small set of pages. + * + * This will be called on non-Zip files, especially during startup, so + * we don't want to be too noisy about failures. (Do we want a "quiet" + * flag?) + * + * On success, we fill out the contents of "pArchive". + */ +int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive) +{ + int err; + + if (length < ENDHDR) { + err = -1; + LOG(WARNING) << "Archive " << pArchive << " is too small to be zip (" + << length << ")"; + goto bail; + } + + pArchive->addr = addr; + pArchive->length = length; + + if (!parseZipArchive(pArchive)) { + err = -1; + LOG(WARNING) << "Parsing archive " << pArchive << " failed"; + goto bail; + } + + err = 0; + +bail: + if (err != 0) + mzCloseZipArchive(pArchive); + return err; +} + +/* + * Close a ZipArchive, closing the file and freeing the contents. + * + * NOTE: the ZipArchive may not have been fully created. + */ +void mzCloseZipArchive(ZipArchive* pArchive) +{ + LOG(VERBOSE) << "Closing archive " << pArchive; + + free(pArchive->pEntries); + + mzHashTableFree(pArchive->pHash); + + pArchive->pHash = NULL; + pArchive->pEntries = NULL; +} + +/* + * Find a matching entry. + * + * Returns NULL if no matching entry found. + */ +const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive, + const char* entryName) +{ + unsigned int itemHash = computeHash(entryName, strlen(entryName)); + + return (const ZipEntry*)mzHashTableLookup(pArchive->pHash, + itemHash, (char*) entryName, hashcmpZipName, false); +} + +/* + * Return true if the entry is a symbolic link. + */ +static bool mzIsZipEntrySymlink(const ZipEntry* pEntry) +{ + if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) { + return S_ISLNK(pEntry->externalFileAttributes >> 16); + } + return false; +} + +/* Call processFunction on the uncompressed data of a STORED entry. + */ +static bool processStoredEntry(const ZipArchive *pArchive, + const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, + void *cookie) +{ + return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie); +} + +static bool processDeflatedEntry(const ZipArchive *pArchive, + const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, + void *cookie) +{ + bool success = false; + unsigned long totalOut = 0; + unsigned char procBuf[32 * 1024]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = pArchive->addr + pEntry->offset; + zstream.avail_in = pEntry->compLen; + zstream.next_out = (Bytef*) procBuf; + zstream.avail_out = sizeof(procBuf); + 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) { + LOG(ERROR) << "Installed zlib is not compatible with linked version (" + << ZLIB_VERSION << ")"; + } else { + LOG(ERROR) << "Call to inflateInit2 failed (zerr=" << zerr << ")"; + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOG(WARNING) << "zlib inflate call failed (zerr=" << zerr << ")"; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf))) + { + long procSize = zstream.next_out - procBuf; + LOG(VERBOSE) << "+++ processing " << procSize << " bytes"; + bool ret = processFunction(procBuf, procSize, cookie); + if (!ret) { + LOG(WARNING) << "Process function elected to fail (in inflate)"; + goto z_bail; + } + + zstream.next_out = procBuf; + zstream.avail_out = sizeof(procBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + // success! + totalOut = zstream.total_out; + success = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + if (totalOut != pEntry->uncompLen) { + if (success) { // error already shown? + LOG(WARNING) << "Size mismatch on inflated file (" << totalOut << " vs " + << pEntry->uncompLen << ")"; + } + return false; + } + return true; +} + +/* + * Stream the uncompressed data through the supplied function, + * passing cookie to it each time it gets called. processFunction + * may be called more than once. + * + * If processFunction returns false, the operation is abandoned and + * mzProcessZipEntryContents() immediately returns false. + * + * This is useful for calculating the hash of an entry's uncompressed contents. + */ +bool mzProcessZipEntryContents(const ZipArchive *pArchive, + const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, + void *cookie) +{ + bool ret = false; + + switch (pEntry->compression) { + case STORED: + ret = processStoredEntry(pArchive, pEntry, processFunction, cookie); + break; + case DEFLATED: + ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie); + break; + default: + LOG(ERROR) << "Unsupported compression type " << pEntry->compression + << " for entry '" << pEntry->fileName << "'"; + break; + } + + return ret; +} + +typedef struct { + char *buf; + int bufLen; +} CopyProcessArgs; + +static bool copyProcessFunction(const unsigned char *data, int dataLen, + void *cookie) +{ + CopyProcessArgs *args = (CopyProcessArgs *)cookie; + if (dataLen <= args->bufLen) { + memcpy(args->buf, data, dataLen); + args->buf += dataLen; + args->bufLen -= dataLen; + return true; + } + return false; +} + +/* + * Read an entry into a buffer allocated by the caller. + */ +bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, + char *buf, int bufLen) +{ + CopyProcessArgs args; + bool ret; + + args.buf = buf; + args.bufLen = bufLen; + ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction, + (void *)&args); + if (!ret) { + LOG(ERROR) << "Can't extract entry to buffer"; + return false; + } + return true; +} + +static bool writeProcessFunction(const unsigned char *data, int dataLen, + void *cookie) +{ + int fd = (int)(intptr_t)cookie; + if (dataLen == 0) { + return true; + } + ssize_t soFar = 0; + while (true) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar)); + if (n <= 0) { + PLOG(ERROR) << "Error writing " << dataLen-soFar << " bytes from zip file from " + << data+soFar; + return false; + } else if (n > 0) { + soFar += n; + if (soFar == dataLen) return true; + if (soFar > dataLen) { + LOG(ERROR) << "write overrun? (" << soFar << " bytes instead of " + << dataLen << ")"; + return false; + } + } + } +} + +/* + * Uncompress "pEntry" in "pArchive" to "fd" at the current offset. + */ +bool mzExtractZipEntryToFile(const ZipArchive *pArchive, + const ZipEntry *pEntry, int fd) +{ + bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, + (void*)(intptr_t)fd); + if (!ret) { + LOG(ERROR) << "Can't extract entry to file."; + return false; + } + return true; +} + +typedef struct { + unsigned char* buffer; + long len; +} BufferExtractCookie; + +static bool bufferProcessFunction(const unsigned char *data, int dataLen, + void *cookie) { + BufferExtractCookie *bec = (BufferExtractCookie*)cookie; + + memmove(bec->buffer, data, dataLen); + bec->buffer += dataLen; + bec->len -= dataLen; + + return true; +} + +/* + * Uncompress "pEntry" in "pArchive" to buffer, which must be large + * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. + */ +bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char *buffer) +{ + BufferExtractCookie bec; + bec.buffer = buffer; + bec.len = mzGetZipEntryUncompLen(pEntry); + + bool ret = mzProcessZipEntryContents(pArchive, pEntry, + bufferProcessFunction, (void*)&bec); + if (!ret || bec.len != 0) { + LOG(ERROR) << "Can't extract entry to memory buffer."; + return false; + } + return true; +} + + +/* Helper state to make path translation easier and less malloc-happy. + */ +typedef struct { + const char *targetDir; + const char *zipDir; + char *buf; + int targetDirLen; + int zipDirLen; + int bufLen; +} MzPathHelper; + +/* Given the values of targetDir and zipDir in the helper, + * return the target filename of the provided entry. + * The helper must be initialized first. + */ +static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry) +{ + int needLen; + bool firstTime = (helper->buf == NULL); + + /* target file <-- targetDir + / + entry[zipDirLen:] + */ + needLen = helper->targetDirLen + 1 + + pEntry->fileNameLen - helper->zipDirLen + 1; + if (firstTime || needLen > helper->bufLen) { + char *newBuf; + + needLen *= 2; + newBuf = (char *)realloc(helper->buf, needLen); + if (newBuf == NULL) { + return NULL; + } + helper->buf = newBuf; + helper->bufLen = needLen; + } + + /* Every path will start with the target path and a slash. + */ + if (firstTime) { + char *p = helper->buf; + memcpy(p, helper->targetDir, helper->targetDirLen); + p += helper->targetDirLen; + if (p == helper->buf || p[-1] != '/') { + helper->targetDirLen += 1; + *p++ = '/'; + } + } + + /* Replace the custom part of the path with the appropriate + * part of the entry's path. + */ + char *epath = helper->buf + helper->targetDirLen; + memcpy(epath, pEntry->fileName + helper->zipDirLen, + pEntry->fileNameLen - helper->zipDirLen); + epath += pEntry->fileNameLen - helper->zipDirLen; + *epath = '\0'; + + return helper->buf; +} + +/* + * Inflate all entries under zipDir to the directory specified by + * targetDir, which must exist and be a writable directory. + * + * The immediate children of zipDir will become the immediate + * children of targetDir; e.g., if the archive contains the entries + * + * a/b/c/one + * a/b/c/two + * a/b/c/d/three + * + * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting + * files will be + * + * /tmp/one + * /tmp/two + * /tmp/d/three + * + * Returns true on success, false on failure. + */ +bool mzExtractRecursive(const ZipArchive *pArchive, + const char *zipDir, const char *targetDir, + const struct utimbuf *timestamp, + void (*callback)(const char *fn, void *), void *cookie, + struct selabel_handle *sehnd) +{ + if (zipDir[0] == '/') { + LOG(ERROR) << "mzExtractRecursive(): zipDir must be a relative path."; + return false; + } + if (targetDir[0] != '/') { + LOG(ERROR) << "mzExtractRecursive(): targetDir must be an absolute path.\n"; + return false; + } + + unsigned int zipDirLen; + char *zpath; + + zipDirLen = strlen(zipDir); + zpath = (char *)malloc(zipDirLen + 2); + if (zpath == NULL) { + LOG(ERROR) << "Can't allocate " << (zipDirLen + 2) << " bytes for zip path"; + return false; + } + /* If zipDir is empty, we'll extract the entire zip file. + * Otherwise, canonicalize the path. + */ + if (zipDirLen > 0) { + /* Make sure there's (hopefully, exactly one) slash at the + * end of the path. This way we don't need to worry about + * accidentally extracting "one/twothree" when a path like + * "one/two" is specified. + */ + memcpy(zpath, zipDir, zipDirLen); + if (zpath[zipDirLen-1] != '/') { + zpath[zipDirLen++] = '/'; + } + } + zpath[zipDirLen] = '\0'; + + /* Set up the helper structure that we'll use to assemble paths. + */ + MzPathHelper helper; + helper.targetDir = targetDir; + helper.targetDirLen = strlen(helper.targetDir); + helper.zipDir = zpath; + helper.zipDirLen = strlen(helper.zipDir); + helper.buf = NULL; + helper.bufLen = 0; + + /* Walk through the entries and extract anything whose path begins + * with zpath. + //TODO: since the entries are sorted, binary search for the first match + // and stop after the first non-match. + */ + unsigned int i; + bool seenMatch = false; + int ok = true; + int extractCount = 0; + for (i = 0; i < pArchive->numEntries; i++) { + ZipEntry *pEntry = pArchive->pEntries + i; + if (pEntry->fileNameLen < zipDirLen) { + //TODO: look out for a single empty directory entry that matches zpath, but + // missing the trailing slash. Most zip files seem to include + // the trailing slash, but I think it's legal to leave it off. + // e.g., zpath "a/b/", entry "a/b", with no children of the entry. + /* No chance of matching. + */ +#if SORT_ENTRIES + if (seenMatch) { + /* Since the entries are sorted, we can give up + * on the first mismatch after the first match. + */ + break; + } +#endif + continue; + } + /* If zpath is empty, this strncmp() will match everything, + * which is what we want. + */ + if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) { +#if SORT_ENTRIES + if (seenMatch) { + /* Since the entries are sorted, we can give up + * on the first mismatch after the first match. + */ + break; + } +#endif + continue; + } + /* This entry begins with zipDir, so we'll extract it. + */ + seenMatch = true; + + /* Find the target location of the entry. + */ + const char *targetFile = targetEntryPath(&helper, pEntry); + if (targetFile == NULL) { + LOG(ERROR) << "Can't assemble target path for \"" << std::string(pEntry->fileName, + pEntry->fileNameLen) << "\""; + ok = false; + break; + } + +#define UNZIP_DIRMODE 0755 +#define UNZIP_FILEMODE 0644 + /* + * Create the file or directory. We ignore directory entries + * because we recursively create paths to each file entry we encounter + * in the zip archive anyway. + * + * NOTE: A "directory entry" in a zip archive is just a zero length + * entry that ends in a "/". They're not mandatory and many tools get + * rid of them. We need to process them only if we want to preserve + * empty directories from the archive. + */ + if (pEntry->fileName[pEntry->fileNameLen-1] != '/') { + /* This is not a directory. First, make sure that + * the containing directory exists. + */ + int ret = dirCreateHierarchy( + targetFile, UNZIP_DIRMODE, timestamp, true, sehnd); + if (ret != 0) { + PLOG(ERROR) << "Can't create containing directory for \"" << targetFile << "\""; + ok = false; + break; + } + + /* + * The entry is a regular file or a symlink. Open the target for writing. + * + * TODO: This behavior for symlinks seems rather bizarre. For a + * symlink foo/bar/baz -> foo/tar/taz, we will create a file called + * "foo/bar/baz" whose contents are the literal "foo/tar/taz". We + * warn about this for now and preserve older behavior. + */ + if (mzIsZipEntrySymlink(pEntry)) { + LOG(ERROR) << "Symlink entry \"" << std::string(pEntry->fileName, + pEntry->fileNameLen) << "\" will be output as a regular file."; + } + + char *secontext = NULL; + + if (sehnd) { + selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); + setfscreatecon(secontext); + } + + int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC, + UNZIP_FILEMODE); + + if (secontext) { + freecon(secontext); + setfscreatecon(NULL); + } + + if (fd < 0) { + PLOG(ERROR) << "Can't create target file \"" << targetFile << "\""; + ok = false; + break; + } + + bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); + if (ok) { + ok = (fsync(fd) == 0); + } + if (close(fd) != 0) { + ok = false; + } + if (!ok) { + LOG(ERROR) << "Error extracting \"" << targetFile << "\""; + ok = false; + break; + } + + if (timestamp != NULL && utime(targetFile, timestamp)) { + LOG(ERROR) << "Error touching \"" << targetFile << "\""; + ok = false; + break; + } + + LOG(VERBOSE) <<"Extracted file \"" << targetFile << "\""; + ++extractCount; + } + + if (callback != NULL) callback(targetFile, cookie); + } + + LOG(VERBOSE) << "Extracted " << extractCount << " file(s)"; + + free(helper.buf); + free(zpath); + + return ok; +} -- cgit v1.2.3