xref: /reactos/base/setup/usetup/spapisup/cabinet.c (revision 8f1ab791)
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     RtlInitAnsiString(&AnsiString, Search->File->FileName);
1022     wcscpy(DestName, CabinetContext->DestPath);
1023     UnicodeString.MaximumLength = sizeof(DestName) - wcslen(DestName) * sizeof(WCHAR);
1024     UnicodeString.Buffer = DestName + wcslen(DestName);
1025     UnicodeString.Length = 0;
1026     RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
1027 
1028     /* Create destination file, fail if it already exists */
1029     RtlInitUnicodeString(&UnicodeString, DestName);
1030 
1031     InitializeObjectAttributes(&ObjectAttributes,
1032                                &UnicodeString,
1033                                OBJ_CASE_INSENSITIVE,
1034                                NULL, NULL);
1035 
1036     NtStatus = NtCreateFile(&DestFile,
1037                             GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
1038                             &ObjectAttributes,
1039                             &IoStatusBlock,
1040                             NULL,
1041                             FILE_ATTRIBUTE_NORMAL,
1042                             0,
1043                             FILE_CREATE,
1044                             FILE_SYNCHRONOUS_IO_NONALERT,
1045                             NULL, 0);
1046 
1047     if (!NT_SUCCESS(NtStatus))
1048     {
1049         DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus);
1050 
1051         /* If file exists, ask to overwrite file */
1052         if (CabinetContext->OverwriteHandler == NULL ||
1053             CabinetContext->OverwriteHandler(CabinetContext, Search->File, DestName))
1054         {
1055             /* Create destination file, overwrite if it already exists */
1056             NtStatus = NtCreateFile(&DestFile,
1057                                     GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
1058                                     &ObjectAttributes,
1059                                     &IoStatusBlock,
1060                                     NULL,
1061                                     FILE_ATTRIBUTE_NORMAL,
1062                                     0,
1063                                     FILE_OVERWRITE,
1064                                     FILE_SYNCHRONOUS_IO_ALERT,
1065                                     NULL, 0);
1066 
1067             if (!NT_SUCCESS(NtStatus))
1068             {
1069                 DPRINT1("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus);
1070                 return CAB_STATUS_CANNOT_CREATE;
1071             }
1072         }
1073         else
1074         {
1075             DPRINT1("File (%S) exists\n", DestName);
1076             return CAB_STATUS_FILE_EXISTS;
1077         }
1078     }
1079 
1080     MaxDestFileSize.QuadPart = Search->File->FileSize;
1081     NtStatus = NtCreateSection(&DestFileSection,
1082                                SECTION_ALL_ACCESS,
1083                                0,
1084                                &MaxDestFileSize,
1085                                PAGE_READWRITE,
1086                                SEC_COMMIT,
1087                                DestFile);
1088 
1089     if (!NT_SUCCESS(NtStatus))
1090     {
1091         DPRINT1("NtCreateSection failed for %ls: %x\n", DestName, NtStatus);
1092         Status = CAB_STATUS_NOMEMORY;
1093         goto CloseDestFile;
1094     }
1095 
1096     DestFileBuffer = 0;
1097     CabinetContext->DestFileSize = 0;
1098     NtStatus = NtMapViewOfSection(DestFileSection,
1099                                   NtCurrentProcess(),
1100                                   &DestFileBuffer,
1101                                   0, 0, 0,
1102                                   &CabinetContext->DestFileSize,
1103                                   ViewUnmap,
1104                                   0,
1105                                   PAGE_READWRITE);
1106 
1107     if (!NT_SUCCESS(NtStatus))
1108     {
1109         DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus);
1110         Status = CAB_STATUS_NOMEMORY;
1111         goto CloseDestFileSection;
1112     }
1113 
1114     CurrentDestBuffer = DestFileBuffer;
1115     if (!ConvertDosDateTimeToFileTime(Search->File->FileDate,
1116                                       Search->File->FileTime,
1117                                       &FileTime))
1118     {
1119         DPRINT1("DosDateTimeToFileTime() failed\n");
1120         Status = CAB_STATUS_CANNOT_WRITE;
1121         goto UnmapDestFile;
1122     }
1123 
1124     NtStatus = NtQueryInformationFile(DestFile,
1125                                       &IoStatusBlock,
1126                                       &FileBasic,
1127                                       sizeof(FILE_BASIC_INFORMATION),
1128                                       FileBasicInformation);
1129     if (!NT_SUCCESS(NtStatus))
1130     {
1131         DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus);
1132     }
1133     else
1134     {
1135         memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME));
1136 
1137         NtStatus = NtSetInformationFile(DestFile,
1138                                         &IoStatusBlock,
1139                                         &FileBasic,
1140                                         sizeof(FILE_BASIC_INFORMATION),
1141                                         FileBasicInformation);
1142         if (!NT_SUCCESS(NtStatus))
1143         {
1144             DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus);
1145         }
1146     }
1147 
1148     SetAttributesOnFile(Search->File, DestFile);
1149 
1150     /* Call extract event handler */
1151     if (CabinetContext->ExtractHandler != NULL)
1152         CabinetContext->ExtractHandler(CabinetContext, Search->File, DestName);
1153 
1154     if (Search->CFData)
1155         CFData = Search->CFData;
1156     else
1157         CFData = (PCFDATA)(CabinetContext->CabinetFolders[Search->File->FolderIndex].DataOffset + CabinetContext->FileBuffer);
1158 
1159     CurrentOffset = Search->Offset;
1160     while (CurrentOffset + CFData->UncompSize <= Search->File->FileOffset)
1161     {
1162         /* walk the data blocks until we reach
1163            the one containing the start of the file */
1164         CurrentOffset += CFData->UncompSize;
1165         CFData = (PCFDATA)((char *)(CFData + 1) + CabinetContext->DataReserved + CFData->CompSize);
1166     }
1167 
1168     Search->CFData = CFData;
1169     Search->Offset = CurrentOffset;
1170 
1171     /* now decompress and discard any data in
1172        the block before the start of the file */
1173 
1174     /* start of comp data */
1175     CurrentBuffer = ((unsigned char *)(CFData + 1)) + CabinetContext->DataReserved;
1176     RemainingBlock = CFData->CompSize;
1177     InputLength = RemainingBlock;
1178 
1179     while (CurrentOffset < Search->File->FileOffset)
1180     {
1181         /* compute remaining uncomp bytes to start
1182            of file, bounded by size of chunk */
1183         OutputLength = Search->File->FileOffset - CurrentOffset;
1184         if (OutputLength > (LONG)sizeof(Chunk))
1185             OutputLength = sizeof(Chunk);
1186 
1187         /* negate to signal NOT end of block */
1188         OutputLength = -OutputLength;
1189 
1190         CabinetContext->Codec->Uncompress(CabinetContext->Codec,
1191                                           Chunk,
1192                                           CurrentBuffer,
1193                                           &InputLength,
1194                                           &OutputLength);
1195 
1196         /* add the uncomp bytes extracted to current folder offset */
1197         CurrentOffset += OutputLength;
1198         /* add comp bytes consumed to CurrentBuffer */
1199         CurrentBuffer += InputLength;
1200         /* subtract bytes consumed from bytes remaining in block */
1201         RemainingBlock -= InputLength;
1202         /* neg for resume decompression of the same block */
1203         InputLength = -RemainingBlock;
1204     }
1205 
1206     /* now CurrentBuffer points to the first comp byte
1207        of the file, so we can begin decompressing */
1208 
1209     /* Size = remaining uncomp bytes of the file to decompress */
1210     Size = Search->File->FileSize;
1211     while (Size > 0)
1212     {
1213         OutputLength = Size;
1214         DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n",
1215                CurrentBuffer, RemainingBlock, Size);
1216 
1217         Status = CabinetContext->Codec->Uncompress(CabinetContext->Codec,
1218                                                    CurrentDestBuffer,
1219                                                    CurrentBuffer,
1220                                                    &InputLength,
1221                                                    &OutputLength);
1222         if (Status != CS_SUCCESS)
1223         {
1224             DPRINT("Cannot uncompress block\n");
1225             if (Status == CS_NOMEMORY)
1226                 Status = CAB_STATUS_NOMEMORY;
1227             Status = CAB_STATUS_INVALID_CAB;
1228             goto UnmapDestFile;
1229         }
1230 
1231         /* advance dest buffer by bytes produced */
1232         CurrentDestBuffer = (PVOID)((ULONG_PTR)CurrentDestBuffer + OutputLength);
1233         /* advance src buffer by bytes consumed */
1234         CurrentBuffer += InputLength;
1235         /* reduce remaining file bytes by bytes produced */
1236         Size -= OutputLength;
1237         /* reduce remaining block size by bytes consumed */
1238         RemainingBlock -= InputLength;
1239         if (Size > 0 && RemainingBlock == 0)
1240         {
1241             /* used up this block, move on to the next */
1242             DPRINT("Out of block data\n");
1243             CFData = (PCFDATA)CurrentBuffer;
1244             RemainingBlock = CFData->CompSize;
1245             CurrentBuffer = (unsigned char *)(CFData + 1) + CabinetContext->DataReserved;
1246             InputLength = RemainingBlock;
1247         }
1248     }
1249 
1250     Status = CAB_STATUS_SUCCESS;
1251 
1252 UnmapDestFile:
1253     NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer);
1254 
1255 CloseDestFileSection:
1256     NtClose(DestFileSection);
1257 
1258 CloseDestFile:
1259     NtClose(DestFile);
1260 
1261     return Status;
1262 }
1263 
1264 /*
1265  * FUNCTION: Selects codec engine to use
1266  * ARGUMENTS:
1267  *     Id = Codec identifier
1268  */
1269 VOID
1270 CabinetSelectCodec(
1271     IN PCABINET_CONTEXT CabinetContext,
1272     IN ULONG Id)
1273 {
1274     if (CabinetContext->CodecSelected)
1275     {
1276         if (Id == CabinetContext->CodecId)
1277             return;
1278 
1279         CabinetContext->CodecSelected = FALSE;
1280     }
1281 
1282     switch (Id)
1283     {
1284         case CAB_CODEC_RAW:
1285         {
1286             CabinetContext->Codec = &RawCodec;
1287             break;
1288         }
1289 
1290         case CAB_CODEC_MSZIP:
1291         {
1292             CabinetContext->Codec = &MSZipCodec;
1293             CabinetContext->Codec->ZStream.zalloc = MSZipAlloc;
1294             CabinetContext->Codec->ZStream.zfree = MSZipFree;
1295             CabinetContext->Codec->ZStream.opaque = (voidpf)0;
1296             break;
1297         }
1298 
1299         default:
1300             return;
1301     }
1302 
1303     CabinetContext->CodecId = Id;
1304     CabinetContext->CodecSelected = TRUE;
1305 }
1306 
1307 /*
1308  * FUNCTION: Set event handlers
1309  * ARGUMENTS:
1310  *     Overwrite = Handler called when a file is to be overwritten
1311  *     Extract = Handler called when a file is to be extracted
1312  *     DiskChange = Handler called when changing the disk
1313  */
1314 VOID
1315 CabinetSetEventHandlers(
1316     IN PCABINET_CONTEXT CabinetContext,
1317     IN PCABINET_OVERWRITE Overwrite,
1318     IN PCABINET_EXTRACT Extract,
1319     IN PCABINET_DISK_CHANGE DiskChange)
1320 {
1321     CabinetContext->OverwriteHandler = Overwrite;
1322     CabinetContext->ExtractHandler = Extract;
1323     CabinetContext->DiskChangeHandler = DiskChange;
1324 }
1325 
1326 /*
1327  * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1328  */
1329 PVOID
1330 CabinetGetCabinetReservedArea(
1331     IN PCABINET_CONTEXT CabinetContext,
1332     OUT PULONG Size)
1333 {
1334     if (CabinetContext->CabinetReservedArea != NULL)
1335     {
1336         if (Size != NULL)
1337         {
1338             *Size = CabinetContext->CabinetReserved;
1339         }
1340 
1341         return CabinetContext->CabinetReservedArea;
1342     }
1343     else
1344     {
1345         if (Size != NULL)
1346         {
1347             *Size = 0;
1348         }
1349 
1350         return NULL;
1351     }
1352 }
1353