| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2006 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | // | 
 | 18 | // Access to entries in a Zip archive. | 
 | 19 | // | 
 | 20 |  | 
 | 21 | #define LOG_TAG "zip" | 
 | 22 |  | 
| Mathias Agopian | 55e3d60 | 2009-06-05 14:56:35 -0700 | [diff] [blame] | 23 | #include "ZipEntry.h" | 
| Mathias Agopian | 8ae2335 | 2009-06-04 13:53:57 -0700 | [diff] [blame] | 24 | #include <utils/Log.h> | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 25 |  | 
 | 26 | #include <stdio.h> | 
 | 27 | #include <string.h> | 
 | 28 | #include <assert.h> | 
 | 29 |  | 
 | 30 | using namespace android; | 
 | 31 |  | 
 | 32 | /* | 
 | 33 |  * Initialize a new ZipEntry structure from a FILE* positioned at a | 
 | 34 |  * CentralDirectoryEntry. | 
 | 35 |  * | 
 | 36 |  * On exit, the file pointer will be at the start of the next CDE or | 
 | 37 |  * at the EOCD. | 
 | 38 |  */ | 
 | 39 | status_t ZipEntry::initFromCDE(FILE* fp) | 
 | 40 | { | 
 | 41 |     status_t result; | 
 | 42 |     long posn; | 
 | 43 |     bool hasDD; | 
 | 44 |  | 
 | 45 |     //LOGV("initFromCDE ---\n"); | 
 | 46 |  | 
 | 47 |     /* read the CDE */ | 
 | 48 |     result = mCDE.read(fp); | 
 | 49 |     if (result != NO_ERROR) { | 
 | 50 |         LOGD("mCDE.read failed\n"); | 
 | 51 |         return result; | 
 | 52 |     } | 
 | 53 |  | 
 | 54 |     //mCDE.dump(); | 
 | 55 |  | 
 | 56 |     /* using the info in the CDE, go load up the LFH */ | 
 | 57 |     posn = ftell(fp); | 
 | 58 |     if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { | 
 | 59 |         LOGD("local header seek failed (%ld)\n", | 
 | 60 |             mCDE.mLocalHeaderRelOffset); | 
 | 61 |         return UNKNOWN_ERROR; | 
 | 62 |     } | 
 | 63 |  | 
 | 64 |     result = mLFH.read(fp); | 
 | 65 |     if (result != NO_ERROR) { | 
 | 66 |         LOGD("mLFH.read failed\n"); | 
 | 67 |         return result; | 
 | 68 |     } | 
 | 69 |  | 
 | 70 |     if (fseek(fp, posn, SEEK_SET) != 0) | 
 | 71 |         return UNKNOWN_ERROR; | 
 | 72 |  | 
 | 73 |     //mLFH.dump(); | 
 | 74 |  | 
 | 75 |     /* | 
 | 76 |      * We *might* need to read the Data Descriptor at this point and | 
 | 77 |      * integrate it into the LFH.  If this bit is set, the CRC-32, | 
 | 78 |      * compressed size, and uncompressed size will be zero.  In practice | 
 | 79 |      * these seem to be rare. | 
 | 80 |      */ | 
 | 81 |     hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; | 
 | 82 |     if (hasDD) { | 
 | 83 |         // do something clever | 
 | 84 |         //LOGD("+++ has data descriptor\n"); | 
 | 85 |     } | 
 | 86 |  | 
 | 87 |     /* | 
 | 88 |      * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr" | 
 | 89 |      * flag is set, because the LFH is incomplete.  (Not a problem, since we | 
 | 90 |      * prefer the CDE values.) | 
 | 91 |      */ | 
 | 92 |     if (!hasDD && !compareHeaders()) { | 
 | 93 |         LOGW("WARNING: header mismatch\n"); | 
 | 94 |         // keep going? | 
 | 95 |     } | 
 | 96 |  | 
 | 97 |     /* | 
 | 98 |      * If the mVersionToExtract is greater than 20, we may have an | 
 | 99 |      * issue unpacking the record -- could be encrypted, compressed | 
 | 100 |      * with something we don't support, or use Zip64 extensions.  We | 
 | 101 |      * can defer worrying about that to when we're extracting data. | 
 | 102 |      */ | 
 | 103 |  | 
 | 104 |     return NO_ERROR; | 
 | 105 | } | 
 | 106 |  | 
 | 107 | /* | 
 | 108 |  * Initialize a new entry.  Pass in the file name and an optional comment. | 
 | 109 |  * | 
 | 110 |  * Initializes the CDE and the LFH. | 
 | 111 |  */ | 
 | 112 | void ZipEntry::initNew(const char* fileName, const char* comment) | 
 | 113 | { | 
 | 114 |     assert(fileName != NULL && *fileName != '\0');  // name required | 
 | 115 |  | 
 | 116 |     /* most fields are properly initialized by constructor */ | 
 | 117 |     mCDE.mVersionMadeBy = kDefaultMadeBy; | 
 | 118 |     mCDE.mVersionToExtract = kDefaultVersion; | 
 | 119 |     mCDE.mCompressionMethod = kCompressStored; | 
 | 120 |     mCDE.mFileNameLength = strlen(fileName); | 
 | 121 |     if (comment != NULL) | 
 | 122 |         mCDE.mFileCommentLength = strlen(comment); | 
 | 123 |     mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does | 
 | 124 |  | 
 | 125 |     if (mCDE.mFileNameLength > 0) { | 
 | 126 |         mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; | 
 | 127 |         strcpy((char*) mCDE.mFileName, fileName); | 
 | 128 |     } | 
 | 129 |     if (mCDE.mFileCommentLength > 0) { | 
 | 130 |         /* TODO: stop assuming null-terminated ASCII here? */ | 
 | 131 |         mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; | 
 | 132 |         strcpy((char*) mCDE.mFileComment, comment); | 
 | 133 |     } | 
 | 134 |  | 
 | 135 |     copyCDEtoLFH(); | 
 | 136 | } | 
 | 137 |  | 
 | 138 | /* | 
 | 139 |  * Initialize a new entry, starting with the ZipEntry from a different | 
 | 140 |  * archive. | 
 | 141 |  * | 
 | 142 |  * Initializes the CDE and the LFH. | 
 | 143 |  */ | 
 | 144 | status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, | 
 | 145 |     const ZipEntry* pEntry) | 
 | 146 | { | 
 | 147 |     /* | 
 | 148 |      * Copy everything in the CDE over, then fix up the hairy bits. | 
 | 149 |      */ | 
 | 150 |     memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); | 
 | 151 |  | 
 | 152 |     if (mCDE.mFileNameLength > 0) { | 
 | 153 |         mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; | 
 | 154 |         if (mCDE.mFileName == NULL) | 
 | 155 |             return NO_MEMORY; | 
 | 156 |         strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); | 
 | 157 |     } | 
 | 158 |     if (mCDE.mFileCommentLength > 0) { | 
 | 159 |         mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; | 
 | 160 |         if (mCDE.mFileComment == NULL) | 
 | 161 |             return NO_MEMORY; | 
 | 162 |         strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); | 
 | 163 |     } | 
 | 164 |     if (mCDE.mExtraFieldLength > 0) { | 
 | 165 |         /* we null-terminate this, though it may not be a string */ | 
 | 166 |         mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; | 
 | 167 |         if (mCDE.mExtraField == NULL) | 
 | 168 |             return NO_MEMORY; | 
 | 169 |         memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, | 
 | 170 |             mCDE.mExtraFieldLength+1); | 
 | 171 |     } | 
 | 172 |  | 
 | 173 |     /* construct the LFH from the CDE */ | 
 | 174 |     copyCDEtoLFH(); | 
 | 175 |  | 
 | 176 |     /* | 
 | 177 |      * The LFH "extra" field is independent of the CDE "extra", so we | 
 | 178 |      * handle it here. | 
 | 179 |      */ | 
 | 180 |     assert(mLFH.mExtraField == NULL); | 
 | 181 |     mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; | 
 | 182 |     if (mLFH.mExtraFieldLength > 0) { | 
 | 183 |         mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; | 
 | 184 |         if (mLFH.mExtraField == NULL) | 
 | 185 |             return NO_MEMORY; | 
 | 186 |         memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, | 
 | 187 |             mLFH.mExtraFieldLength+1); | 
 | 188 |     } | 
 | 189 |  | 
 | 190 |     return NO_ERROR; | 
 | 191 | } | 
 | 192 |  | 
 | 193 | /* | 
 | 194 |  * Insert pad bytes in the LFH by tweaking the "extra" field.  This will | 
 | 195 |  * potentially confuse something that put "extra" data in here earlier, | 
 | 196 |  * but I can't find an actual problem. | 
 | 197 |  */ | 
 | 198 | status_t ZipEntry::addPadding(int padding) | 
 | 199 | { | 
 | 200 |     if (padding <= 0) | 
 | 201 |         return INVALID_OPERATION; | 
 | 202 |  | 
 | 203 |     //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", | 
 | 204 |     //    padding, mLFH.mExtraFieldLength, mCDE.mFileName); | 
 | 205 |  | 
 | 206 |     if (mLFH.mExtraFieldLength > 0) { | 
 | 207 |         /* extend existing field */ | 
 | 208 |         unsigned char* newExtra; | 
 | 209 |  | 
 | 210 |         newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; | 
 | 211 |         if (newExtra == NULL) | 
 | 212 |             return NO_MEMORY; | 
 | 213 |         memset(newExtra + mLFH.mExtraFieldLength, 0, padding); | 
 | 214 |         memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); | 
 | 215 |  | 
 | 216 |         delete[] mLFH.mExtraField; | 
 | 217 |         mLFH.mExtraField = newExtra; | 
 | 218 |         mLFH.mExtraFieldLength += padding; | 
 | 219 |     } else { | 
 | 220 |         /* create new field */ | 
 | 221 |         mLFH.mExtraField = new unsigned char[padding]; | 
 | 222 |         memset(mLFH.mExtraField, 0, padding); | 
 | 223 |         mLFH.mExtraFieldLength = padding; | 
 | 224 |     } | 
 | 225 |  | 
 | 226 |     return NO_ERROR; | 
 | 227 | } | 
 | 228 |  | 
 | 229 | /* | 
 | 230 |  * Set the fields in the LFH equal to the corresponding fields in the CDE. | 
 | 231 |  * | 
 | 232 |  * This does not touch the LFH "extra" field. | 
 | 233 |  */ | 
 | 234 | void ZipEntry::copyCDEtoLFH(void) | 
 | 235 | { | 
 | 236 |     mLFH.mVersionToExtract  = mCDE.mVersionToExtract; | 
 | 237 |     mLFH.mGPBitFlag         = mCDE.mGPBitFlag; | 
 | 238 |     mLFH.mCompressionMethod = mCDE.mCompressionMethod; | 
 | 239 |     mLFH.mLastModFileTime   = mCDE.mLastModFileTime; | 
 | 240 |     mLFH.mLastModFileDate   = mCDE.mLastModFileDate; | 
 | 241 |     mLFH.mCRC32             = mCDE.mCRC32; | 
 | 242 |     mLFH.mCompressedSize    = mCDE.mCompressedSize; | 
 | 243 |     mLFH.mUncompressedSize  = mCDE.mUncompressedSize; | 
 | 244 |     mLFH.mFileNameLength    = mCDE.mFileNameLength; | 
 | 245 |     // the "extra field" is independent | 
 | 246 |  | 
 | 247 |     delete[] mLFH.mFileName; | 
 | 248 |     if (mLFH.mFileNameLength > 0) { | 
 | 249 |         mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; | 
 | 250 |         strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); | 
 | 251 |     } else { | 
 | 252 |         mLFH.mFileName = NULL; | 
 | 253 |     } | 
 | 254 | } | 
 | 255 |  | 
 | 256 | /* | 
 | 257 |  * Set some information about a file after we add it. | 
 | 258 |  */ | 
 | 259 | void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, | 
 | 260 |     int compressionMethod) | 
 | 261 | { | 
 | 262 |     mCDE.mCompressionMethod = compressionMethod; | 
 | 263 |     mCDE.mCRC32 = crc32; | 
 | 264 |     mCDE.mCompressedSize = compLen; | 
 | 265 |     mCDE.mUncompressedSize = uncompLen; | 
 | 266 |     mCDE.mCompressionMethod = compressionMethod; | 
 | 267 |     if (compressionMethod == kCompressDeflated) { | 
 | 268 |         mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used | 
 | 269 |     } | 
 | 270 |     copyCDEtoLFH(); | 
 | 271 | } | 
 | 272 |  | 
 | 273 | /* | 
 | 274 |  * See if the data in mCDE and mLFH match up.  This is mostly useful for | 
 | 275 |  * debugging these classes, but it can be used to identify damaged | 
 | 276 |  * archives. | 
 | 277 |  * | 
 | 278 |  * Returns "false" if they differ. | 
 | 279 |  */ | 
 | 280 | bool ZipEntry::compareHeaders(void) const | 
 | 281 | { | 
 | 282 |     if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { | 
 | 283 |         LOGV("cmp: VersionToExtract\n"); | 
 | 284 |         return false; | 
 | 285 |     } | 
 | 286 |     if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { | 
 | 287 |         LOGV("cmp: GPBitFlag\n"); | 
 | 288 |         return false; | 
 | 289 |     } | 
 | 290 |     if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { | 
 | 291 |         LOGV("cmp: CompressionMethod\n"); | 
 | 292 |         return false; | 
 | 293 |     } | 
 | 294 |     if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { | 
 | 295 |         LOGV("cmp: LastModFileTime\n"); | 
 | 296 |         return false; | 
 | 297 |     } | 
 | 298 |     if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { | 
 | 299 |         LOGV("cmp: LastModFileDate\n"); | 
 | 300 |         return false; | 
 | 301 |     } | 
 | 302 |     if (mCDE.mCRC32 != mLFH.mCRC32) { | 
 | 303 |         LOGV("cmp: CRC32\n"); | 
 | 304 |         return false; | 
 | 305 |     } | 
 | 306 |     if (mCDE.mCompressedSize != mLFH.mCompressedSize) { | 
 | 307 |         LOGV("cmp: CompressedSize\n"); | 
 | 308 |         return false; | 
 | 309 |     } | 
 | 310 |     if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { | 
 | 311 |         LOGV("cmp: UncompressedSize\n"); | 
 | 312 |         return false; | 
 | 313 |     } | 
 | 314 |     if (mCDE.mFileNameLength != mLFH.mFileNameLength) { | 
 | 315 |         LOGV("cmp: FileNameLength\n"); | 
 | 316 |         return false; | 
 | 317 |     } | 
 | 318 | #if 0       // this seems to be used for padding, not real data | 
 | 319 |     if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { | 
 | 320 |         LOGV("cmp: ExtraFieldLength\n"); | 
 | 321 |         return false; | 
 | 322 |     } | 
 | 323 | #endif | 
 | 324 |     if (mCDE.mFileName != NULL) { | 
 | 325 |         if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { | 
 | 326 |             LOGV("cmp: FileName\n"); | 
 | 327 |             return false; | 
 | 328 |         } | 
 | 329 |     } | 
 | 330 |  | 
 | 331 |     return true; | 
 | 332 | } | 
 | 333 |  | 
 | 334 |  | 
 | 335 | /* | 
 | 336 |  * Convert the DOS date/time stamp into a UNIX time stamp. | 
 | 337 |  */ | 
 | 338 | time_t ZipEntry::getModWhen(void) const | 
 | 339 | { | 
 | 340 |     struct tm parts; | 
 | 341 |  | 
 | 342 |     parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; | 
 | 343 |     parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; | 
 | 344 |     parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; | 
 | 345 |     parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); | 
 | 346 |     parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; | 
 | 347 |     parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; | 
 | 348 |     parts.tm_wday = parts.tm_yday = 0; | 
 | 349 |     parts.tm_isdst = -1;        // DST info "not available" | 
 | 350 |  | 
 | 351 |     return mktime(&parts); | 
 | 352 | } | 
 | 353 |  | 
 | 354 | /* | 
 | 355 |  * Set the CDE/LFH timestamp from UNIX time. | 
 | 356 |  */ | 
 | 357 | void ZipEntry::setModWhen(time_t when) | 
 | 358 | { | 
 | 359 | #ifdef HAVE_LOCALTIME_R | 
 | 360 |     struct tm tmResult; | 
 | 361 | #endif | 
 | 362 |     time_t even; | 
 | 363 |     unsigned short zdate, ztime; | 
 | 364 |  | 
 | 365 |     struct tm* ptm; | 
 | 366 |  | 
 | 367 |     /* round up to an even number of seconds */ | 
 | 368 |     even = (time_t)(((unsigned long)(when) + 1) & (~1)); | 
 | 369 |  | 
 | 370 |     /* expand */ | 
 | 371 | #ifdef HAVE_LOCALTIME_R | 
 | 372 |     ptm = localtime_r(&even, &tmResult); | 
 | 373 | #else | 
 | 374 |     ptm = localtime(&even); | 
 | 375 | #endif | 
 | 376 |  | 
 | 377 |     int year; | 
 | 378 |     year = ptm->tm_year; | 
 | 379 |     if (year < 80) | 
 | 380 |         year = 80; | 
 | 381 |  | 
 | 382 |     zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; | 
 | 383 |     ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; | 
 | 384 |  | 
 | 385 |     mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; | 
 | 386 |     mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; | 
 | 387 | } | 
 | 388 |  | 
 | 389 |  | 
 | 390 | /* | 
 | 391 |  * =========================================================================== | 
 | 392 |  *      ZipEntry::LocalFileHeader | 
 | 393 |  * =========================================================================== | 
 | 394 |  */ | 
 | 395 |  | 
 | 396 | /* | 
 | 397 |  * Read a local file header. | 
 | 398 |  * | 
 | 399 |  * On entry, "fp" points to the signature at the start of the header. | 
 | 400 |  * On exit, "fp" points to the start of data. | 
 | 401 |  */ | 
 | 402 | status_t ZipEntry::LocalFileHeader::read(FILE* fp) | 
 | 403 | { | 
 | 404 |     status_t result = NO_ERROR; | 
 | 405 |     unsigned char buf[kLFHLen]; | 
 | 406 |  | 
 | 407 |     assert(mFileName == NULL); | 
 | 408 |     assert(mExtraField == NULL); | 
 | 409 |  | 
 | 410 |     if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { | 
 | 411 |         result = UNKNOWN_ERROR; | 
 | 412 |         goto bail; | 
 | 413 |     } | 
 | 414 |  | 
 | 415 |     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { | 
 | 416 |         LOGD("whoops: didn't find expected signature\n"); | 
 | 417 |         result = UNKNOWN_ERROR; | 
 | 418 |         goto bail; | 
 | 419 |     } | 
 | 420 |  | 
 | 421 |     mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); | 
 | 422 |     mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); | 
 | 423 |     mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); | 
 | 424 |     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); | 
 | 425 |     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); | 
 | 426 |     mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); | 
 | 427 |     mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); | 
 | 428 |     mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); | 
 | 429 |     mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); | 
 | 430 |     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); | 
 | 431 |  | 
 | 432 |     // TODO: validate sizes | 
 | 433 |  | 
 | 434 |     /* grab filename */ | 
 | 435 |     if (mFileNameLength != 0) { | 
 | 436 |         mFileName = new unsigned char[mFileNameLength+1]; | 
 | 437 |         if (mFileName == NULL) { | 
 | 438 |             result = NO_MEMORY; | 
 | 439 |             goto bail; | 
 | 440 |         } | 
 | 441 |         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { | 
 | 442 |             result = UNKNOWN_ERROR; | 
 | 443 |             goto bail; | 
 | 444 |         } | 
 | 445 |         mFileName[mFileNameLength] = '\0'; | 
 | 446 |     } | 
 | 447 |  | 
 | 448 |     /* grab extra field */ | 
 | 449 |     if (mExtraFieldLength != 0) { | 
 | 450 |         mExtraField = new unsigned char[mExtraFieldLength+1]; | 
 | 451 |         if (mExtraField == NULL) { | 
 | 452 |             result = NO_MEMORY; | 
 | 453 |             goto bail; | 
 | 454 |         } | 
 | 455 |         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { | 
 | 456 |             result = UNKNOWN_ERROR; | 
 | 457 |             goto bail; | 
 | 458 |         } | 
 | 459 |         mExtraField[mExtraFieldLength] = '\0'; | 
 | 460 |     } | 
 | 461 |  | 
 | 462 | bail: | 
 | 463 |     return result; | 
 | 464 | } | 
 | 465 |  | 
 | 466 | /* | 
 | 467 |  * Write a local file header. | 
 | 468 |  */ | 
 | 469 | status_t ZipEntry::LocalFileHeader::write(FILE* fp) | 
 | 470 | { | 
 | 471 |     unsigned char buf[kLFHLen]; | 
 | 472 |  | 
 | 473 |     ZipEntry::putLongLE(&buf[0x00], kSignature); | 
 | 474 |     ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); | 
 | 475 |     ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); | 
 | 476 |     ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); | 
 | 477 |     ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); | 
 | 478 |     ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); | 
 | 479 |     ZipEntry::putLongLE(&buf[0x0e], mCRC32); | 
 | 480 |     ZipEntry::putLongLE(&buf[0x12], mCompressedSize); | 
 | 481 |     ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); | 
 | 482 |     ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); | 
 | 483 |     ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); | 
 | 484 |  | 
 | 485 |     if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) | 
 | 486 |         return UNKNOWN_ERROR; | 
 | 487 |  | 
 | 488 |     /* write filename */ | 
 | 489 |     if (mFileNameLength != 0) { | 
 | 490 |         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) | 
 | 491 |             return UNKNOWN_ERROR; | 
 | 492 |     } | 
 | 493 |  | 
 | 494 |     /* write "extra field" */ | 
 | 495 |     if (mExtraFieldLength != 0) { | 
 | 496 |         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) | 
 | 497 |             return UNKNOWN_ERROR; | 
 | 498 |     } | 
 | 499 |  | 
 | 500 |     return NO_ERROR; | 
 | 501 | } | 
 | 502 |  | 
 | 503 |  | 
 | 504 | /* | 
 | 505 |  * Dump the contents of a LocalFileHeader object. | 
 | 506 |  */ | 
 | 507 | void ZipEntry::LocalFileHeader::dump(void) const | 
 | 508 | { | 
 | 509 |     LOGD(" LocalFileHeader contents:\n"); | 
 | 510 |     LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n", | 
 | 511 |         mVersionToExtract, mGPBitFlag, mCompressionMethod); | 
 | 512 |     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", | 
 | 513 |         mLastModFileTime, mLastModFileDate, mCRC32); | 
 | 514 |     LOGD("  compressedSize=%lu uncompressedSize=%lu\n", | 
 | 515 |         mCompressedSize, mUncompressedSize); | 
 | 516 |     LOGD("  filenameLen=%u extraLen=%u\n", | 
 | 517 |         mFileNameLength, mExtraFieldLength); | 
 | 518 |     if (mFileName != NULL) | 
 | 519 |         LOGD("  filename: '%s'\n", mFileName); | 
 | 520 | } | 
 | 521 |  | 
 | 522 |  | 
 | 523 | /* | 
 | 524 |  * =========================================================================== | 
 | 525 |  *      ZipEntry::CentralDirEntry | 
 | 526 |  * =========================================================================== | 
 | 527 |  */ | 
 | 528 |  | 
 | 529 | /* | 
 | 530 |  * Read the central dir entry that appears next in the file. | 
 | 531 |  * | 
 | 532 |  * On entry, "fp" should be positioned on the signature bytes for the | 
 | 533 |  * entry.  On exit, "fp" will point at the signature word for the next | 
 | 534 |  * entry or for the EOCD. | 
 | 535 |  */ | 
 | 536 | status_t ZipEntry::CentralDirEntry::read(FILE* fp) | 
 | 537 | { | 
 | 538 |     status_t result = NO_ERROR; | 
 | 539 |     unsigned char buf[kCDELen]; | 
 | 540 |  | 
 | 541 |     /* no re-use */ | 
 | 542 |     assert(mFileName == NULL); | 
 | 543 |     assert(mExtraField == NULL); | 
 | 544 |     assert(mFileComment == NULL); | 
 | 545 |  | 
 | 546 |     if (fread(buf, 1, kCDELen, fp) != kCDELen) { | 
 | 547 |         result = UNKNOWN_ERROR; | 
 | 548 |         goto bail; | 
 | 549 |     } | 
 | 550 |  | 
 | 551 |     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { | 
 | 552 |         LOGD("Whoops: didn't find expected signature\n"); | 
 | 553 |         result = UNKNOWN_ERROR; | 
 | 554 |         goto bail; | 
 | 555 |     } | 
 | 556 |  | 
 | 557 |     mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); | 
 | 558 |     mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); | 
 | 559 |     mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); | 
 | 560 |     mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); | 
 | 561 |     mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); | 
 | 562 |     mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); | 
 | 563 |     mCRC32 = ZipEntry::getLongLE(&buf[0x10]); | 
 | 564 |     mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); | 
 | 565 |     mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); | 
 | 566 |     mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); | 
 | 567 |     mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); | 
 | 568 |     mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); | 
 | 569 |     mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); | 
 | 570 |     mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); | 
 | 571 |     mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); | 
 | 572 |     mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); | 
 | 573 |  | 
 | 574 |     // TODO: validate sizes and offsets | 
 | 575 |  | 
 | 576 |     /* grab filename */ | 
 | 577 |     if (mFileNameLength != 0) { | 
 | 578 |         mFileName = new unsigned char[mFileNameLength+1]; | 
 | 579 |         if (mFileName == NULL) { | 
 | 580 |             result = NO_MEMORY; | 
 | 581 |             goto bail; | 
 | 582 |         } | 
 | 583 |         if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { | 
 | 584 |             result = UNKNOWN_ERROR; | 
 | 585 |             goto bail; | 
 | 586 |         } | 
 | 587 |         mFileName[mFileNameLength] = '\0'; | 
 | 588 |     } | 
 | 589 |  | 
 | 590 |     /* read "extra field" */ | 
 | 591 |     if (mExtraFieldLength != 0) { | 
 | 592 |         mExtraField = new unsigned char[mExtraFieldLength+1]; | 
 | 593 |         if (mExtraField == NULL) { | 
 | 594 |             result = NO_MEMORY; | 
 | 595 |             goto bail; | 
 | 596 |         } | 
 | 597 |         if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { | 
 | 598 |             result = UNKNOWN_ERROR; | 
 | 599 |             goto bail; | 
 | 600 |         } | 
 | 601 |         mExtraField[mExtraFieldLength] = '\0'; | 
 | 602 |     } | 
 | 603 |  | 
 | 604 |  | 
 | 605 |     /* grab comment, if any */ | 
 | 606 |     if (mFileCommentLength != 0) { | 
 | 607 |         mFileComment = new unsigned char[mFileCommentLength+1]; | 
 | 608 |         if (mFileComment == NULL) { | 
 | 609 |             result = NO_MEMORY; | 
 | 610 |             goto bail; | 
 | 611 |         } | 
 | 612 |         if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) | 
 | 613 |         { | 
 | 614 |             result = UNKNOWN_ERROR; | 
 | 615 |             goto bail; | 
 | 616 |         } | 
 | 617 |         mFileComment[mFileCommentLength] = '\0'; | 
 | 618 |     } | 
 | 619 |  | 
 | 620 | bail: | 
 | 621 |     return result; | 
 | 622 | } | 
 | 623 |  | 
 | 624 | /* | 
 | 625 |  * Write a central dir entry. | 
 | 626 |  */ | 
 | 627 | status_t ZipEntry::CentralDirEntry::write(FILE* fp) | 
 | 628 | { | 
 | 629 |     unsigned char buf[kCDELen]; | 
 | 630 |  | 
 | 631 |     ZipEntry::putLongLE(&buf[0x00], kSignature); | 
 | 632 |     ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); | 
 | 633 |     ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); | 
 | 634 |     ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); | 
 | 635 |     ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); | 
 | 636 |     ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); | 
 | 637 |     ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); | 
 | 638 |     ZipEntry::putLongLE(&buf[0x10], mCRC32); | 
 | 639 |     ZipEntry::putLongLE(&buf[0x14], mCompressedSize); | 
 | 640 |     ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); | 
 | 641 |     ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); | 
 | 642 |     ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); | 
 | 643 |     ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); | 
 | 644 |     ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); | 
 | 645 |     ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); | 
 | 646 |     ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); | 
 | 647 |     ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); | 
 | 648 |  | 
 | 649 |     if (fwrite(buf, 1, kCDELen, fp) != kCDELen) | 
 | 650 |         return UNKNOWN_ERROR; | 
 | 651 |  | 
 | 652 |     /* write filename */ | 
 | 653 |     if (mFileNameLength != 0) { | 
 | 654 |         if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) | 
 | 655 |             return UNKNOWN_ERROR; | 
 | 656 |     } | 
 | 657 |  | 
 | 658 |     /* write "extra field" */ | 
 | 659 |     if (mExtraFieldLength != 0) { | 
 | 660 |         if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) | 
 | 661 |             return UNKNOWN_ERROR; | 
 | 662 |     } | 
 | 663 |  | 
 | 664 |     /* write comment */ | 
 | 665 |     if (mFileCommentLength != 0) { | 
 | 666 |         if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) | 
 | 667 |             return UNKNOWN_ERROR; | 
 | 668 |     } | 
 | 669 |  | 
 | 670 |     return NO_ERROR; | 
 | 671 | } | 
 | 672 |  | 
 | 673 | /* | 
 | 674 |  * Dump the contents of a CentralDirEntry object. | 
 | 675 |  */ | 
 | 676 | void ZipEntry::CentralDirEntry::dump(void) const | 
 | 677 | { | 
 | 678 |     LOGD(" CentralDirEntry contents:\n"); | 
 | 679 |     LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", | 
 | 680 |         mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); | 
 | 681 |     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", | 
 | 682 |         mLastModFileTime, mLastModFileDate, mCRC32); | 
 | 683 |     LOGD("  compressedSize=%lu uncompressedSize=%lu\n", | 
 | 684 |         mCompressedSize, mUncompressedSize); | 
 | 685 |     LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n", | 
 | 686 |         mFileNameLength, mExtraFieldLength, mFileCommentLength); | 
 | 687 |     LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", | 
 | 688 |         mDiskNumberStart, mInternalAttrs, mExternalAttrs, | 
 | 689 |         mLocalHeaderRelOffset); | 
 | 690 |  | 
 | 691 |     if (mFileName != NULL) | 
 | 692 |         LOGD("  filename: '%s'\n", mFileName); | 
 | 693 |     if (mFileComment != NULL) | 
 | 694 |         LOGD("  comment: '%s'\n", mFileComment); | 
 | 695 | } | 
 | 696 |  |