xref: /reactos/base/setup/usetup/spapisup/cabinet.c (revision 7d2f6b65)
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