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