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)
8 * CSH 15/08-2003 Created
20 #define SEEK_CURRENT 1
25 typedef struct _DOSTIME
33 typedef struct _DOSDATE
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;
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
;
70 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
74 return RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, size
);
80 RtlFreeHeap(ProcessHeap
, 0, ptr
);
84 calloc(size_t nmemb
, size_t size
)
86 return (void *)RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, nmemb
* size
);
92 * FUNCTION: Uncompresses data in a buffer
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
102 RawCodecUncompress(PVOID OutputBuffer
,
107 LONG Len
= min(abs(*InputLength
), abs(*OutputLength
));
109 memcpy(OutputBuffer
, InputBuffer
, Len
);
110 *InputLength
= *OutputLength
= Len
;
118 * FUNCTION: Uncompresses data in a buffer
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
128 MSZipCodecUncompress(PVOID OutputBuffer
,
136 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, "
137 "InputLength = %d, OutputLength = %d)\n", OutputBuffer
,
138 InputBuffer
, *InputLength
, *OutputLength
);
139 if (*InputLength
> 0)
141 Magic
= *(PUSHORT
)InputBuffer
;
143 if (Magic
!= MSZIP_MAGIC
)
145 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
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
);
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.
159 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
162 DPRINT("inflateInit2() returned (%d)\n", Status
);
165 ZStream
.total_in
= 2;
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;
176 ZStream
.total_out
= 0;
177 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
178 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
180 DPRINT("inflate() returned (%d) (%s)\n", Status
, ZStream
.msg
);
181 if (Status
== Z_MEM_ERROR
)
186 if (*OutputLength
> 0)
188 Status
= inflateEnd(&ZStream
);
191 DPRINT("inflateEnd() returned (%d)\n", Status
);
196 *OutputLength
= ZStream
.total_out
;
197 *InputLength
= ZStream
.total_in
;
202 /* Memory functions */
205 MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
207 return (voidpf
)RtlAllocateHeap(ProcessHeap
, 0, items
* size
);
211 MSZipFree(voidpf opaque
, voidpf address
)
213 RtlFreeHeap(ProcessHeap
, 0, address
);
217 ConvertSystemTimeToFileTime(CONST SYSTEMTIME
*lpSystemTime
,
218 LPFILETIME lpFileTime
)
220 TIME_FIELDS TimeFields
;
221 LARGE_INTEGER liTime
;
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
;
231 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
233 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
234 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
242 ConvertDosDateTimeToFileTime(WORD wFatDate
,
244 LPFILETIME lpFileTime
)
246 PDOSTIME pdtime
= (PDOSTIME
)&wFatTime
;
247 PDOSDATE pddate
= (PDOSDATE
)&wFatDate
;
248 SYSTEMTIME SystemTime
;
250 if (lpFileTime
== NULL
)
253 SystemTime
.wMilliseconds
= 0;
254 SystemTime
.wSecond
= pdtime
->Second
;
255 SystemTime
.wMinute
= pdtime
->Minute
;
256 SystemTime
.wHour
= pdtime
->Hour
;
258 SystemTime
.wDay
= pddate
->Day
;
259 SystemTime
.wMonth
= pddate
->Month
;
260 SystemTime
.wYear
= 1980 + pddate
->Year
;
262 ConvertSystemTimeToFileTime(&SystemTime
, lpFileTime
);
268 * FUNCTION: Returns a pointer to file name
270 * Path = Pointer to string with pathname
272 * Pointer to filename
275 GetFileName(PWCHAR Path
)
283 if (Path
[i
- 1] == L
'\\')
291 * FUNCTION: Removes a file name from a path
293 * Path = Pointer to string with path
296 RemoveFileName(PWCHAR Path
)
302 FileName
= GetFileName(Path
+ i
);
304 if (FileName
!= Path
+ i
&& FileName
[-1] == L
'\\')
307 if (FileName
== Path
+ i
&& FileName
[0] == L
'\\')
314 * FUNCTION: Sets attributes on a file
316 * File = Pointer to CFFILE node for file
318 * Status of operation
321 SetAttributesOnFile(PCFFILE File
,
324 FILE_BASIC_INFORMATION FileBasic
;
325 IO_STATUS_BLOCK IoStatusBlock
;
327 ULONG Attributes
= 0;
329 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
330 Attributes
|= FILE_ATTRIBUTE_READONLY
;
332 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
333 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
335 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
336 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
338 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
339 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
341 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
342 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
344 NtStatus
= NtQueryInformationFile(hFile
,
347 sizeof(FILE_BASIC_INFORMATION
),
348 FileBasicInformation
);
349 if (!NT_SUCCESS(NtStatus
))
351 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
355 FileBasic
.FileAttributes
= Attributes
;
357 NtStatus
= NtSetInformationFile(hFile
,
360 sizeof(FILE_BASIC_INFORMATION
),
361 FileBasicInformation
);
362 if (!NT_SUCCESS(NtStatus
))
364 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
368 return NT_SUCCESS(NtStatus
);
372 * FUNCTION: Closes the current cabinet
374 * Status of operation
381 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer
);
382 NtClose(FileSectionHandle
);
391 * FUNCTION: Initialize archiver
394 CabinetInitialize(VOID
)
396 ZStream
.zalloc
= MSZipAlloc
;
397 ZStream
.zfree
= MSZipFree
;
398 ZStream
.opaque
= (voidpf
)0;
401 wcscpy(DestPath
, L
"");
403 CodecId
= CAB_CODEC_RAW
;
404 CodecSelected
= TRUE
;
406 FolderUncompSize
= 0;
407 BytesLeftInBlock
= 0;
411 CabinetReservedArea
= NULL
;
416 * FUNCTION: Cleanup archiver
425 * FUNCTION: Normalizes a path
427 * Path = Pointer to string with pathname
428 * Length = Number of characters in Path
430 * TRUE if there was enough room in Path, or FALSE
433 CabinetNormalizePath(PWCHAR Path
,
440 Ok
= (n
+ 1) < Length
;
442 if (n
!= 0 && Path
[n
- 1] != L
'\\' && Ok
)
452 * FUNCTION: Returns pointer to cabinet file name
454 * Pointer to string with name of cabinet
457 CabinetGetCabinetName(VOID
)
463 * FUNCTION: Sets cabinet file name
465 * FileName = Pointer to string with name of cabinet
468 CabinetSetCabinetName(PWCHAR FileName
)
470 wcscpy(CabinetName
, FileName
);
474 * FUNCTION: Sets destination path
476 * DestinationPath = Pointer to string with name of destination path
479 CabinetSetDestinationPath(PWCHAR DestinationPath
)
481 wcscpy(DestPath
, DestinationPath
);
483 if (wcslen(DestPath
) > 0)
484 CabinetNormalizePath(DestPath
, MAX_PATH
);
488 * FUNCTION: Returns destination path
490 * Pointer to string with name of destination path
493 CabinetGetDestinationPath(VOID
)
499 * FUNCTION: Opens a cabinet file
501 * Status of operation
507 UNICODE_STRING ustring
;
512 OBJECT_ATTRIBUTES ObjectAttributes
;
513 IO_STATUS_BLOCK IoStatusBlock
;
514 UNICODE_STRING FileName
;
517 RtlInitUnicodeString(&FileName
, CabinetName
);
519 InitializeObjectAttributes(&ObjectAttributes
,
521 OBJ_CASE_INSENSITIVE
,
524 NtStatus
= NtOpenFile(&FileHandle
,
525 GENERIC_READ
| SYNCHRONIZE
,
529 FILE_SYNCHRONOUS_IO_NONALERT
);
531 if (!NT_SUCCESS(NtStatus
))
533 DPRINT1("Cannot open file (%S) (%x)\n", CabinetName
, NtStatus
);
534 return CAB_STATUS_CANNOT_OPEN
;
539 NtStatus
= NtCreateSection(&FileSectionHandle
,
546 if (!NT_SUCCESS(NtStatus
))
548 DPRINT1("NtCreateSection failed for %ls: %x\n", CabinetName
, NtStatus
);
549 return CAB_STATUS_NOMEMORY
;
555 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
557 (PVOID
*)&FileBuffer
,
564 if (!NT_SUCCESS(NtStatus
))
566 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus
);
567 return CAB_STATUS_NOMEMORY
;
570 DPRINT("Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
571 PCABHeader
= (PCFHEADER
) FileBuffer
;
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
))
582 DPRINT1("File has invalid header\n");
583 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 the next file in the cabinet that matches a search criteria
702 * FileName = Pointer to search criteria
703 * Search = Pointer to search structure
705 * Status of operation
708 CabinetFindNextFileSequential(PWCHAR FileName
,
711 DPRINT("CabinetFindNextFileSequential( FileName = %S )\n", FileName
);
712 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
713 return CabinetFindNext(Search
);
717 * FUNCTION: Finds next file in the cabinet that matches a search criteria
719 * Search = Pointer to search structure
721 * Status of operation
724 CabinetFindNext(PCAB_SEARCH Search
)
727 ANSI_STRING AnsiString
;
728 UNICODE_STRING UnicodeString
;
729 WCHAR FileName
[MAX_PATH
];
731 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
733 /* restart search of cabinet has changed since last find */
739 /* starting new search or cabinet */
740 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
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)
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
);
760 // FIXME: check for match against search criteria
761 if (Search
->File
!= Prev
)
763 if (Prev
== NULL
|| Search
->File
->FolderIndex
!= Prev
->FolderIndex
)
765 Search
->CFData
= NULL
;
769 /* don't match the file we started with */
770 if (wcscmp(Search
->Search
, L
"*") == 0)
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)
790 /* if we make it here we found no match, so move to the next file */
792 if (Search
->Index
>= PCABHeader
->FileCount
)
794 /* we have reached the end of this cabinet */
795 DPRINT("End of cabinet reached\n");
796 return CAB_STATUS_NOFILE
;
799 Search
->File
= (PCFFILE
)(strchr((char *)(Search
->File
+ 1), 0) + 1);
802 DPRINT("Found file %s\n", Search
->File
->FileName
);
803 return CAB_STATUS_SUCCESS
;
810 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
815 * FUNCTION: Extracts a file from the cabinet
817 * Search = Pointer to PCAB_SEARCH structure used to locate the file
819 * Status of operation
822 CabinetExtractFile(PCAB_SEARCH Search
)
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
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
835 WCHAR DestName
[MAX_PATH
];
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
;
847 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
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
;
855 /* look up the folder that the file specifies */
856 if (Search
->File
->FolderIndex
== 0xFFFD ||
857 Search
->File
->FolderIndex
== 0xFFFF)
859 /* folder is continued from previous cabinet,
860 that shouldn't happen here */
861 return CAB_STATUS_NOFILE
;
863 else if (Search
->File
->FolderIndex
== 0xFFFE)
865 /* folder is the last in this cabinet and continues into next */
866 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
- 1];
870 /* folder is completely contained within this cabinet */
871 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
874 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
877 CabinetSelectCodec(CAB_CODEC_RAW
);
880 CabinetSelectCodec(CAB_CODEC_MSZIP
);
883 return CAB_STATUS_UNSUPPCOMP
;
886 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n",
887 (UINT
)Search
->File
->FileOffset
, (UINT
)Search
->File
->FileSize
);
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
);
896 /* Create destination file, fail if it already exists */
897 RtlInitUnicodeString(&UnicodeString
, DestName
);
899 InitializeObjectAttributes(&ObjectAttributes
,
901 OBJ_CASE_INSENSITIVE
,
904 NtStatus
= NtCreateFile(&DestFile
,
905 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
909 FILE_ATTRIBUTE_NORMAL
,
912 FILE_SYNCHRONOUS_IO_NONALERT
,
915 if (!NT_SUCCESS(NtStatus
))
917 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
919 /* If file exists, ask to overwrite file */
920 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
922 /* Create destination file, overwrite if it already exists */
923 NtStatus
= NtCreateFile(&DestFile
,
924 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
928 FILE_ATTRIBUTE_NORMAL
,
931 FILE_SYNCHRONOUS_IO_ALERT
,
934 if (!NT_SUCCESS(NtStatus
))
936 DPRINT1("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
937 return CAB_STATUS_CANNOT_CREATE
;
942 DPRINT1("File (%S) exists\n", DestName
);
943 return CAB_STATUS_FILE_EXISTS
;
947 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
948 NtStatus
= NtCreateSection(&DestFileSection
,
956 if (!NT_SUCCESS(NtStatus
))
958 DPRINT1("NtCreateSection failed for %ls, %x\n", DestName
, NtStatus
);
959 Status
= CAB_STATUS_NOMEMORY
;
965 NtStatus
= NtMapViewOfSection(DestFileSection
,
974 if (!NT_SUCCESS(NtStatus
))
976 DPRINT1("NtMapViewOfSection failed: %x\n", NtStatus
);
977 Status
= CAB_STATUS_NOMEMORY
;
978 goto CloseDestFileSection
;
981 CurrentDestBuffer
= DestFileBuffer
;
982 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
,
983 Search
->File
->FileTime
,
986 DPRINT1("DosDateTimeToFileTime() failed\n");
987 Status
= CAB_STATUS_CANNOT_WRITE
;
991 NtStatus
= NtQueryInformationFile(DestFile
,
994 sizeof(FILE_BASIC_INFORMATION
),
995 FileBasicInformation
);
996 if (!NT_SUCCESS(NtStatus
))
998 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
1002 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1004 NtStatus
= NtSetInformationFile(DestFile
,
1007 sizeof(FILE_BASIC_INFORMATION
),
1008 FileBasicInformation
);
1009 if (!NT_SUCCESS(NtStatus
))
1011 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
1015 SetAttributesOnFile(Search
->File
, DestFile
);
1017 /* Call extract event handler */
1018 if (ExtractHandler
!= NULL
)
1020 ExtractHandler(Search
->File
, DestName
);
1024 CFData
= Search
->CFData
;
1026 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
1028 CurrentOffset
= Search
->Offset
;
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 Search
->CFData
= CFData
;
1038 Search
->Offset
= CurrentOffset
;
1040 /* now decompress and discard any data in
1041 the block before the start of the file */
1043 /* start of comp data */
1044 CurrentBuffer
= ((unsigned char *)(CFData
+ 1)) + DataReserved
;
1045 RemainingBlock
= CFData
->CompSize
;
1046 InputLength
= RemainingBlock
;
1048 while (CurrentOffset
< Search
->File
->FileOffset
)
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
);
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
;
1069 /* now CurrentBuffer points to the first comp byte
1070 of the file, so we can begin decompressing */
1072 /* Size = remaining uncomp bytes of the file to decompress */
1073 Size
= Search
->File
->FileSize
;
1076 OutputLength
= Size
;
1077 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n",
1078 CurrentBuffer
, RemainingBlock
, Size
);
1080 Status
= CodecUncompress(CurrentDestBuffer
,
1085 if (Status
!= CS_SUCCESS
)
1087 DPRINT("Cannot uncompress block\n");
1088 if (Status
== CS_NOMEMORY
)
1089 Status
= CAB_STATUS_NOMEMORY
;
1090 Status
= CAB_STATUS_INVALID_CAB
;
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)
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
;
1113 Status
= CAB_STATUS_SUCCESS
;
1116 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1118 CloseDestFileSection
:
1119 NtClose(DestFileSection
);
1128 * FUNCTION: Selects codec engine to use
1130 * Id = Codec identifier
1133 CabinetSelectCodec(ULONG Id
)
1140 CodecSelected
= FALSE
;
1146 CodecUncompress
= RawCodecUncompress
;
1148 case CAB_CODEC_MSZIP
:
1149 CodecUncompress
= MSZipCodecUncompress
;
1156 CodecSelected
= TRUE
;
1160 * FUNCTION: Set event handlers
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
1167 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1168 PCABINET_EXTRACT Extract
,
1169 PCABINET_DISK_CHANGE DiskChange
)
1171 OverwriteHandler
= Overwrite
;
1172 ExtractHandler
= Extract
;
1173 DiskChangeHandler
= DiskChange
;
1177 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1180 CabinetGetCabinetReservedArea(PULONG Size
)
1182 if (CabinetReservedArea
!= NULL
)
1186 *Size
= CabinetReserved
;
1189 return CabinetReservedArea
;