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