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