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