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