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