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