1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS cabinet manager 4 * FILE: tools/cabman/cabinet.cxx 5 * PURPOSE: Cabinet routines 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * Colin Finck <mail@colinfinck.de> 8 * NOTES: Define CAB_READ_ONLY for read only version 9 * REVISIONS: 10 * CSH 21/03-2001 Created 11 * CSH 15/08-2003 Made it portable 12 * CF 04/05-2007 Made it compatible with 64-bit operating systems 13 * TODO: 14 * - Checksum of datablocks should be calculated 15 * - EXTRACT.EXE complains if a disk is created manually 16 * - Folders that are created manually and span disks will result in a damaged cabinet 17 */ 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #if !defined(_WIN32) 22 # include <dirent.h> 23 # include <sys/stat.h> 24 # include <sys/types.h> 25 #endif 26 #include "cabinet.h" 27 #include "raw.h" 28 #include "mszip.h" 29 30 #ifndef CAB_READ_ONLY 31 32 #if 0 33 #if DBG 34 35 void DumpBuffer(void* Buffer, ULONG Size) 36 { 37 HANDLE FileHandle; 38 ULONG BytesWritten; 39 40 /* Create file, overwrite if it already exists */ 41 FileHandle = CreateFile("dump.bin", // Create this file 42 GENERIC_WRITE, // Open for writing 43 0, // No sharing 44 NULL, // No security 45 CREATE_ALWAYS, // Create or overwrite 46 FILE_ATTRIBUTE_NORMAL, // Normal file 47 NULL); // No attribute template 48 if (FileHandle == INVALID_HANDLE_VALUE) 49 { 50 DPRINT(MID_TRACE, ("ERROR OPENING '%u'.\n", (UINT)GetLastError())); 51 return; 52 } 53 54 if (!WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL)) 55 { 56 DPRINT(MID_TRACE, ("ERROR WRITING '%u'.\n", (UINT)GetLastError())); 57 } 58 59 CloseFile(FileHandle); 60 } 61 62 #endif /* DBG */ 63 #endif 64 65 #endif /* CAB_READ_ONLY */ 66 67 68 /* CCabinet */ 69 70 CCabinet::CCabinet() 71 /* 72 * FUNCTION: Default constructor 73 */ 74 { 75 *CabinetName = '\0'; 76 *CabinetPrev = '\0'; 77 *DiskPrev = '\0'; 78 *CabinetNext = '\0'; 79 *DiskNext = '\0'; 80 *DestPath = '\0'; 81 *CabinetReservedFile = '\0'; 82 83 FileOpen = false; 84 CabinetReservedFileBuffer = NULL; 85 CabinetReservedFileSize = 0; 86 87 FolderListHead = NULL; 88 FolderListTail = NULL; 89 FileListHead = NULL; 90 FileListTail = NULL; 91 CriteriaListHead = NULL; 92 CriteriaListTail = NULL; 93 94 Codec = NULL; 95 CodecId = -1; 96 CodecSelected = false; 97 98 OutputBuffer = NULL; 99 InputBuffer = NULL; 100 MaxDiskSize = 0; 101 BlockIsSplit = false; 102 ScratchFile = NULL; 103 104 FolderUncompSize = 0; 105 BytesLeftInBlock = 0; 106 ReuseBlock = false; 107 CurrentDataNode = NULL; 108 } 109 110 111 CCabinet::~CCabinet() 112 /* 113 * FUNCTION: Default destructor 114 */ 115 { 116 if (CabinetReservedFileBuffer != NULL) 117 { 118 free(CabinetReservedFileBuffer); 119 CabinetReservedFileBuffer = NULL; 120 CabinetReservedFileSize = 0; 121 } 122 123 if (CodecSelected) 124 delete Codec; 125 } 126 127 bool CCabinet::IsSeparator(char Char) 128 /* 129 * FUNCTION: Determines if a character is a separator 130 * ARGUMENTS: 131 * Char = Character to check 132 * RETURNS: 133 * Whether it is a separator 134 */ 135 { 136 if ((Char == '\\') || (Char == '/')) 137 return true; 138 else 139 return false; 140 } 141 142 char* CCabinet::ConvertPath(char* Path, bool Allocate) 143 /* 144 * FUNCTION: Replaces \ or / with the one used by the host environment 145 * ARGUMENTS: 146 * Path = Pointer to string with pathname 147 * Allocate = Specifies whether to allocate memory for the new 148 * string or to change the existing buffer 149 * RETURNS: 150 * Pointer to new path 151 */ 152 { 153 char *newpath; 154 int i; 155 156 if (Allocate) 157 newpath = strdup(Path); 158 else 159 newpath = Path; 160 161 i = 0; 162 while (Path[i] != 0) 163 { 164 #if defined(_WIN32) 165 if (Path[i] == '/') 166 newpath[i] = '\\'; 167 else 168 #else 169 if (Path[i] == '\\') 170 newpath[i] = '/'; 171 else 172 #endif 173 newpath[i] = Path[i]; 174 175 i++; 176 } 177 newpath[i] = 0; 178 179 return(newpath); 180 } 181 182 183 char* CCabinet::GetFileName(char* Path) 184 /* 185 * FUNCTION: Returns a pointer to file name 186 * ARGUMENTS: 187 * Path = Pointer to string with pathname 188 * RETURNS: 189 * Pointer to filename 190 */ 191 { 192 ULONG i, j; 193 194 j = i = (Path[0] ? (Path[1] == ':' ? 2 : 0) : 0); 195 196 while (Path [i++]) 197 if (IsSeparator(Path [i - 1])) 198 j = i; 199 200 return Path + j; 201 } 202 203 204 void CCabinet::RemoveFileName(char* Path) 205 /* 206 * FUNCTION: Removes a file name from a path 207 * ARGUMENTS: 208 * Path = Pointer to string with path 209 */ 210 { 211 char* FileName; 212 ULONG i; 213 214 i = (Path [0] ? (Path[1] == ':' ? 2 : 0) : 0); 215 FileName = GetFileName(Path + i); 216 217 if ((FileName != (Path + i)) && (IsSeparator(FileName [-1]))) 218 FileName--; 219 if ((FileName == (Path + i)) && (IsSeparator(FileName [0]))) 220 FileName++; 221 FileName[0] = 0; 222 } 223 224 225 bool CCabinet::NormalizePath(char* Path, 226 ULONG Length) 227 /* 228 * FUNCTION: Normalizes a path 229 * ARGUMENTS: 230 * Path = Pointer to string with pathname 231 * Length = Number of bytes in Path 232 * RETURNS: 233 * true if there was enough room in Path, or false 234 */ 235 { 236 ULONG n; 237 bool OK = true; 238 239 if ((n = (ULONG)strlen(Path)) && 240 (!IsSeparator(Path[n - 1])) && 241 (OK = ((n + 1) < Length))) 242 { 243 Path[n] = DIR_SEPARATOR_CHAR; 244 Path[n + 1] = 0; 245 } 246 return OK; 247 } 248 249 250 char* CCabinet::GetCabinetName() 251 /* 252 * FUNCTION: Returns pointer to cabinet file name 253 * RETURNS: 254 * Pointer to string with name of cabinet 255 */ 256 { 257 return CabinetName; 258 } 259 260 261 void CCabinet::SetCabinetName(char* FileName) 262 /* 263 * FUNCTION: Sets cabinet file name 264 * ARGUMENTS: 265 * FileName = Pointer to string with name of cabinet 266 */ 267 { 268 strcpy(CabinetName, FileName); 269 } 270 271 272 void CCabinet::SetDestinationPath(char* DestinationPath) 273 /* 274 * FUNCTION: Sets destination path 275 * ARGUMENTS: 276 * DestinationPath = Pointer to string with name of destination path 277 */ 278 { 279 strcpy(DestPath, DestinationPath); 280 ConvertPath(DestPath, false); 281 if (strlen(DestPath) > 0) 282 NormalizePath(DestPath, PATH_MAX); 283 } 284 285 ULONG CCabinet::AddSearchCriteria(char* SearchCriteria) 286 /* 287 * FUNCTION: Adds a criteria to the search criteria list 288 * ARGUMENTS: 289 * SearchCriteria = String with the search criteria to add 290 * RETURNS: 291 * Status of operation 292 */ 293 { 294 PSEARCH_CRITERIA Criteria; 295 296 // Add the criteria to the list of search criteria 297 Criteria = (PSEARCH_CRITERIA)malloc(sizeof(SEARCH_CRITERIA)); 298 if(!Criteria) 299 { 300 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 301 return CAB_STATUS_NOMEMORY; 302 } 303 304 Criteria->Prev = CriteriaListTail; 305 Criteria->Next = NULL; 306 307 if(CriteriaListTail) 308 CriteriaListTail->Next = Criteria; 309 else 310 CriteriaListHead = Criteria; 311 312 CriteriaListTail = Criteria; 313 314 // Set the actual criteria string 315 Criteria->Search = (char*)malloc(strlen(SearchCriteria) + 1); 316 if (!Criteria->Search) 317 { 318 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 319 return CAB_STATUS_NOMEMORY; 320 } 321 322 strcpy(Criteria->Search, SearchCriteria); 323 324 return CAB_STATUS_SUCCESS; 325 } 326 327 void CCabinet::DestroySearchCriteria() 328 /* 329 * FUNCTION: Destroys the list with the search criteria 330 */ 331 { 332 PSEARCH_CRITERIA Criteria; 333 PSEARCH_CRITERIA NextCriteria; 334 335 Criteria = CriteriaListHead; 336 337 while(Criteria) 338 { 339 NextCriteria = Criteria->Next; 340 341 free(Criteria->Search); 342 free(Criteria); 343 344 Criteria = NextCriteria; 345 } 346 347 CriteriaListHead = NULL; 348 CriteriaListTail = NULL; 349 } 350 351 bool CCabinet::HasSearchCriteria() 352 /* 353 * FUNCTION: Returns whether we have search criteria 354 * RETURNS: 355 * Whether we have search criteria or not. 356 */ 357 { 358 return (CriteriaListHead != NULL); 359 } 360 361 bool CCabinet::SetCompressionCodec(char* CodecName) 362 /* 363 * FUNCTION: Selects the codec to use for compression 364 * ARGUMENTS: 365 * CodecName = Pointer to a string with the name of the codec 366 */ 367 { 368 if( !strcasecmp(CodecName, "raw") ) 369 SelectCodec(CAB_CODEC_RAW); 370 else if( !strcasecmp(CodecName, "mszip") ) 371 SelectCodec(CAB_CODEC_MSZIP); 372 else 373 { 374 printf("ERROR: Invalid codec specified!\n"); 375 return false; 376 } 377 378 return true; 379 } 380 381 char* CCabinet::GetDestinationPath() 382 /* 383 * FUNCTION: Returns destination path 384 * RETURNS: 385 * Pointer to string with name of destination path 386 */ 387 { 388 return DestPath; 389 } 390 391 392 bool CCabinet::SetCabinetReservedFile(char* FileName) 393 /* 394 * FUNCTION: Sets cabinet reserved file 395 * ARGUMENTS: 396 * FileName = Pointer to string with name of cabinet reserved file 397 */ 398 { 399 FILE* FileHandle; 400 ULONG BytesRead; 401 char* ConvertedFileName; 402 403 ConvertedFileName = ConvertPath(FileName, true); 404 405 FileHandle = fopen(ConvertedFileName, "rb"); 406 free(ConvertedFileName); 407 if (FileHandle == NULL) 408 { 409 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); 410 return false; 411 } 412 413 CabinetReservedFileSize = GetSizeOfFile(FileHandle); 414 if (CabinetReservedFileSize == (ULONG)-1) 415 { 416 DPRINT(MIN_TRACE, ("Cannot read from cabinet reserved file.\n")); 417 fclose(FileHandle); 418 return false; 419 } 420 421 if (CabinetReservedFileSize == 0) 422 { 423 fclose(FileHandle); 424 return false; 425 } 426 427 CabinetReservedFileBuffer = malloc(CabinetReservedFileSize); 428 if (!CabinetReservedFileBuffer) 429 { 430 fclose(FileHandle); 431 return false; 432 } 433 434 BytesRead = fread(CabinetReservedFileBuffer, 1, CabinetReservedFileSize, FileHandle); 435 if( BytesRead != CabinetReservedFileSize ) 436 { 437 fclose(FileHandle); 438 return false; 439 } 440 441 fclose(FileHandle); 442 443 strcpy(CabinetReservedFile, FileName); 444 445 return true; 446 } 447 448 449 char* CCabinet::GetCabinetReservedFile() 450 /* 451 * FUNCTION: Returns cabionet reserved file 452 * RETURNS: 453 * Pointer to string with name of cabinet reserved file 454 */ 455 { 456 return CabinetReservedFile; 457 } 458 459 460 ULONG CCabinet::GetCurrentDiskNumber() 461 /* 462 * FUNCTION: Returns current disk number 463 * RETURNS: 464 * Current disk number 465 */ 466 { 467 return CurrentDiskNumber; 468 } 469 470 471 ULONG CCabinet::Open() 472 /* 473 * FUNCTION: Opens a cabinet file 474 * RETURNS: 475 * Status of operation 476 */ 477 { 478 PCFFOLDER_NODE FolderNode; 479 ULONG Status; 480 ULONG Index; 481 482 if (!FileOpen) 483 { 484 ULONG BytesRead; 485 ULONG Size; 486 487 OutputBuffer = malloc(CAB_BLOCKSIZE + 12); // This should be enough 488 if (!OutputBuffer) 489 return CAB_STATUS_NOMEMORY; 490 491 FileHandle = fopen(CabinetName, "rb"); 492 if (FileHandle == NULL) 493 { 494 DPRINT(MID_TRACE, ("Cannot open file.\n")); 495 return CAB_STATUS_CANNOT_OPEN; 496 } 497 498 FileOpen = true; 499 500 /* Load CAB header */ 501 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) 502 != CAB_STATUS_SUCCESS) 503 { 504 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 505 return CAB_STATUS_INVALID_CAB; 506 } 507 508 /* Check header */ 509 if ((BytesRead != sizeof(CFHEADER)) || 510 (CABHeader.Signature != CAB_SIGNATURE ) || 511 (CABHeader.Version != CAB_VERSION ) || 512 (CABHeader.FolderCount == 0 ) || 513 (CABHeader.FileCount == 0 ) || 514 (CABHeader.FileTableOffset < sizeof(CFHEADER))) 515 { 516 CloseCabinet(); 517 DPRINT(MID_TRACE, ("File has invalid header.\n")); 518 return CAB_STATUS_INVALID_CAB; 519 } 520 521 Size = 0; 522 523 /* Read/skip any reserved bytes */ 524 if (CABHeader.Flags & CAB_FLAG_RESERVE) 525 { 526 if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) 527 != CAB_STATUS_SUCCESS) 528 { 529 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 530 return CAB_STATUS_INVALID_CAB; 531 } 532 CabinetReserved = Size & 0xFFFF; 533 FolderReserved = (Size >> 16) & 0xFF; 534 DataReserved = (Size >> 24) & 0xFF; 535 536 if (fseek(FileHandle, CabinetReserved, SEEK_CUR) != 0) 537 { 538 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 539 return CAB_STATUS_FAILURE; 540 } 541 } 542 543 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 544 { 545 /* Read name of previous cabinet */ 546 Status = ReadString(CabinetPrev, 256); 547 if (Status != CAB_STATUS_SUCCESS) 548 return Status; 549 /* Read label of previous disk */ 550 Status = ReadString(DiskPrev, 256); 551 if (Status != CAB_STATUS_SUCCESS) 552 return Status; 553 } 554 else 555 { 556 strcpy(CabinetPrev, ""); 557 strcpy(DiskPrev, ""); 558 } 559 560 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 561 { 562 /* Read name of next cabinet */ 563 Status = ReadString(CabinetNext, 256); 564 if (Status != CAB_STATUS_SUCCESS) 565 return Status; 566 /* Read label of next disk */ 567 Status = ReadString(DiskNext, 256); 568 if (Status != CAB_STATUS_SUCCESS) 569 return Status; 570 } 571 else 572 { 573 strcpy(CabinetNext, ""); 574 strcpy(DiskNext, ""); 575 } 576 577 /* Read all folders */ 578 for (Index = 0; Index < CABHeader.FolderCount; Index++) 579 { 580 FolderNode = NewFolderNode(); 581 if (!FolderNode) 582 { 583 DPRINT(MIN_TRACE, ("Insufficient resources.\n")); 584 return CAB_STATUS_NOMEMORY; 585 } 586 587 if (Index == 0) 588 FolderNode->UncompOffset = FolderUncompSize; 589 590 FolderNode->Index = Index; 591 592 if ((Status = ReadBlock(&FolderNode->Folder, 593 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) 594 { 595 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 596 return CAB_STATUS_INVALID_CAB; 597 } 598 } 599 600 /* Read file entries */ 601 Status = ReadFileTable(); 602 if (Status != CAB_STATUS_SUCCESS) 603 { 604 DPRINT(MIN_TRACE, ("ReadFileTable() failed (%u).\n", (UINT)Status)); 605 return Status; 606 } 607 608 /* Read data blocks for all folders */ 609 FolderNode = FolderListHead; 610 while (FolderNode != NULL) 611 { 612 Status = ReadDataBlocks(FolderNode); 613 if (Status != CAB_STATUS_SUCCESS) 614 { 615 DPRINT(MIN_TRACE, ("ReadDataBlocks() failed (%u).\n", (UINT)Status)); 616 return Status; 617 } 618 FolderNode = FolderNode->Next; 619 } 620 } 621 return CAB_STATUS_SUCCESS; 622 } 623 624 625 void CCabinet::Close() 626 /* 627 * FUNCTION: Closes the cabinet file 628 */ 629 { 630 if (FileOpen) 631 { 632 fclose(FileHandle); 633 FileOpen = false; 634 } 635 } 636 637 638 ULONG CCabinet::FindFirst(PCAB_SEARCH Search) 639 /* 640 * FUNCTION: Finds the first file in the cabinet that matches a search criteria 641 * ARGUMENTS: 642 * Search = Pointer to search structure 643 * RETURNS: 644 * Status of operation 645 */ 646 { 647 RestartSearch = false; 648 Search->Next = FileListHead; 649 return FindNext(Search); 650 } 651 652 653 ULONG CCabinet::FindNext(PCAB_SEARCH Search) 654 /* 655 * FUNCTION: Finds next file in the cabinet that matches a search criteria 656 * ARGUMENTS: 657 * Search = Pointer to search structure 658 * RETURNS: 659 * Status of operation 660 */ 661 { 662 bool bFound = false; 663 PSEARCH_CRITERIA Criteria; 664 ULONG Status; 665 666 if (RestartSearch) 667 { 668 Search->Next = FileListHead; 669 670 /* Skip split files already extracted */ 671 while ((Search->Next) && 672 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) && 673 (Search->Next->File.FileOffset <= LastFileOffset)) 674 { 675 DPRINT(MAX_TRACE, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n", 676 Search->Next->FileName, (UINT)Search->Next->File.FileOffset, (UINT)LastFileOffset)); 677 Search->Next = Search->Next->Next; 678 } 679 680 RestartSearch = false; 681 } 682 683 /* Check each search criteria against each file */ 684 while(Search->Next) 685 { 686 // Some features (like displaying cabinets) don't require search criteria, so we can just break here. 687 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx". 688 if(!CriteriaListHead) 689 break; 690 691 Criteria = CriteriaListHead; 692 693 while(Criteria) 694 { 695 if(MatchFileNamePattern(Search->Next->FileName, Criteria->Search)) 696 { 697 bFound = true; 698 break; 699 } 700 701 Criteria = Criteria->Next; 702 } 703 704 if(bFound) 705 break; 706 707 Search->Next = Search->Next->Next; 708 } 709 710 if (!Search->Next) 711 { 712 if (strlen(DiskNext) > 0) 713 { 714 CloseCabinet(); 715 716 SetCabinetName(CabinetNext); 717 718 OnDiskChange(CabinetNext, DiskNext); 719 720 Status = Open(); 721 if (Status != CAB_STATUS_SUCCESS) 722 return Status; 723 724 Search->Next = FileListHead; 725 if (!Search->Next) 726 return CAB_STATUS_NOFILE; 727 } 728 else 729 return CAB_STATUS_NOFILE; 730 } 731 732 Search->File = &Search->Next->File; 733 Search->FileName = Search->Next->FileName; 734 Search->Next = Search->Next->Next; 735 return CAB_STATUS_SUCCESS; 736 } 737 738 739 ULONG CCabinet::ExtractFile(char* FileName) 740 /* 741 * FUNCTION: Extracts a file from the cabinet 742 * ARGUMENTS: 743 * FileName = Pointer to buffer with name of file 744 * RETURNS 745 * Status of operation 746 */ 747 { 748 ULONG Size; 749 ULONG Offset; 750 ULONG BytesRead; 751 ULONG BytesToRead; 752 ULONG BytesWritten; 753 ULONG BytesSkipped; 754 ULONG BytesToWrite; 755 ULONG TotalBytesRead; 756 ULONG CurrentOffset; 757 PUCHAR Buffer; 758 PUCHAR CurrentBuffer; 759 FILE* DestFile; 760 PCFFILE_NODE File; 761 CFDATA CFData; 762 ULONG Status; 763 bool Skip; 764 #if defined(_WIN32) 765 FILETIME FileTime; 766 #endif 767 CHAR DestName[PATH_MAX]; 768 CHAR TempName[PATH_MAX]; 769 770 Status = LocateFile(FileName, &File); 771 if (Status != CAB_STATUS_SUCCESS) 772 { 773 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status)); 774 return Status; 775 } 776 777 LastFileOffset = File->File.FileOffset; 778 779 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) 780 { 781 case CAB_COMP_NONE: 782 SelectCodec(CAB_CODEC_RAW); 783 break; 784 785 case CAB_COMP_MSZIP: 786 SelectCodec(CAB_CODEC_MSZIP); 787 break; 788 789 default: 790 return CAB_STATUS_UNSUPPCOMP; 791 } 792 793 DPRINT(MAX_TRACE, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n", 794 (UINT)File->File.FileOffset, 795 (UINT)File->File.FileSize, 796 (UINT)File->DataBlock->AbsoluteOffset, 797 (UINT)File->DataBlock->UncompOffset)); 798 799 strcpy(DestName, DestPath); 800 strcat(DestName, FileName); 801 802 /* Create destination file, fail if it already exists */ 803 DestFile = fopen(DestName, "rb"); 804 if (DestFile != NULL) 805 { 806 fclose(DestFile); 807 /* If file exists, ask to overwrite file */ 808 if (OnOverwrite(&File->File, FileName)) 809 { 810 DestFile = fopen(DestName, "w+b"); 811 if (DestFile == NULL) 812 return CAB_STATUS_CANNOT_CREATE; 813 } 814 else 815 return CAB_STATUS_FILE_EXISTS; 816 } 817 else 818 { 819 DestFile = fopen(DestName, "w+b"); 820 if (DestFile == NULL) 821 return CAB_STATUS_CANNOT_CREATE; 822 } 823 824 #if defined(_WIN32) 825 if (!DosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) 826 { 827 fclose(DestFile); 828 DPRINT(MIN_TRACE, ("DosDateTimeToFileTime() failed (%u).\n", (UINT)GetLastError())); 829 return CAB_STATUS_CANNOT_WRITE; 830 } 831 832 SetFileTime(DestFile, NULL, &FileTime, NULL); 833 #else 834 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n")); 835 #endif 836 837 SetAttributesOnFile(DestName, File->File.Attributes); 838 839 Buffer = (PUCHAR)malloc(CAB_BLOCKSIZE + 12); // This should be enough 840 if (!Buffer) 841 { 842 fclose(DestFile); 843 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 844 return CAB_STATUS_NOMEMORY; 845 } 846 847 /* Call OnExtract event handler */ 848 OnExtract(&File->File, FileName); 849 850 /* Search to start of file */ 851 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) 852 { 853 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 854 fclose(DestFile); 855 free(Buffer); 856 return CAB_STATUS_INVALID_CAB; 857 } 858 859 Size = File->File.FileSize; 860 Offset = File->File.FileOffset; 861 CurrentOffset = File->DataBlock->UncompOffset; 862 863 Skip = true; 864 865 ReuseBlock = (CurrentDataNode == File->DataBlock); 866 if (Size > 0) 867 { 868 do 869 { 870 DPRINT(MAX_TRACE, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n", 871 (UINT)File->DataBlock->UncompOffset, (UINT)ReuseBlock, (UINT)Offset, (UINT)Size, 872 (UINT)BytesLeftInBlock)); 873 874 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) 875 { 876 DPRINT(MAX_TRACE, ("Filling buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock)); 877 878 CurrentBuffer = Buffer; 879 TotalBytesRead = 0; 880 do 881 { 882 DPRINT(MAX_TRACE, ("Size (%u bytes).\n", (UINT)Size)); 883 884 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != 885 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) 886 { 887 fclose(DestFile); 888 free(Buffer); 889 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 890 return CAB_STATUS_INVALID_CAB; 891 } 892 893 DPRINT(MAX_TRACE, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n", 894 (UINT)CFData.Checksum, 895 CFData.CompSize, 896 CFData.UncompSize)); 897 898 ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12); 899 900 BytesToRead = CFData.CompSize; 901 902 DPRINT(MAX_TRACE, ("Read: (0x%lX,0x%lX).\n", 903 (unsigned long)CurrentBuffer, (unsigned long)Buffer)); 904 905 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) != 906 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) 907 { 908 fclose(DestFile); 909 free(Buffer); 910 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 911 return CAB_STATUS_INVALID_CAB; 912 } 913 914 /* FIXME: Does not work with files generated by makecab.exe */ 915 /* 916 if (CFData.Checksum != 0) 917 { 918 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0); 919 if (Checksum != CFData.Checksum) 920 { 921 CloseFile(DestFile); 922 free(Buffer); 923 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n", 924 Checksum, CFData.Checksum)); 925 return CAB_STATUS_INVALID_CAB; 926 } 927 } 928 */ 929 TotalBytesRead += BytesRead; 930 931 CurrentBuffer += BytesRead; 932 933 if (CFData.UncompSize == 0) 934 { 935 if (strlen(DiskNext) == 0) 936 { 937 fclose(DestFile); 938 free(Buffer); 939 return CAB_STATUS_NOFILE; 940 } 941 942 /* CloseCabinet() will destroy all file entries so in case 943 FileName refers to the FileName field of a CFFOLDER_NODE 944 structure, we have to save a copy of the filename */ 945 strcpy(TempName, FileName); 946 947 CloseCabinet(); 948 949 SetCabinetName(CabinetNext); 950 951 OnDiskChange(CabinetNext, DiskNext); 952 953 Status = Open(); 954 if (Status != CAB_STATUS_SUCCESS) 955 { 956 fclose(DestFile); 957 free(Buffer); 958 return Status; 959 } 960 961 /* The first data block of the file will not be 962 found as it is located in the previous file */ 963 Status = LocateFile(TempName, &File); 964 if (Status == CAB_STATUS_NOFILE) 965 { 966 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status)); 967 fclose(DestFile); 968 free(Buffer); 969 return Status; 970 } 971 972 /* The file is continued in the first data block in the folder */ 973 File->DataBlock = CurrentFolderNode->DataListHead; 974 975 /* Search to start of file */ 976 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) 977 { 978 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 979 fclose(DestFile); 980 free(Buffer); 981 return CAB_STATUS_INVALID_CAB; 982 } 983 984 DPRINT(MAX_TRACE, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n", 985 (UINT)File->File.FileOffset, 986 (UINT)File->File.FileSize, 987 (UINT)File->DataBlock->AbsoluteOffset, 988 (UINT)File->DataBlock->UncompOffset)); 989 990 CurrentDataNode = File->DataBlock; 991 ReuseBlock = true; 992 993 RestartSearch = true; 994 } 995 } while (CFData.UncompSize == 0); 996 997 DPRINT(MAX_TRACE, ("TotalBytesRead (%u).\n", (UINT)TotalBytesRead)); 998 999 Status = Codec->Uncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite); 1000 if (Status != CS_SUCCESS) 1001 { 1002 fclose(DestFile); 1003 free(Buffer); 1004 DPRINT(MID_TRACE, ("Cannot uncompress block.\n")); 1005 if (Status == CS_NOMEMORY) 1006 return CAB_STATUS_NOMEMORY; 1007 return CAB_STATUS_INVALID_CAB; 1008 } 1009 1010 if (BytesToWrite != CFData.UncompSize) 1011 { 1012 DPRINT(MID_TRACE, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n", 1013 (UINT)BytesToWrite, CFData.UncompSize)); 1014 fclose(DestFile); 1015 free(Buffer); 1016 return CAB_STATUS_INVALID_CAB; 1017 } 1018 1019 BytesLeftInBlock = BytesToWrite; 1020 } 1021 else 1022 { 1023 DPRINT(MAX_TRACE, ("Using same buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock)); 1024 1025 BytesToWrite = BytesLeftInBlock; 1026 1027 DPRINT(MAX_TRACE, ("Seeking to absolute offset 0x%X.\n", 1028 (UINT)(CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + CurrentDataNode->Data.CompSize))); 1029 1030 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != 1031 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) 1032 { 1033 fclose(DestFile); 1034 free(Buffer); 1035 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 1036 return CAB_STATUS_INVALID_CAB; 1037 } 1038 1039 DPRINT(MAX_TRACE, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n", 1040 CFData.CompSize, CFData.UncompSize)); 1041 1042 /* Go to next data block */ 1043 if (fseek(FileHandle, (off_t)CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + 1044 CurrentDataNode->Data.CompSize, SEEK_SET) != 0) 1045 { 1046 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 1047 fclose(DestFile); 1048 free(Buffer); 1049 return CAB_STATUS_INVALID_CAB; 1050 } 1051 1052 ReuseBlock = false; 1053 } 1054 1055 if (Skip) 1056 BytesSkipped = (Offset - CurrentOffset); 1057 else 1058 BytesSkipped = 0; 1059 1060 BytesToWrite -= BytesSkipped; 1061 1062 if (Size < BytesToWrite) 1063 BytesToWrite = Size; 1064 1065 DPRINT(MAX_TRACE, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n", 1066 (UINT)Offset, 1067 (UINT)CurrentOffset, 1068 (UINT)BytesToWrite, 1069 (UINT)BytesSkipped, (UINT)Skip, 1070 (UINT)Size)); 1071 1072 BytesWritten = BytesToWrite; 1073 if (fwrite((void*)((PUCHAR)OutputBuffer + BytesSkipped), 1074 BytesToWrite, 1, DestFile) < 1) 1075 { 1076 fclose(DestFile); 1077 free(Buffer); 1078 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 1079 1080 return CAB_STATUS_CANNOT_WRITE; 1081 } 1082 1083 Size -= BytesToWrite; 1084 1085 CurrentOffset += BytesToWrite; 1086 1087 /* Don't skip any more bytes */ 1088 Skip = false; 1089 } while (Size > 0); 1090 } 1091 1092 fclose(DestFile); 1093 1094 free(Buffer); 1095 1096 return CAB_STATUS_SUCCESS; 1097 } 1098 1099 bool CCabinet::IsCodecSelected() 1100 /* 1101 * FUNCTION: Returns the value of CodecSelected 1102 * RETURNS: 1103 * Whether a codec is selected 1104 */ 1105 { 1106 return CodecSelected; 1107 } 1108 1109 void CCabinet::SelectCodec(LONG Id) 1110 /* 1111 * FUNCTION: Selects codec engine to use 1112 * ARGUMENTS: 1113 * Id = Codec identifier 1114 */ 1115 { 1116 if (CodecSelected) 1117 { 1118 if (Id == CodecId) 1119 return; 1120 1121 CodecSelected = false; 1122 delete Codec; 1123 } 1124 1125 switch (Id) 1126 { 1127 case CAB_CODEC_RAW: 1128 Codec = new CRawCodec(); 1129 break; 1130 1131 case CAB_CODEC_MSZIP: 1132 Codec = new CMSZipCodec(); 1133 break; 1134 1135 default: 1136 return; 1137 } 1138 1139 CodecId = Id; 1140 CodecSelected = true; 1141 } 1142 1143 1144 #ifndef CAB_READ_ONLY 1145 1146 /* CAB write methods */ 1147 1148 ULONG CCabinet::NewCabinet() 1149 /* 1150 * FUNCTION: Creates a new cabinet 1151 * RETURNS: 1152 * Status of operation 1153 */ 1154 { 1155 ULONG Status; 1156 1157 CurrentDiskNumber = 0; 1158 1159 OutputBuffer = malloc(CAB_BLOCKSIZE + 12); // This should be enough 1160 InputBuffer = malloc(CAB_BLOCKSIZE + 12); // This should be enough 1161 if ((!OutputBuffer) || (!InputBuffer)) 1162 { 1163 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 1164 return CAB_STATUS_NOMEMORY; 1165 } 1166 CurrentIBuffer = InputBuffer; 1167 CurrentIBufferSize = 0; 1168 1169 CABHeader.Signature = CAB_SIGNATURE; 1170 CABHeader.Reserved1 = 0; // Not used 1171 CABHeader.CabinetSize = 0; // Not yet known 1172 CABHeader.Reserved2 = 0; // Not used 1173 CABHeader.Reserved3 = 0; // Not used 1174 CABHeader.Version = CAB_VERSION; 1175 CABHeader.FolderCount = 0; // Not yet known 1176 CABHeader.FileCount = 0; // Not yet known 1177 CABHeader.Flags = 0; // Not yet known 1178 // FIXME: Should be random 1179 CABHeader.SetID = 0x534F; 1180 CABHeader.CabinetNumber = 0; 1181 1182 1183 TotalFolderSize = 0; 1184 TotalFileSize = 0; 1185 1186 DiskSize = sizeof(CFHEADER); 1187 1188 InitCabinetHeader(); 1189 1190 // NextFolderNumber is 0-based 1191 NextFolderNumber = 0; 1192 1193 CurrentFolderNode = NULL; 1194 Status = NewFolder(); 1195 if (Status != CAB_STATUS_SUCCESS) 1196 return Status; 1197 1198 CurrentFolderNode->Folder.DataOffset = DiskSize - TotalHeaderSize; 1199 1200 ScratchFile = new CCFDATAStorage; 1201 if (!ScratchFile) 1202 { 1203 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 1204 return CAB_STATUS_NOMEMORY; 1205 } 1206 1207 Status = ScratchFile->Create(); 1208 1209 CreateNewFolder = false; 1210 1211 CreateNewDisk = false; 1212 1213 PrevCabinetNumber = 0; 1214 1215 return Status; 1216 } 1217 1218 1219 ULONG CCabinet::NewDisk() 1220 /* 1221 * FUNCTION: Forces a new disk to be created 1222 * RETURNS: 1223 * Status of operation 1224 */ 1225 { 1226 // NextFolderNumber is 0-based 1227 NextFolderNumber = 1; 1228 1229 CreateNewDisk = false; 1230 1231 DiskSize = sizeof(CFHEADER) + TotalFolderSize + TotalFileSize; 1232 1233 InitCabinetHeader(); 1234 1235 CurrentFolderNode->TotalFolderSize = 0; 1236 1237 CurrentFolderNode->Folder.DataBlockCount = 0; 1238 1239 return CAB_STATUS_SUCCESS; 1240 } 1241 1242 1243 ULONG CCabinet::NewFolder() 1244 /* 1245 * FUNCTION: Forces a new folder to be created 1246 * RETURNS: 1247 * Status of operation 1248 */ 1249 { 1250 DPRINT(MAX_TRACE, ("Creating new folder.\n")); 1251 1252 CurrentFolderNode = NewFolderNode(); 1253 if (!CurrentFolderNode) 1254 { 1255 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 1256 return CAB_STATUS_NOMEMORY; 1257 } 1258 1259 switch (CodecId) { 1260 case CAB_CODEC_RAW: 1261 CurrentFolderNode->Folder.CompressionType = CAB_COMP_NONE; 1262 break; 1263 1264 case CAB_CODEC_MSZIP: 1265 CurrentFolderNode->Folder.CompressionType = CAB_COMP_MSZIP; 1266 break; 1267 1268 default: 1269 return CAB_STATUS_UNSUPPCOMP; 1270 } 1271 1272 /* FIXME: This won't work if no files are added to the new folder */ 1273 1274 DiskSize += sizeof(CFFOLDER); 1275 1276 TotalFolderSize += sizeof(CFFOLDER); 1277 1278 NextFolderNumber++; 1279 1280 CABHeader.FolderCount++; 1281 1282 LastBlockStart = 0; 1283 1284 return CAB_STATUS_SUCCESS; 1285 } 1286 1287 1288 ULONG CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode) 1289 /* 1290 * FUNCTION: Writes a file to the scratch file 1291 * ARGUMENTS: 1292 * FileNode = Pointer to file node 1293 * RETURNS: 1294 * Status of operation 1295 */ 1296 { 1297 ULONG BytesToRead; 1298 ULONG BytesRead; 1299 ULONG Status; 1300 ULONG Size; 1301 1302 if (!ContinueFile) 1303 { 1304 /* Try to open file */ 1305 SourceFile = fopen(FileNode->FileName, "rb"); 1306 if (SourceFile == NULL) 1307 { 1308 DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName)); 1309 return CAB_STATUS_NOFILE; 1310 } 1311 1312 if (CreateNewFolder) 1313 { 1314 /* There is always a new folder after 1315 a split file is completely stored */ 1316 Status = NewFolder(); 1317 if (Status != CAB_STATUS_SUCCESS) 1318 return Status; 1319 CreateNewFolder = false; 1320 } 1321 1322 /* Call OnAdd event handler */ 1323 OnAdd(&FileNode->File, FileNode->FileName); 1324 1325 TotalBytesLeft = FileNode->File.FileSize; 1326 1327 FileNode->File.FileOffset = CurrentFolderNode->UncompOffset; 1328 CurrentFolderNode->UncompOffset += TotalBytesLeft; 1329 FileNode->File.FileControlID = (USHORT)(NextFolderNumber - 1); 1330 CurrentFolderNode->Commit = true; 1331 PrevCabinetNumber = CurrentDiskNumber; 1332 1333 Size = sizeof(CFFILE) + (ULONG)strlen(GetFileName(FileNode->FileName)) + 1; 1334 CABHeader.FileTableOffset += Size; 1335 TotalFileSize += Size; 1336 DiskSize += Size; 1337 } 1338 1339 FileNode->Commit = true; 1340 1341 if (TotalBytesLeft > 0) 1342 { 1343 do 1344 { 1345 if (TotalBytesLeft > (ULONG)CAB_BLOCKSIZE - CurrentIBufferSize) 1346 BytesToRead = CAB_BLOCKSIZE - CurrentIBufferSize; 1347 else 1348 BytesToRead = TotalBytesLeft; 1349 1350 if ( (BytesRead = fread(CurrentIBuffer, 1, BytesToRead, SourceFile)) != BytesToRead ) 1351 { 1352 DPRINT(MIN_TRACE, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n", 1353 (UINT)BytesToRead, (UINT)BytesRead, (UINT)CurrentIBufferSize)); 1354 return CAB_STATUS_INVALID_CAB; 1355 } 1356 1357 CurrentIBuffer = (unsigned char*)CurrentIBuffer + BytesRead; 1358 CurrentIBufferSize += (USHORT)BytesRead; 1359 1360 if (CurrentIBufferSize == CAB_BLOCKSIZE) 1361 { 1362 Status = WriteDataBlock(); 1363 if (Status != CAB_STATUS_SUCCESS) 1364 return Status; 1365 } 1366 TotalBytesLeft -= BytesRead; 1367 } while ((TotalBytesLeft > 0) && (!CreateNewDisk)); 1368 } 1369 1370 if (TotalBytesLeft == 0) 1371 { 1372 fclose(SourceFile); 1373 FileNode->Delete = true; 1374 1375 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER) 1376 { 1377 FileNode->File.FileControlID = CAB_FILE_CONTINUED; 1378 CurrentFolderNode->Delete = true; 1379 1380 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 1381 { 1382 Status = WriteDataBlock(); 1383 if (Status != CAB_STATUS_SUCCESS) 1384 return Status; 1385 } 1386 1387 CreateNewFolder = true; 1388 } 1389 } 1390 else 1391 { 1392 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER) 1393 FileNode->File.FileControlID = CAB_FILE_SPLIT; 1394 else 1395 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT; 1396 } 1397 1398 return CAB_STATUS_SUCCESS; 1399 } 1400 1401 1402 ULONG CCabinet::WriteDisk(ULONG MoreDisks) 1403 /* 1404 * FUNCTION: Forces the current disk to be written 1405 * ARGUMENTS: 1406 * MoreDisks = true if there is one or more disks after this disk 1407 * RETURNS: 1408 * Status of operation 1409 */ 1410 { 1411 PCFFILE_NODE FileNode; 1412 ULONG Status; 1413 1414 ContinueFile = false; 1415 FileNode = FileListHead; 1416 while (FileNode != NULL) 1417 { 1418 Status = WriteFileToScratchStorage(FileNode); 1419 if (Status != CAB_STATUS_SUCCESS) 1420 return Status; 1421 1422 if (CreateNewDisk) 1423 { 1424 /* A data block could span more than two 1425 disks if MaxDiskSize is very small */ 1426 while (CreateNewDisk) 1427 { 1428 DPRINT(MAX_TRACE, ("Creating new disk.\n")); 1429 CommitDisk(true); 1430 CloseDisk(); 1431 NewDisk(); 1432 1433 ContinueFile = true; 1434 CreateNewDisk = false; 1435 1436 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n", 1437 (UINT)CurrentIBufferSize, (UINT)CurrentOBufferSize)); 1438 1439 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 1440 { 1441 Status = WriteDataBlock(); 1442 if (Status != CAB_STATUS_SUCCESS) 1443 return Status; 1444 } 1445 } 1446 } 1447 else 1448 { 1449 ContinueFile = false; 1450 FileNode = FileNode->Next; 1451 } 1452 } 1453 1454 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 1455 { 1456 /* A data block could span more than two 1457 disks if MaxDiskSize is very small */ 1458 1459 ASSERT(CreateNewDisk == false); 1460 1461 do 1462 { 1463 if (CreateNewDisk) 1464 { 1465 DPRINT(MID_TRACE, ("Creating new disk 2.\n")); 1466 CommitDisk(true); 1467 CloseDisk(); 1468 NewDisk(); 1469 CreateNewDisk = false; 1470 1471 ASSERT(FileNode == FileListHead); 1472 } 1473 1474 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 1475 { 1476 Status = WriteDataBlock(); 1477 if (Status != CAB_STATUS_SUCCESS) 1478 return Status; 1479 } 1480 } while (CreateNewDisk); 1481 } 1482 CommitDisk(MoreDisks); 1483 1484 return CAB_STATUS_SUCCESS; 1485 } 1486 1487 1488 ULONG CCabinet::CommitDisk(ULONG MoreDisks) 1489 /* 1490 * FUNCTION: Commits the current disk 1491 * ARGUMENTS: 1492 * MoreDisks = true if there is one or more disks after this disk 1493 * RETURNS: 1494 * Status of operation 1495 */ 1496 { 1497 PCFFOLDER_NODE FolderNode; 1498 ULONG Status; 1499 1500 OnCabinetName(CurrentDiskNumber, CabinetName); 1501 1502 /* Create file, fail if it already exists */ 1503 FileHandle = fopen(CabinetName, "rb"); 1504 if (FileHandle != NULL) 1505 { 1506 fclose(FileHandle); 1507 /* If file exists, ask to overwrite file */ 1508 if (OnOverwrite(NULL, CabinetName)) 1509 { 1510 FileHandle = fopen(CabinetName, "w+b"); 1511 if (FileHandle == NULL) 1512 return CAB_STATUS_CANNOT_CREATE; 1513 } 1514 else 1515 return CAB_STATUS_FILE_EXISTS; 1516 1517 } 1518 else 1519 { 1520 FileHandle = fopen(CabinetName, "w+b"); 1521 if (FileHandle == NULL) 1522 return CAB_STATUS_CANNOT_CREATE; 1523 } 1524 1525 WriteCabinetHeader(MoreDisks != 0); 1526 1527 Status = WriteFolderEntries(); 1528 if (Status != CAB_STATUS_SUCCESS) 1529 return Status; 1530 1531 /* Write file entries */ 1532 WriteFileEntries(); 1533 1534 /* Write data blocks */ 1535 FolderNode = FolderListHead; 1536 while (FolderNode != NULL) 1537 { 1538 if (FolderNode->Commit) 1539 { 1540 Status = CommitDataBlocks(FolderNode); 1541 if (Status != CAB_STATUS_SUCCESS) 1542 return Status; 1543 /* Remove data blocks for folder */ 1544 DestroyDataNodes(FolderNode); 1545 } 1546 FolderNode = FolderNode->Next; 1547 } 1548 1549 fclose(FileHandle); 1550 1551 ScratchFile->Truncate(); 1552 1553 return CAB_STATUS_SUCCESS; 1554 } 1555 1556 1557 ULONG CCabinet::CloseDisk() 1558 /* 1559 * FUNCTION: Closes the current disk 1560 * RETURNS: 1561 * Status of operation 1562 */ 1563 { 1564 DestroyDeletedFileNodes(); 1565 1566 /* Destroy folder nodes that are completely stored */ 1567 DestroyDeletedFolderNodes(); 1568 1569 CurrentDiskNumber++; 1570 1571 return CAB_STATUS_SUCCESS; 1572 } 1573 1574 1575 ULONG CCabinet::CloseCabinet() 1576 /* 1577 * FUNCTION: Closes the current cabinet 1578 * RETURNS: 1579 * Status of operation 1580 */ 1581 { 1582 ULONG Status; 1583 1584 DestroyFileNodes(); 1585 1586 DestroyFolderNodes(); 1587 1588 if (InputBuffer) 1589 { 1590 free(InputBuffer); 1591 InputBuffer = NULL; 1592 } 1593 1594 if (OutputBuffer) 1595 { 1596 free(OutputBuffer); 1597 OutputBuffer = NULL; 1598 } 1599 1600 Close(); 1601 1602 if (ScratchFile) 1603 { 1604 Status = ScratchFile->Destroy(); 1605 delete ScratchFile; 1606 return Status; 1607 } 1608 1609 return CAB_STATUS_SUCCESS; 1610 } 1611 1612 1613 ULONG CCabinet::AddFile(char* FileName) 1614 /* 1615 * FUNCTION: Adds a file to the current disk 1616 * ARGUMENTS: 1617 * FileName = Pointer to string with file name (full path) 1618 * RETURNS: 1619 * Status of operation 1620 */ 1621 { 1622 FILE* SrcFile; 1623 PCFFILE_NODE FileNode; 1624 char* NewFileName; 1625 1626 NewFileName = (char*)malloc(strlen(FileName) + 1); 1627 if (!NewFileName) 1628 { 1629 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 1630 return CAB_STATUS_NOMEMORY; 1631 } 1632 strcpy(NewFileName, FileName); 1633 ConvertPath(NewFileName, false); 1634 1635 /* Try to open file */ 1636 SrcFile = fopen(NewFileName, "rb"); 1637 if (SrcFile == NULL) 1638 { 1639 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName)); 1640 free(NewFileName); 1641 return CAB_STATUS_CANNOT_OPEN; 1642 } 1643 1644 FileNode = NewFileNode(); 1645 if (!FileNode) 1646 { 1647 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 1648 free(NewFileName); 1649 fclose(SrcFile); 1650 return CAB_STATUS_NOMEMORY; 1651 } 1652 1653 FileNode->FolderNode = CurrentFolderNode; 1654 FileNode->FileName = NewFileName; 1655 1656 /* FIXME: Check for and handle large files (>= 2GB) */ 1657 FileNode->File.FileSize = GetSizeOfFile(SrcFile); 1658 if (FileNode->File.FileSize == (ULONG)-1) 1659 { 1660 DPRINT(MIN_TRACE, ("Cannot read from file.\n")); 1661 fclose(SrcFile); 1662 return CAB_STATUS_CANNOT_READ; 1663 } 1664 1665 if (GetFileTimes(SrcFile, FileNode) != CAB_STATUS_SUCCESS) 1666 { 1667 DPRINT(MIN_TRACE, ("Cannot read file times.\n")); 1668 fclose(SrcFile); 1669 return CAB_STATUS_CANNOT_READ; 1670 } 1671 1672 if (GetAttributesOnFile(FileNode) != CAB_STATUS_SUCCESS) 1673 { 1674 DPRINT(MIN_TRACE, ("Cannot read file attributes.\n")); 1675 fclose(SrcFile); 1676 return CAB_STATUS_CANNOT_READ; 1677 } 1678 1679 fclose(SrcFile); 1680 1681 return CAB_STATUS_SUCCESS; 1682 } 1683 1684 bool CCabinet::CreateSimpleCabinet() 1685 /* 1686 * FUNCTION: Create a simple cabinet based on the files in the criteria list 1687 */ 1688 { 1689 bool bRet = false; 1690 char* pszFile; 1691 char szFilePath[PATH_MAX]; 1692 char szFile[PATH_MAX]; 1693 PSEARCH_CRITERIA Criteria; 1694 ULONG Status; 1695 1696 #if defined(_WIN32) 1697 HANDLE hFind; 1698 WIN32_FIND_DATA FindFileData; 1699 #else 1700 DIR* dirp; 1701 struct dirent* dp; 1702 struct stat stbuf; 1703 #endif 1704 1705 // Initialize a new cabinet 1706 Status = NewCabinet(); 1707 if (Status != CAB_STATUS_SUCCESS) 1708 { 1709 DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status)); 1710 goto cleanup2; 1711 } 1712 1713 // Add each file in the criteria list 1714 Criteria = CriteriaListHead; 1715 1716 while(Criteria) 1717 { 1718 // Store the file path with a trailing slash in szFilePath 1719 ConvertPath(Criteria->Search, false); 1720 pszFile = strrchr(Criteria->Search, DIR_SEPARATOR_CHAR); 1721 1722 if(pszFile) 1723 { 1724 // Set the pointer to the start of the file name, not the slash 1725 pszFile++; 1726 1727 strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search); 1728 szFilePath[pszFile - Criteria->Search] = 0; 1729 } 1730 else 1731 { 1732 pszFile = Criteria->Search; 1733 1734 #if defined(_WIN32) 1735 szFilePath[0] = 0; 1736 #else 1737 // needed for opendir() 1738 strcpy(szFilePath, "./"); 1739 #endif 1740 } 1741 1742 #if defined(_WIN32) 1743 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern 1744 hFind = FindFirstFile(Criteria->Search, &FindFileData); 1745 1746 // Don't stop if a search criteria is not found 1747 if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND) 1748 { 1749 DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError())); 1750 goto cleanup; 1751 } 1752 1753 do 1754 { 1755 if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 1756 { 1757 strcpy(szFile, szFilePath); 1758 strcat(szFile, FindFileData.cFileName); 1759 1760 Status = AddFile(szFile); 1761 1762 if(Status != CAB_STATUS_SUCCESS) 1763 { 1764 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); 1765 FindClose(hFind); 1766 goto cleanup; 1767 } 1768 } 1769 } 1770 while(FindNextFile(hFind, &FindFileData)); 1771 1772 FindClose(hFind); 1773 #else 1774 // Unix: Use opendir/readdir to loop through all entries, stat to check if it's a file and MatchFileNamePattern to match the file against the pattern 1775 dirp = opendir(szFilePath); 1776 1777 if(dirp) 1778 { 1779 while( (dp = readdir(dirp)) ) 1780 { 1781 strcpy(szFile, szFilePath); 1782 strcat(szFile, dp->d_name); 1783 1784 if(stat(szFile, &stbuf) == 0) 1785 { 1786 if(stbuf.st_mode != S_IFDIR) 1787 { 1788 if(MatchFileNamePattern(dp->d_name, pszFile)) 1789 { 1790 Status = AddFile(szFile); 1791 1792 if(Status != CAB_STATUS_SUCCESS) 1793 { 1794 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); 1795 goto cleanup; 1796 } 1797 } 1798 } 1799 } 1800 else 1801 { 1802 DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno)); 1803 goto cleanup; 1804 } 1805 } 1806 1807 closedir(dirp); 1808 } 1809 #endif 1810 1811 Criteria = Criteria->Next; 1812 } 1813 1814 Status = WriteDisk(false); 1815 if (Status == CAB_STATUS_SUCCESS) 1816 Status = CloseDisk(); 1817 if (Status != CAB_STATUS_SUCCESS) 1818 { 1819 DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status)); 1820 goto cleanup; 1821 } 1822 1823 cleanup: 1824 CloseCabinet(); 1825 bRet = true; 1826 1827 cleanup2: 1828 DestroySearchCriteria(); 1829 return bRet; 1830 } 1831 1832 void CCabinet::SetMaxDiskSize(ULONG Size) 1833 /* 1834 * FUNCTION: Sets the maximum size of the current disk 1835 * ARGUMENTS: 1836 * Size = Maximum size of current disk (0 means no maximum size) 1837 */ 1838 { 1839 MaxDiskSize = Size; 1840 } 1841 1842 #endif /* CAB_READ_ONLY */ 1843 1844 1845 /* Default event handlers */ 1846 1847 bool CCabinet::OnOverwrite(PCFFILE File, 1848 char* FileName) 1849 /* 1850 * FUNCTION: Called when extracting a file and it already exists 1851 * ARGUMENTS: 1852 * File = Pointer to CFFILE for file being extracted 1853 * FileName = Pointer to buffer with name of file (full path) 1854 * RETURNS 1855 * true if the file should be overwritten, false if not 1856 */ 1857 { 1858 return false; 1859 } 1860 1861 1862 void CCabinet::OnExtract(PCFFILE File, 1863 char* FileName) 1864 /* 1865 * FUNCTION: Called just before extracting a file 1866 * ARGUMENTS: 1867 * File = Pointer to CFFILE for file being extracted 1868 * FileName = Pointer to buffer with name of file (full path) 1869 */ 1870 { 1871 } 1872 1873 1874 void CCabinet::OnDiskChange(char* CabinetName, 1875 char* DiskLabel) 1876 /* 1877 * FUNCTION: Called when a new disk is to be processed 1878 * ARGUMENTS: 1879 * CabinetName = Pointer to buffer with name of cabinet 1880 * DiskLabel = Pointer to buffer with label of disk 1881 */ 1882 { 1883 } 1884 1885 1886 #ifndef CAB_READ_ONLY 1887 1888 void CCabinet::OnAdd(PCFFILE File, 1889 char* FileName) 1890 /* 1891 * FUNCTION: Called just before adding a file to a cabinet 1892 * ARGUMENTS: 1893 * File = Pointer to CFFILE for file being added 1894 * FileName = Pointer to buffer with name of file (full path) 1895 */ 1896 { 1897 } 1898 1899 1900 bool CCabinet::OnDiskLabel(ULONG Number, char* Label) 1901 /* 1902 * FUNCTION: Called when a disk needs a label 1903 * ARGUMENTS: 1904 * Number = Cabinet number that needs a label 1905 * Label = Pointer to buffer to place label of disk 1906 * RETURNS: 1907 * true if a disk label was returned, false if not 1908 */ 1909 { 1910 return false; 1911 } 1912 1913 1914 bool CCabinet::OnCabinetName(ULONG Number, char* Name) 1915 /* 1916 * FUNCTION: Called when a cabinet needs a name 1917 * ARGUMENTS: 1918 * Number = Disk number that needs a name 1919 * Name = Pointer to buffer to place name of cabinet 1920 * RETURNS: 1921 * true if a cabinet name was returned, false if not 1922 */ 1923 { 1924 return false; 1925 } 1926 1927 #endif /* CAB_READ_ONLY */ 1928 1929 PCFFOLDER_NODE CCabinet::LocateFolderNode(ULONG Index) 1930 /* 1931 * FUNCTION: Locates a folder node 1932 * ARGUMENTS: 1933 * Index = Folder index 1934 * RETURNS: 1935 * Pointer to folder node or NULL if the folder node was not found 1936 */ 1937 { 1938 PCFFOLDER_NODE Node; 1939 1940 switch (Index) 1941 { 1942 case CAB_FILE_SPLIT: 1943 return FolderListTail; 1944 1945 case CAB_FILE_CONTINUED: 1946 case CAB_FILE_PREV_NEXT: 1947 return FolderListHead; 1948 } 1949 1950 Node = FolderListHead; 1951 while (Node != NULL) 1952 { 1953 if (Node->Index == Index) 1954 return Node; 1955 Node = Node->Next; 1956 } 1957 return NULL; 1958 } 1959 1960 1961 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File) 1962 /* 1963 * FUNCTION: Returns the absolute offset of a file 1964 * ARGUMENTS: 1965 * File = Pointer to CFFILE_NODE structure for file 1966 * RETURNS: 1967 * Status of operation 1968 */ 1969 { 1970 PCFDATA_NODE Node; 1971 1972 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n", 1973 File->FileName, 1974 (UINT)File->File.FileOffset, 1975 (UINT)File->File.FileSize)); 1976 1977 Node = CurrentFolderNode->DataListHead; 1978 while (Node != NULL) 1979 { 1980 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n", 1981 (UINT)Node->UncompOffset, 1982 (UINT)(Node->UncompOffset + Node->Data.UncompSize), 1983 (UINT)Node->Data.UncompSize)); 1984 1985 /* Node->Data.UncompSize will be 0 if the block is split 1986 (ie. it is the last block in this cabinet) */ 1987 if ((Node->Data.UncompSize == 0) || 1988 ((File->File.FileOffset >= Node->UncompOffset) && 1989 (File->File.FileOffset < Node->UncompOffset + 1990 Node->Data.UncompSize))) 1991 { 1992 File->DataBlock = Node; 1993 return CAB_STATUS_SUCCESS; 1994 } 1995 1996 Node = Node->Next; 1997 } 1998 return CAB_STATUS_INVALID_CAB; 1999 } 2000 2001 2002 ULONG CCabinet::LocateFile(char* FileName, 2003 PCFFILE_NODE *File) 2004 /* 2005 * FUNCTION: Locates a file in the cabinet 2006 * ARGUMENTS: 2007 * FileName = Pointer to string with name of file to locate 2008 * File = Address of pointer to CFFILE_NODE structure to fill 2009 * RETURNS: 2010 * Status of operation 2011 * NOTES: 2012 * Current folder is set to the folder of the file 2013 */ 2014 { 2015 PCFFILE_NODE Node; 2016 ULONG Status; 2017 2018 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName)); 2019 2020 Node = FileListHead; 2021 while (Node != NULL) 2022 { 2023 if (strcasecmp(FileName, Node->FileName) == 0) 2024 { 2025 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID); 2026 if (!CurrentFolderNode) 2027 { 2028 DPRINT(MID_TRACE, ("Folder with index number (%u) not found.\n", 2029 Node->File.FileControlID)); 2030 return CAB_STATUS_INVALID_CAB; 2031 } 2032 2033 if (Node->DataBlock == NULL) 2034 Status = GetAbsoluteOffset(Node); 2035 else 2036 Status = CAB_STATUS_SUCCESS; 2037 2038 *File = Node; 2039 return Status; 2040 } 2041 Node = Node->Next; 2042 } 2043 return CAB_STATUS_NOFILE; 2044 } 2045 2046 2047 ULONG CCabinet::ReadString(char* String, LONG MaxLength) 2048 /* 2049 * FUNCTION: Reads a NULL-terminated string from the cabinet 2050 * ARGUMENTS: 2051 * String = Pointer to buffer to place string 2052 * MaxLength = Maximum length of string 2053 * RETURNS: 2054 * Status of operation 2055 */ 2056 { 2057 ULONG BytesRead; 2058 ULONG Status; 2059 LONG Size; 2060 bool Found; 2061 2062 Found = false; 2063 2064 Status = ReadBlock(String, MaxLength, &BytesRead); 2065 if (Status != CAB_STATUS_SUCCESS) 2066 { 2067 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 2068 return CAB_STATUS_INVALID_CAB; 2069 } 2070 2071 // Find the terminating NULL character 2072 for (Size = 0; Size < MaxLength; Size++) 2073 { 2074 if (String[Size] == '\0') 2075 { 2076 Found = true; 2077 break; 2078 } 2079 } 2080 2081 if (!Found) 2082 { 2083 DPRINT(MIN_TRACE, ("Filename in the cabinet file is too long.\n")); 2084 return CAB_STATUS_INVALID_CAB; 2085 } 2086 2087 // Compute the offset of the next CFFILE. 2088 // We have to subtract from the current offset here, because we read MaxLength characters above and most-probably the file name isn't MaxLength characters long. 2089 // + 1 to skip the terminating NULL character as well. 2090 Size = -(MaxLength - Size) + 1; 2091 2092 if (fseek(FileHandle, (off_t)Size, SEEK_CUR) != 0) 2093 { 2094 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 2095 return CAB_STATUS_INVALID_CAB; 2096 } 2097 2098 return CAB_STATUS_SUCCESS; 2099 } 2100 2101 2102 ULONG CCabinet::ReadFileTable() 2103 /* 2104 * FUNCTION: Reads the file table from the cabinet file 2105 * RETURNS: 2106 * Status of operation 2107 */ 2108 { 2109 ULONG i; 2110 ULONG Status; 2111 ULONG BytesRead; 2112 PCFFILE_NODE File; 2113 2114 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n", 2115 (UINT)CABHeader.FileTableOffset)); 2116 2117 /* Seek to file table */ 2118 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0) 2119 { 2120 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 2121 return CAB_STATUS_INVALID_CAB; 2122 } 2123 2124 for (i = 0; i < CABHeader.FileCount; i++) 2125 { 2126 File = NewFileNode(); 2127 if (!File) 2128 { 2129 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 2130 return CAB_STATUS_NOMEMORY; 2131 } 2132 2133 if ((Status = ReadBlock(&File->File, sizeof(CFFILE), 2134 &BytesRead)) != CAB_STATUS_SUCCESS) 2135 { 2136 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 2137 return CAB_STATUS_INVALID_CAB; 2138 } 2139 2140 File->FileName = (char*)malloc(PATH_MAX); 2141 if (!File->FileName) 2142 { 2143 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 2144 return CAB_STATUS_NOMEMORY; 2145 } 2146 2147 /* Read file name */ 2148 Status = ReadString(File->FileName, PATH_MAX); 2149 if (Status != CAB_STATUS_SUCCESS) 2150 return Status; 2151 2152 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n", 2153 File->FileName, 2154 (UINT)File->File.FileOffset, 2155 (UINT)File->File.FileSize, 2156 File->File.FileControlID)); 2157 2158 } 2159 return CAB_STATUS_SUCCESS; 2160 } 2161 2162 2163 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode) 2164 /* 2165 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file 2166 * ARGUMENTS: 2167 * FolderNode = Pointer to CFFOLDER_NODE structure for folder 2168 * RETURNS: 2169 * Status of operation 2170 */ 2171 { 2172 ULONG AbsoluteOffset; 2173 ULONG UncompOffset; 2174 PCFDATA_NODE Node; 2175 ULONG BytesRead; 2176 ULONG Status; 2177 ULONG i; 2178 2179 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n", 2180 (UINT)FolderNode->Index, (UINT)FolderNode->Folder.DataOffset)); 2181 2182 AbsoluteOffset = FolderNode->Folder.DataOffset; 2183 UncompOffset = FolderNode->UncompOffset; 2184 2185 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) 2186 { 2187 Node = NewDataNode(FolderNode); 2188 if (!Node) 2189 { 2190 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 2191 return CAB_STATUS_NOMEMORY; 2192 } 2193 2194 /* Seek to data block */ 2195 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0) 2196 { 2197 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 2198 return CAB_STATUS_INVALID_CAB; 2199 } 2200 2201 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA), 2202 &BytesRead)) != CAB_STATUS_SUCCESS) 2203 { 2204 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 2205 return CAB_STATUS_INVALID_CAB; 2206 } 2207 2208 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n", 2209 (UINT)AbsoluteOffset, 2210 (UINT)UncompOffset, 2211 (UINT)Node->Data.Checksum, 2212 Node->Data.CompSize, 2213 Node->Data.UncompSize)); 2214 2215 Node->AbsoluteOffset = AbsoluteOffset; 2216 Node->UncompOffset = UncompOffset; 2217 2218 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize; 2219 UncompOffset += Node->Data.UncompSize; 2220 } 2221 2222 FolderUncompSize = UncompOffset; 2223 2224 return CAB_STATUS_SUCCESS; 2225 } 2226 2227 2228 PCFFOLDER_NODE CCabinet::NewFolderNode() 2229 /* 2230 * FUNCTION: Creates a new folder node 2231 * RETURNS: 2232 * Pointer to node if there was enough free memory available, otherwise NULL 2233 */ 2234 { 2235 PCFFOLDER_NODE Node; 2236 2237 Node = (PCFFOLDER_NODE)malloc(sizeof(CFFOLDER_NODE)); 2238 if (!Node) 2239 return NULL; 2240 2241 memset(Node, 0, sizeof(CFFOLDER_NODE)); 2242 2243 Node->Folder.CompressionType = CAB_COMP_NONE; 2244 2245 Node->Prev = FolderListTail; 2246 2247 if (FolderListTail != NULL) 2248 FolderListTail->Next = Node; 2249 else 2250 FolderListHead = Node; 2251 2252 FolderListTail = Node; 2253 2254 return Node; 2255 } 2256 2257 2258 PCFFILE_NODE CCabinet::NewFileNode() 2259 /* 2260 * FUNCTION: Creates a new file node 2261 * ARGUMENTS: 2262 * FolderNode = Pointer to folder node to bind file to 2263 * RETURNS: 2264 * Pointer to node if there was enough free memory available, otherwise NULL 2265 */ 2266 { 2267 PCFFILE_NODE Node; 2268 2269 Node = (PCFFILE_NODE)malloc(sizeof(CFFILE_NODE)); 2270 if (!Node) 2271 return NULL; 2272 2273 memset(Node, 0, sizeof(CFFILE_NODE)); 2274 2275 Node->Prev = FileListTail; 2276 2277 if (FileListTail != NULL) 2278 FileListTail->Next = Node; 2279 else 2280 FileListHead = Node; 2281 2282 FileListTail = Node; 2283 2284 return Node; 2285 } 2286 2287 2288 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode) 2289 /* 2290 * FUNCTION: Creates a new data block node 2291 * ARGUMENTS: 2292 * FolderNode = Pointer to folder node to bind data block to 2293 * RETURNS: 2294 * Pointer to node if there was enough free memory available, otherwise NULL 2295 */ 2296 { 2297 PCFDATA_NODE Node; 2298 2299 Node = (PCFDATA_NODE)malloc(sizeof(CFDATA_NODE)); 2300 if (!Node) 2301 return NULL; 2302 2303 memset(Node, 0, sizeof(CFDATA_NODE)); 2304 2305 Node->Prev = FolderNode->DataListTail; 2306 2307 if (FolderNode->DataListTail != NULL) 2308 FolderNode->DataListTail->Next = Node; 2309 else 2310 FolderNode->DataListHead = Node; 2311 2312 FolderNode->DataListTail = Node; 2313 2314 return Node; 2315 } 2316 2317 2318 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode) 2319 /* 2320 * FUNCTION: Destroys data block nodes bound to a folder node 2321 * ARGUMENTS: 2322 * FolderNode = Pointer to folder node 2323 */ 2324 { 2325 PCFDATA_NODE PrevNode; 2326 PCFDATA_NODE NextNode; 2327 2328 NextNode = FolderNode->DataListHead; 2329 while (NextNode != NULL) 2330 { 2331 PrevNode = NextNode->Next; 2332 free(NextNode); 2333 NextNode = PrevNode; 2334 } 2335 FolderNode->DataListHead = NULL; 2336 FolderNode->DataListTail = NULL; 2337 } 2338 2339 2340 void CCabinet::DestroyFileNodes() 2341 /* 2342 * FUNCTION: Destroys file nodes 2343 */ 2344 { 2345 PCFFILE_NODE PrevNode; 2346 PCFFILE_NODE NextNode; 2347 2348 NextNode = FileListHead; 2349 while (NextNode != NULL) 2350 { 2351 PrevNode = NextNode->Next; 2352 if (NextNode->FileName) 2353 free(NextNode->FileName); 2354 free(NextNode); 2355 NextNode = PrevNode; 2356 } 2357 FileListHead = NULL; 2358 FileListTail = NULL; 2359 } 2360 2361 2362 void CCabinet::DestroyDeletedFileNodes() 2363 /* 2364 * FUNCTION: Destroys file nodes that are marked for deletion 2365 */ 2366 { 2367 PCFFILE_NODE CurNode; 2368 PCFFILE_NODE NextNode; 2369 2370 CurNode = FileListHead; 2371 while (CurNode != NULL) 2372 { 2373 NextNode = CurNode->Next; 2374 2375 if (CurNode->Delete) 2376 { 2377 if (CurNode->Prev != NULL) 2378 CurNode->Prev->Next = CurNode->Next; 2379 else 2380 { 2381 FileListHead = CurNode->Next; 2382 if (FileListHead) 2383 FileListHead->Prev = NULL; 2384 } 2385 2386 if (CurNode->Next != NULL) 2387 CurNode->Next->Prev = CurNode->Prev; 2388 else 2389 { 2390 FileListTail = CurNode->Prev; 2391 if (FileListTail) 2392 FileListTail->Next = NULL; 2393 } 2394 2395 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName)); 2396 2397 TotalFileSize -= (sizeof(CFFILE) + (ULONG)strlen(GetFileName(CurNode->FileName)) + 1); 2398 2399 if (CurNode->FileName) 2400 free(CurNode->FileName); 2401 free(CurNode); 2402 } 2403 CurNode = NextNode; 2404 } 2405 } 2406 2407 2408 void CCabinet::DestroyFolderNodes() 2409 /* 2410 * FUNCTION: Destroys folder nodes 2411 */ 2412 { 2413 PCFFOLDER_NODE PrevNode; 2414 PCFFOLDER_NODE NextNode; 2415 2416 NextNode = FolderListHead; 2417 while (NextNode != NULL) 2418 { 2419 PrevNode = NextNode->Next; 2420 DestroyDataNodes(NextNode); 2421 free(NextNode); 2422 NextNode = PrevNode; 2423 } 2424 FolderListHead = NULL; 2425 FolderListTail = NULL; 2426 } 2427 2428 2429 void CCabinet::DestroyDeletedFolderNodes() 2430 /* 2431 * FUNCTION: Destroys folder nodes that are marked for deletion 2432 */ 2433 { 2434 PCFFOLDER_NODE CurNode; 2435 PCFFOLDER_NODE NextNode; 2436 2437 CurNode = FolderListHead; 2438 while (CurNode != NULL) 2439 { 2440 NextNode = CurNode->Next; 2441 2442 if (CurNode->Delete) 2443 { 2444 if (CurNode->Prev != NULL) 2445 CurNode->Prev->Next = CurNode->Next; 2446 else 2447 { 2448 FolderListHead = CurNode->Next; 2449 if (FolderListHead) 2450 FolderListHead->Prev = NULL; 2451 } 2452 2453 if (CurNode->Next != NULL) 2454 CurNode->Next->Prev = CurNode->Prev; 2455 else 2456 { 2457 FolderListTail = CurNode->Prev; 2458 if (FolderListTail) 2459 FolderListTail->Next = NULL; 2460 } 2461 2462 DestroyDataNodes(CurNode); 2463 free(CurNode); 2464 2465 TotalFolderSize -= sizeof(CFFOLDER); 2466 } 2467 CurNode = NextNode; 2468 } 2469 } 2470 2471 2472 ULONG CCabinet::ComputeChecksum(void* Buffer, 2473 ULONG Size, 2474 ULONG Seed) 2475 /* 2476 * FUNCTION: Computes checksum for data block 2477 * ARGUMENTS: 2478 * Buffer = Pointer to data buffer 2479 * Size = Length of data buffer 2480 * Seed = Previously computed checksum 2481 * RETURNS: 2482 * Checksum of buffer 2483 */ 2484 { 2485 int UlongCount; // Number of ULONGs in block 2486 ULONG Checksum; // Checksum accumulator 2487 unsigned char* pb; 2488 ULONG ul; 2489 2490 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE 2491 won't accept checksums computed by this routine */ 2492 2493 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer, (UINT)Size)); 2494 2495 UlongCount = Size / 4; // Number of ULONGs 2496 Checksum = Seed; // Init checksum 2497 pb = (unsigned char*)Buffer; // Start at front of data block 2498 2499 /* Checksum integral multiple of ULONGs */ 2500 while (UlongCount-- > 0) 2501 { 2502 /* NOTE: Build ULONG in big/little-endian independent manner */ 2503 ul = *pb++; // Get low-order byte 2504 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte 2505 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte 2506 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte 2507 2508 Checksum ^= ul; // Update checksum 2509 } 2510 2511 /* Checksum remainder bytes */ 2512 ul = 0; 2513 switch (Size % 4) 2514 { 2515 case 3: 2516 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte 2517 case 2: 2518 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte 2519 case 1: 2520 ul |= *pb++; // Get low-order byte 2521 default: 2522 break; 2523 } 2524 Checksum ^= ul; // Update checksum 2525 2526 /* Return computed checksum */ 2527 return Checksum; 2528 } 2529 2530 2531 ULONG CCabinet::ReadBlock(void* Buffer, 2532 ULONG Size, 2533 PULONG BytesRead) 2534 /* 2535 * FUNCTION: Read a block of data from file 2536 * ARGUMENTS: 2537 * Buffer = Pointer to data buffer 2538 * Size = Length of data buffer 2539 * BytesRead = Pointer to ULONG that on return will contain 2540 * number of bytes read 2541 * RETURNS: 2542 * Status of operation 2543 */ 2544 { 2545 *BytesRead = fread(Buffer, 1, Size, FileHandle); 2546 if ( *BytesRead != Size ) 2547 return CAB_STATUS_INVALID_CAB; 2548 return CAB_STATUS_SUCCESS; 2549 } 2550 2551 bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern) 2552 /* 2553 * FUNCTION: Matches a wildcard character pattern against a file 2554 * ARGUMENTS: 2555 * FileName = The file name to check 2556 * Pattern = The pattern 2557 * RETURNS: 2558 * Whether the pattern matches the file 2559 * 2560 * COPYRIGHT: 2561 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version. 2562 * Adapted from code written by Ingo Wilken. 2563 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup 2564 */ 2565 { 2566 char* retryPattern = NULL; 2567 char* retryFileName = NULL; 2568 char ch; 2569 2570 while (*FileName || *Pattern) 2571 { 2572 ch = *Pattern++; 2573 2574 switch (ch) 2575 { 2576 case '*': 2577 retryPattern = Pattern; 2578 retryFileName = FileName; 2579 break; 2580 2581 case '?': 2582 if (*FileName++ == '\0') 2583 return false; 2584 2585 break; 2586 2587 default: 2588 if (*FileName == ch) 2589 { 2590 if (*FileName) 2591 FileName++; 2592 break; 2593 } 2594 2595 if (*FileName) 2596 { 2597 Pattern = retryPattern; 2598 FileName = ++retryFileName; 2599 break; 2600 } 2601 2602 return false; 2603 } 2604 2605 if (!Pattern) 2606 return false; 2607 } 2608 2609 return true; 2610 } 2611 2612 #ifndef CAB_READ_ONLY 2613 2614 ULONG CCabinet::InitCabinetHeader() 2615 /* 2616 * FUNCTION: Initializes cabinet header and optional fields 2617 * RETURNS: 2618 * Status of operation 2619 */ 2620 { 2621 ULONG TotalSize; 2622 ULONG Size; 2623 2624 CABHeader.FileTableOffset = 0; // Not known yet 2625 CABHeader.FolderCount = 0; // Not known yet 2626 CABHeader.FileCount = 0; // Not known yet 2627 CABHeader.Flags = 0; // Not known yet 2628 2629 CABHeader.CabinetNumber = (USHORT)CurrentDiskNumber; 2630 2631 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev))) 2632 { 2633 CABHeader.Flags |= CAB_FLAG_HASPREV; 2634 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev)) 2635 strcpy(CabinetPrev, ""); 2636 } 2637 2638 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext)) 2639 { 2640 CABHeader.Flags |= CAB_FLAG_HASNEXT; 2641 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext)) 2642 strcpy(DiskNext, ""); 2643 } 2644 2645 TotalSize = 0; 2646 2647 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 2648 { 2649 2650 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); 2651 2652 /* Calculate size of name of previous cabinet */ 2653 TotalSize += (ULONG)strlen(CabinetPrev) + 1; 2654 2655 /* Calculate size of label of previous disk */ 2656 TotalSize += (ULONG)strlen(DiskPrev) + 1; 2657 } 2658 2659 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 2660 { 2661 2662 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); 2663 2664 /* Calculate size of name of next cabinet */ 2665 Size = (ULONG)strlen(CabinetNext) + 1; 2666 TotalSize += Size; 2667 NextFieldsSize = Size; 2668 2669 /* Calculate size of label of next disk */ 2670 Size = (ULONG)strlen(DiskNext) + 1; 2671 TotalSize += Size; 2672 NextFieldsSize += Size; 2673 } 2674 else 2675 NextFieldsSize = 0; 2676 2677 /* Add cabinet reserved area size if present */ 2678 if (CabinetReservedFileSize > 0) 2679 { 2680 CABHeader.Flags |= CAB_FLAG_RESERVE; 2681 TotalSize += CabinetReservedFileSize; 2682 TotalSize += sizeof(ULONG); /* For CabinetResSize, FolderResSize, and FileResSize fields */ 2683 } 2684 2685 DiskSize += TotalSize; 2686 2687 TotalHeaderSize = sizeof(CFHEADER) + TotalSize; 2688 2689 return CAB_STATUS_SUCCESS; 2690 } 2691 2692 2693 ULONG CCabinet::WriteCabinetHeader(bool MoreDisks) 2694 /* 2695 * FUNCTION: Writes the cabinet header and optional fields 2696 * ARGUMENTS: 2697 * MoreDisks = true if next cabinet name should be included 2698 * RETURNS: 2699 * Status of operation 2700 */ 2701 { 2702 PCFFOLDER_NODE FolderNode; 2703 PCFFILE_NODE FileNode; 2704 ULONG BytesWritten; 2705 ULONG Size; 2706 2707 if (MoreDisks) 2708 { 2709 CABHeader.Flags |= CAB_FLAG_HASNEXT; 2710 Size = TotalHeaderSize; 2711 } 2712 else 2713 { 2714 CABHeader.Flags &= ~CAB_FLAG_HASNEXT; 2715 DiskSize -= NextFieldsSize; 2716 Size = TotalHeaderSize - NextFieldsSize; 2717 } 2718 2719 /* Set absolute folder offsets */ 2720 BytesWritten = Size + TotalFolderSize + TotalFileSize; 2721 CABHeader.FolderCount = 0; 2722 FolderNode = FolderListHead; 2723 while (FolderNode != NULL) 2724 { 2725 FolderNode->Folder.DataOffset = BytesWritten; 2726 2727 BytesWritten += FolderNode->TotalFolderSize; 2728 2729 CABHeader.FolderCount++; 2730 2731 FolderNode = FolderNode->Next; 2732 } 2733 2734 /* Set absolute offset of file table */ 2735 CABHeader.FileTableOffset = Size + TotalFolderSize; 2736 2737 /* Count number of files to be committed */ 2738 CABHeader.FileCount = 0; 2739 FileNode = FileListHead; 2740 while (FileNode != NULL) 2741 { 2742 if (FileNode->Commit) 2743 CABHeader.FileCount++; 2744 FileNode = FileNode->Next; 2745 } 2746 2747 CABHeader.CabinetSize = DiskSize; 2748 2749 /* Write header */ 2750 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1) 2751 { 2752 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2753 return CAB_STATUS_CANNOT_WRITE; 2754 } 2755 2756 /* Write per-cabinet reserved area if present */ 2757 if (CABHeader.Flags & CAB_FLAG_RESERVE) 2758 { 2759 ULONG ReservedSize; 2760 2761 ReservedSize = CabinetReservedFileSize & 0xffff; 2762 ReservedSize |= (0 << 16); /* Folder reserved area size */ 2763 ReservedSize |= (0 << 24); /* Folder reserved area size */ 2764 2765 BytesWritten = sizeof(ULONG); 2766 if (fwrite(&ReservedSize, sizeof(ULONG), 1, FileHandle) < 1) 2767 { 2768 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2769 return CAB_STATUS_CANNOT_WRITE; 2770 } 2771 2772 BytesWritten = CabinetReservedFileSize; 2773 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1) 2774 { 2775 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2776 return CAB_STATUS_CANNOT_WRITE; 2777 } 2778 } 2779 2780 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 2781 { 2782 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); 2783 2784 /* Write name of previous cabinet */ 2785 Size = (ULONG)strlen(CabinetPrev) + 1; 2786 BytesWritten = Size; 2787 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1) 2788 { 2789 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2790 return CAB_STATUS_CANNOT_WRITE; 2791 } 2792 2793 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev)); 2794 2795 /* Write label of previous disk */ 2796 Size = (ULONG)strlen(DiskPrev) + 1; 2797 BytesWritten = Size; 2798 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1) 2799 { 2800 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2801 return CAB_STATUS_CANNOT_WRITE; 2802 } 2803 } 2804 2805 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 2806 { 2807 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); 2808 2809 /* Write name of next cabinet */ 2810 Size = (ULONG)strlen(CabinetNext) + 1; 2811 BytesWritten = Size; 2812 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1) 2813 { 2814 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2815 return CAB_STATUS_CANNOT_WRITE; 2816 } 2817 2818 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext)); 2819 2820 /* Write label of next disk */ 2821 Size = (ULONG)strlen(DiskNext) + 1; 2822 BytesWritten = Size; 2823 if (fwrite(DiskNext, Size, 1, FileHandle) < 1) 2824 { 2825 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2826 return CAB_STATUS_CANNOT_WRITE; 2827 } 2828 } 2829 2830 return CAB_STATUS_SUCCESS; 2831 } 2832 2833 2834 ULONG CCabinet::WriteFolderEntries() 2835 /* 2836 * FUNCTION: Writes folder entries 2837 * RETURNS: 2838 * Status of operation 2839 */ 2840 { 2841 PCFFOLDER_NODE FolderNode; 2842 2843 DPRINT(MAX_TRACE, ("Writing folder table.\n")); 2844 2845 FolderNode = FolderListHead; 2846 while (FolderNode != NULL) 2847 { 2848 if (FolderNode->Commit) 2849 { 2850 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n", 2851 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, (UINT)FolderNode->Folder.DataOffset)); 2852 2853 if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1) 2854 { 2855 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2856 return CAB_STATUS_CANNOT_WRITE; 2857 } 2858 } 2859 FolderNode = FolderNode->Next; 2860 } 2861 2862 return CAB_STATUS_SUCCESS; 2863 } 2864 2865 2866 ULONG CCabinet::WriteFileEntries() 2867 /* 2868 * FUNCTION: Writes file entries for all files 2869 * RETURNS: 2870 * Status of operation 2871 */ 2872 { 2873 PCFFILE_NODE File; 2874 bool SetCont = false; 2875 2876 DPRINT(MAX_TRACE, ("Writing file table.\n")); 2877 2878 File = FileListHead; 2879 while (File != NULL) 2880 { 2881 if (File->Commit) 2882 { 2883 /* Remove any continued files that ends in this disk */ 2884 if (File->File.FileControlID == CAB_FILE_CONTINUED) 2885 File->Delete = true; 2886 2887 /* The file could end in the last (split) block and should therefore 2888 appear in the next disk too */ 2889 2890 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) && 2891 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit)) 2892 { 2893 File->File.FileControlID = CAB_FILE_SPLIT; 2894 File->Delete = false; 2895 SetCont = true; 2896 } 2897 2898 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n", 2899 File->File.FileControlID, (UINT)File->File.FileOffset, (UINT)File->File.FileSize, File->FileName)); 2900 2901 if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1) 2902 { 2903 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2904 return CAB_STATUS_CANNOT_WRITE; 2905 } 2906 2907 if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1) 2908 { 2909 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2910 return CAB_STATUS_CANNOT_WRITE; 2911 } 2912 2913 if (SetCont) 2914 { 2915 File->File.FileControlID = CAB_FILE_CONTINUED; 2916 SetCont = false; 2917 } 2918 } 2919 2920 File = File->Next; 2921 } 2922 return CAB_STATUS_SUCCESS; 2923 } 2924 2925 2926 ULONG CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode) 2927 /* 2928 * FUNCTION: Writes data blocks to the cabinet 2929 * ARGUMENTS: 2930 * FolderNode = Pointer to folder node containing the data blocks 2931 * RETURNS: 2932 * Status of operation 2933 */ 2934 { 2935 PCFDATA_NODE DataNode; 2936 ULONG BytesRead; 2937 ULONG Status; 2938 2939 DataNode = FolderNode->DataListHead; 2940 if (DataNode != NULL) 2941 Status = ScratchFile->Seek(DataNode->ScratchFilePosition); 2942 2943 while (DataNode != NULL) 2944 { 2945 DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n", 2946 (UINT)DataNode->ScratchFilePosition, 2947 DataNode->Data.CompSize, 2948 DataNode->Data.UncompSize)); 2949 2950 /* InputBuffer is free for us to use here, so we use it and avoid a 2951 memory allocation. OutputBuffer can't be used here because it may 2952 still contain valid data (if a data block spans two or more disks) */ 2953 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead); 2954 if (Status != CAB_STATUS_SUCCESS) 2955 { 2956 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%u).\n", (UINT)Status)); 2957 return Status; 2958 } 2959 2960 if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1) 2961 { 2962 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2963 return CAB_STATUS_CANNOT_WRITE; 2964 } 2965 2966 if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1) 2967 { 2968 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 2969 return CAB_STATUS_CANNOT_WRITE; 2970 } 2971 2972 DataNode = DataNode->Next; 2973 } 2974 return CAB_STATUS_SUCCESS; 2975 } 2976 2977 2978 ULONG CCabinet::WriteDataBlock() 2979 /* 2980 * FUNCTION: Writes the current data block to the scratch file 2981 * RETURNS: 2982 * Status of operation 2983 */ 2984 { 2985 ULONG Status; 2986 ULONG BytesWritten; 2987 PCFDATA_NODE DataNode; 2988 2989 if (!BlockIsSplit) 2990 { 2991 Status = Codec->Compress(OutputBuffer, 2992 InputBuffer, 2993 CurrentIBufferSize, 2994 &TotalCompSize); 2995 2996 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n", 2997 (UINT)CurrentIBufferSize, (UINT)TotalCompSize)); 2998 2999 CurrentOBuffer = OutputBuffer; 3000 CurrentOBufferSize = TotalCompSize; 3001 } 3002 3003 DataNode = NewDataNode(CurrentFolderNode); 3004 if (!DataNode) 3005 { 3006 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 3007 return CAB_STATUS_NOMEMORY; 3008 } 3009 3010 DiskSize += sizeof(CFDATA); 3011 3012 if (MaxDiskSize > 0) 3013 /* Disk size is limited */ 3014 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize); 3015 else 3016 BlockIsSplit = false; 3017 3018 if (BlockIsSplit) 3019 { 3020 DataNode->Data.CompSize = (USHORT)(MaxDiskSize - DiskSize); 3021 DataNode->Data.UncompSize = 0; 3022 CreateNewDisk = true; 3023 } 3024 else 3025 { 3026 DataNode->Data.CompSize = (USHORT)CurrentOBufferSize; 3027 DataNode->Data.UncompSize = (USHORT)CurrentIBufferSize; 3028 } 3029 3030 DataNode->Data.Checksum = 0; 3031 DataNode->ScratchFilePosition = ScratchFile->Position(); 3032 3033 // FIXME: MAKECAB.EXE does not like this checksum algorithm 3034 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0); 3035 3036 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n", 3037 (UINT)DataNode->Data.Checksum, 3038 DataNode->Data.CompSize, 3039 DataNode->Data.UncompSize)); 3040 3041 Status = ScratchFile->WriteBlock(&DataNode->Data, 3042 CurrentOBuffer, &BytesWritten); 3043 if (Status != CAB_STATUS_SUCCESS) 3044 return Status; 3045 3046 DiskSize += BytesWritten; 3047 3048 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA)); 3049 CurrentFolderNode->Folder.DataBlockCount++; 3050 3051 CurrentOBuffer = (unsigned char*)CurrentOBuffer + DataNode->Data.CompSize; 3052 CurrentOBufferSize -= DataNode->Data.CompSize; 3053 3054 LastBlockStart += DataNode->Data.UncompSize; 3055 3056 if (!BlockIsSplit) 3057 { 3058 CurrentIBufferSize = 0; 3059 CurrentIBuffer = InputBuffer; 3060 } 3061 3062 return CAB_STATUS_SUCCESS; 3063 } 3064 3065 #if !defined(_WIN32) 3066 3067 void CCabinet::ConvertDateAndTime(time_t* Time, 3068 PUSHORT DosDate, 3069 PUSHORT DosTime) 3070 /* 3071 * FUNCTION: Returns file times of a file 3072 * ARGUMENTS: 3073 * FileHandle = File handle of file to get file times from 3074 * File = Pointer to CFFILE node for file 3075 * RETURNS: 3076 * Status of operation 3077 */ 3078 { 3079 struct tm *timedef; 3080 3081 timedef = localtime(Time); 3082 3083 DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n", 3084 timedef->tm_mday, timedef->tm_mon, timedef->tm_year, 3085 timedef->tm_sec, timedef->tm_min, timedef->tm_hour)); 3086 3087 *DosDate = ((timedef->tm_mday + 1) << 0) 3088 | ((timedef->tm_mon + 1) << 5) 3089 | (((timedef->tm_year + 1900) - 1980) << 9); 3090 3091 *DosTime = (timedef->tm_sec << 0) 3092 | (timedef->tm_min << 5) 3093 | (timedef->tm_hour << 11); 3094 } 3095 3096 #endif // !_WIN32 3097 3098 3099 ULONG CCabinet::GetFileTimes(FILE* FileHandle, PCFFILE_NODE File) 3100 /* 3101 * FUNCTION: Returns file times of a file 3102 * ARGUMENTS: 3103 * FileHandle = File handle of file to get file times from 3104 * File = Pointer to CFFILE node for file 3105 * RETURNS: 3106 * Status of operation 3107 */ 3108 { 3109 #if defined(_WIN32) 3110 FILETIME FileTime; 3111 HANDLE FileNo = UlongToHandle(_fileno(FileHandle)); 3112 3113 if (GetFileTime(FileNo, NULL, NULL, &FileTime)) 3114 FileTimeToDosDateTime(&FileTime, 3115 &File->File.FileDate, 3116 &File->File.FileTime); 3117 #else 3118 struct stat stbuf; 3119 char buf[PATH_MAX]; 3120 3121 // Check for an absolute path 3122 if (IsSeparator(File->FileName[0])) 3123 strcpy(buf, File->FileName); 3124 else 3125 { 3126 if (!getcwd(buf, sizeof(buf))) 3127 return CAB_STATUS_CANNOT_READ; 3128 strcat(buf, DIR_SEPARATOR_STRING); 3129 strcat(buf, File->FileName); 3130 } 3131 3132 if (stat(buf, &stbuf) == -1) 3133 return CAB_STATUS_CANNOT_READ; 3134 3135 ConvertDateAndTime(&stbuf.st_mtime, &File->File.FileDate, &File->File.FileTime); 3136 #endif 3137 return CAB_STATUS_SUCCESS; 3138 } 3139 3140 3141 ULONG CCabinet::GetAttributesOnFile(PCFFILE_NODE File) 3142 /* 3143 * FUNCTION: Returns attributes on a file 3144 * ARGUMENTS: 3145 * File = Pointer to CFFILE node for file 3146 * RETURNS: 3147 * Status of operation 3148 */ 3149 { 3150 #if defined(_WIN32) 3151 LONG Attributes; 3152 3153 Attributes = GetFileAttributes(File->FileName); 3154 if (Attributes == -1) 3155 return CAB_STATUS_CANNOT_READ; 3156 3157 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE 3158 // The IDs for these attributes are the same in the CAB file and under Windows 3159 // If the file has any other attributes, strip them off by the logical AND. 3160 File->File.Attributes = (USHORT)(Attributes & 0x37); 3161 #else 3162 struct stat stbuf; 3163 char buf[PATH_MAX]; 3164 3165 // Check for an absolute path 3166 if (IsSeparator(File->FileName[0])) 3167 strcpy(buf, File->FileName); 3168 else 3169 { 3170 if (!getcwd(buf, sizeof(buf))) 3171 return CAB_STATUS_CANNOT_READ; 3172 strcat(buf, DIR_SEPARATOR_STRING); 3173 strcat(buf, File->FileName); 3174 } 3175 3176 if (stat(buf, &stbuf) == -1) 3177 return CAB_STATUS_CANNOT_READ; 3178 3179 #if 0 3180 File->File.Attributes |= CAB_ATTRIB_READONLY; 3181 File->File.Attributes |= CAB_ATTRIB_HIDDEN; 3182 File->File.Attributes |= CAB_ATTRIB_SYSTEM; 3183 #endif 3184 3185 if (stbuf.st_mode & S_IFDIR) 3186 File->File.Attributes |= CAB_ATTRIB_DIRECTORY; 3187 3188 File->File.Attributes |= CAB_ATTRIB_ARCHIVE; 3189 3190 #endif 3191 return CAB_STATUS_SUCCESS; 3192 } 3193 3194 3195 ULONG CCabinet::SetAttributesOnFile(char* FileName, USHORT FileAttributes) 3196 /* 3197 * FUNCTION: Sets attributes on a file 3198 * ARGUMENTS: 3199 * FileName = File name with path 3200 * FileAttributes = Attributes of that file 3201 * RETURNS: 3202 * Status of operation 3203 */ 3204 { 3205 #if defined(_WIN32) 3206 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE 3207 // The IDs for these attributes are the same in the CAB file and under Windows 3208 // If the file has any other attributes, strip them off by the logical AND. 3209 SetFileAttributes(FileName, (DWORD)(FileAttributes & 0x37)); 3210 3211 return CAB_STATUS_SUCCESS; 3212 #else 3213 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n")); 3214 return CAB_STATUS_SUCCESS; 3215 #endif 3216 } 3217 3218 #endif /* CAB_READ_ONLY */ 3219 3220 /* EOF */ 3221