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 NTSTATUS NtStatus; 632 633 if (CabinetContext->FileOpen) 634 { 635 /* Cabinet file already opened */ 636 DPRINT("CabinetOpen returning SUCCESS\n"); 637 return CAB_STATUS_SUCCESS; 638 } 639 640 RtlInitUnicodeString(&FileName, CabinetContext->CabinetName); 641 642 InitializeObjectAttributes(&ObjectAttributes, 643 &FileName, 644 OBJ_CASE_INSENSITIVE, 645 NULL, NULL); 646 647 NtStatus = NtOpenFile(&CabinetContext->FileHandle, 648 GENERIC_READ | SYNCHRONIZE, 649 &ObjectAttributes, 650 &IoStatusBlock, 651 FILE_SHARE_READ, 652 FILE_SYNCHRONOUS_IO_NONALERT); 653 654 if (!NT_SUCCESS(NtStatus)) 655 { 656 DPRINT1("Cannot open file (%S) (%x)\n", CabinetContext->CabinetName, NtStatus); 657 return CAB_STATUS_CANNOT_OPEN; 658 } 659 660 CabinetContext->FileOpen = TRUE; 661 662 NtStatus = NtCreateSection(&CabinetContext->FileSectionHandle, 663 SECTION_ALL_ACCESS, 664 0, 0, 665 PAGE_READONLY, 666 SEC_COMMIT, 667 CabinetContext->FileHandle); 668 669 if (!NT_SUCCESS(NtStatus)) 670 { 671 DPRINT1("NtCreateSection failed for %ls: %x\n", CabinetContext->CabinetName, NtStatus); 672 return CAB_STATUS_NOMEMORY; 673 } 674 675 CabinetContext->FileBuffer = 0; 676 CabinetContext->FileSize = 0; 677 678 NtStatus = NtMapViewOfSection(CabinetContext->FileSectionHandle, 679 NtCurrentProcess(), 680 (PVOID*)&CabinetContext->FileBuffer, 681 0, 0, 0, 682 &CabinetContext->FileSize, 683 ViewUnmap, 684 0, 685 PAGE_READONLY); 686 687 if (!NT_SUCCESS(NtStatus)) 688 { 689 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus); 690 return CAB_STATUS_NOMEMORY; 691 } 692 693 DPRINT("Cabinet file %S opened and mapped to %x\n", 694 CabinetContext->CabinetName, CabinetContext->FileBuffer); 695 CabinetContext->PCABHeader = (PCFHEADER)CabinetContext->FileBuffer; 696 697 /* Check header */ 698 if (CabinetContext->FileSize <= sizeof(CFHEADER) || 699 CabinetContext->PCABHeader->Signature != CAB_SIGNATURE || 700 CabinetContext->PCABHeader->Version != CAB_VERSION || 701 CabinetContext->PCABHeader->FolderCount == 0 || 702 CabinetContext->PCABHeader->FileCount == 0 || 703 CabinetContext->PCABHeader->FileTableOffset < sizeof(CFHEADER)) 704 { 705 CloseCabinet(CabinetContext); 706 DPRINT1("File has invalid header\n"); 707 return CAB_STATUS_INVALID_CAB; 708 } 709 710 Buffer = (PUCHAR)(CabinetContext->PCABHeader + 1); 711 712 /* Read/skip any reserved bytes */ 713 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_RESERVE) 714 { 715 CabinetContext->CabinetReserved = *(PUSHORT)Buffer; 716 Buffer += 2; 717 CabinetContext->FolderReserved = *Buffer; 718 Buffer++; 719 CabinetContext->DataReserved = *Buffer; 720 Buffer++; 721 722 if (CabinetContext->CabinetReserved > 0) 723 { 724 CabinetContext->CabinetReservedArea = Buffer; 725 Buffer += CabinetContext->CabinetReserved; 726 } 727 } 728 729 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_HASPREV) 730 { 731 /* The previous cabinet file is in 732 the same directory as the current */ 733 wcscpy(CabinetContext->CabinetPrev, CabinetContext->CabinetName); 734 RemoveFileName(CabinetContext->CabinetPrev); 735 CabinetNormalizePath(CabinetContext->CabinetPrev, 256); 736 RtlInitAnsiString(&astring, (LPSTR)Buffer); 737 ustring.Length = wcslen(CabinetContext->CabinetPrev); 738 ustring.Buffer = CabinetContext->CabinetPrev + ustring.Length; 739 ustring.MaximumLength = sizeof(CabinetContext->CabinetPrev) - ustring.Length; 740 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 741 Buffer += astring.Length + 1; 742 743 /* Read label of prev disk */ 744 RtlInitAnsiString(&astring, (LPSTR)Buffer); 745 ustring.Length = 0; 746 ustring.Buffer = CabinetContext->DiskPrev; 747 ustring.MaximumLength = sizeof(CabinetContext->DiskPrev); 748 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 749 Buffer += astring.Length + 1; 750 } 751 else 752 { 753 wcscpy(CabinetContext->CabinetPrev, L""); 754 wcscpy(CabinetContext->DiskPrev, L""); 755 } 756 757 if (CabinetContext->PCABHeader->Flags & CAB_FLAG_HASNEXT) 758 { 759 /* The next cabinet file is in 760 the same directory as the previous */ 761 wcscpy(CabinetContext->CabinetNext, CabinetContext->CabinetName); 762 RemoveFileName(CabinetContext->CabinetNext); 763 CabinetNormalizePath(CabinetContext->CabinetNext, 256); 764 RtlInitAnsiString(&astring, (LPSTR)Buffer); 765 ustring.Length = wcslen(CabinetContext->CabinetNext); 766 ustring.Buffer = CabinetContext->CabinetNext + ustring.Length; 767 ustring.MaximumLength = sizeof(CabinetContext->CabinetNext) - ustring.Length; 768 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 769 Buffer += astring.Length + 1; 770 771 /* Read label of next disk */ 772 RtlInitAnsiString(&astring, (LPSTR)Buffer); 773 ustring.Length = 0; 774 ustring.Buffer = CabinetContext->DiskNext; 775 ustring.MaximumLength = sizeof(CabinetContext->DiskNext); 776 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 777 Buffer += astring.Length + 1; 778 } 779 else 780 { 781 wcscpy(CabinetContext->CabinetNext, L""); 782 wcscpy(CabinetContext->DiskNext, L""); 783 } 784 CabinetContext->CabinetFolders = (PCFFOLDER)Buffer; 785 786 DPRINT("CabinetOpen returning SUCCESS\n"); 787 return CAB_STATUS_SUCCESS; 788 } 789 790 /* 791 * FUNCTION: Closes the cabinet file 792 */ 793 VOID 794 CabinetClose( 795 IN OUT PCABINET_CONTEXT CabinetContext) 796 { 797 if (!CabinetContext->FileOpen) 798 return; 799 800 CloseCabinet(CabinetContext); 801 CabinetContext->FileOpen = FALSE; 802 } 803 804 /* 805 * FUNCTION: Finds the first file in the cabinet that matches a search criteria 806 * ARGUMENTS: 807 * FileName = Pointer to search criteria 808 * Search = Pointer to search structure 809 * RETURNS: 810 * Status of operation 811 */ 812 ULONG 813 CabinetFindFirst( 814 IN PCABINET_CONTEXT CabinetContext, 815 IN PCWSTR FileName, 816 IN OUT PCAB_SEARCH Search) 817 { 818 DPRINT("CabinetFindFirst(FileName = %S)\n", FileName); 819 wcsncpy(Search->Search, FileName, MAX_PATH); 820 wcsncpy(Search->Cabinet, CabinetContext->CabinetName, MAX_PATH); 821 Search->File = 0; 822 return CabinetFindNext(CabinetContext, Search); 823 } 824 825 /* 826 * FUNCTION: Finds next file in the cabinet that matches a search criteria 827 * ARGUMENTS: 828 * Search = Pointer to search structure 829 * RETURNS: 830 * Status of operation 831 */ 832 ULONG 833 CabinetFindNext( 834 IN PCABINET_CONTEXT CabinetContext, 835 IN OUT PCAB_SEARCH Search) 836 { 837 PCFFILE Prev; 838 ANSI_STRING AnsiString; 839 UNICODE_STRING UnicodeString; 840 WCHAR FileName[MAX_PATH]; 841 842 if (wcscmp(Search->Cabinet, CabinetContext->CabinetName) != 0) 843 { 844 /* restart search of cabinet has changed since last find */ 845 Search->File = 0; 846 } 847 848 if (!Search->File) 849 { 850 /* starting new search or cabinet */ 851 Search->File = (PCFFILE)(CabinetContext->FileBuffer + CabinetContext->PCABHeader->FileTableOffset); 852 Search->Index = 0; 853 Prev = 0; 854 } 855 else 856 Prev = Search->File; 857 858 while (TRUE) 859 { 860 /* look at each file in the archive and see if we found a match */ 861 if (Search->File->FolderIndex == 0xFFFD || 862 Search->File->FolderIndex == 0xFFFF) 863 { 864 /* skip files continued from previous cab */ 865 DPRINT("Skipping file (%s): FileOffset (0x%X), " 866 "LastFileOffset (0x%X)\n", (char *)(Search->File + 1), 867 Search->File->FileOffset, CabinetContext->LastFileOffset); 868 } 869 else 870 { 871 // FIXME: check for match against search criteria 872 if (Search->File != Prev) 873 { 874 if (Prev == NULL || Search->File->FolderIndex != Prev->FolderIndex) 875 { 876 Search->CFData = NULL; 877 Search->Offset = 0; 878 } 879 880 /* don't match the file we started with */ 881 if (wcscmp(Search->Search, L"*") == 0) 882 { 883 /* take any file */ 884 break; 885 } 886 else 887 { 888 /* otherwise, try to match the exact file name */ 889 RtlInitAnsiString(&AnsiString, Search->File->FileName); 890 UnicodeString.Buffer = FileName; 891 UnicodeString.Buffer[0] = 0; 892 UnicodeString.Length = 0; 893 UnicodeString.MaximumLength = sizeof(FileName); 894 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 895 if (wcscmp(Search->Search, UnicodeString.Buffer) == 0) 896 break; 897 } 898 } 899 } 900 901 /* if we make it here we found no match, so move to the next file */ 902 Search->Index++; 903 if (Search->Index >= CabinetContext->PCABHeader->FileCount) 904 { 905 /* we have reached the end of this cabinet */ 906 DPRINT("End of cabinet reached\n"); 907 return CAB_STATUS_NOFILE; 908 } 909 else 910 Search->File = (PCFFILE)(strchr((char *)(Search->File + 1), 0) + 1); 911 } 912 913 DPRINT("Found file %s\n", Search->File->FileName); 914 return CAB_STATUS_SUCCESS; 915 } 916 917 /* 918 * FUNCTION: Finds the next file in the cabinet that matches a search criteria 919 * ARGUMENTS: 920 * FileName = Pointer to search criteria 921 * Search = Pointer to search structure 922 * RETURNS: 923 * Status of operation 924 */ 925 ULONG 926 CabinetFindNextFileSequential( 927 IN PCABINET_CONTEXT CabinetContext, 928 IN PCWSTR FileName, 929 IN OUT PCAB_SEARCH Search) 930 { 931 DPRINT("CabinetFindNextFileSequential(FileName = %S)\n", FileName); 932 wcsncpy(Search->Search, FileName, MAX_PATH); 933 return CabinetFindNext(CabinetContext, Search); 934 } 935 936 #if 0 937 int 938 Validate(VOID) 939 { 940 return (int)RtlValidateHeap(ProcessHeap, 0, 0); 941 } 942 #endif 943 944 /* 945 * FUNCTION: Extracts a file from the cabinet 946 * ARGUMENTS: 947 * Search = Pointer to PCAB_SEARCH structure used to locate the file 948 * RETURNS 949 * Status of operation 950 */ 951 ULONG 952 CabinetExtractFile( 953 IN PCABINET_CONTEXT CabinetContext, 954 IN PCAB_SEARCH Search) 955 { 956 ULONG Size; // remaining file bytes to decompress 957 ULONG CurrentOffset; // current uncompressed offset within the folder 958 PUCHAR CurrentBuffer; // current pointer to compressed data in the block 959 LONG RemainingBlock; // remaining comp data in the block 960 HANDLE DestFile; 961 HANDLE DestFileSection; 962 PVOID DestFileBuffer; // mapped view of dest file 963 PVOID CurrentDestBuffer; // pointer to the current position in the dest view 964 PCFDATA CFData; // current data block 965 ULONG Status; 966 FILETIME FileTime; 967 WCHAR DestName[MAX_PATH]; 968 NTSTATUS NtStatus; 969 UNICODE_STRING UnicodeString; 970 ANSI_STRING AnsiString; 971 IO_STATUS_BLOCK IoStatusBlock; 972 OBJECT_ATTRIBUTES ObjectAttributes; 973 FILE_BASIC_INFORMATION FileBasic; 974 PCFFOLDER CurrentFolder; 975 LARGE_INTEGER MaxDestFileSize; 976 LONG InputLength, OutputLength; 977 char Chunk[512]; 978 979 if (wcscmp(Search->Cabinet, CabinetContext->CabinetName) != 0) 980 { 981 /* the file is not in the current cabinet */ 982 DPRINT("File is not in this cabinet (%S != %S)\n", 983 Search->Cabinet, CabinetContext->CabinetName); 984 return CAB_STATUS_NOFILE; 985 } 986 987 /* look up the folder that the file specifies */ 988 if (Search->File->FolderIndex == 0xFFFD || 989 Search->File->FolderIndex == 0xFFFF) 990 { 991 /* folder is continued from previous cabinet, 992 that shouldn't happen here */ 993 return CAB_STATUS_NOFILE; 994 } 995 else if (Search->File->FolderIndex == 0xFFFE) 996 { 997 /* folder is the last in this cabinet and continues into next */ 998 CurrentFolder = &CabinetContext->CabinetFolders[CabinetContext->PCABHeader->FolderCount - 1]; 999 } 1000 else 1001 { 1002 /* folder is completely contained within this cabinet */ 1003 CurrentFolder = &CabinetContext->CabinetFolders[Search->File->FolderIndex]; 1004 } 1005 1006 switch (CurrentFolder->CompressionType & CAB_COMP_MASK) 1007 { 1008 case CAB_COMP_NONE: 1009 CabinetSelectCodec(CabinetContext, CAB_CODEC_RAW); 1010 break; 1011 case CAB_COMP_MSZIP: 1012 CabinetSelectCodec(CabinetContext, CAB_CODEC_MSZIP); 1013 break; 1014 default: 1015 return CAB_STATUS_UNSUPPCOMP; 1016 } 1017 1018 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n", 1019 (UINT)Search->File->FileOffset, (UINT)Search->File->FileSize); 1020 1021 if (CabinetContext->CreateFileHandler) 1022 { 1023 /* Call create context */ 1024 CurrentDestBuffer = CabinetContext->CreateFileHandler(CabinetContext, Search->File->FileSize); 1025 if (!CurrentDestBuffer) 1026 { 1027 DPRINT1("CreateFileHandler() failed\n"); 1028 return CAB_STATUS_CANNOT_CREATE; 1029 } 1030 } 1031 else 1032 { 1033 RtlInitAnsiString(&AnsiString, Search->File->FileName); 1034 wcscpy(DestName, CabinetContext->DestPath); 1035 UnicodeString.MaximumLength = sizeof(DestName) - wcslen(DestName) * sizeof(WCHAR); 1036 UnicodeString.Buffer = DestName + wcslen(DestName); 1037 UnicodeString.Length = 0; 1038 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 1039 1040 /* Create destination file, fail if it already exists */ 1041 RtlInitUnicodeString(&UnicodeString, DestName); 1042 1043 InitializeObjectAttributes(&ObjectAttributes, 1044 &UnicodeString, 1045 OBJ_CASE_INSENSITIVE, 1046 NULL, NULL); 1047 1048 NtStatus = NtCreateFile(&DestFile, 1049 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1050 &ObjectAttributes, 1051 &IoStatusBlock, 1052 NULL, 1053 FILE_ATTRIBUTE_NORMAL, 1054 0, 1055 FILE_CREATE, 1056 FILE_SYNCHRONOUS_IO_NONALERT, 1057 NULL, 0); 1058 1059 if (!NT_SUCCESS(NtStatus)) 1060 { 1061 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 1062 1063 /* If file exists, ask to overwrite file */ 1064 if (CabinetContext->OverwriteHandler == NULL || 1065 CabinetContext->OverwriteHandler(CabinetContext, Search->File, DestName)) 1066 { 1067 /* Create destination file, overwrite if it already exists */ 1068 NtStatus = NtCreateFile(&DestFile, 1069 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1070 &ObjectAttributes, 1071 &IoStatusBlock, 1072 NULL, 1073 FILE_ATTRIBUTE_NORMAL, 1074 0, 1075 FILE_OVERWRITE, 1076 FILE_SYNCHRONOUS_IO_ALERT, 1077 NULL, 0); 1078 1079 if (!NT_SUCCESS(NtStatus)) 1080 { 1081 DPRINT1("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 1082 return CAB_STATUS_CANNOT_CREATE; 1083 } 1084 } 1085 else 1086 { 1087 DPRINT1("File (%S) exists\n", DestName); 1088 return CAB_STATUS_FILE_EXISTS; 1089 } 1090 } 1091 1092 MaxDestFileSize.QuadPart = Search->File->FileSize; 1093 NtStatus = NtCreateSection(&DestFileSection, 1094 SECTION_ALL_ACCESS, 1095 0, 1096 &MaxDestFileSize, 1097 PAGE_READWRITE, 1098 SEC_COMMIT, 1099 DestFile); 1100 1101 if (!NT_SUCCESS(NtStatus)) 1102 { 1103 DPRINT1("NtCreateSection failed for %ls: %x\n", DestName, NtStatus); 1104 Status = CAB_STATUS_NOMEMORY; 1105 goto CloseDestFile; 1106 } 1107 1108 DestFileBuffer = 0; 1109 CabinetContext->DestFileSize = 0; 1110 NtStatus = NtMapViewOfSection(DestFileSection, 1111 NtCurrentProcess(), 1112 &DestFileBuffer, 1113 0, 0, 0, 1114 &CabinetContext->DestFileSize, 1115 ViewUnmap, 1116 0, 1117 PAGE_READWRITE); 1118 1119 if (!NT_SUCCESS(NtStatus)) 1120 { 1121 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus); 1122 Status = CAB_STATUS_NOMEMORY; 1123 goto CloseDestFileSection; 1124 } 1125 1126 CurrentDestBuffer = DestFileBuffer; 1127 if (!ConvertDosDateTimeToFileTime(Search->File->FileDate, 1128 Search->File->FileTime, 1129 &FileTime)) 1130 { 1131 DPRINT1("DosDateTimeToFileTime() failed\n"); 1132 Status = CAB_STATUS_CANNOT_WRITE; 1133 goto UnmapDestFile; 1134 } 1135 1136 NtStatus = NtQueryInformationFile(DestFile, 1137 &IoStatusBlock, 1138 &FileBasic, 1139 sizeof(FILE_BASIC_INFORMATION), 1140 FileBasicInformation); 1141 if (!NT_SUCCESS(NtStatus)) 1142 { 1143 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus); 1144 } 1145 else 1146 { 1147 memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME)); 1148 1149 NtStatus = NtSetInformationFile(DestFile, 1150 &IoStatusBlock, 1151 &FileBasic, 1152 sizeof(FILE_BASIC_INFORMATION), 1153 FileBasicInformation); 1154 if (!NT_SUCCESS(NtStatus)) 1155 { 1156 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus); 1157 } 1158 } 1159 1160 SetAttributesOnFile(Search->File, DestFile); 1161 } 1162 1163 /* Call extract event handler */ 1164 if (CabinetContext->ExtractHandler != NULL) 1165 CabinetContext->ExtractHandler(CabinetContext, Search->File, DestName); 1166 1167 if (Search->CFData) 1168 CFData = Search->CFData; 1169 else 1170 CFData = (PCFDATA)(CabinetContext->CabinetFolders[Search->File->FolderIndex].DataOffset + CabinetContext->FileBuffer); 1171 1172 CurrentOffset = Search->Offset; 1173 while (CurrentOffset + CFData->UncompSize <= Search->File->FileOffset) 1174 { 1175 /* walk the data blocks until we reach 1176 the one containing the start of the file */ 1177 CurrentOffset += CFData->UncompSize; 1178 CFData = (PCFDATA)((char *)(CFData + 1) + CabinetContext->DataReserved + CFData->CompSize); 1179 } 1180 1181 Search->CFData = CFData; 1182 Search->Offset = CurrentOffset; 1183 1184 /* now decompress and discard any data in 1185 the block before the start of the file */ 1186 1187 /* start of comp data */ 1188 CurrentBuffer = ((unsigned char *)(CFData + 1)) + CabinetContext->DataReserved; 1189 RemainingBlock = CFData->CompSize; 1190 InputLength = RemainingBlock; 1191 1192 while (CurrentOffset < Search->File->FileOffset) 1193 { 1194 /* compute remaining uncomp bytes to start 1195 of file, bounded by size of chunk */ 1196 OutputLength = Search->File->FileOffset - CurrentOffset; 1197 if (OutputLength > (LONG)sizeof(Chunk)) 1198 OutputLength = sizeof(Chunk); 1199 1200 /* negate to signal NOT end of block */ 1201 OutputLength = -OutputLength; 1202 1203 CabinetContext->Codec->Uncompress(CabinetContext->Codec, 1204 Chunk, 1205 CurrentBuffer, 1206 &InputLength, 1207 &OutputLength); 1208 1209 /* add the uncomp bytes extracted to current folder offset */ 1210 CurrentOffset += OutputLength; 1211 /* add comp bytes consumed to CurrentBuffer */ 1212 CurrentBuffer += InputLength; 1213 /* subtract bytes consumed from bytes remaining in block */ 1214 RemainingBlock -= InputLength; 1215 /* neg for resume decompression of the same block */ 1216 InputLength = -RemainingBlock; 1217 } 1218 1219 /* now CurrentBuffer points to the first comp byte 1220 of the file, so we can begin decompressing */ 1221 1222 /* Size = remaining uncomp bytes of the file to decompress */ 1223 Size = Search->File->FileSize; 1224 while (Size > 0) 1225 { 1226 OutputLength = Size; 1227 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n", 1228 CurrentBuffer, RemainingBlock, Size); 1229 1230 Status = CabinetContext->Codec->Uncompress(CabinetContext->Codec, 1231 CurrentDestBuffer, 1232 CurrentBuffer, 1233 &InputLength, 1234 &OutputLength); 1235 if (Status != CS_SUCCESS) 1236 { 1237 DPRINT("Cannot uncompress block\n"); 1238 if (Status == CS_NOMEMORY) 1239 Status = CAB_STATUS_NOMEMORY; 1240 Status = CAB_STATUS_INVALID_CAB; 1241 goto UnmapDestFile; 1242 } 1243 1244 /* advance dest buffer by bytes produced */ 1245 CurrentDestBuffer = (PVOID)((ULONG_PTR)CurrentDestBuffer + OutputLength); 1246 /* advance src buffer by bytes consumed */ 1247 CurrentBuffer += InputLength; 1248 /* reduce remaining file bytes by bytes produced */ 1249 Size -= OutputLength; 1250 /* reduce remaining block size by bytes consumed */ 1251 RemainingBlock -= InputLength; 1252 if (Size > 0 && RemainingBlock == 0) 1253 { 1254 /* used up this block, move on to the next */ 1255 DPRINT("Out of block data\n"); 1256 CFData = (PCFDATA)CurrentBuffer; 1257 RemainingBlock = CFData->CompSize; 1258 CurrentBuffer = (unsigned char *)(CFData + 1) + CabinetContext->DataReserved; 1259 InputLength = RemainingBlock; 1260 } 1261 } 1262 1263 Status = CAB_STATUS_SUCCESS; 1264 1265 UnmapDestFile: 1266 if (!CabinetContext->CreateFileHandler) 1267 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer); 1268 1269 CloseDestFileSection: 1270 if (!CabinetContext->CreateFileHandler) 1271 NtClose(DestFileSection); 1272 1273 CloseDestFile: 1274 if (!CabinetContext->CreateFileHandler) 1275 NtClose(DestFile); 1276 1277 return Status; 1278 } 1279 1280 /* 1281 * FUNCTION: Selects codec engine to use 1282 * ARGUMENTS: 1283 * Id = Codec identifier 1284 */ 1285 VOID 1286 CabinetSelectCodec( 1287 IN PCABINET_CONTEXT CabinetContext, 1288 IN ULONG Id) 1289 { 1290 if (CabinetContext->CodecSelected) 1291 { 1292 if (Id == CabinetContext->CodecId) 1293 return; 1294 1295 CabinetContext->CodecSelected = FALSE; 1296 } 1297 1298 switch (Id) 1299 { 1300 case CAB_CODEC_RAW: 1301 { 1302 CabinetContext->Codec = &RawCodec; 1303 break; 1304 } 1305 1306 case CAB_CODEC_MSZIP: 1307 { 1308 CabinetContext->Codec = &MSZipCodec; 1309 CabinetContext->Codec->ZStream.zalloc = MSZipAlloc; 1310 CabinetContext->Codec->ZStream.zfree = MSZipFree; 1311 CabinetContext->Codec->ZStream.opaque = (voidpf)0; 1312 break; 1313 } 1314 1315 default: 1316 return; 1317 } 1318 1319 CabinetContext->CodecId = Id; 1320 CabinetContext->CodecSelected = TRUE; 1321 } 1322 1323 /* 1324 * FUNCTION: Set event handlers 1325 * ARGUMENTS: 1326 * Overwrite = Handler called when a file is to be overwritten 1327 * Extract = Handler called when a file is to be extracted 1328 * DiskChange = Handler called when changing the disk 1329 */ 1330 VOID 1331 CabinetSetEventHandlers( 1332 IN PCABINET_CONTEXT CabinetContext, 1333 IN PCABINET_OVERWRITE Overwrite, 1334 IN PCABINET_EXTRACT Extract, 1335 IN PCABINET_DISK_CHANGE DiskChange, 1336 IN PCABINET_CREATE_FILE CreateFile) 1337 { 1338 CabinetContext->OverwriteHandler = Overwrite; 1339 CabinetContext->ExtractHandler = Extract; 1340 CabinetContext->DiskChangeHandler = DiskChange; 1341 CabinetContext->CreateFileHandler = CreateFile; 1342 } 1343 1344 /* 1345 * FUNCTION: Get pointer to cabinet reserved area. NULL if none 1346 */ 1347 PVOID 1348 CabinetGetCabinetReservedArea( 1349 IN PCABINET_CONTEXT CabinetContext, 1350 OUT PULONG Size) 1351 { 1352 if (CabinetContext->CabinetReservedArea != NULL) 1353 { 1354 if (Size != NULL) 1355 { 1356 *Size = CabinetContext->CabinetReserved; 1357 } 1358 1359 return CabinetContext->CabinetReservedArea; 1360 } 1361 else 1362 { 1363 if (Size != NULL) 1364 { 1365 *Size = 0; 1366 } 1367 1368 return NULL; 1369 } 1370 } 1371