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 30using 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 */ 39status_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 */ 112void 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 */ 144status_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 */ 198status_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 */ 234void 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 */ 259void 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 */ 280bool 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 */ 338time_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 */ 357void 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 */ 402status_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 462bail: 463 return result; 464} 465 466/* 467 * Write a local file header. 468 */ 469status_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 */ 507void 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 */ 536status_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 620bail: 621 return result; 622} 623 624/* 625 * Write a central dir entry. 626 */ 627status_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 */ 676void 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 697