1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS text-mode setup 4 * FILE: base/setup/usetup/cabinet.c 5 * PURPOSE: Cabinet routines 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * REVISIONS: 8 * CSH 15/08-2003 Created 9 */ 10 11 #ifndef _USETUP_PCH_ 12 #include "usetup.h" 13 #endif 14 15 #define Z_SOLO 16 #include <zlib.h> 17 18 #define NDEBUG 19 #include <debug.h> 20 21 22 /* DEFINITIONS **************************************************************/ 23 24 /* File management definitions */ 25 26 #define SEEK_BEGIN 0 27 #define SEEK_CURRENT 1 28 #ifndef SEEK_END 29 #define SEEK_END 2 30 #endif 31 32 typedef struct _DOSTIME 33 { 34 WORD Second:5; 35 WORD Minute:6; 36 WORD Hour:5; 37 } DOSTIME, *PDOSTIME; 38 39 typedef struct _DOSDATE 40 { 41 WORD Day:5; 42 WORD Month:4; 43 WORD Year:5; 44 } DOSDATE, *PDOSDATE; 45 46 47 /* Cabinet constants */ 48 49 #define CAB_SIGNATURE 0x4643534D // "MSCF" 50 #define CAB_VERSION 0x0103 51 #define CAB_BLOCKSIZE 32768 52 53 #define CAB_COMP_MASK 0x00FF 54 #define CAB_COMP_NONE 0x0000 55 #define CAB_COMP_MSZIP 0x0001 56 #define CAB_COMP_QUANTUM 0x0002 57 #define CAB_COMP_LZX 0x0003 58 59 #define CAB_FLAG_HASPREV 0x0001 60 #define CAB_FLAG_HASNEXT 0x0002 61 #define CAB_FLAG_RESERVE 0x0004 62 63 #define CAB_ATTRIB_READONLY 0x0001 64 #define CAB_ATTRIB_HIDDEN 0x0002 65 #define CAB_ATTRIB_SYSTEM 0x0004 66 #define CAB_ATTRIB_VOLUME 0x0008 67 #define CAB_ATTRIB_DIRECTORY 0x0010 68 #define CAB_ATTRIB_ARCHIVE 0x0020 69 #define CAB_ATTRIB_EXECUTE 0x0040 70 #define CAB_ATTRIB_UTF_NAME 0x0080 71 72 #define CAB_FILE_MAX_FOLDER 0xFFFC 73 #define CAB_FILE_CONTINUED 0xFFFD 74 #define CAB_FILE_SPLIT 0xFFFE 75 #define CAB_FILE_PREV_NEXT 0xFFFF 76 77 78 /* Cabinet structures */ 79 80 typedef struct _CFHEADER 81 { 82 ULONG Signature; // File signature 'MSCF' (CAB_SIGNATURE) 83 ULONG Reserved1; // Reserved field 84 ULONG CabinetSize; // Cabinet file size 85 ULONG Reserved2; // Reserved field 86 ULONG FileTableOffset; // Offset of first CFFILE 87 ULONG Reserved3; // Reserved field 88 USHORT Version; // Cabinet version (CAB_VERSION) 89 USHORT FolderCount; // Number of folders 90 USHORT FileCount; // Number of files 91 USHORT Flags; // Cabinet flags (CAB_FLAG_*) 92 USHORT SetID; // Cabinet set id 93 USHORT CabinetNumber; // Zero-based cabinet number 94 /* Optional fields (depends on Flags) 95 USHORT CabinetResSize // Per-cabinet reserved area size 96 CHAR FolderResSize // Per-folder reserved area size 97 CHAR FileResSize // Per-file reserved area size 98 CHAR CabinetReserved[] // Per-cabinet reserved area 99 CHAR CabinetPrev[] // Name of previous cabinet file 100 CHAR DiskPrev[] // Name of previous disk 101 CHAR CabinetNext[] // Name of next cabinet file 102 CHAR DiskNext[] // Name of next disk 103 */ 104 } CFHEADER, *PCFHEADER; 105 106 typedef struct _CFFOLDER 107 { 108 ULONG DataOffset; // Absolute offset of first CFDATA block in this folder 109 USHORT DataBlockCount; // Number of CFDATA blocks in this folder in this cabinet 110 USHORT CompressionType; // Type of compression used for all CFDATA blocks in this folder 111 /* Optional fields (depends on Flags) 112 CHAR FolderReserved[] // Per-folder reserved area 113 */ 114 } CFFOLDER, *PCFFOLDER; 115 116 typedef struct _CFFILE 117 { 118 ULONG FileSize; // Uncompressed file size in bytes 119 ULONG FileOffset; // Uncompressed offset of file in the folder 120 USHORT FolderIndex; // Index number of the folder that contains this file 121 USHORT FileDate; // File date stamp, as used by DOS 122 USHORT FileTime; // File time stamp, as used by DOS 123 USHORT Attributes; // File attributes (CAB_ATTRIB_*) 124 CHAR FileName[ANYSIZE_ARRAY]; 125 /* After this is the NULL terminated filename */ 126 } CFFILE, *PCFFILE; 127 128 typedef struct _CFDATA 129 { 130 ULONG Checksum; // Checksum of CFDATA entry 131 USHORT CompSize; // Number of compressed bytes in this block 132 USHORT UncompSize; // Number of uncompressed bytes in this block 133 /* Optional fields (depends on Flags) 134 CHAR DataReserved[] // Per-datablock reserved area 135 */ 136 } CFDATA, *PCFDATA; 137 138 139 /* FUNCTIONS ****************************************************************/ 140 141 #if !defined(_INC_MALLOC) && !defined(_INC_STDLIB) 142 143 /* Needed by zlib, but we don't want the dependency on the CRT */ 144 void *__cdecl 145 malloc(size_t size) 146 { 147 return RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, size); 148 } 149 150 void __cdecl 151 free(void *ptr) 152 { 153 RtlFreeHeap(ProcessHeap, 0, ptr); 154 } 155 156 void *__cdecl 157 calloc(size_t nmemb, size_t size) 158 { 159 return (void *)RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, nmemb * size); 160 } 161 162 #endif // !_INC_MALLOC && !_INC_STDLIB 163 164 165 /* Codecs */ 166 167 /* Uncompresses a data block */ 168 typedef ULONG (*PCABINET_CODEC_UNCOMPRESS)( 169 IN struct _CAB_CODEC* Codec, 170 OUT PVOID OutputBuffer, 171 IN PVOID InputBuffer, 172 IN OUT PLONG InputLength, 173 IN OUT PLONG OutputLength); 174 175 typedef struct _CAB_CODEC 176 { 177 PCABINET_CODEC_UNCOMPRESS Uncompress; 178 z_stream ZStream; 179 // Other CODEC-related structures 180 } CAB_CODEC, *PCAB_CODEC; 181 182 183 /* RAW codec */ 184 185 /* 186 * FUNCTION: Uncompresses data in a buffer 187 * ARGUMENTS: 188 * OutputBuffer = Pointer to buffer to place uncompressed data 189 * InputBuffer = Pointer to buffer with data to be uncompressed 190 * InputLength = Length of input buffer before, and amount consumed after 191 * Negative to indicate that this is not the start of a new block 192 * OutputLength = Length of output buffer before, amount filled after 193 * Negative to indicate that this is not the end of the block 194 */ 195 ULONG 196 RawCodecUncompress( 197 IN OUT PCAB_CODEC Codec, 198 OUT PVOID OutputBuffer, 199 IN PVOID InputBuffer, 200 IN OUT PLONG InputLength, 201 IN OUT PLONG OutputLength) 202 { 203 LONG Len = min(abs(*InputLength), abs(*OutputLength)); 204 205 memcpy(OutputBuffer, InputBuffer, Len); 206 *InputLength = *OutputLength = Len; 207 208 return CS_SUCCESS; 209 } 210 211 static CAB_CODEC RawCodec = 212 { 213 RawCodecUncompress, {0} 214 }; 215 216 /* MSZIP codec */ 217 218 #define MSZIP_MAGIC 0x4B43 219 220 /* 221 * FUNCTION: Uncompresses data in a buffer 222 * ARGUMENTS: 223 * OutputBuffer = Pointer to buffer to place uncompressed data 224 * InputBuffer = Pointer to buffer with data to be uncompressed 225 * InputLength = Length of input buffer before, and amount consumed after 226 * Negative to indicate that this is not the start of a new block 227 * OutputLength = Length of output buffer before, amount filled after 228 * Negative to indicate that this is not the end of the block 229 */ 230 ULONG 231 MSZipCodecUncompress( 232 IN OUT PCAB_CODEC Codec, 233 OUT PVOID OutputBuffer, 234 IN PVOID InputBuffer, 235 IN OUT PLONG InputLength, 236 IN OUT PLONG OutputLength) 237 { 238 USHORT Magic; 239 INT Status; 240 241 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, " 242 "InputLength = %d, OutputLength = %d)\n", OutputBuffer, 243 InputBuffer, *InputLength, *OutputLength); 244 245 if (*InputLength > 0) 246 { 247 Magic = *(PUSHORT)InputBuffer; 248 249 if (Magic != MSZIP_MAGIC) 250 { 251 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic); 252 return CS_BADSTREAM; 253 } 254 255 Codec->ZStream.next_in = (PUCHAR)InputBuffer + 2; 256 Codec->ZStream.avail_in = *InputLength - 2; 257 Codec->ZStream.next_out = (PUCHAR)OutputBuffer; 258 Codec->ZStream.avail_out = abs(*OutputLength); 259 260 /* WindowBits is passed < 0 to tell that there is no zlib header. 261 * Note that in this case inflate *requires* an extra "dummy" byte 262 * after the compressed stream in order to complete decompression and 263 * return Z_STREAM_END. 264 */ 265 Status = inflateInit2(&Codec->ZStream, -MAX_WBITS); 266 if (Status != Z_OK) 267 { 268 DPRINT("inflateInit2() returned (%d)\n", Status); 269 return CS_BADSTREAM; 270 } 271 Codec->ZStream.total_in = 2; 272 } 273 else 274 { 275 Codec->ZStream.avail_in = -*InputLength; 276 Codec->ZStream.next_in = (PUCHAR)InputBuffer; 277 Codec->ZStream.next_out = (PUCHAR)OutputBuffer; 278 Codec->ZStream.avail_out = abs(*OutputLength); 279 Codec->ZStream.total_in = 0; 280 } 281 282 Codec->ZStream.total_out = 0; 283 Status = inflate(&Codec->ZStream, Z_SYNC_FLUSH); 284 if (Status != Z_OK && Status != Z_STREAM_END) 285 { 286 DPRINT("inflate() returned (%d) (%s)\n", Status, Codec->ZStream.msg); 287 if (Status == Z_MEM_ERROR) 288 return CS_NOMEMORY; 289 return CS_BADSTREAM; 290 } 291 292 if (*OutputLength > 0) 293 { 294 Status = inflateEnd(&Codec->ZStream); 295 if (Status != Z_OK) 296 { 297 DPRINT("inflateEnd() returned (%d)\n", Status); 298 return CS_BADSTREAM; 299 } 300 } 301 302 *InputLength = Codec->ZStream.total_in; 303 *OutputLength = Codec->ZStream.total_out; 304 305 return CS_SUCCESS; 306 } 307 308 static CAB_CODEC MSZipCodec = 309 { 310 MSZipCodecUncompress, {0} 311 }; 312 313 314 /* Memory functions */ 315 316 voidpf 317 MSZipAlloc(voidpf opaque, uInt items, uInt size) 318 { 319 return (voidpf)RtlAllocateHeap(ProcessHeap, 0, items * size); 320 } 321 322 void 323 MSZipFree(voidpf opaque, voidpf address) 324 { 325 RtlFreeHeap(ProcessHeap, 0, address); 326 } 327 328 static BOOL 329 ConvertSystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, 330 LPFILETIME lpFileTime) 331 { 332 TIME_FIELDS TimeFields; 333 LARGE_INTEGER liTime; 334 335 TimeFields.Year = lpSystemTime->wYear; 336 TimeFields.Month = lpSystemTime->wMonth; 337 TimeFields.Day = lpSystemTime->wDay; 338 TimeFields.Hour = lpSystemTime->wHour; 339 TimeFields.Minute = lpSystemTime->wMinute; 340 TimeFields.Second = lpSystemTime->wSecond; 341 TimeFields.Milliseconds = lpSystemTime->wMilliseconds; 342 343 if (RtlTimeFieldsToTime(&TimeFields, &liTime)) 344 { 345 lpFileTime->dwLowDateTime = liTime.u.LowPart; 346 lpFileTime->dwHighDateTime = liTime.u.HighPart; 347 return TRUE; 348 } 349 350 return FALSE; 351 } 352 353 static BOOL 354 ConvertDosDateTimeToFileTime(WORD wFatDate, 355 WORD wFatTime, 356 LPFILETIME lpFileTime) 357 { 358 PDOSTIME pdtime = (PDOSTIME)&wFatTime; 359 PDOSDATE pddate = (PDOSDATE)&wFatDate; 360 SYSTEMTIME SystemTime; 361 362 if (lpFileTime == NULL) 363 return FALSE; 364 365 SystemTime.wMilliseconds = 0; 366 SystemTime.wSecond = pdtime->Second; 367 SystemTime.wMinute = pdtime->Minute; 368 SystemTime.wHour = pdtime->Hour; 369 370 SystemTime.wDay = pddate->Day; 371 SystemTime.wMonth = pddate->Month; 372 SystemTime.wYear = 1980 + pddate->Year; 373 374 ConvertSystemTimeToFileTime(&SystemTime, lpFileTime); 375 376 return TRUE; 377 } 378 379 /* 380 * FUNCTION: Returns a pointer to file name 381 * ARGUMENTS: 382 * Path = Pointer to string with pathname 383 * RETURNS: 384 * Pointer to filename 385 */ 386 static PWCHAR 387 GetFileName(PWCHAR Path) 388 { 389 ULONG i, j; 390 391 j = i = 0; 392 393 while (Path[i++]) 394 { 395 if (Path[i - 1] == L'\\') 396 j = i; 397 } 398 399 return Path + j; 400 } 401 402 /* 403 * FUNCTION: Removes a file name from a path 404 * ARGUMENTS: 405 * Path = Pointer to string with path 406 */ 407 static VOID 408 RemoveFileName(PWCHAR Path) 409 { 410 PWCHAR FileName; 411 DWORD i; 412 413 i = 0; 414 FileName = GetFileName(Path + i); 415 416 if (FileName != Path + i && FileName[-1] == L'\\') 417 FileName--; 418 419 if (FileName == Path + i && FileName[0] == L'\\') 420 FileName++; 421 422 FileName[0] = 0; 423 } 424 425 /* 426 * FUNCTION: Sets attributes on a file 427 * ARGUMENTS: 428 * File = Pointer to CFFILE node for file 429 * RETURNS: 430 * Status of operation 431 */ 432 static BOOL 433 SetAttributesOnFile(PCFFILE File, 434 HANDLE hFile) 435 { 436 FILE_BASIC_INFORMATION FileBasic; 437 IO_STATUS_BLOCK IoStatusBlock; 438 NTSTATUS NtStatus; 439 ULONG Attributes = 0; 440 441 if (File->Attributes & CAB_ATTRIB_READONLY) 442 Attributes |= FILE_ATTRIBUTE_READONLY; 443 444 if (File->Attributes & CAB_ATTRIB_HIDDEN) 445 Attributes |= FILE_ATTRIBUTE_HIDDEN; 446 447 if (File->Attributes & CAB_ATTRIB_SYSTEM) 448 Attributes |= FILE_ATTRIBUTE_SYSTEM; 449 450 if (File->Attributes & CAB_ATTRIB_DIRECTORY) 451 Attributes |= FILE_ATTRIBUTE_DIRECTORY; 452 453 if (File->Attributes & CAB_ATTRIB_ARCHIVE) 454 Attributes |= FILE_ATTRIBUTE_ARCHIVE; 455 456 NtStatus = NtQueryInformationFile(hFile, 457 &IoStatusBlock, 458 &FileBasic, 459 sizeof(FILE_BASIC_INFORMATION), 460 FileBasicInformation); 461 if (!NT_SUCCESS(NtStatus)) 462 { 463 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus); 464 } 465 else 466 { 467 FileBasic.FileAttributes = Attributes; 468 469 NtStatus = NtSetInformationFile(hFile, 470 &IoStatusBlock, 471 &FileBasic, 472 sizeof(FILE_BASIC_INFORMATION), 473 FileBasicInformation); 474 if (!NT_SUCCESS(NtStatus)) 475 { 476 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus); 477 } 478 } 479 480 return NT_SUCCESS(NtStatus); 481 } 482 483 /* 484 * FUNCTION: Closes the current cabinet 485 * RETURNS: 486 * Status of operation 487 */ 488 static ULONG 489 CloseCabinet( 490 IN PCABINET_CONTEXT CabinetContext) 491 { 492 if (CabinetContext->FileBuffer) 493 { 494 NtUnmapViewOfSection(NtCurrentProcess(), CabinetContext->FileBuffer); 495 NtClose(CabinetContext->FileSectionHandle); 496 NtClose(CabinetContext->FileHandle); 497 CabinetContext->FileBuffer = NULL; 498 } 499 500 return 0; 501 } 502 503 /* 504 * FUNCTION: Initialize archiver 505 */ 506 VOID 507 CabinetInitialize( 508 IN OUT PCABINET_CONTEXT CabinetContext) 509 { 510 RtlZeroMemory(CabinetContext, sizeof(*CabinetContext)); 511 512 CabinetContext->FileOpen = FALSE; 513 wcscpy(CabinetContext->DestPath, L""); 514 515 CabinetContext->CodecSelected = FALSE; 516 CabinetSelectCodec(CabinetContext, CAB_CODEC_RAW); 517 518 CabinetContext->OverwriteHandler = NULL; 519 CabinetContext->ExtractHandler = NULL; 520 CabinetContext->DiskChangeHandler = NULL; 521 522 CabinetContext->FolderUncompSize = 0; 523 CabinetContext->BytesLeftInBlock = 0; 524 CabinetContext->CabinetReserved = 0; 525 CabinetContext->FolderReserved = 0; 526 CabinetContext->DataReserved = 0; 527 CabinetContext->CabinetReservedArea = NULL; 528 CabinetContext->LastFileOffset = 0; 529 } 530 531 /* 532 * FUNCTION: Cleanup archiver 533 */ 534 VOID 535 CabinetCleanup( 536 IN OUT PCABINET_CONTEXT CabinetContext) 537 { 538 CabinetClose(CabinetContext); 539 } 540 541 /* 542 * FUNCTION: Normalizes a path 543 * ARGUMENTS: 544 * Path = Pointer to string with pathname 545 * Length = Number of characters in Path 546 * RETURNS: 547 * TRUE if there was enough room in Path, or FALSE 548 */ 549 static BOOL 550 CabinetNormalizePath(PWCHAR Path, 551 ULONG Length) 552 { 553 ULONG n; 554 BOOL Ok; 555 556 n = wcslen(Path); 557 Ok = (n + 1) < Length; 558 559 if (n != 0 && Path[n - 1] != L'\\' && Ok) 560 { 561 Path[n] = L'\\'; 562 Path[n + 1] = 0; 563 } 564 565 return Ok; 566 } 567 568 /* 569 * FUNCTION: Returns pointer to cabinet file name 570 * RETURNS: 571 * Pointer to string with name of cabinet 572 */ 573 PCWSTR 574 CabinetGetCabinetName( 575 IN PCABINET_CONTEXT CabinetContext) 576 { 577 return CabinetContext->CabinetName; 578 } 579 580 /* 581 * FUNCTION: Sets cabinet file name 582 * ARGUMENTS: 583 * FileName = Pointer to string with name of cabinet 584 */ 585 VOID 586 CabinetSetCabinetName( 587 IN PCABINET_CONTEXT CabinetContext, 588 IN PCWSTR FileName) 589 { 590 wcscpy(CabinetContext->CabinetName, FileName); 591 } 592 593 /* 594 * FUNCTION: Sets destination path 595 * ARGUMENTS: 596 * DestinationPath = Pointer to string with name of destination path 597 */ 598 VOID 599 CabinetSetDestinationPath( 600 IN PCABINET_CONTEXT CabinetContext, 601 IN PCWSTR DestinationPath) 602 { 603 wcscpy(CabinetContext->DestPath, DestinationPath); 604 605 if (wcslen(CabinetContext->DestPath) > 0) 606 CabinetNormalizePath(CabinetContext->DestPath, MAX_PATH); 607 } 608 609 /* 610 * FUNCTION: Returns destination path 611 * RETURNS: 612 * Pointer to string with name of destination path 613 */ 614 PCWSTR 615 CabinetGetDestinationPath( 616 IN PCABINET_CONTEXT CabinetContext) 617 { 618 return CabinetContext->DestPath; 619 } 620 621 /* 622 * FUNCTION: Opens a cabinet file 623 * RETURNS: 624 * Status of operation 625 */ 626 ULONG 627 CabinetOpen( 628 IN OUT PCABINET_CONTEXT CabinetContext) 629 { 630 PUCHAR Buffer; 631 UNICODE_STRING ustring; 632 ANSI_STRING astring; 633 634 OBJECT_ATTRIBUTES ObjectAttributes; 635 IO_STATUS_BLOCK IoStatusBlock; 636 UNICODE_STRING FileName; 637 USHORT StringLength; 638 NTSTATUS NtStatus; 639 640 if (CabinetContext->FileOpen) 641 { 642 /* Cabinet file already opened */ 643 DPRINT("CabinetOpen returning SUCCESS\n"); 644 return CAB_STATUS_SUCCESS; 645 } 646 647 RtlInitUnicodeString(&FileName, CabinetContext->CabinetName); 648 649 InitializeObjectAttributes(&ObjectAttributes, 650 &FileName, 651 OBJ_CASE_INSENSITIVE, 652 NULL, NULL); 653 654 NtStatus = NtOpenFile(&CabinetContext->FileHandle, 655 GENERIC_READ | SYNCHRONIZE, 656 &ObjectAttributes, 657 &IoStatusBlock, 658 FILE_SHARE_READ, 659 FILE_SYNCHRONOUS_IO_NONALERT); 660 661 if (!NT_SUCCESS(NtStatus)) 662 { 663 DPRINT1("Cannot open file (%S) (%x)\n", CabinetContext->CabinetName, NtStatus); 664 return CAB_STATUS_CANNOT_OPEN; 665 } 666 667 CabinetContext->FileOpen = TRUE; 668 669 NtStatus = NtCreateSection(&CabinetContext->FileSectionHandle, 670 SECTION_ALL_ACCESS, 671 0, 0, 672 PAGE_READONLY, 673 SEC_COMMIT, 674 CabinetContext->FileHandle); 675 676 if (!NT_SUCCESS(NtStatus)) 677 { 678 DPRINT1("NtCreateSection failed for %ls: %x\n", CabinetContext->CabinetName, NtStatus); 679 return CAB_STATUS_NOMEMORY; 680 } 681 682 CabinetContext->FileBuffer = 0; 683 CabinetContext->FileSize = 0; 684 685 NtStatus = NtMapViewOfSection(CabinetContext->FileSectionHandle, 686 NtCurrentProcess(), 687 (PVOID*)&CabinetContext->FileBuffer, 688 0, 0, 0, 689 &CabinetContext->FileSize, 690 ViewUnmap, 691 0, 692 PAGE_READONLY); 693 694 if (!NT_SUCCESS(NtStatus)) 695 { 696 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus); 697 return CAB_STATUS_NOMEMORY; 698 } 699 700 DPRINT("Cabinet file %S opened and mapped to %x\n", 701 CabinetContext->CabinetName, CabinetContext->FileBuffer); 702 CabinetContext->PCABHeader = (PCFHEADER)CabinetContext->FileBuffer; 703 704 /* Check header */ 705 if (CabinetContext->FileSize <= sizeof(CFHEADER) || 706 CabinetContext->PCABHeader->Signature != CAB_SIGNATURE || 707 CabinetContext->PCABHeader->Version != CAB_VERSION || 708 CabinetContext->PCABHeader->FolderCount == 0 || 709 CabinetContext->PCABHeader->FileCount == 0 || 710 CabinetContext->PCABHeader->FileTableOffset < sizeof(CFHEADER)) 711 { 712 CloseCabinet(CabinetContext); 713 DPRINT1("File has invalid header\n"); 714 return CAB_STATUS_INVALID_CAB; 715 } 716 717 Buffer = (PUCHAR)(CabinetContext->PCABHeader + 1); 718 719 /* Read/skip any reserved bytes */ 720 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_RESERVE) 721 { 722 CabinetContext->CabinetReserved = *(PUSHORT)Buffer; 723 Buffer += 2; 724 CabinetContext->FolderReserved = *Buffer; 725 Buffer++; 726 CabinetContext->DataReserved = *Buffer; 727 Buffer++; 728 729 if (CabinetContext->CabinetReserved > 0) 730 { 731 CabinetContext->CabinetReservedArea = Buffer; 732 Buffer += CabinetContext->CabinetReserved; 733 } 734 } 735 736 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_HASPREV) 737 { 738 /* The previous cabinet file is in 739 the same directory as the current */ 740 wcscpy(CabinetContext->CabinetPrev, CabinetContext->CabinetName); 741 RemoveFileName(CabinetContext->CabinetPrev); 742 CabinetNormalizePath(CabinetContext->CabinetPrev, sizeof(CabinetContext->CabinetPrev)); 743 RtlInitAnsiString(&astring, (LPSTR)Buffer); 744 745 /* Initialize ustring with the remaining buffer */ 746 StringLength = (USHORT)wcslen(CabinetContext->CabinetPrev) * sizeof(WCHAR); 747 ustring.Buffer = CabinetContext->CabinetPrev + StringLength; 748 ustring.MaximumLength = sizeof(CabinetContext->CabinetPrev) - StringLength; 749 ustring.Length = 0; 750 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 751 Buffer += astring.Length + 1; 752 753 /* Read label of prev disk */ 754 RtlInitAnsiString(&astring, (LPSTR)Buffer); 755 ustring.Length = 0; 756 ustring.Buffer = CabinetContext->DiskPrev; 757 ustring.MaximumLength = sizeof(CabinetContext->DiskPrev); 758 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 759 Buffer += astring.Length + 1; 760 } 761 else 762 { 763 wcscpy(CabinetContext->CabinetPrev, L""); 764 wcscpy(CabinetContext->DiskPrev, L""); 765 } 766 767 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_HASNEXT) 768 { 769 /* The next cabinet file is in 770 the same directory as the previous */ 771 wcscpy(CabinetContext->CabinetNext, CabinetContext->CabinetName); 772 RemoveFileName(CabinetContext->CabinetNext); 773 CabinetNormalizePath(CabinetContext->CabinetNext, 256); 774 RtlInitAnsiString(&astring, (LPSTR)Buffer); 775 776 /* Initialize ustring with the remaining buffer */ 777 StringLength = (USHORT)wcslen(CabinetContext->CabinetNext) * sizeof(WCHAR); 778 ustring.Buffer = CabinetContext->CabinetNext + StringLength; 779 ustring.MaximumLength = sizeof(CabinetContext->CabinetNext) - StringLength; 780 ustring.Length = 0; 781 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 782 Buffer += astring.Length + 1; 783 784 /* Read label of next disk */ 785 RtlInitAnsiString(&astring, (LPSTR)Buffer); 786 ustring.Length = 0; 787 ustring.Buffer = CabinetContext->DiskNext; 788 ustring.MaximumLength = sizeof(CabinetContext->DiskNext); 789 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 790 Buffer += astring.Length + 1; 791 } 792 else 793 { 794 wcscpy(CabinetContext->CabinetNext, L""); 795 wcscpy(CabinetContext->DiskNext, L""); 796 } 797 CabinetContext->CabinetFolders = (PCFFOLDER)Buffer; 798 799 DPRINT("CabinetOpen returning SUCCESS\n"); 800 return CAB_STATUS_SUCCESS; 801 } 802 803 /* 804 * FUNCTION: Closes the cabinet file 805 */ 806 VOID 807 CabinetClose( 808 IN OUT PCABINET_CONTEXT CabinetContext) 809 { 810 if (!CabinetContext->FileOpen) 811 return; 812 813 CloseCabinet(CabinetContext); 814 CabinetContext->FileOpen = FALSE; 815 } 816 817 /* 818 * FUNCTION: Finds the first file in the cabinet that matches a search criteria 819 * ARGUMENTS: 820 * FileName = Pointer to search criteria 821 * Search = Pointer to search structure 822 * RETURNS: 823 * Status of operation 824 */ 825 ULONG 826 CabinetFindFirst( 827 IN PCABINET_CONTEXT CabinetContext, 828 IN PCWSTR FileName, 829 IN OUT PCAB_SEARCH Search) 830 { 831 DPRINT("CabinetFindFirst(FileName = %S)\n", FileName); 832 wcsncpy(Search->Search, FileName, MAX_PATH); 833 wcsncpy(Search->Cabinet, CabinetContext->CabinetName, MAX_PATH); 834 Search->File = 0; 835 return CabinetFindNext(CabinetContext, Search); 836 } 837 838 /* 839 * FUNCTION: Finds next file in the cabinet that matches a search criteria 840 * ARGUMENTS: 841 * Search = Pointer to search structure 842 * RETURNS: 843 * Status of operation 844 */ 845 ULONG 846 CabinetFindNext( 847 IN PCABINET_CONTEXT CabinetContext, 848 IN OUT PCAB_SEARCH Search) 849 { 850 PCFFILE Prev; 851 ANSI_STRING AnsiString; 852 UNICODE_STRING UnicodeString; 853 WCHAR FileName[MAX_PATH]; 854 855 if (wcscmp(Search->Cabinet, CabinetContext->CabinetName) != 0) 856 { 857 /* restart search of cabinet has changed since last find */ 858 Search->File = 0; 859 } 860 861 if (!Search->File) 862 { 863 /* starting new search or cabinet */ 864 Search->File = (PCFFILE)(CabinetContext->FileBuffer + CabinetContext->PCABHeader->FileTableOffset); 865 Search->Index = 0; 866 Prev = 0; 867 } 868 else 869 Prev = Search->File; 870 871 while (TRUE) 872 { 873 /* look at each file in the archive and see if we found a match */ 874 if (Search->File->FolderIndex == 0xFFFD || 875 Search->File->FolderIndex == 0xFFFF) 876 { 877 /* skip files continued from previous cab */ 878 DPRINT("Skipping file (%s): FileOffset (0x%X), " 879 "LastFileOffset (0x%X)\n", (char *)(Search->File + 1), 880 Search->File->FileOffset, CabinetContext->LastFileOffset); 881 } 882 else 883 { 884 // FIXME: check for match against search criteria 885 if (Search->File != Prev) 886 { 887 if (Prev == NULL || Search->File->FolderIndex != Prev->FolderIndex) 888 { 889 Search->CFData = NULL; 890 Search->Offset = 0; 891 } 892 893 /* don't match the file we started with */ 894 if (wcscmp(Search->Search, L"*") == 0) 895 { 896 /* take any file */ 897 break; 898 } 899 else 900 { 901 /* otherwise, try to match the exact file name */ 902 RtlInitAnsiString(&AnsiString, Search->File->FileName); 903 UnicodeString.Buffer = FileName; 904 UnicodeString.Buffer[0] = 0; 905 UnicodeString.Length = 0; 906 UnicodeString.MaximumLength = sizeof(FileName); 907 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 908 if (wcscmp(Search->Search, UnicodeString.Buffer) == 0) 909 break; 910 } 911 } 912 } 913 914 /* if we make it here we found no match, so move to the next file */ 915 Search->Index++; 916 if (Search->Index >= CabinetContext->PCABHeader->FileCount) 917 { 918 /* we have reached the end of this cabinet */ 919 DPRINT("End of cabinet reached\n"); 920 return CAB_STATUS_NOFILE; 921 } 922 else 923 Search->File = (PCFFILE)(strchr((char *)(Search->File + 1), 0) + 1); 924 } 925 926 DPRINT("Found file %s\n", Search->File->FileName); 927 return CAB_STATUS_SUCCESS; 928 } 929 930 /* 931 * FUNCTION: Finds the next file in the cabinet that matches a search criteria 932 * ARGUMENTS: 933 * FileName = Pointer to search criteria 934 * Search = Pointer to search structure 935 * RETURNS: 936 * Status of operation 937 */ 938 ULONG 939 CabinetFindNextFileSequential( 940 IN PCABINET_CONTEXT CabinetContext, 941 IN PCWSTR FileName, 942 IN OUT PCAB_SEARCH Search) 943 { 944 DPRINT("CabinetFindNextFileSequential(FileName = %S)\n", FileName); 945 wcsncpy(Search->Search, FileName, MAX_PATH); 946 return CabinetFindNext(CabinetContext, Search); 947 } 948 949 #if 0 950 int 951 Validate(VOID) 952 { 953 return (int)RtlValidateHeap(ProcessHeap, 0, 0); 954 } 955 #endif 956 957 /* 958 * FUNCTION: Extracts a file from the cabinet 959 * ARGUMENTS: 960 * Search = Pointer to PCAB_SEARCH structure used to locate the file 961 * RETURNS 962 * Status of operation 963 */ 964 ULONG 965 CabinetExtractFile( 966 IN PCABINET_CONTEXT CabinetContext, 967 IN PCAB_SEARCH Search) 968 { 969 ULONG Size; // remaining file bytes to decompress 970 ULONG CurrentOffset; // current uncompressed offset within the folder 971 PUCHAR CurrentBuffer; // current pointer to compressed data in the block 972 LONG RemainingBlock; // remaining comp data in the block 973 HANDLE DestFile; 974 HANDLE DestFileSection; 975 PVOID DestFileBuffer; // mapped view of dest file 976 PVOID CurrentDestBuffer; // pointer to the current position in the dest view 977 PCFDATA CFData; // current data block 978 ULONG Status; 979 FILETIME FileTime; 980 WCHAR DestName[MAX_PATH]; 981 NTSTATUS NtStatus; 982 UNICODE_STRING UnicodeString; 983 ANSI_STRING AnsiString; 984 IO_STATUS_BLOCK IoStatusBlock; 985 OBJECT_ATTRIBUTES ObjectAttributes; 986 FILE_BASIC_INFORMATION FileBasic; 987 PCFFOLDER CurrentFolder; 988 LARGE_INTEGER MaxDestFileSize; 989 LONG InputLength, OutputLength; 990 SIZE_T StringLength; 991 char Chunk[512]; 992 993 if (wcscmp(Search->Cabinet, CabinetContext->CabinetName) != 0) 994 { 995 /* the file is not in the current cabinet */ 996 DPRINT("File is not in this cabinet (%S != %S)\n", 997 Search->Cabinet, CabinetContext->CabinetName); 998 return CAB_STATUS_NOFILE; 999 } 1000 1001 /* look up the folder that the file specifies */ 1002 if (Search->File->FolderIndex == 0xFFFD || 1003 Search->File->FolderIndex == 0xFFFF) 1004 { 1005 /* folder is continued from previous cabinet, 1006 that shouldn't happen here */ 1007 return CAB_STATUS_NOFILE; 1008 } 1009 else if (Search->File->FolderIndex == 0xFFFE) 1010 { 1011 /* folder is the last in this cabinet and continues into next */ 1012 CurrentFolder = &CabinetContext->CabinetFolders[CabinetContext->PCABHeader->FolderCount - 1]; 1013 } 1014 else 1015 { 1016 /* folder is completely contained within this cabinet */ 1017 CurrentFolder = &CabinetContext->CabinetFolders[Search->File->FolderIndex]; 1018 } 1019 1020 switch (CurrentFolder->CompressionType & CAB_COMP_MASK) 1021 { 1022 case CAB_COMP_NONE: 1023 CabinetSelectCodec(CabinetContext, CAB_CODEC_RAW); 1024 break; 1025 case CAB_COMP_MSZIP: 1026 CabinetSelectCodec(CabinetContext, CAB_CODEC_MSZIP); 1027 break; 1028 default: 1029 return CAB_STATUS_UNSUPPCOMP; 1030 } 1031 1032 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n", 1033 (UINT)Search->File->FileOffset, (UINT)Search->File->FileSize); 1034 1035 if (CabinetContext->CreateFileHandler) 1036 { 1037 /* Call create context */ 1038 CurrentDestBuffer = CabinetContext->CreateFileHandler(CabinetContext, Search->File->FileSize); 1039 if (!CurrentDestBuffer) 1040 { 1041 DPRINT1("CreateFileHandler() failed\n"); 1042 return CAB_STATUS_CANNOT_CREATE; 1043 } 1044 } 1045 else 1046 { 1047 RtlInitAnsiString(&AnsiString, Search->File->FileName); 1048 wcscpy(DestName, CabinetContext->DestPath); 1049 StringLength = wcslen(DestName); 1050 UnicodeString.MaximumLength = sizeof(DestName) - (USHORT)StringLength * sizeof(WCHAR); 1051 UnicodeString.Buffer = DestName + StringLength; 1052 UnicodeString.Length = 0; 1053 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 1054 1055 /* Create destination file, fail if it already exists */ 1056 RtlInitUnicodeString(&UnicodeString, DestName); 1057 1058 InitializeObjectAttributes(&ObjectAttributes, 1059 &UnicodeString, 1060 OBJ_CASE_INSENSITIVE, 1061 NULL, NULL); 1062 1063 NtStatus = NtCreateFile(&DestFile, 1064 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1065 &ObjectAttributes, 1066 &IoStatusBlock, 1067 NULL, 1068 FILE_ATTRIBUTE_NORMAL, 1069 0, 1070 FILE_CREATE, 1071 FILE_SYNCHRONOUS_IO_NONALERT, 1072 NULL, 0); 1073 1074 if (!NT_SUCCESS(NtStatus)) 1075 { 1076 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 1077 1078 /* If file exists, ask to overwrite file */ 1079 if (CabinetContext->OverwriteHandler == NULL || 1080 CabinetContext->OverwriteHandler(CabinetContext, Search->File, DestName)) 1081 { 1082 /* Create destination file, overwrite if it already exists */ 1083 NtStatus = NtCreateFile(&DestFile, 1084 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1085 &ObjectAttributes, 1086 &IoStatusBlock, 1087 NULL, 1088 FILE_ATTRIBUTE_NORMAL, 1089 0, 1090 FILE_OVERWRITE, 1091 FILE_SYNCHRONOUS_IO_ALERT, 1092 NULL, 0); 1093 1094 if (!NT_SUCCESS(NtStatus)) 1095 { 1096 DPRINT1("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 1097 return CAB_STATUS_CANNOT_CREATE; 1098 } 1099 } 1100 else 1101 { 1102 DPRINT1("File (%S) exists\n", DestName); 1103 return CAB_STATUS_FILE_EXISTS; 1104 } 1105 } 1106 1107 MaxDestFileSize.QuadPart = Search->File->FileSize; 1108 NtStatus = NtCreateSection(&DestFileSection, 1109 SECTION_ALL_ACCESS, 1110 0, 1111 &MaxDestFileSize, 1112 PAGE_READWRITE, 1113 SEC_COMMIT, 1114 DestFile); 1115 1116 if (!NT_SUCCESS(NtStatus)) 1117 { 1118 DPRINT1("NtCreateSection failed for %ls: %x\n", DestName, NtStatus); 1119 Status = CAB_STATUS_NOMEMORY; 1120 goto CloseDestFile; 1121 } 1122 1123 DestFileBuffer = 0; 1124 CabinetContext->DestFileSize = 0; 1125 NtStatus = NtMapViewOfSection(DestFileSection, 1126 NtCurrentProcess(), 1127 &DestFileBuffer, 1128 0, 0, 0, 1129 &CabinetContext->DestFileSize, 1130 ViewUnmap, 1131 0, 1132 PAGE_READWRITE); 1133 1134 if (!NT_SUCCESS(NtStatus)) 1135 { 1136 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus); 1137 Status = CAB_STATUS_NOMEMORY; 1138 goto CloseDestFileSection; 1139 } 1140 1141 CurrentDestBuffer = DestFileBuffer; 1142 if (!ConvertDosDateTimeToFileTime(Search->File->FileDate, 1143 Search->File->FileTime, 1144 &FileTime)) 1145 { 1146 DPRINT1("DosDateTimeToFileTime() failed\n"); 1147 Status = CAB_STATUS_CANNOT_WRITE; 1148 goto UnmapDestFile; 1149 } 1150 1151 NtStatus = NtQueryInformationFile(DestFile, 1152 &IoStatusBlock, 1153 &FileBasic, 1154 sizeof(FILE_BASIC_INFORMATION), 1155 FileBasicInformation); 1156 if (!NT_SUCCESS(NtStatus)) 1157 { 1158 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus); 1159 } 1160 else 1161 { 1162 memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME)); 1163 1164 NtStatus = NtSetInformationFile(DestFile, 1165 &IoStatusBlock, 1166 &FileBasic, 1167 sizeof(FILE_BASIC_INFORMATION), 1168 FileBasicInformation); 1169 if (!NT_SUCCESS(NtStatus)) 1170 { 1171 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus); 1172 } 1173 } 1174 1175 SetAttributesOnFile(Search->File, DestFile); 1176 } 1177 1178 /* Call extract event handler */ 1179 if (CabinetContext->ExtractHandler != NULL) 1180 CabinetContext->ExtractHandler(CabinetContext, Search->File, DestName); 1181 1182 if (Search->CFData) 1183 CFData = Search->CFData; 1184 else 1185 CFData = (PCFDATA)(CabinetContext->CabinetFolders[Search->File->FolderIndex].DataOffset + CabinetContext->FileBuffer); 1186 1187 CurrentOffset = Search->Offset; 1188 while (CurrentOffset + CFData->UncompSize <= Search->File->FileOffset) 1189 { 1190 /* walk the data blocks until we reach 1191 the one containing the start of the file */ 1192 CurrentOffset += CFData->UncompSize; 1193 CFData = (PCFDATA)((char *)(CFData + 1) + CabinetContext->DataReserved + CFData->CompSize); 1194 } 1195 1196 Search->CFData = CFData; 1197 Search->Offset = CurrentOffset; 1198 1199 /* now decompress and discard any data in 1200 the block before the start of the file */ 1201 1202 /* start of comp data */ 1203 CurrentBuffer = ((unsigned char *)(CFData + 1)) + CabinetContext->DataReserved; 1204 RemainingBlock = CFData->CompSize; 1205 InputLength = RemainingBlock; 1206 1207 while (CurrentOffset < Search->File->FileOffset) 1208 { 1209 /* compute remaining uncomp bytes to start 1210 of file, bounded by size of chunk */ 1211 OutputLength = Search->File->FileOffset - CurrentOffset; 1212 if (OutputLength > (LONG)sizeof(Chunk)) 1213 OutputLength = sizeof(Chunk); 1214 1215 /* negate to signal NOT end of block */ 1216 OutputLength = -OutputLength; 1217 1218 CabinetContext->Codec->Uncompress(CabinetContext->Codec, 1219 Chunk, 1220 CurrentBuffer, 1221 &InputLength, 1222 &OutputLength); 1223 1224 /* add the uncomp bytes extracted to current folder offset */ 1225 CurrentOffset += OutputLength; 1226 /* add comp bytes consumed to CurrentBuffer */ 1227 CurrentBuffer += InputLength; 1228 /* subtract bytes consumed from bytes remaining in block */ 1229 RemainingBlock -= InputLength; 1230 /* neg for resume decompression of the same block */ 1231 InputLength = -RemainingBlock; 1232 } 1233 1234 /* now CurrentBuffer points to the first comp byte 1235 of the file, so we can begin decompressing */ 1236 1237 /* Size = remaining uncomp bytes of the file to decompress */ 1238 Size = Search->File->FileSize; 1239 while (Size > 0) 1240 { 1241 OutputLength = Size; 1242 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n", 1243 CurrentBuffer, RemainingBlock, Size); 1244 1245 Status = CabinetContext->Codec->Uncompress(CabinetContext->Codec, 1246 CurrentDestBuffer, 1247 CurrentBuffer, 1248 &InputLength, 1249 &OutputLength); 1250 if (Status != CS_SUCCESS) 1251 { 1252 DPRINT("Cannot uncompress block\n"); 1253 if (Status == CS_NOMEMORY) 1254 Status = CAB_STATUS_NOMEMORY; 1255 Status = CAB_STATUS_INVALID_CAB; 1256 goto UnmapDestFile; 1257 } 1258 1259 /* advance dest buffer by bytes produced */ 1260 CurrentDestBuffer = (PVOID)((ULONG_PTR)CurrentDestBuffer + OutputLength); 1261 /* advance src buffer by bytes consumed */ 1262 CurrentBuffer += InputLength; 1263 /* reduce remaining file bytes by bytes produced */ 1264 Size -= OutputLength; 1265 /* reduce remaining block size by bytes consumed */ 1266 RemainingBlock -= InputLength; 1267 if (Size > 0 && RemainingBlock == 0) 1268 { 1269 /* used up this block, move on to the next */ 1270 DPRINT("Out of block data\n"); 1271 CFData = (PCFDATA)CurrentBuffer; 1272 RemainingBlock = CFData->CompSize; 1273 CurrentBuffer = (unsigned char *)(CFData + 1) + CabinetContext->DataReserved; 1274 InputLength = RemainingBlock; 1275 } 1276 } 1277 1278 Status = CAB_STATUS_SUCCESS; 1279 1280 UnmapDestFile: 1281 if (!CabinetContext->CreateFileHandler) 1282 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer); 1283 1284 CloseDestFileSection: 1285 if (!CabinetContext->CreateFileHandler) 1286 NtClose(DestFileSection); 1287 1288 CloseDestFile: 1289 if (!CabinetContext->CreateFileHandler) 1290 NtClose(DestFile); 1291 1292 return Status; 1293 } 1294 1295 /* 1296 * FUNCTION: Selects codec engine to use 1297 * ARGUMENTS: 1298 * Id = Codec identifier 1299 */ 1300 VOID 1301 CabinetSelectCodec( 1302 IN PCABINET_CONTEXT CabinetContext, 1303 IN ULONG Id) 1304 { 1305 if (CabinetContext->CodecSelected) 1306 { 1307 if (Id == CabinetContext->CodecId) 1308 return; 1309 1310 CabinetContext->CodecSelected = FALSE; 1311 } 1312 1313 switch (Id) 1314 { 1315 case CAB_CODEC_RAW: 1316 { 1317 CabinetContext->Codec = &RawCodec; 1318 break; 1319 } 1320 1321 case CAB_CODEC_MSZIP: 1322 { 1323 CabinetContext->Codec = &MSZipCodec; 1324 CabinetContext->Codec->ZStream.zalloc = MSZipAlloc; 1325 CabinetContext->Codec->ZStream.zfree = MSZipFree; 1326 CabinetContext->Codec->ZStream.opaque = (voidpf)0; 1327 break; 1328 } 1329 1330 default: 1331 return; 1332 } 1333 1334 CabinetContext->CodecId = Id; 1335 CabinetContext->CodecSelected = TRUE; 1336 } 1337 1338 /* 1339 * FUNCTION: Set event handlers 1340 * ARGUMENTS: 1341 * Overwrite = Handler called when a file is to be overwritten 1342 * Extract = Handler called when a file is to be extracted 1343 * DiskChange = Handler called when changing the disk 1344 */ 1345 VOID 1346 CabinetSetEventHandlers( 1347 IN PCABINET_CONTEXT CabinetContext, 1348 IN PCABINET_OVERWRITE Overwrite, 1349 IN PCABINET_EXTRACT Extract, 1350 IN PCABINET_DISK_CHANGE DiskChange, 1351 IN PCABINET_CREATE_FILE CreateFile) 1352 { 1353 CabinetContext->OverwriteHandler = Overwrite; 1354 CabinetContext->ExtractHandler = Extract; 1355 CabinetContext->DiskChangeHandler = DiskChange; 1356 CabinetContext->CreateFileHandler = CreateFile; 1357 } 1358 1359 /* 1360 * FUNCTION: Get pointer to cabinet reserved area. NULL if none 1361 */ 1362 PVOID 1363 CabinetGetCabinetReservedArea( 1364 IN PCABINET_CONTEXT CabinetContext, 1365 OUT PULONG Size) 1366 { 1367 if (CabinetContext->CabinetReservedArea != NULL) 1368 { 1369 if (Size != NULL) 1370 { 1371 *Size = CabinetContext->CabinetReserved; 1372 } 1373 1374 return CabinetContext->CabinetReservedArea; 1375 } 1376 else 1377 { 1378 if (Size != NULL) 1379 { 1380 *Size = 0; 1381 } 1382 1383 return NULL; 1384 } 1385 } 1386