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