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)
8 * CSH 15/08-2003 Created
18 #define SEEK_CURRENT 1
23 typedef struct _DOSTIME
31 typedef struct _DOSDATE
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;
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
;
68 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
72 return RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, size
);
78 RtlFreeHeap(ProcessHeap
, 0, ptr
);
82 calloc(size_t nmemb
, size_t size
)
84 return (void *)RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, nmemb
* size
);
90 * FUNCTION: Uncompresses data in a buffer
92 * OutputBuffer = Pointer to buffer to place uncompressed data
93 * InputBuffer = Pointer to buffer with data to be uncompressed
94 * InputLength = Length of input buffer before, and amount consumed after
95 * Negative to indicate that this is not the start of a new block
96 * OutputLength = Length of output buffer before, amount filled after
97 * Negative to indicate that this is not the end of the block
100 RawCodecUncompress(PVOID OutputBuffer
,
105 LONG Len
= min(abs(*InputLength
), abs(*OutputLength
));
107 memcpy(OutputBuffer
, InputBuffer
, Len
);
108 *InputLength
= *OutputLength
= Len
;
116 * FUNCTION: Uncompresses data in a buffer
118 * OutputBuffer = Pointer to buffer to place uncompressed data
119 * InputBuffer = Pointer to buffer with data to be uncompressed
120 * InputLength = Length of input buffer before, and amount consumed after
121 * Negative to indicate that this is not the start of a new block
122 * OutputLength = Length of output buffer before, amount filled after
123 * Negative to indicate that this is not the end of the block
126 MSZipCodecUncompress(PVOID OutputBuffer
,
134 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, "
135 "InputLength = %d, OutputLength = %d)\n", OutputBuffer
,
136 InputBuffer
, *InputLength
, *OutputLength
);
137 if (*InputLength
> 0)
139 Magic
= *(PUSHORT
)InputBuffer
;
141 if (Magic
!= MSZIP_MAGIC
)
143 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
147 ZStream
.next_in
= (PUCHAR
)InputBuffer
+ 2;
148 ZStream
.avail_in
= *InputLength
- 2;
149 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
150 ZStream
.avail_out
= abs(*OutputLength
);
152 /* WindowBits is passed < 0 to tell that there is no zlib header.
153 * Note that in this case inflate *requires* an extra "dummy" byte
154 * after the compressed stream in order to complete decompression and
155 * return Z_STREAM_END.
157 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
160 DPRINT("inflateInit2() returned (%d)\n", Status
);
163 ZStream
.total_in
= 2;
167 ZStream
.avail_in
= -*InputLength
;
168 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
169 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
170 ZStream
.avail_out
= abs(*OutputLength
);
171 ZStream
.total_in
= 0;
174 ZStream
.total_out
= 0;
175 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
176 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
178 DPRINT("inflate() returned (%d) (%s)\n", Status
, ZStream
.msg
);
179 if (Status
== Z_MEM_ERROR
)
184 if (*OutputLength
> 0)
186 Status
= inflateEnd(&ZStream
);
189 DPRINT("inflateEnd() returned (%d)\n", Status
);
194 *OutputLength
= ZStream
.total_out
;
195 *InputLength
= ZStream
.total_in
;
200 /* Memory functions */
203 MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
205 return (voidpf
)RtlAllocateHeap(ProcessHeap
, 0, items
* size
);
209 MSZipFree(voidpf opaque
, voidpf address
)
211 RtlFreeHeap(ProcessHeap
, 0, address
);
215 ConvertSystemTimeToFileTime(CONST SYSTEMTIME
*lpSystemTime
,
216 LPFILETIME lpFileTime
)
218 TIME_FIELDS TimeFields
;
219 LARGE_INTEGER liTime
;
221 TimeFields
.Year
= lpSystemTime
->wYear
;
222 TimeFields
.Month
= lpSystemTime
->wMonth
;
223 TimeFields
.Day
= lpSystemTime
->wDay
;
224 TimeFields
.Hour
= lpSystemTime
->wHour
;
225 TimeFields
.Minute
= lpSystemTime
->wMinute
;
226 TimeFields
.Second
= lpSystemTime
->wSecond
;
227 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
229 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
231 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
232 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
240 ConvertDosDateTimeToFileTime(WORD wFatDate
,
242 LPFILETIME lpFileTime
)
244 PDOSTIME pdtime
= (PDOSTIME
)&wFatTime
;
245 PDOSDATE pddate
= (PDOSDATE
)&wFatDate
;
246 SYSTEMTIME SystemTime
;
248 if (lpFileTime
== NULL
)
251 SystemTime
.wMilliseconds
= 0;
252 SystemTime
.wSecond
= pdtime
->Second
;
253 SystemTime
.wMinute
= pdtime
->Minute
;
254 SystemTime
.wHour
= pdtime
->Hour
;
256 SystemTime
.wDay
= pddate
->Day
;
257 SystemTime
.wMonth
= pddate
->Month
;
258 SystemTime
.wYear
= 1980 + pddate
->Year
;
260 ConvertSystemTimeToFileTime(&SystemTime
, lpFileTime
);
266 * FUNCTION: Returns a pointer to file name
268 * Path = Pointer to string with pathname
270 * Pointer to filename
273 GetFileName(PWCHAR Path
)
281 if (Path
[i
- 1] == L
'\\')
289 * FUNCTION: Removes a file name from a path
291 * Path = Pointer to string with path
294 RemoveFileName(PWCHAR Path
)
300 FileName
= GetFileName(Path
+ i
);
302 if (FileName
!= Path
+ i
&& FileName
[-1] == L
'\\')
305 if (FileName
== Path
+ i
&& FileName
[0] == L
'\\')
312 * FUNCTION: Sets attributes on a file
314 * File = Pointer to CFFILE node for file
316 * Status of operation
319 SetAttributesOnFile(PCFFILE File
,
322 FILE_BASIC_INFORMATION FileBasic
;
323 IO_STATUS_BLOCK IoStatusBlock
;
325 ULONG Attributes
= 0;
327 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
328 Attributes
|= FILE_ATTRIBUTE_READONLY
;
330 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
331 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
333 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
334 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
336 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
337 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
339 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
340 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
342 NtStatus
= NtQueryInformationFile(hFile
,
345 sizeof(FILE_BASIC_INFORMATION
),
346 FileBasicInformation
);
347 if (!NT_SUCCESS(NtStatus
))
349 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
353 FileBasic
.FileAttributes
= Attributes
;
355 NtStatus
= NtSetInformationFile(hFile
,
358 sizeof(FILE_BASIC_INFORMATION
),
359 FileBasicInformation
);
360 if (!NT_SUCCESS(NtStatus
))
362 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
366 return NT_SUCCESS(NtStatus
);
370 * FUNCTION: Closes the current cabinet
372 * Status of operation
379 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer
);
380 NtClose(FileSectionHandle
);
389 * FUNCTION: Initialize archiver
392 CabinetInitialize(VOID
)
394 ZStream
.zalloc
= MSZipAlloc
;
395 ZStream
.zfree
= MSZipFree
;
396 ZStream
.opaque
= (voidpf
)0;
399 wcscpy(DestPath
, L
"");
401 CodecId
= CAB_CODEC_RAW
;
402 CodecSelected
= TRUE
;
404 FolderUncompSize
= 0;
405 BytesLeftInBlock
= 0;
409 CabinetReservedArea
= NULL
;
414 * FUNCTION: Cleanup archiver
423 * FUNCTION: Normalizes a path
425 * Path = Pointer to string with pathname
426 * Length = Number of characters in Path
428 * TRUE if there was enough room in Path, or FALSE
431 CabinetNormalizePath(PWCHAR Path
,
438 Ok
= (n
+ 1) < Length
;
440 if (n
!= 0 && Path
[n
- 1] != L
'\\' && Ok
)
450 * FUNCTION: Returns pointer to cabinet file name
452 * Pointer to string with name of cabinet
455 CabinetGetCabinetName(VOID
)
461 * FUNCTION: Sets cabinet file name
463 * FileName = Pointer to string with name of cabinet
466 CabinetSetCabinetName(PWCHAR FileName
)
468 wcscpy(CabinetName
, FileName
);
472 * FUNCTION: Sets destination path
474 * DestinationPath = Pointer to string with name of destination path
477 CabinetSetDestinationPath(PWCHAR DestinationPath
)
479 wcscpy(DestPath
, DestinationPath
);
481 if (wcslen(DestPath
) > 0)
482 CabinetNormalizePath(DestPath
, MAX_PATH
);
486 * FUNCTION: Returns destination path
488 * Pointer to string with name of destination path
491 CabinetGetDestinationPath(VOID
)
497 * FUNCTION: Opens a cabinet file
499 * Status of operation
505 UNICODE_STRING ustring
;
510 OBJECT_ATTRIBUTES ObjectAttributes
;
511 IO_STATUS_BLOCK IoStatusBlock
;
512 UNICODE_STRING FileName
;
516 RtlInitUnicodeString(&FileName
, CabinetName
);
518 InitializeObjectAttributes(&ObjectAttributes
,
520 OBJ_CASE_INSENSITIVE
,
523 NtStatus
= NtOpenFile(&FileHandle
,
524 GENERIC_READ
| SYNCHRONIZE
,
528 FILE_SYNCHRONOUS_IO_NONALERT
);
530 if (!NT_SUCCESS(NtStatus
))
532 DPRINT("Cannot open file (%S) (%x)\n", CabinetName
, NtStatus
);
533 return CAB_STATUS_CANNOT_OPEN
;
538 NtStatus
= NtCreateSection(&FileSectionHandle
,
545 if (!NT_SUCCESS(NtStatus
))
547 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
548 return CAB_STATUS_NOMEMORY
;
554 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
556 (PVOID
*)&FileBuffer
,
563 if (!NT_SUCCESS(NtStatus
))
565 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
566 return CAB_STATUS_NOMEMORY
;
569 DPRINT("Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
570 PCABHeader
= (PCFHEADER
) FileBuffer
;
573 if (FileSize
<= sizeof(CFHEADER
) ||
574 PCABHeader
->Signature
!= CAB_SIGNATURE
||
575 PCABHeader
->Version
!= CAB_VERSION
||
576 PCABHeader
->FolderCount
== 0 ||
577 PCABHeader
->FileCount
== 0 ||
578 PCABHeader
->FileTableOffset
< sizeof(CFHEADER
))
581 DPRINT("File has invalid header\n");
582 return CAB_STATUS_INVALID_CAB
;
586 Buffer
= (PUCHAR
)(PCABHeader
+ 1);
588 /* Read/skip any reserved bytes */
589 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
591 CabinetReserved
= *(PUSHORT
)Buffer
;
593 FolderReserved
= *Buffer
;
595 DataReserved
= *Buffer
;
598 if (CabinetReserved
> 0)
600 CabinetReservedArea
= Buffer
;
601 Buffer
+= CabinetReserved
;
605 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
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;
619 /* Read label of prev disk */
620 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
622 ustring
.Buffer
= DiskPrev
;
623 ustring
.MaximumLength
= sizeof(DiskPrev
);
624 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
625 Buffer
+= astring
.Length
+ 1;
629 wcscpy(CabinetPrev
, L
"");
630 wcscpy(DiskPrev
, L
"");
633 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
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;
647 /* Read label of next disk */
648 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
650 ustring
.Buffer
= DiskNext
;
651 ustring
.MaximumLength
= sizeof(DiskNext
);
652 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
653 Buffer
+= astring
.Length
+ 1;
657 wcscpy(CabinetNext
, L
"");
658 wcscpy(DiskNext
, L
"");
660 CabinetFolders
= (PCFFOLDER
)Buffer
;
663 DPRINT("CabinetOpen returning SUCCESS\n");
664 return CAB_STATUS_SUCCESS
;
668 * FUNCTION: Closes the cabinet file
681 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
683 * FileName = Pointer to search criteria
684 * Search = Pointer to search structure
686 * Status of operation
689 CabinetFindFirst(PWCHAR FileName
,
692 DPRINT("CabinetFindFirst( FileName = %S )\n", FileName
);
693 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
694 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
696 return CabinetFindNext(Search
);
700 * FUNCTION: Finds next file in the cabinet that matches a search criteria
702 * Search = Pointer to search structure
704 * Status of operation
707 CabinetFindNext(PCAB_SEARCH Search
)
711 ANSI_STRING AnsiString
;
712 UNICODE_STRING UnicodeString
;
713 WCHAR FileName
[MAX_PATH
];
715 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
717 /* restart search of cabinet has changed since last find */
723 /* starting new search or cabinet */
724 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
733 /* look at each file in the archive and see if we found a match */
734 if (Search
->File
->FolderIndex
== 0xFFFD ||
735 Search
->File
->FolderIndex
== 0xFFFF)
737 /* skip files continued from previous cab */
738 DPRINT("Skipping file (%s): FileOffset (0x%X), "
739 "LastFileOffset (0x%X)\n", (char *)(Search
->File
+ 1),
740 Search
->File
->FileOffset
, LastFileOffset
);
744 // FIXME: check for match against search criteria
745 if (Search
->File
!= Prev
)
747 /* don't match the file we started with */
748 if (wcscmp(Search
->Search
, L
"*") == 0)
755 /* otherwise, try to match the exact file name */
756 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
757 UnicodeString
.Buffer
= FileName
;
758 UnicodeString
.Buffer
[0] = 0;
759 UnicodeString
.Length
= 0;
760 UnicodeString
.MaximumLength
= sizeof(FileName
);
761 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
762 if (wcscmp(Search
->Search
, UnicodeString
.Buffer
) == 0)
768 /* if we make it here we found no match, so move to the next file */
770 if (Search
->Index
>= PCABHeader
->FileCount
)
772 /* we have reached the end of this cabinet, try to open the next */
773 DPRINT("End of cabinet reached\n");
774 if (wcslen(DiskNext
) > 0)
778 CabinetSetCabinetName(CabinetNext
);
779 wcscpy(Search
->Cabinet
, CabinetName
);
781 if (DiskChangeHandler
!= NULL
)
783 DiskChangeHandler(CabinetNext
, DiskNext
);
786 Status
= CabinetOpen();
787 if (Status
!= CAB_STATUS_SUCCESS
)
792 return CAB_STATUS_NOFILE
;
795 /* starting new search or cabinet */
796 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
801 Search
->File
= (PCFFILE
)(strchr((char *)(Search
->File
+ 1), 0) + 1);
804 DPRINT("Found file %s\n", Search
->File
->FileName
);
805 return CAB_STATUS_SUCCESS
;
812 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
817 * FUNCTION: Extracts a file from the cabinet
819 * Search = Pointer to PCAB_SEARCH structure used to locate the file
821 * Status of operation
824 CabinetExtractFile(PCAB_SEARCH Search
)
826 ULONG Size
; // remaining file bytes to decompress
827 ULONG CurrentOffset
; // current uncompressed offset within the folder
828 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
829 LONG RemainingBlock
; // remaining comp data in the block
831 HANDLE DestFileSection
;
832 PVOID DestFileBuffer
; // mapped view of dest file
833 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
834 PCFDATA CFData
; // current data block
837 WCHAR DestName
[MAX_PATH
];
839 UNICODE_STRING UnicodeString
;
840 ANSI_STRING AnsiString
;
841 IO_STATUS_BLOCK IoStatusBlock
;
842 OBJECT_ATTRIBUTES ObjectAttributes
;
843 FILE_BASIC_INFORMATION FileBasic
;
844 PCFFOLDER CurrentFolder
;
845 LARGE_INTEGER MaxDestFileSize
;
846 LONG InputLength
, OutputLength
;
849 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
851 /* the file is not in the current cabinet */
852 DPRINT("File is not in this cabinet (%S != %S)\n",
853 Search
->Cabinet
, CabinetName
);
854 return CAB_STATUS_NOFILE
;
857 /* look up the folder that the file specifies */
858 if (Search
->File
->FolderIndex
== 0xFFFD ||
859 Search
->File
->FolderIndex
== 0xFFFF)
861 /* folder is continued from previous cabinet,
862 that shouldn't happen here */
863 return CAB_STATUS_NOFILE
;
865 else if (Search
->File
->FolderIndex
== 0xFFFE)
867 /* folder is the last in this cabinet and continues into next */
868 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
- 1];
872 /* folder is completely contained within this cabinet */
873 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
876 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
879 CabinetSelectCodec(CAB_CODEC_RAW
);
882 CabinetSelectCodec(CAB_CODEC_MSZIP
);
885 return CAB_STATUS_UNSUPPCOMP
;
888 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n",
889 (UINT
)Search
->File
->FileOffset
, (UINT
)Search
->File
->FileSize
);
891 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
892 wcscpy(DestName
, DestPath
);
893 UnicodeString
.MaximumLength
= sizeof(DestName
) - wcslen(DestName
);
894 UnicodeString
.Buffer
= DestName
+ wcslen(DestName
);
895 UnicodeString
.Length
= 0;
896 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
898 /* Create destination file, fail if it already exists */
899 RtlInitUnicodeString(&UnicodeString
, DestName
);
901 InitializeObjectAttributes(&ObjectAttributes
,
903 OBJ_CASE_INSENSITIVE
,
906 NtStatus
= NtCreateFile(&DestFile
,
907 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
911 FILE_ATTRIBUTE_NORMAL
,
914 FILE_SYNCHRONOUS_IO_NONALERT
,
917 if (!NT_SUCCESS(NtStatus
))
919 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
921 /* If file exists, ask to overwrite file */
922 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
924 /* Create destination file, overwrite if it already exists */
925 NtStatus
= NtCreateFile(&DestFile
,
926 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
930 FILE_ATTRIBUTE_NORMAL
,
933 FILE_SYNCHRONOUS_IO_ALERT
,
936 if (!NT_SUCCESS(NtStatus
))
938 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
939 return CAB_STATUS_CANNOT_CREATE
;
944 DPRINT("File (%S) exists\n", DestName
);
945 return CAB_STATUS_FILE_EXISTS
;
949 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
950 NtStatus
= NtCreateSection(&DestFileSection
,
958 if (!NT_SUCCESS(NtStatus
))
960 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
961 Status
= CAB_STATUS_NOMEMORY
;
967 NtStatus
= NtMapViewOfSection(DestFileSection
,
976 if (!NT_SUCCESS(NtStatus
))
978 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
979 Status
= CAB_STATUS_NOMEMORY
;
980 goto CloseDestFileSection
;
983 CurrentDestBuffer
= DestFileBuffer
;
984 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
,
985 Search
->File
->FileTime
,
988 DPRINT("DosDateTimeToFileTime() failed\n");
989 Status
= CAB_STATUS_CANNOT_WRITE
;
993 NtStatus
= NtQueryInformationFile(DestFile
,
996 sizeof(FILE_BASIC_INFORMATION
),
997 FileBasicInformation
);
998 if (!NT_SUCCESS(NtStatus
))
1000 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
1004 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1006 NtStatus
= NtSetInformationFile(DestFile
,
1009 sizeof(FILE_BASIC_INFORMATION
),
1010 FileBasicInformation
);
1011 if (!NT_SUCCESS(NtStatus
))
1013 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
1017 SetAttributesOnFile(Search
->File
, DestFile
);
1019 /* Call extract event handler */
1020 if (ExtractHandler
!= NULL
)
1022 ExtractHandler(Search
->File
, DestName
);
1025 /* find the starting block of the file
1026 start with the first data block of the folder */
1027 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
1029 while (CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
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
);
1037 /* now decompress and discard any data in
1038 the block before the start of the file */
1040 /* start of comp data */
1041 CurrentBuffer
= ((unsigned char *)(CFData
+ 1)) + DataReserved
;
1042 RemainingBlock
= CFData
->CompSize
;
1043 InputLength
= RemainingBlock
;
1045 while (CurrentOffset
< Search
->File
->FileOffset
)
1047 /* compute remaining uncomp bytes to start
1048 of file, bounded by sizeof junk */
1049 OutputLength
= Search
->File
->FileOffset
- CurrentOffset
;
1050 if (OutputLength
> (LONG
)sizeof(Junk
))
1051 OutputLength
= sizeof (Junk
);
1053 /* negate to signal NOT end of block */
1054 OutputLength
= -OutputLength
;
1055 CodecUncompress(Junk
, CurrentBuffer
, &InputLength
, &OutputLength
);
1056 /* add the uncomp bytes extracted to current folder offset */
1057 CurrentOffset
+= OutputLength
;
1058 /* add comp bytes consumed to CurrentBuffer */
1059 CurrentBuffer
+= InputLength
;
1060 /* subtract bytes consumed from bytes remaining in block */
1061 RemainingBlock
-= InputLength
;
1062 /* neg for resume decompression of the same block */
1063 InputLength
= -RemainingBlock
;
1066 /* now CurrentBuffer points to the first comp byte
1067 of the file, so we can begin decompressing */
1069 /* Size = remaining uncomp bytes of the file to decompress */
1070 Size
= Search
->File
->FileSize
;
1073 OutputLength
= Size
;
1074 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n",
1075 CurrentBuffer
, RemainingBlock
, Size
);
1077 Status
= CodecUncompress(CurrentDestBuffer
,
1082 if (Status
!= CS_SUCCESS
)
1084 DPRINT("Cannot uncompress block\n");
1085 if (Status
== CS_NOMEMORY
)
1086 Status
= CAB_STATUS_NOMEMORY
;
1087 Status
= CAB_STATUS_INVALID_CAB
;
1091 /* advance dest buffer by bytes produced */
1092 CurrentDestBuffer
= (PVOID
)((ULONG_PTR
)CurrentDestBuffer
+ OutputLength
);
1093 /* advance src buffer by bytes consumed */
1094 CurrentBuffer
+= InputLength
;
1095 /* reduce remaining file bytes by bytes produced */
1096 Size
-= OutputLength
;
1097 /* reduce remaining block size by bytes consumed */
1098 RemainingBlock
-= InputLength
;
1099 if (RemainingBlock
== 0)
1101 /* used up this block, move on to the next */
1102 DPRINT("Out of block data\n");
1103 CFData
= (PCFDATA
)CurrentBuffer
;
1104 RemainingBlock
= CFData
->CompSize
;
1105 CurrentBuffer
= (unsigned char *)(CFData
+ 1) + DataReserved
;
1106 InputLength
= RemainingBlock
;
1110 Status
= CAB_STATUS_SUCCESS
;
1113 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1115 CloseDestFileSection
:
1116 NtClose(DestFileSection
);
1125 * FUNCTION: Selects codec engine to use
1127 * Id = Codec identifier
1130 CabinetSelectCodec(ULONG Id
)
1137 CodecSelected
= FALSE
;
1143 CodecUncompress
= RawCodecUncompress
;
1145 case CAB_CODEC_MSZIP
:
1146 CodecUncompress
= MSZipCodecUncompress
;
1153 CodecSelected
= TRUE
;
1157 * FUNCTION: Set event handlers
1159 * Overwrite = Handler called when a file is to be overwritten
1160 * Extract = Handler called when a file is to be extracted
1161 * DiskChange = Handler called when changing the disk
1164 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1165 PCABINET_EXTRACT Extract
,
1166 PCABINET_DISK_CHANGE DiskChange
)
1168 OverwriteHandler
= Overwrite
;
1169 ExtractHandler
= Extract
;
1170 DiskChangeHandler
= DiskChange
;
1174 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1177 CabinetGetCabinetReservedArea(PULONG Size
)
1179 if (CabinetReservedArea
!= NULL
)
1183 *Size
= CabinetReserved
;
1186 return CabinetReservedArea
;