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 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 //ALOGV("initFromCDE ---\n"); 46 47 /* read the CDE */ 48 result = mCDE.read(fp); 49 if (result != NO_ERROR) { 50 ALOGD("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 ALOGD("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 ALOGD("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 //ALOGD("+++ 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 ALOGW("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 mCDE = pEntry->mCDE; 148 // Check whether we got all the memory needed. 149 if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) || 150 (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) || 151 (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) { 152 return NO_MEMORY; 153 } 154 155 /* construct the LFH from the CDE */ 156 copyCDEtoLFH(); 157 158 /* 159 * The LFH "extra" field is independent of the CDE "extra", so we 160 * handle it here. 161 */ 162 assert(mLFH.mExtraField == NULL); 163 mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; 164 if (mLFH.mExtraFieldLength > 0) { 165 mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; 166 if (mLFH.mExtraField == NULL) 167 return NO_MEMORY; 168 memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, 169 mLFH.mExtraFieldLength+1); 170 } 171 172 return NO_ERROR; 173 } 174 175 /* 176 * Insert pad bytes in the LFH by tweaking the "extra" field. This will 177 * potentially confuse something that put "extra" data in here earlier, 178 * but I can't find an actual problem. 179 */ 180 status_t ZipEntry::addPadding(int padding) 181 { 182 if (padding <= 0) 183 return INVALID_OPERATION; 184 185 //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n", 186 // padding, mLFH.mExtraFieldLength, mCDE.mFileName); 187 188 if (mLFH.mExtraFieldLength > 0) { 189 /* extend existing field */ 190 unsigned char* newExtra; 191 192 newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; 193 if (newExtra == NULL) 194 return NO_MEMORY; 195 memset(newExtra + mLFH.mExtraFieldLength, 0, padding); 196 memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); 197 198 delete[] mLFH.mExtraField; 199 mLFH.mExtraField = newExtra; 200 mLFH.mExtraFieldLength += padding; 201 } else { 202 /* create new field */ 203 mLFH.mExtraField = new unsigned char[padding]; 204 memset(mLFH.mExtraField, 0, padding); 205 mLFH.mExtraFieldLength = padding; 206 } 207 208 return NO_ERROR; 209 } 210 211 /* 212 * Set the fields in the LFH equal to the corresponding fields in the CDE. 213 * 214 * This does not touch the LFH "extra" field. 215 */ 216 void ZipEntry::copyCDEtoLFH(void) 217 { 218 mLFH.mVersionToExtract = mCDE.mVersionToExtract; 219 mLFH.mGPBitFlag = mCDE.mGPBitFlag; 220 mLFH.mCompressionMethod = mCDE.mCompressionMethod; 221 mLFH.mLastModFileTime = mCDE.mLastModFileTime; 222 mLFH.mLastModFileDate = mCDE.mLastModFileDate; 223 mLFH.mCRC32 = mCDE.mCRC32; 224 mLFH.mCompressedSize = mCDE.mCompressedSize; 225 mLFH.mUncompressedSize = mCDE.mUncompressedSize; 226 mLFH.mFileNameLength = mCDE.mFileNameLength; 227 // the "extra field" is independent 228 229 delete[] mLFH.mFileName; 230 if (mLFH.mFileNameLength > 0) { 231 mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; 232 strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); 233 } else { 234 mLFH.mFileName = NULL; 235 } 236 } 237 238 /* 239 * Set some information about a file after we add it. 240 */ 241 void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, 242 int compressionMethod) 243 { 244 mCDE.mCompressionMethod = compressionMethod; 245 mCDE.mCRC32 = crc32; 246 mCDE.mCompressedSize = compLen; 247 mCDE.mUncompressedSize = uncompLen; 248 mCDE.mCompressionMethod = compressionMethod; 249 if (compressionMethod == kCompressDeflated) { 250 mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used 251 } 252 copyCDEtoLFH(); 253 } 254 255 /* 256 * See if the data in mCDE and mLFH match up. This is mostly useful for 257 * debugging these classes, but it can be used to identify damaged 258 * archives. 259 * 260 * Returns "false" if they differ. 261 */ 262 bool ZipEntry::compareHeaders(void) const 263 { 264 if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { 265 ALOGV("cmp: VersionToExtract\n"); 266 return false; 267 } 268 if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { 269 ALOGV("cmp: GPBitFlag\n"); 270 return false; 271 } 272 if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { 273 ALOGV("cmp: CompressionMethod\n"); 274 return false; 275 } 276 if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { 277 ALOGV("cmp: LastModFileTime\n"); 278 return false; 279 } 280 if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { 281 ALOGV("cmp: LastModFileDate\n"); 282 return false; 283 } 284 if (mCDE.mCRC32 != mLFH.mCRC32) { 285 ALOGV("cmp: CRC32\n"); 286 return false; 287 } 288 if (mCDE.mCompressedSize != mLFH.mCompressedSize) { 289 ALOGV("cmp: CompressedSize\n"); 290 return false; 291 } 292 if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { 293 ALOGV("cmp: UncompressedSize\n"); 294 return false; 295 } 296 if (mCDE.mFileNameLength != mLFH.mFileNameLength) { 297 ALOGV("cmp: FileNameLength\n"); 298 return false; 299 } 300 #if 0 // this seems to be used for padding, not real data 301 if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { 302 ALOGV("cmp: ExtraFieldLength\n"); 303 return false; 304 } 305 #endif 306 if (mCDE.mFileName != NULL) { 307 if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { 308 ALOGV("cmp: FileName\n"); 309 return false; 310 } 311 } 312 313 return true; 314 } 315 316 317 /* 318 * Convert the DOS date/time stamp into a UNIX time stamp. 319 */ 320 time_t ZipEntry::getModWhen(void) const 321 { 322 struct tm parts; 323 324 parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; 325 parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; 326 parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; 327 parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); 328 parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; 329 parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; 330 parts.tm_wday = parts.tm_yday = 0; 331 parts.tm_isdst = -1; // DST info "not available" 332 333 return mktime(&parts); 334 } 335 336 /* 337 * Set the CDE/LFH timestamp from UNIX time. 338 */ 339 void ZipEntry::setModWhen(time_t when) 340 { 341 #if !defined(_WIN32) 342 struct tm tmResult; 343 #endif 344 time_t even; 345 unsigned short zdate, ztime; 346 347 struct tm* ptm; 348 349 /* round up to an even number of seconds */ 350 even = (time_t)(((unsigned long)(when) + 1) & (~1)); 351 352 /* expand */ 353 #if !defined(_WIN32) 354 ptm = localtime_r(&even, &tmResult); 355 #else 356 ptm = localtime(&even); 357 #endif 358 359 int year; 360 year = ptm->tm_year; 361 if (year < 80) 362 year = 80; 363 364 zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; 365 ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; 366 367 mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; 368 mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; 369 } 370 371 372 /* 373 * =========================================================================== 374 * ZipEntry::LocalFileHeader 375 * =========================================================================== 376 */ 377 378 /* 379 * Read a local file header. 380 * 381 * On entry, "fp" points to the signature at the start of the header. 382 * On exit, "fp" points to the start of data. 383 */ 384 status_t ZipEntry::LocalFileHeader::read(FILE* fp) 385 { 386 status_t result = NO_ERROR; 387 unsigned char buf[kLFHLen]; 388 389 assert(mFileName == NULL); 390 assert(mExtraField == NULL); 391 392 if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { 393 result = UNKNOWN_ERROR; 394 goto bail; 395 } 396 397 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { 398 ALOGD("whoops: didn't find expected signature\n"); 399 result = UNKNOWN_ERROR; 400 goto bail; 401 } 402 403 mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); 404 mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); 405 mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); 406 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); 407 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); 408 mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); 409 mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); 410 mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); 411 mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); 412 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); 413 414 // TODO: validate sizes 415 416 /* grab filename */ 417 if (mFileNameLength != 0) { 418 mFileName = new unsigned char[mFileNameLength+1]; 419 if (mFileName == NULL) { 420 result = NO_MEMORY; 421 goto bail; 422 } 423 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { 424 result = UNKNOWN_ERROR; 425 goto bail; 426 } 427 mFileName[mFileNameLength] = '\0'; 428 } 429 430 /* grab extra field */ 431 if (mExtraFieldLength != 0) { 432 mExtraField = new unsigned char[mExtraFieldLength+1]; 433 if (mExtraField == NULL) { 434 result = NO_MEMORY; 435 goto bail; 436 } 437 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { 438 result = UNKNOWN_ERROR; 439 goto bail; 440 } 441 mExtraField[mExtraFieldLength] = '\0'; 442 } 443 444 bail: 445 return result; 446 } 447 448 /* 449 * Write a local file header. 450 */ 451 status_t ZipEntry::LocalFileHeader::write(FILE* fp) 452 { 453 unsigned char buf[kLFHLen]; 454 455 ZipEntry::putLongLE(&buf[0x00], kSignature); 456 ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); 457 ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); 458 ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); 459 ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); 460 ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); 461 ZipEntry::putLongLE(&buf[0x0e], mCRC32); 462 ZipEntry::putLongLE(&buf[0x12], mCompressedSize); 463 ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); 464 ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); 465 ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); 466 467 if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) 468 return UNKNOWN_ERROR; 469 470 /* write filename */ 471 if (mFileNameLength != 0) { 472 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) 473 return UNKNOWN_ERROR; 474 } 475 476 /* write "extra field" */ 477 if (mExtraFieldLength != 0) { 478 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) 479 return UNKNOWN_ERROR; 480 } 481 482 return NO_ERROR; 483 } 484 485 486 /* 487 * Dump the contents of a LocalFileHeader object. 488 */ 489 void ZipEntry::LocalFileHeader::dump(void) const 490 { 491 ALOGD(" LocalFileHeader contents:\n"); 492 ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", 493 mVersionToExtract, mGPBitFlag, mCompressionMethod); 494 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", 495 mLastModFileTime, mLastModFileDate, mCRC32); 496 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n", 497 mCompressedSize, mUncompressedSize); 498 ALOGD(" filenameLen=%u extraLen=%u\n", 499 mFileNameLength, mExtraFieldLength); 500 if (mFileName != NULL) 501 ALOGD(" filename: '%s'\n", mFileName); 502 } 503 504 505 /* 506 * =========================================================================== 507 * ZipEntry::CentralDirEntry 508 * =========================================================================== 509 */ 510 511 /* 512 * Read the central dir entry that appears next in the file. 513 * 514 * On entry, "fp" should be positioned on the signature bytes for the 515 * entry. On exit, "fp" will point at the signature word for the next 516 * entry or for the EOCD. 517 */ 518 status_t ZipEntry::CentralDirEntry::read(FILE* fp) 519 { 520 status_t result = NO_ERROR; 521 unsigned char buf[kCDELen]; 522 523 /* no re-use */ 524 assert(mFileName == NULL); 525 assert(mExtraField == NULL); 526 assert(mFileComment == NULL); 527 528 if (fread(buf, 1, kCDELen, fp) != kCDELen) { 529 result = UNKNOWN_ERROR; 530 goto bail; 531 } 532 533 if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { 534 ALOGD("Whoops: didn't find expected signature\n"); 535 result = UNKNOWN_ERROR; 536 goto bail; 537 } 538 539 mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); 540 mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); 541 mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); 542 mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); 543 mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); 544 mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); 545 mCRC32 = ZipEntry::getLongLE(&buf[0x10]); 546 mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); 547 mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); 548 mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); 549 mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); 550 mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); 551 mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); 552 mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); 553 mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); 554 mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); 555 556 // TODO: validate sizes and offsets 557 558 /* grab filename */ 559 if (mFileNameLength != 0) { 560 mFileName = new unsigned char[mFileNameLength+1]; 561 if (mFileName == NULL) { 562 result = NO_MEMORY; 563 goto bail; 564 } 565 if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { 566 result = UNKNOWN_ERROR; 567 goto bail; 568 } 569 mFileName[mFileNameLength] = '\0'; 570 } 571 572 /* read "extra field" */ 573 if (mExtraFieldLength != 0) { 574 mExtraField = new unsigned char[mExtraFieldLength+1]; 575 if (mExtraField == NULL) { 576 result = NO_MEMORY; 577 goto bail; 578 } 579 if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { 580 result = UNKNOWN_ERROR; 581 goto bail; 582 } 583 mExtraField[mExtraFieldLength] = '\0'; 584 } 585 586 587 /* grab comment, if any */ 588 if (mFileCommentLength != 0) { 589 mFileComment = new unsigned char[mFileCommentLength+1]; 590 if (mFileComment == NULL) { 591 result = NO_MEMORY; 592 goto bail; 593 } 594 if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) 595 { 596 result = UNKNOWN_ERROR; 597 goto bail; 598 } 599 mFileComment[mFileCommentLength] = '\0'; 600 } 601 602 bail: 603 return result; 604 } 605 606 /* 607 * Write a central dir entry. 608 */ 609 status_t ZipEntry::CentralDirEntry::write(FILE* fp) 610 { 611 unsigned char buf[kCDELen]; 612 613 ZipEntry::putLongLE(&buf[0x00], kSignature); 614 ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); 615 ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); 616 ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); 617 ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); 618 ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); 619 ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); 620 ZipEntry::putLongLE(&buf[0x10], mCRC32); 621 ZipEntry::putLongLE(&buf[0x14], mCompressedSize); 622 ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); 623 ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); 624 ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); 625 ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); 626 ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); 627 ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); 628 ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); 629 ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); 630 631 if (fwrite(buf, 1, kCDELen, fp) != kCDELen) 632 return UNKNOWN_ERROR; 633 634 /* write filename */ 635 if (mFileNameLength != 0) { 636 if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) 637 return UNKNOWN_ERROR; 638 } 639 640 /* write "extra field" */ 641 if (mExtraFieldLength != 0) { 642 if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) 643 return UNKNOWN_ERROR; 644 } 645 646 /* write comment */ 647 if (mFileCommentLength != 0) { 648 if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) 649 return UNKNOWN_ERROR; 650 } 651 652 return NO_ERROR; 653 } 654 655 /* 656 * Dump the contents of a CentralDirEntry object. 657 */ 658 void ZipEntry::CentralDirEntry::dump(void) const 659 { 660 ALOGD(" CentralDirEntry contents:\n"); 661 ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", 662 mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); 663 ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", 664 mLastModFileTime, mLastModFileDate, mCRC32); 665 ALOGD(" compressedSize=%lu uncompressedSize=%lu\n", 666 mCompressedSize, mUncompressedSize); 667 ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", 668 mFileNameLength, mExtraFieldLength, mFileCommentLength); 669 ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", 670 mDiskNumberStart, mInternalAttrs, mExternalAttrs, 671 mLocalHeaderRelOffset); 672 673 if (mFileName != NULL) 674 ALOGD(" filename: '%s'\n", mFileName); 675 if (mFileComment != NULL) 676 ALOGD(" comment: '%s'\n", mFileComment); 677 } 678 679 /* 680 * Copy-assignment operator for CentralDirEntry. 681 */ 682 ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) { 683 if (this == &src) { 684 return *this; 685 } 686 687 // Free up old data. 688 delete[] mFileName; 689 delete[] mExtraField; 690 delete[] mFileComment; 691 692 // Copy scalars. 693 mVersionMadeBy = src.mVersionMadeBy; 694 mVersionToExtract = src.mVersionToExtract; 695 mGPBitFlag = src.mGPBitFlag; 696 mCompressionMethod = src.mCompressionMethod; 697 mLastModFileTime = src.mLastModFileTime; 698 mLastModFileDate = src.mLastModFileDate; 699 mCRC32 = src.mCRC32; 700 mCompressedSize = src.mCompressedSize; 701 mUncompressedSize = src.mUncompressedSize; 702 mFileNameLength = src.mFileNameLength; 703 mExtraFieldLength = src.mExtraFieldLength; 704 mFileCommentLength = src.mFileCommentLength; 705 mDiskNumberStart = src.mDiskNumberStart; 706 mInternalAttrs = src.mInternalAttrs; 707 mExternalAttrs = src.mExternalAttrs; 708 mLocalHeaderRelOffset = src.mLocalHeaderRelOffset; 709 710 // Copy strings, if necessary. 711 if (mFileNameLength > 0) { 712 mFileName = new unsigned char[mFileNameLength + 1]; 713 if (mFileName != NULL) 714 strcpy((char*)mFileName, (char*)src.mFileName); 715 } else { 716 mFileName = NULL; 717 } 718 if (mFileCommentLength > 0) { 719 mFileComment = new unsigned char[mFileCommentLength + 1]; 720 if (mFileComment != NULL) 721 strcpy((char*)mFileComment, (char*)src.mFileComment); 722 } else { 723 mFileComment = NULL; 724 } 725 if (mExtraFieldLength > 0) { 726 /* we null-terminate this, though it may not be a string */ 727 mExtraField = new unsigned char[mExtraFieldLength + 1]; 728 if (mExtraField != NULL) 729 memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1); 730 } else { 731 mExtraField = NULL; 732 } 733 734 return *this; 735 } 736