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