You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
21 KiB
698 lines
21 KiB
/* |
|
* Copyright (C) 2006 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
// |
|
// Access to entries in a Zip archive. |
|
// |
|
|
|
#define LOG_TAG "zip" |
|
|
|
#include "ZipEntry.h" |
|
#include <utils/Log.h> |
|
|
|
#include <assert.h> |
|
#include <inttypes.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <time.h> |
|
|
|
using namespace android; |
|
|
|
/* |
|
* Initialize a new ZipEntry structure from a FILE* positioned at a |
|
* CentralDirectoryEntry. |
|
* |
|
* On exit, the file pointer will be at the start of the next CDE or |
|
* at the EOCD. |
|
*/ |
|
status_t ZipEntry::initFromCDE(FILE* fp) |
|
{ |
|
status_t result; |
|
long posn; |
|
bool hasDD; |
|
|
|
//ALOGV("initFromCDE ---\n"); |
|
|
|
/* read the CDE */ |
|
result = mCDE.read(fp); |
|
if (result != NO_ERROR) { |
|
ALOGD("mCDE.read failed\n"); |
|
return result; |
|
} |
|
|
|
//mCDE.dump(); |
|
|
|
/* using the info in the CDE, go load up the LFH */ |
|
posn = ftell(fp); |
|
if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { |
|
ALOGD("local header seek failed (%" PRIu32 ")\n", |
|
mCDE.mLocalHeaderRelOffset); |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
result = mLFH.read(fp); |
|
if (result != NO_ERROR) { |
|
ALOGD("mLFH.read failed\n"); |
|
return result; |
|
} |
|
|
|
if (fseek(fp, posn, SEEK_SET) != 0) |
|
return UNKNOWN_ERROR; |
|
|
|
//mLFH.dump(); |
|
|
|
/* |
|
* We *might* need to read the Data Descriptor at this point and |
|
* integrate it into the LFH. If this bit is set, the CRC-32, |
|
* compressed size, and uncompressed size will be zero. In practice |
|
* these seem to be rare. |
|
*/ |
|
hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; |
|
if (hasDD) { |
|
// do something clever |
|
//ALOGD("+++ has data descriptor\n"); |
|
} |
|
|
|
/* |
|
* Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" |
|
* flag is set, because the LFH is incomplete. (Not a problem, since we |
|
* prefer the CDE values.) |
|
*/ |
|
if (!hasDD && !compareHeaders()) { |
|
ALOGW("WARNING: header mismatch\n"); |
|
// keep going? |
|
} |
|
|
|
/* |
|
* If the mVersionToExtract is greater than 20, we may have an |
|
* issue unpacking the record -- could be encrypted, compressed |
|
* with something we don't support, or use Zip64 extensions. We |
|
* can defer worrying about that to when we're extracting data. |
|
*/ |
|
|
|
return NO_ERROR; |
|
} |
|
|
|
/* |
|
* Initialize a new entry. Pass in the file name and an optional comment. |
|
* |
|
* Initializes the CDE and the LFH. |
|
*/ |
|
void ZipEntry::initNew(const char* fileName, const char* comment) |
|
{ |
|
assert(fileName != NULL && *fileName != '\0'); // name required |
|
|
|
/* most fields are properly initialized by constructor */ |
|
mCDE.mVersionMadeBy = kDefaultMadeBy; |
|
mCDE.mVersionToExtract = kDefaultVersion; |
|
mCDE.mCompressionMethod = kCompressStored; |
|
mCDE.mFileNameLength = strlen(fileName); |
|
if (comment != NULL) |
|
mCDE.mFileCommentLength = strlen(comment); |
|
mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does |
|
|
|
if (mCDE.mFileNameLength > 0) { |
|
mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1]; |
|
strcpy((char*) mCDE.mFileName, fileName); |
|
} |
|
if (mCDE.mFileCommentLength > 0) { |
|
/* TODO: stop assuming null-terminated ASCII here? */ |
|
mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1]; |
|
assert(comment != NULL); |
|
strcpy((char*) mCDE.mFileComment, comment); |
|
} |
|
|
|
copyCDEtoLFH(); |
|
} |
|
|
|
/* |
|
* Initialize a new entry, starting with the ZipEntry from a different |
|
* archive. |
|
* |
|
* Initializes the CDE and the LFH. |
|
*/ |
|
status_t ZipEntry::initFromExternal(const ZipEntry* pEntry) |
|
{ |
|
/* |
|
* Copy everything in the CDE over, then fix up the hairy bits. |
|
*/ |
|
memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); |
|
|
|
if (mCDE.mFileNameLength > 0) { |
|
mCDE.mFileName = new uint8_t[mCDE.mFileNameLength+1]; |
|
if (mCDE.mFileName == NULL) |
|
return NO_MEMORY; |
|
strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); |
|
} |
|
if (mCDE.mFileCommentLength > 0) { |
|
mCDE.mFileComment = new uint8_t[mCDE.mFileCommentLength+1]; |
|
if (mCDE.mFileComment == NULL) |
|
return NO_MEMORY; |
|
strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); |
|
} |
|
if (mCDE.mExtraFieldLength > 0) { |
|
/* we null-terminate this, though it may not be a string */ |
|
mCDE.mExtraField = new uint8_t[mCDE.mExtraFieldLength+1]; |
|
if (mCDE.mExtraField == NULL) |
|
return NO_MEMORY; |
|
memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, |
|
mCDE.mExtraFieldLength+1); |
|
} |
|
|
|
/* construct the LFH from the CDE */ |
|
copyCDEtoLFH(); |
|
|
|
/* |
|
* The LFH "extra" field is independent of the CDE "extra", so we |
|
* handle it here. |
|
*/ |
|
assert(mLFH.mExtraField == NULL); |
|
mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; |
|
if (mLFH.mExtraFieldLength > 0) { |
|
mLFH.mExtraField = new uint8_t[mLFH.mExtraFieldLength+1]; |
|
if (mLFH.mExtraField == NULL) |
|
return NO_MEMORY; |
|
memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, |
|
mLFH.mExtraFieldLength+1); |
|
} |
|
|
|
return NO_ERROR; |
|
} |
|
|
|
/* |
|
* Insert pad bytes in the LFH by tweaking the "extra" field. This will |
|
* potentially confuse something that put "extra" data in here earlier, |
|
* but I can't find an actual problem. |
|
*/ |
|
status_t ZipEntry::addPadding(int padding) |
|
{ |
|
if (padding <= 0) |
|
return INVALID_OPERATION; |
|
|
|
//ALOGI("HEY: adding %d pad bytes to existing %d in %s\n", |
|
// padding, mLFH.mExtraFieldLength, mCDE.mFileName); |
|
|
|
if (mLFH.mExtraFieldLength > 0) { |
|
/* extend existing field */ |
|
uint8_t* newExtra; |
|
|
|
newExtra = new uint8_t[mLFH.mExtraFieldLength + padding]; |
|
if (newExtra == NULL) |
|
return NO_MEMORY; |
|
memset(newExtra + mLFH.mExtraFieldLength, 0, padding); |
|
memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); |
|
|
|
delete[] mLFH.mExtraField; |
|
mLFH.mExtraField = newExtra; |
|
mLFH.mExtraFieldLength += padding; |
|
} else { |
|
/* create new field */ |
|
mLFH.mExtraField = new uint8_t[padding]; |
|
memset(mLFH.mExtraField, 0, padding); |
|
mLFH.mExtraFieldLength = padding; |
|
} |
|
|
|
return NO_ERROR; |
|
} |
|
|
|
/* |
|
* Set the fields in the LFH equal to the corresponding fields in the CDE. |
|
* |
|
* This does not touch the LFH "extra" field. |
|
*/ |
|
void ZipEntry::copyCDEtoLFH(void) |
|
{ |
|
mLFH.mVersionToExtract = mCDE.mVersionToExtract; |
|
mLFH.mGPBitFlag = mCDE.mGPBitFlag; |
|
mLFH.mCompressionMethod = mCDE.mCompressionMethod; |
|
mLFH.mLastModFileTime = mCDE.mLastModFileTime; |
|
mLFH.mLastModFileDate = mCDE.mLastModFileDate; |
|
mLFH.mCRC32 = mCDE.mCRC32; |
|
mLFH.mCompressedSize = mCDE.mCompressedSize; |
|
mLFH.mUncompressedSize = mCDE.mUncompressedSize; |
|
mLFH.mFileNameLength = mCDE.mFileNameLength; |
|
// the "extra field" is independent |
|
|
|
delete[] mLFH.mFileName; |
|
if (mLFH.mFileNameLength > 0) { |
|
mLFH.mFileName = new uint8_t[mLFH.mFileNameLength+1]; |
|
strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); |
|
} else { |
|
mLFH.mFileName = NULL; |
|
} |
|
} |
|
|
|
/* |
|
* Set some information about a file after we add it. |
|
*/ |
|
void ZipEntry::setDataInfo(long uncompLen, long compLen, uint32_t crc32, |
|
int compressionMethod) |
|
{ |
|
mCDE.mCompressionMethod = compressionMethod; |
|
mCDE.mCRC32 = crc32; |
|
mCDE.mCompressedSize = compLen; |
|
mCDE.mUncompressedSize = uncompLen; |
|
mCDE.mCompressionMethod = compressionMethod; |
|
if (compressionMethod == kCompressDeflated) { |
|
mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used |
|
} |
|
copyCDEtoLFH(); |
|
} |
|
|
|
/* |
|
* See if the data in mCDE and mLFH match up. This is mostly useful for |
|
* debugging these classes, but it can be used to identify damaged |
|
* archives. |
|
* |
|
* Returns "false" if they differ. |
|
*/ |
|
bool ZipEntry::compareHeaders(void) const |
|
{ |
|
if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { |
|
ALOGV("cmp: VersionToExtract\n"); |
|
return false; |
|
} |
|
if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { |
|
ALOGV("cmp: GPBitFlag\n"); |
|
return false; |
|
} |
|
if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { |
|
ALOGV("cmp: CompressionMethod\n"); |
|
return false; |
|
} |
|
if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { |
|
ALOGV("cmp: LastModFileTime\n"); |
|
return false; |
|
} |
|
if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { |
|
ALOGV("cmp: LastModFileDate\n"); |
|
return false; |
|
} |
|
if (mCDE.mCRC32 != mLFH.mCRC32) { |
|
ALOGV("cmp: CRC32\n"); |
|
return false; |
|
} |
|
if (mCDE.mCompressedSize != mLFH.mCompressedSize) { |
|
ALOGV("cmp: CompressedSize\n"); |
|
return false; |
|
} |
|
if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { |
|
ALOGV("cmp: UncompressedSize\n"); |
|
return false; |
|
} |
|
if (mCDE.mFileNameLength != mLFH.mFileNameLength) { |
|
ALOGV("cmp: FileNameLength\n"); |
|
return false; |
|
} |
|
#if 0 // this seems to be used for padding, not real data |
|
if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { |
|
ALOGV("cmp: ExtraFieldLength\n"); |
|
return false; |
|
} |
|
#endif |
|
if (mCDE.mFileName != NULL) { |
|
if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { |
|
ALOGV("cmp: FileName\n"); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
* Convert the DOS date/time stamp into a UNIX time stamp. |
|
*/ |
|
time_t ZipEntry::getModWhen(void) const |
|
{ |
|
struct tm parts; |
|
|
|
parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; |
|
parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; |
|
parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; |
|
parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); |
|
parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; |
|
parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; |
|
parts.tm_wday = parts.tm_yday = 0; |
|
parts.tm_isdst = -1; // DST info "not available" |
|
|
|
return mktime(&parts); |
|
} |
|
|
|
/* |
|
* Set the CDE/LFH timestamp from UNIX time. |
|
*/ |
|
void ZipEntry::setModWhen(time_t when) |
|
{ |
|
#if !defined(_WIN32) |
|
struct tm tmResult; |
|
#endif |
|
time_t even; |
|
uint16_t zdate, ztime; |
|
|
|
struct tm* ptm; |
|
|
|
/* round up to an even number of seconds */ |
|
even = (time_t)(((unsigned long)(when) + 1) & (~1)); |
|
|
|
/* expand */ |
|
#if !defined(_WIN32) |
|
ptm = localtime_r(&even, &tmResult); |
|
#else |
|
ptm = localtime(&even); |
|
#endif |
|
|
|
int year; |
|
year = ptm->tm_year; |
|
if (year < 80) |
|
year = 80; |
|
|
|
zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; |
|
ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; |
|
|
|
mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; |
|
mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; |
|
} |
|
|
|
|
|
/* |
|
* =========================================================================== |
|
* ZipEntry::LocalFileHeader |
|
* =========================================================================== |
|
*/ |
|
|
|
/* |
|
* Read a local file header. |
|
* |
|
* On entry, "fp" points to the signature at the start of the header. |
|
* On exit, "fp" points to the start of data. |
|
*/ |
|
status_t ZipEntry::LocalFileHeader::read(FILE* fp) |
|
{ |
|
status_t result = NO_ERROR; |
|
uint8_t buf[kLFHLen]; |
|
|
|
assert(mFileName == NULL); |
|
assert(mExtraField == NULL); |
|
|
|
if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
|
|
if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { |
|
ALOGD("whoops: didn't find expected signature\n"); |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
|
|
mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); |
|
mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); |
|
mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); |
|
mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); |
|
mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); |
|
mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); |
|
mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); |
|
mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); |
|
mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); |
|
mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); |
|
|
|
// TODO: validate sizes |
|
|
|
/* grab filename */ |
|
if (mFileNameLength != 0) { |
|
mFileName = new uint8_t[mFileNameLength+1]; |
|
if (mFileName == NULL) { |
|
result = NO_MEMORY; |
|
goto bail; |
|
} |
|
if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
mFileName[mFileNameLength] = '\0'; |
|
} |
|
|
|
/* grab extra field */ |
|
if (mExtraFieldLength != 0) { |
|
mExtraField = new uint8_t[mExtraFieldLength+1]; |
|
if (mExtraField == NULL) { |
|
result = NO_MEMORY; |
|
goto bail; |
|
} |
|
if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
mExtraField[mExtraFieldLength] = '\0'; |
|
} |
|
|
|
bail: |
|
return result; |
|
} |
|
|
|
/* |
|
* Write a local file header. |
|
*/ |
|
status_t ZipEntry::LocalFileHeader::write(FILE* fp) |
|
{ |
|
uint8_t buf[kLFHLen]; |
|
|
|
ZipEntry::putLongLE(&buf[0x00], kSignature); |
|
ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); |
|
ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); |
|
ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); |
|
ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); |
|
ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); |
|
ZipEntry::putLongLE(&buf[0x0e], mCRC32); |
|
ZipEntry::putLongLE(&buf[0x12], mCompressedSize); |
|
ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); |
|
ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); |
|
ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); |
|
|
|
if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) |
|
return UNKNOWN_ERROR; |
|
|
|
/* write filename */ |
|
if (mFileNameLength != 0) { |
|
if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
/* write "extra field" */ |
|
if (mExtraFieldLength != 0) { |
|
if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
return NO_ERROR; |
|
} |
|
|
|
|
|
/* |
|
* Dump the contents of a LocalFileHeader object. |
|
*/ |
|
void ZipEntry::LocalFileHeader::dump(void) const |
|
{ |
|
ALOGD(" LocalFileHeader contents:\n"); |
|
ALOGD(" versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n", |
|
mVersionToExtract, mGPBitFlag, mCompressionMethod); |
|
ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n", |
|
mLastModFileTime, mLastModFileDate, mCRC32); |
|
ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n", |
|
mCompressedSize, mUncompressedSize); |
|
ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 "\n", |
|
mFileNameLength, mExtraFieldLength); |
|
if (mFileName != NULL) |
|
ALOGD(" filename: '%s'\n", mFileName); |
|
} |
|
|
|
|
|
/* |
|
* =========================================================================== |
|
* ZipEntry::CentralDirEntry |
|
* =========================================================================== |
|
*/ |
|
|
|
/* |
|
* Read the central dir entry that appears next in the file. |
|
* |
|
* On entry, "fp" should be positioned on the signature bytes for the |
|
* entry. On exit, "fp" will point at the signature word for the next |
|
* entry or for the EOCD. |
|
*/ |
|
status_t ZipEntry::CentralDirEntry::read(FILE* fp) |
|
{ |
|
status_t result = NO_ERROR; |
|
uint8_t buf[kCDELen]; |
|
|
|
/* no re-use */ |
|
assert(mFileName == NULL); |
|
assert(mExtraField == NULL); |
|
assert(mFileComment == NULL); |
|
|
|
if (fread(buf, 1, kCDELen, fp) != kCDELen) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
|
|
if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { |
|
ALOGD("Whoops: didn't find expected signature\n"); |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
|
|
mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); |
|
mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); |
|
mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); |
|
mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); |
|
mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); |
|
mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); |
|
mCRC32 = ZipEntry::getLongLE(&buf[0x10]); |
|
mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); |
|
mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); |
|
mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); |
|
mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); |
|
mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); |
|
mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); |
|
mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); |
|
mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); |
|
mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); |
|
|
|
// TODO: validate sizes and offsets |
|
|
|
/* grab filename */ |
|
if (mFileNameLength != 0) { |
|
mFileName = new uint8_t[mFileNameLength+1]; |
|
if (mFileName == NULL) { |
|
result = NO_MEMORY; |
|
goto bail; |
|
} |
|
if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
mFileName[mFileNameLength] = '\0'; |
|
} |
|
|
|
/* read "extra field" */ |
|
if (mExtraFieldLength != 0) { |
|
mExtraField = new uint8_t[mExtraFieldLength+1]; |
|
if (mExtraField == NULL) { |
|
result = NO_MEMORY; |
|
goto bail; |
|
} |
|
if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
mExtraField[mExtraFieldLength] = '\0'; |
|
} |
|
|
|
|
|
/* grab comment, if any */ |
|
if (mFileCommentLength != 0) { |
|
mFileComment = new uint8_t[mFileCommentLength+1]; |
|
if (mFileComment == NULL) { |
|
result = NO_MEMORY; |
|
goto bail; |
|
} |
|
if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) |
|
{ |
|
result = UNKNOWN_ERROR; |
|
goto bail; |
|
} |
|
mFileComment[mFileCommentLength] = '\0'; |
|
} |
|
|
|
bail: |
|
return result; |
|
} |
|
|
|
/* |
|
* Write a central dir entry. |
|
*/ |
|
status_t ZipEntry::CentralDirEntry::write(FILE* fp) |
|
{ |
|
uint8_t buf[kCDELen]; |
|
|
|
ZipEntry::putLongLE(&buf[0x00], kSignature); |
|
ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); |
|
ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); |
|
ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); |
|
ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); |
|
ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); |
|
ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); |
|
ZipEntry::putLongLE(&buf[0x10], mCRC32); |
|
ZipEntry::putLongLE(&buf[0x14], mCompressedSize); |
|
ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); |
|
ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); |
|
ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); |
|
ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); |
|
ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); |
|
ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); |
|
ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); |
|
ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); |
|
|
|
if (fwrite(buf, 1, kCDELen, fp) != kCDELen) |
|
return UNKNOWN_ERROR; |
|
|
|
/* write filename */ |
|
if (mFileNameLength != 0) { |
|
if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
/* write "extra field" */ |
|
if (mExtraFieldLength != 0) { |
|
if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
/* write comment */ |
|
if (mFileCommentLength != 0) { |
|
if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) |
|
return UNKNOWN_ERROR; |
|
} |
|
|
|
return NO_ERROR; |
|
} |
|
|
|
/* |
|
* Dump the contents of a CentralDirEntry object. |
|
*/ |
|
void ZipEntry::CentralDirEntry::dump(void) const |
|
{ |
|
ALOGD(" CentralDirEntry contents:\n"); |
|
ALOGD(" versMadeBy=%" PRIu16 " versToExt=%" PRIu16 " gpBits=0x%04" PRIx16 " compression=%" PRIu16 "\n", |
|
mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); |
|
ALOGD(" modTime=0x%04" PRIx16 " modDate=0x%04" PRIx16 " crc32=0x%08" PRIx32 "\n", |
|
mLastModFileTime, mLastModFileDate, mCRC32); |
|
ALOGD(" compressedSize=%" PRIu32 " uncompressedSize=%" PRIu32 "\n", |
|
mCompressedSize, mUncompressedSize); |
|
ALOGD(" filenameLen=%" PRIu16 " extraLen=%" PRIu16 " commentLen=%" PRIu16 "\n", |
|
mFileNameLength, mExtraFieldLength, mFileCommentLength); |
|
ALOGD(" diskNumStart=%" PRIu16 " intAttr=0x%04" PRIx16 " extAttr=0x%08" PRIx32 " relOffset=%" PRIu32 "\n", |
|
mDiskNumberStart, mInternalAttrs, mExternalAttrs, |
|
mLocalHeaderRelOffset); |
|
|
|
if (mFileName != NULL) |
|
ALOGD(" filename: '%s'\n", mFileName); |
|
if (mFileComment != NULL) |
|
ALOGD(" comment: '%s'\n", mFileComment); |
|
} |
|
|
|
|