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