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
17 #define SEEK_CURRENT 1
22 typedef struct _DOSTIME
30 typedef struct _DOSDATE
37 static WCHAR CabinetName
[256]; // Filename of current cabinet
38 static WCHAR CabinetPrev
[256]; // Filename of previous cabinet
39 static WCHAR DiskPrev
[256]; // Label of cabinet in file CabinetPrev
40 static WCHAR CabinetNext
[256]; // Filename of next cabinet
41 static WCHAR DiskNext
[256]; // Label of cabinet in file CabinetNext
42 static ULONG FolderUncompSize
= 0; // Uncompressed size of folder
43 static ULONG BytesLeftInBlock
= 0; // Number of bytes left in current block
44 static WCHAR DestPath
[MAX_PATH
];
45 static HANDLE FileHandle
;
46 static HANDLE FileSectionHandle
;
47 static PUCHAR FileBuffer
;
48 static SIZE_T DestFileSize
;
49 static SIZE_T FileSize
;
50 static BOOL FileOpen
= FALSE
;
51 static PCFHEADER PCABHeader
;
52 static PCFFOLDER CabinetFolders
;
53 static ULONG CabinetReserved
= 0;
54 static ULONG FolderReserved
= 0;
55 static ULONG DataReserved
= 0;
57 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
58 static BOOL CodecSelected
= FALSE
;
59 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
60 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
61 static PCABINET_EXTRACT ExtractHandler
= NULL
;
62 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
63 static z_stream ZStream
;
64 static PVOID CabinetReservedArea
= NULL
;
67 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
71 return RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, size
);
77 RtlFreeHeap(ProcessHeap
, 0, ptr
);
81 calloc(size_t nmemb
, size_t size
)
83 return (void *)RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, nmemb
* size
);
89 * FUNCTION: Uncompresses data in a buffer
91 * OutputBuffer = Pointer to buffer to place uncompressed data
92 * InputBuffer = Pointer to buffer with data to be uncompressed
93 * InputLength = Length of input buffer before, and amount consumed after
94 * Negative to indicate that this is not the start of a new block
95 * OutputLength = Length of output buffer before, amount filled after
96 * Negative to indicate that this is not the end of the block
99 RawCodecUncompress(PVOID OutputBuffer
,
104 LONG Len
= min(abs(*InputLength
), abs(*OutputLength
));
106 memcpy(OutputBuffer
, InputBuffer
, Len
);
107 *InputLength
= *OutputLength
= Len
;
115 * FUNCTION: Uncompresses data in a buffer
117 * OutputBuffer = Pointer to buffer to place uncompressed data
118 * InputBuffer = Pointer to buffer with data to be uncompressed
119 * InputLength = Length of input buffer before, and amount consumed after
120 * Negative to indicate that this is not the start of a new block
121 * OutputLength = Length of output buffer before, amount filled after
122 * Negative to indicate that this is not the end of the block
125 MSZipCodecUncompress(PVOID OutputBuffer
,
133 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, "
134 "InputLength = %d, OutputLength = %d)\n", OutputBuffer
,
135 InputBuffer
, *InputLength
, *OutputLength
);
136 if (*InputLength
> 0)
138 Magic
= *(PUSHORT
)InputBuffer
;
140 if (Magic
!= MSZIP_MAGIC
)
142 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
146 ZStream
.next_in
= (PUCHAR
)InputBuffer
+ 2;
147 ZStream
.avail_in
= *InputLength
- 2;
148 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
149 ZStream
.avail_out
= abs(*OutputLength
);
151 /* WindowBits is passed < 0 to tell that there is no zlib header.
152 * Note that in this case inflate *requires* an extra "dummy" byte
153 * after the compressed stream in order to complete decompression and
154 * return Z_STREAM_END.
156 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
159 DPRINT("inflateInit2() returned (%d)\n", Status
);
162 ZStream
.total_in
= 2;
166 ZStream
.avail_in
= -*InputLength
;
167 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
168 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
169 ZStream
.avail_out
= abs(*OutputLength
);
170 ZStream
.total_in
= 0;
173 ZStream
.total_out
= 0;
174 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
175 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
177 DPRINT("inflate() returned (%d) (%s)\n", Status
, ZStream
.msg
);
178 if (Status
== Z_MEM_ERROR
)
183 if (*OutputLength
> 0)
185 Status
= inflateEnd(&ZStream
);
188 DPRINT("inflateEnd() returned (%d)\n", Status
);
193 *OutputLength
= ZStream
.total_out
;
194 *InputLength
= ZStream
.total_in
;
199 /* Memory functions */
202 MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
204 return (voidpf
)RtlAllocateHeap(ProcessHeap
, 0, items
* size
);
208 MSZipFree(voidpf opaque
, voidpf address
)
210 RtlFreeHeap(ProcessHeap
, 0, address
);
214 ConvertSystemTimeToFileTime(CONST SYSTEMTIME
*lpSystemTime
,
215 LPFILETIME lpFileTime
)
217 TIME_FIELDS TimeFields
;
218 LARGE_INTEGER liTime
;
220 TimeFields
.Year
= lpSystemTime
->wYear
;
221 TimeFields
.Month
= lpSystemTime
->wMonth
;
222 TimeFields
.Day
= lpSystemTime
->wDay
;
223 TimeFields
.Hour
= lpSystemTime
->wHour
;
224 TimeFields
.Minute
= lpSystemTime
->wMinute
;
225 TimeFields
.Second
= lpSystemTime
->wSecond
;
226 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
228 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
230 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
231 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
239 ConvertDosDateTimeToFileTime(WORD wFatDate
,
241 LPFILETIME lpFileTime
)
243 PDOSTIME pdtime
= (PDOSTIME
)&wFatTime
;
244 PDOSDATE pddate
= (PDOSDATE
)&wFatDate
;
245 SYSTEMTIME SystemTime
;
247 if (lpFileTime
== NULL
)
250 SystemTime
.wMilliseconds
= 0;
251 SystemTime
.wSecond
= pdtime
->Second
;
252 SystemTime
.wMinute
= pdtime
->Minute
;
253 SystemTime
.wHour
= pdtime
->Hour
;
255 SystemTime
.wDay
= pddate
->Day
;
256 SystemTime
.wMonth
= pddate
->Month
;
257 SystemTime
.wYear
= 1980 + pddate
->Year
;
259 ConvertSystemTimeToFileTime(&SystemTime
, lpFileTime
);
265 * FUNCTION: Returns a pointer to file name
267 * Path = Pointer to string with pathname
269 * Pointer to filename
272 GetFileName(PWCHAR Path
)
280 if (Path
[i
- 1] == L
'\\')
288 * FUNCTION: Removes a file name from a path
290 * Path = Pointer to string with path
293 RemoveFileName(PWCHAR Path
)
299 FileName
= GetFileName(Path
+ i
);
301 if (FileName
!= Path
+ i
&& FileName
[-1] == L
'\\')
304 if (FileName
== Path
+ i
&& FileName
[0] == L
'\\')
311 * FUNCTION: Sets attributes on a file
313 * File = Pointer to CFFILE node for file
315 * Status of operation
318 SetAttributesOnFile(PCFFILE File
,
321 FILE_BASIC_INFORMATION FileBasic
;
322 IO_STATUS_BLOCK IoStatusBlock
;
324 ULONG Attributes
= 0;
326 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
327 Attributes
|= FILE_ATTRIBUTE_READONLY
;
329 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
330 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
332 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
333 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
335 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
336 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
338 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
339 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
341 NtStatus
= NtQueryInformationFile(hFile
,
344 sizeof(FILE_BASIC_INFORMATION
),
345 FileBasicInformation
);
346 if (!NT_SUCCESS(NtStatus
))
348 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
352 FileBasic
.FileAttributes
= Attributes
;
354 NtStatus
= NtSetInformationFile(hFile
,
357 sizeof(FILE_BASIC_INFORMATION
),
358 FileBasicInformation
);
359 if (!NT_SUCCESS(NtStatus
))
361 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
365 return NT_SUCCESS(NtStatus
);
369 * FUNCTION: Closes the current cabinet
371 * Status of operation
378 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer
);
379 NtClose(FileSectionHandle
);
388 * FUNCTION: Initialize archiver
391 CabinetInitialize(VOID
)
393 ZStream
.zalloc
= MSZipAlloc
;
394 ZStream
.zfree
= MSZipFree
;
395 ZStream
.opaque
= (voidpf
)0;
398 wcscpy(DestPath
, L
"");
400 CodecId
= CAB_CODEC_RAW
;
401 CodecSelected
= TRUE
;
403 FolderUncompSize
= 0;
404 BytesLeftInBlock
= 0;
408 CabinetReservedArea
= NULL
;
413 * FUNCTION: Cleanup archiver
422 * FUNCTION: Normalizes a path
424 * Path = Pointer to string with pathname
425 * Length = Number of characters in Path
427 * TRUE if there was enough room in Path, or FALSE
430 CabinetNormalizePath(PWCHAR Path
,
437 Ok
= (n
+ 1) < Length
;
439 if (n
!= 0 && Path
[n
- 1] != L
'\\' && Ok
)
449 * FUNCTION: Returns pointer to cabinet file name
451 * Pointer to string with name of cabinet
454 CabinetGetCabinetName(VOID
)
460 * FUNCTION: Sets cabinet file name
462 * FileName = Pointer to string with name of cabinet
465 CabinetSetCabinetName(PWCHAR FileName
)
467 wcscpy(CabinetName
, FileName
);
471 * FUNCTION: Sets destination path
473 * DestinationPath = Pointer to string with name of destination path
476 CabinetSetDestinationPath(PWCHAR DestinationPath
)
478 wcscpy(DestPath
, DestinationPath
);
480 if (wcslen(DestPath
) > 0)
481 CabinetNormalizePath(DestPath
, MAX_PATH
);
485 * FUNCTION: Returns destination path
487 * Pointer to string with name of destination path
490 CabinetGetDestinationPath(VOID
)
496 * FUNCTION: Opens a cabinet file
498 * Status of operation
504 UNICODE_STRING ustring
;
509 OBJECT_ATTRIBUTES ObjectAttributes
;
510 IO_STATUS_BLOCK IoStatusBlock
;
511 UNICODE_STRING FileName
;
514 RtlInitUnicodeString(&FileName
, CabinetName
);
516 InitializeObjectAttributes(&ObjectAttributes
,
518 OBJ_CASE_INSENSITIVE
,
521 NtStatus
= NtOpenFile(&FileHandle
,
522 GENERIC_READ
| SYNCHRONIZE
,
526 FILE_SYNCHRONOUS_IO_NONALERT
);
528 if (!NT_SUCCESS(NtStatus
))
530 DPRINT("Cannot open file (%S) (%x)\n", CabinetName
, NtStatus
);
531 return CAB_STATUS_CANNOT_OPEN
;
536 NtStatus
= NtCreateSection(&FileSectionHandle
,
543 if (!NT_SUCCESS(NtStatus
))
545 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
546 return CAB_STATUS_NOMEMORY
;
552 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
554 (PVOID
*)&FileBuffer
,
561 if (!NT_SUCCESS(NtStatus
))
563 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
564 return CAB_STATUS_NOMEMORY
;
567 DPRINT("Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
568 PCABHeader
= (PCFHEADER
) FileBuffer
;
571 if (FileSize
<= sizeof(CFHEADER
) ||
572 PCABHeader
->Signature
!= CAB_SIGNATURE
||
573 PCABHeader
->Version
!= CAB_VERSION
||
574 PCABHeader
->FolderCount
== 0 ||
575 PCABHeader
->FileCount
== 0 ||
576 PCABHeader
->FileTableOffset
< sizeof(CFHEADER
))
579 DPRINT("File has invalid header\n");
580 return CAB_STATUS_INVALID_CAB
;
583 Buffer
= (PUCHAR
)(PCABHeader
+ 1);
585 /* Read/skip any reserved bytes */
586 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
588 CabinetReserved
= *(PUSHORT
)Buffer
;
590 FolderReserved
= *Buffer
;
592 DataReserved
= *Buffer
;
595 if (CabinetReserved
> 0)
597 CabinetReservedArea
= Buffer
;
598 Buffer
+= CabinetReserved
;
602 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
604 /* The previous cabinet file is in
605 the same directory as the current */
606 wcscpy(CabinetPrev
, CabinetName
);
607 RemoveFileName(CabinetPrev
);
608 CabinetNormalizePath(CabinetPrev
, 256);
609 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
610 ustring
.Length
= wcslen(CabinetPrev
);
611 ustring
.Buffer
= CabinetPrev
+ ustring
.Length
;
612 ustring
.MaximumLength
= sizeof(CabinetPrev
) - ustring
.Length
;
613 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
614 Buffer
+= astring
.Length
+ 1;
616 /* Read label of prev disk */
617 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
619 ustring
.Buffer
= DiskPrev
;
620 ustring
.MaximumLength
= sizeof(DiskPrev
);
621 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
622 Buffer
+= astring
.Length
+ 1;
626 wcscpy(CabinetPrev
, L
"");
627 wcscpy(DiskPrev
, L
"");
630 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
632 /* The next cabinet file is in
633 the same directory as the previous */
634 wcscpy(CabinetNext
, CabinetName
);
635 RemoveFileName(CabinetNext
);
636 CabinetNormalizePath(CabinetNext
, 256);
637 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
638 ustring
.Length
= wcslen(CabinetNext
);
639 ustring
.Buffer
= CabinetNext
+ ustring
.Length
;
640 ustring
.MaximumLength
= sizeof(CabinetNext
) - ustring
.Length
;
641 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
642 Buffer
+= astring
.Length
+ 1;
644 /* Read label of next disk */
645 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
647 ustring
.Buffer
= DiskNext
;
648 ustring
.MaximumLength
= sizeof(DiskNext
);
649 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
650 Buffer
+= astring
.Length
+ 1;
654 wcscpy(CabinetNext
, L
"");
655 wcscpy(DiskNext
, L
"");
657 CabinetFolders
= (PCFFOLDER
)Buffer
;
660 DPRINT("CabinetOpen returning SUCCESS\n");
661 return CAB_STATUS_SUCCESS
;
665 * FUNCTION: Closes the cabinet file
678 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
680 * FileName = Pointer to search criteria
681 * Search = Pointer to search structure
683 * Status of operation
686 CabinetFindFirst(PWCHAR FileName
,
689 DPRINT("CabinetFindFirst( FileName = %S )\n", FileName
);
690 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
691 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
693 return CabinetFindNext(Search
);
697 * FUNCTION: Finds the next file in the cabinet that matches a search criteria
699 * FileName = Pointer to search criteria
700 * Search = Pointer to search structure
702 * Status of operation
705 CabinetFindNextFileSequential(PWCHAR FileName
,
708 DPRINT("CabinetFindNextFileSequential( FileName = %S )\n", FileName
);
709 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
710 return CabinetFindNext(Search
);
714 * FUNCTION: Finds next file in the cabinet that matches a search criteria
716 * Search = Pointer to search structure
718 * Status of operation
721 CabinetFindNext(PCAB_SEARCH Search
)
724 ANSI_STRING AnsiString
;
725 UNICODE_STRING UnicodeString
;
726 WCHAR FileName
[MAX_PATH
];
728 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
730 /* restart search of cabinet has changed since last find */
736 /* starting new search or cabinet */
737 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
746 /* look at each file in the archive and see if we found a match */
747 if (Search
->File
->FolderIndex
== 0xFFFD ||
748 Search
->File
->FolderIndex
== 0xFFFF)
750 /* skip files continued from previous cab */
751 DPRINT("Skipping file (%s): FileOffset (0x%X), "
752 "LastFileOffset (0x%X)\n", (char *)(Search
->File
+ 1),
753 Search
->File
->FileOffset
, LastFileOffset
);
757 // FIXME: check for match against search criteria
758 if (Search
->File
!= Prev
)
760 if (Prev
== NULL
|| Search
->File
->FolderIndex
!= Prev
->FolderIndex
)
762 Search
->CFData
= NULL
;
766 /* don't match the file we started with */
767 if (wcscmp(Search
->Search
, L
"*") == 0)
774 /* otherwise, try to match the exact file name */
775 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
776 UnicodeString
.Buffer
= FileName
;
777 UnicodeString
.Buffer
[0] = 0;
778 UnicodeString
.Length
= 0;
779 UnicodeString
.MaximumLength
= sizeof(FileName
);
780 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
781 if (wcscmp(Search
->Search
, UnicodeString
.Buffer
) == 0)
787 /* if we make it here we found no match, so move to the next file */
789 if (Search
->Index
>= PCABHeader
->FileCount
)
791 /* we have reached the end of this cabinet */
792 DPRINT("End of cabinet reached\n");
793 return CAB_STATUS_NOFILE
;
796 Search
->File
= (PCFFILE
)(strchr((char *)(Search
->File
+ 1), 0) + 1);
799 DPRINT("Found file %s\n", Search
->File
->FileName
);
800 return CAB_STATUS_SUCCESS
;
807 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
812 * FUNCTION: Extracts a file from the cabinet
814 * Search = Pointer to PCAB_SEARCH structure used to locate the file
816 * Status of operation
819 CabinetExtractFile(PCAB_SEARCH Search
)
821 ULONG Size
; // remaining file bytes to decompress
822 ULONG CurrentOffset
; // current uncompressed offset within the folder
823 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
824 LONG RemainingBlock
; // remaining comp data in the block
826 HANDLE DestFileSection
;
827 PVOID DestFileBuffer
; // mapped view of dest file
828 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
829 PCFDATA CFData
; // current data block
832 WCHAR DestName
[MAX_PATH
];
834 UNICODE_STRING UnicodeString
;
835 ANSI_STRING AnsiString
;
836 IO_STATUS_BLOCK IoStatusBlock
;
837 OBJECT_ATTRIBUTES ObjectAttributes
;
838 FILE_BASIC_INFORMATION FileBasic
;
839 PCFFOLDER CurrentFolder
;
840 LARGE_INTEGER MaxDestFileSize
;
841 LONG InputLength
, OutputLength
;
844 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
846 /* the file is not in the current cabinet */
847 DPRINT("File is not in this cabinet (%S != %S)\n",
848 Search
->Cabinet
, CabinetName
);
849 return CAB_STATUS_NOFILE
;
852 /* look up the folder that the file specifies */
853 if (Search
->File
->FolderIndex
== 0xFFFD ||
854 Search
->File
->FolderIndex
== 0xFFFF)
856 /* folder is continued from previous cabinet,
857 that shouldn't happen here */
858 return CAB_STATUS_NOFILE
;
860 else if (Search
->File
->FolderIndex
== 0xFFFE)
862 /* folder is the last in this cabinet and continues into next */
863 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
- 1];
867 /* folder is completely contained within this cabinet */
868 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
871 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
874 CabinetSelectCodec(CAB_CODEC_RAW
);
877 CabinetSelectCodec(CAB_CODEC_MSZIP
);
880 return CAB_STATUS_UNSUPPCOMP
;
883 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n",
884 (UINT
)Search
->File
->FileOffset
, (UINT
)Search
->File
->FileSize
);
886 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
887 wcscpy(DestName
, DestPath
);
888 UnicodeString
.MaximumLength
= sizeof(DestName
) - wcslen(DestName
) * sizeof(WCHAR
);
889 UnicodeString
.Buffer
= DestName
+ wcslen(DestName
);
890 UnicodeString
.Length
= 0;
891 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
893 /* Create destination file, fail if it already exists */
894 RtlInitUnicodeString(&UnicodeString
, DestName
);
896 InitializeObjectAttributes(&ObjectAttributes
,
898 OBJ_CASE_INSENSITIVE
,
901 NtStatus
= NtCreateFile(&DestFile
,
902 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
906 FILE_ATTRIBUTE_NORMAL
,
909 FILE_SYNCHRONOUS_IO_NONALERT
,
912 if (!NT_SUCCESS(NtStatus
))
914 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
916 /* If file exists, ask to overwrite file */
917 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
919 /* Create destination file, overwrite if it already exists */
920 NtStatus
= NtCreateFile(&DestFile
,
921 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
925 FILE_ATTRIBUTE_NORMAL
,
928 FILE_SYNCHRONOUS_IO_ALERT
,
931 if (!NT_SUCCESS(NtStatus
))
933 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
934 return CAB_STATUS_CANNOT_CREATE
;
939 DPRINT("File (%S) exists\n", DestName
);
940 return CAB_STATUS_FILE_EXISTS
;
944 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
945 NtStatus
= NtCreateSection(&DestFileSection
,
953 if (!NT_SUCCESS(NtStatus
))
955 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
956 Status
= CAB_STATUS_NOMEMORY
;
962 NtStatus
= NtMapViewOfSection(DestFileSection
,
971 if (!NT_SUCCESS(NtStatus
))
973 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
974 Status
= CAB_STATUS_NOMEMORY
;
975 goto CloseDestFileSection
;
978 CurrentDestBuffer
= DestFileBuffer
;
979 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
,
980 Search
->File
->FileTime
,
983 DPRINT("DosDateTimeToFileTime() failed\n");
984 Status
= CAB_STATUS_CANNOT_WRITE
;
988 NtStatus
= NtQueryInformationFile(DestFile
,
991 sizeof(FILE_BASIC_INFORMATION
),
992 FileBasicInformation
);
993 if (!NT_SUCCESS(NtStatus
))
995 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
999 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1001 NtStatus
= NtSetInformationFile(DestFile
,
1004 sizeof(FILE_BASIC_INFORMATION
),
1005 FileBasicInformation
);
1006 if (!NT_SUCCESS(NtStatus
))
1008 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
1012 SetAttributesOnFile(Search
->File
, DestFile
);
1014 /* Call extract event handler */
1015 if (ExtractHandler
!= NULL
)
1017 ExtractHandler(Search
->File
, DestName
);
1021 CFData
= Search
->CFData
;
1023 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
1025 CurrentOffset
= Search
->Offset
;
1026 while (CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
1028 /* walk the data blocks until we reach
1029 the one containing the start of the file */
1030 CurrentOffset
+= CFData
->UncompSize
;
1031 CFData
= (PCFDATA
)((char *)(CFData
+ 1) + DataReserved
+ CFData
->CompSize
);
1034 Search
->CFData
= CFData
;
1035 Search
->Offset
= CurrentOffset
;
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
;