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
19 #define SEEK_CURRENT 1
24 typedef struct _DOSTIME
32 typedef struct _DOSDATE
39 static WCHAR CabinetName
[256]; // Filename of current cabinet
40 static WCHAR CabinetPrev
[256]; // Filename of previous cabinet
41 static WCHAR DiskPrev
[256]; // Label of cabinet in file CabinetPrev
42 static WCHAR CabinetNext
[256]; // Filename of next cabinet
43 static WCHAR DiskNext
[256]; // Label of cabinet in file CabinetNext
44 static ULONG FolderUncompSize
= 0; // Uncompressed size of folder
45 static ULONG BytesLeftInBlock
= 0; // Number of bytes left in current block
46 static WCHAR DestPath
[MAX_PATH
];
47 static HANDLE FileHandle
;
48 static HANDLE FileSectionHandle
;
49 static PUCHAR FileBuffer
;
50 static SIZE_T DestFileSize
;
51 static SIZE_T FileSize
;
52 static BOOL FileOpen
= FALSE
;
53 static PCFHEADER PCABHeader
;
54 static PCFFOLDER CabinetFolders
;
55 static ULONG CabinetReserved
= 0;
56 static ULONG FolderReserved
= 0;
57 static ULONG DataReserved
= 0;
59 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
60 static BOOL CodecSelected
= FALSE
;
61 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
62 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
63 static PCABINET_EXTRACT ExtractHandler
= NULL
;
64 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
65 static z_stream ZStream
;
66 static PVOID CabinetReservedArea
= NULL
;
69 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
73 return RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, size
);
79 RtlFreeHeap(ProcessHeap
, 0, ptr
);
83 calloc(size_t nmemb
, size_t size
)
85 return (void *)RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, nmemb
* size
);
91 * FUNCTION: Uncompresses data in a buffer
93 * OutputBuffer = Pointer to buffer to place uncompressed data
94 * InputBuffer = Pointer to buffer with data to be uncompressed
95 * InputLength = Length of input buffer before, and amount consumed after
96 * Negative to indicate that this is not the start of a new block
97 * OutputLength = Length of output buffer before, amount filled after
98 * Negative to indicate that this is not the end of the block
101 RawCodecUncompress(PVOID OutputBuffer
,
106 LONG Len
= min(abs(*InputLength
), abs(*OutputLength
));
108 memcpy(OutputBuffer
, InputBuffer
, Len
);
109 *InputLength
= *OutputLength
= Len
;
117 * FUNCTION: Uncompresses data in a buffer
119 * OutputBuffer = Pointer to buffer to place uncompressed data
120 * InputBuffer = Pointer to buffer with data to be uncompressed
121 * InputLength = Length of input buffer before, and amount consumed after
122 * Negative to indicate that this is not the start of a new block
123 * OutputLength = Length of output buffer before, amount filled after
124 * Negative to indicate that this is not the end of the block
127 MSZipCodecUncompress(PVOID OutputBuffer
,
135 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, "
136 "InputLength = %d, OutputLength = %d)\n", OutputBuffer
,
137 InputBuffer
, *InputLength
, *OutputLength
);
138 if (*InputLength
> 0)
140 Magic
= *(PUSHORT
)InputBuffer
;
142 if (Magic
!= MSZIP_MAGIC
)
144 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
148 ZStream
.next_in
= (PUCHAR
)InputBuffer
+ 2;
149 ZStream
.avail_in
= *InputLength
- 2;
150 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
151 ZStream
.avail_out
= abs(*OutputLength
);
153 /* WindowBits is passed < 0 to tell that there is no zlib header.
154 * Note that in this case inflate *requires* an extra "dummy" byte
155 * after the compressed stream in order to complete decompression and
156 * return Z_STREAM_END.
158 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
161 DPRINT("inflateInit2() returned (%d)\n", Status
);
164 ZStream
.total_in
= 2;
168 ZStream
.avail_in
= -*InputLength
;
169 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
170 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
171 ZStream
.avail_out
= abs(*OutputLength
);
172 ZStream
.total_in
= 0;
175 ZStream
.total_out
= 0;
176 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
177 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
179 DPRINT("inflate() returned (%d) (%s)\n", Status
, ZStream
.msg
);
180 if (Status
== Z_MEM_ERROR
)
185 if (*OutputLength
> 0)
187 Status
= inflateEnd(&ZStream
);
190 DPRINT("inflateEnd() returned (%d)\n", Status
);
195 *OutputLength
= ZStream
.total_out
;
196 *InputLength
= ZStream
.total_in
;
201 /* Memory functions */
204 MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
206 return (voidpf
)RtlAllocateHeap(ProcessHeap
, 0, items
* size
);
210 MSZipFree(voidpf opaque
, voidpf address
)
212 RtlFreeHeap(ProcessHeap
, 0, address
);
216 ConvertSystemTimeToFileTime(CONST SYSTEMTIME
*lpSystemTime
,
217 LPFILETIME lpFileTime
)
219 TIME_FIELDS TimeFields
;
220 LARGE_INTEGER liTime
;
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
;
230 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
232 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
233 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
241 ConvertDosDateTimeToFileTime(WORD wFatDate
,
243 LPFILETIME lpFileTime
)
245 PDOSTIME pdtime
= (PDOSTIME
)&wFatTime
;
246 PDOSDATE pddate
= (PDOSDATE
)&wFatDate
;
247 SYSTEMTIME SystemTime
;
249 if (lpFileTime
== NULL
)
252 SystemTime
.wMilliseconds
= 0;
253 SystemTime
.wSecond
= pdtime
->Second
;
254 SystemTime
.wMinute
= pdtime
->Minute
;
255 SystemTime
.wHour
= pdtime
->Hour
;
257 SystemTime
.wDay
= pddate
->Day
;
258 SystemTime
.wMonth
= pddate
->Month
;
259 SystemTime
.wYear
= 1980 + pddate
->Year
;
261 ConvertSystemTimeToFileTime(&SystemTime
, lpFileTime
);
267 * FUNCTION: Returns a pointer to file name
269 * Path = Pointer to string with pathname
271 * Pointer to filename
274 GetFileName(PWCHAR Path
)
282 if (Path
[i
- 1] == L
'\\')
290 * FUNCTION: Removes a file name from a path
292 * Path = Pointer to string with path
295 RemoveFileName(PWCHAR Path
)
301 FileName
= GetFileName(Path
+ i
);
303 if (FileName
!= Path
+ i
&& FileName
[-1] == L
'\\')
306 if (FileName
== Path
+ i
&& FileName
[0] == L
'\\')
313 * FUNCTION: Sets attributes on a file
315 * File = Pointer to CFFILE node for file
317 * Status of operation
320 SetAttributesOnFile(PCFFILE File
,
323 FILE_BASIC_INFORMATION FileBasic
;
324 IO_STATUS_BLOCK IoStatusBlock
;
326 ULONG Attributes
= 0;
328 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
329 Attributes
|= FILE_ATTRIBUTE_READONLY
;
331 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
332 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
334 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
335 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
337 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
338 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
340 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
341 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
343 NtStatus
= NtQueryInformationFile(hFile
,
346 sizeof(FILE_BASIC_INFORMATION
),
347 FileBasicInformation
);
348 if (!NT_SUCCESS(NtStatus
))
350 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
354 FileBasic
.FileAttributes
= Attributes
;
356 NtStatus
= NtSetInformationFile(hFile
,
359 sizeof(FILE_BASIC_INFORMATION
),
360 FileBasicInformation
);
361 if (!NT_SUCCESS(NtStatus
))
363 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
367 return NT_SUCCESS(NtStatus
);
371 * FUNCTION: Closes the current cabinet
373 * Status of operation
380 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer
);
381 NtClose(FileSectionHandle
);
390 * FUNCTION: Initialize archiver
393 CabinetInitialize(VOID
)
395 ZStream
.zalloc
= MSZipAlloc
;
396 ZStream
.zfree
= MSZipFree
;
397 ZStream
.opaque
= (voidpf
)0;
400 wcscpy(DestPath
, L
"");
402 CodecId
= CAB_CODEC_RAW
;
403 CodecSelected
= TRUE
;
405 FolderUncompSize
= 0;
406 BytesLeftInBlock
= 0;
410 CabinetReservedArea
= NULL
;
415 * FUNCTION: Cleanup archiver
424 * FUNCTION: Normalizes a path
426 * Path = Pointer to string with pathname
427 * Length = Number of characters in Path
429 * TRUE if there was enough room in Path, or FALSE
432 CabinetNormalizePath(PWCHAR Path
,
439 Ok
= (n
+ 1) < Length
;
441 if (n
!= 0 && Path
[n
- 1] != L
'\\' && Ok
)
451 * FUNCTION: Returns pointer to cabinet file name
453 * Pointer to string with name of cabinet
456 CabinetGetCabinetName(VOID
)
462 * FUNCTION: Sets cabinet file name
464 * FileName = Pointer to string with name of cabinet
467 CabinetSetCabinetName(PWCHAR FileName
)
469 wcscpy(CabinetName
, FileName
);
473 * FUNCTION: Sets destination path
475 * DestinationPath = Pointer to string with name of destination path
478 CabinetSetDestinationPath(PWCHAR DestinationPath
)
480 wcscpy(DestPath
, DestinationPath
);
482 if (wcslen(DestPath
) > 0)
483 CabinetNormalizePath(DestPath
, MAX_PATH
);
487 * FUNCTION: Returns destination path
489 * Pointer to string with name of destination path
492 CabinetGetDestinationPath(VOID
)
498 * FUNCTION: Opens a cabinet file
500 * Status of operation
506 UNICODE_STRING ustring
;
511 OBJECT_ATTRIBUTES ObjectAttributes
;
512 IO_STATUS_BLOCK IoStatusBlock
;
513 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
;
585 Buffer
= (PUCHAR
)(PCABHeader
+ 1);
587 /* Read/skip any reserved bytes */
588 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
590 CabinetReserved
= *(PUSHORT
)Buffer
;
592 FolderReserved
= *Buffer
;
594 DataReserved
= *Buffer
;
597 if (CabinetReserved
> 0)
599 CabinetReservedArea
= Buffer
;
600 Buffer
+= CabinetReserved
;
604 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
606 /* The previous cabinet file is in
607 the same directory as the current */
608 wcscpy(CabinetPrev
, CabinetName
);
609 RemoveFileName(CabinetPrev
);
610 CabinetNormalizePath(CabinetPrev
, 256);
611 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
612 ustring
.Length
= wcslen(CabinetPrev
);
613 ustring
.Buffer
= CabinetPrev
+ ustring
.Length
;
614 ustring
.MaximumLength
= sizeof(CabinetPrev
) - ustring
.Length
;
615 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
616 Buffer
+= astring
.Length
+ 1;
618 /* Read label of prev disk */
619 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
621 ustring
.Buffer
= DiskPrev
;
622 ustring
.MaximumLength
= sizeof(DiskPrev
);
623 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
624 Buffer
+= astring
.Length
+ 1;
628 wcscpy(CabinetPrev
, L
"");
629 wcscpy(DiskPrev
, L
"");
632 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
634 /* The next cabinet file is in
635 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;
646 /* Read label of next disk */
647 RtlInitAnsiString(&astring
, (LPSTR
)Buffer
);
649 ustring
.Buffer
= DiskNext
;
650 ustring
.MaximumLength
= sizeof(DiskNext
);
651 RtlAnsiStringToUnicodeString(&ustring
, &astring
, FALSE
);
652 Buffer
+= astring
.Length
+ 1;
656 wcscpy(CabinetNext
, L
"");
657 wcscpy(DiskNext
, L
"");
659 CabinetFolders
= (PCFFOLDER
)Buffer
;
662 DPRINT("CabinetOpen returning SUCCESS\n");
663 return CAB_STATUS_SUCCESS
;
667 * FUNCTION: Closes the cabinet file
680 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
682 * FileName = Pointer to search criteria
683 * Search = Pointer to search structure
685 * Status of operation
688 CabinetFindFirst(PWCHAR FileName
,
691 DPRINT("CabinetFindFirst( FileName = %S )\n", FileName
);
692 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
693 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
695 return CabinetFindNext(Search
);
699 * FUNCTION: Finds the next file in the cabinet that matches a search criteria
701 * FileName = Pointer to search criteria
702 * Search = Pointer to search structure
704 * Status of operation
707 CabinetFindNextFileSequential(PWCHAR FileName
,
710 DPRINT("CabinetFindNextFileSequential( FileName = %S )\n", FileName
);
711 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
712 return CabinetFindNext(Search
);
716 * FUNCTION: Finds next file in the cabinet that matches a search criteria
718 * Search = Pointer to search structure
720 * Status of operation
723 CabinetFindNext(PCAB_SEARCH Search
)
726 ANSI_STRING AnsiString
;
727 UNICODE_STRING UnicodeString
;
728 WCHAR FileName
[MAX_PATH
];
730 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
732 /* restart search of cabinet has changed since last find */
738 /* starting new search or cabinet */
739 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
748 /* look at each file in the archive and see if we found a match */
749 if (Search
->File
->FolderIndex
== 0xFFFD ||
750 Search
->File
->FolderIndex
== 0xFFFF)
752 /* skip files continued from previous cab */
753 DPRINT("Skipping file (%s): FileOffset (0x%X), "
754 "LastFileOffset (0x%X)\n", (char *)(Search
->File
+ 1),
755 Search
->File
->FileOffset
, LastFileOffset
);
759 // FIXME: check for match against search criteria
760 if (Search
->File
!= Prev
)
762 if (Prev
== NULL
|| Search
->File
->FolderIndex
!= Prev
->FolderIndex
)
764 Search
->CFData
= NULL
;
768 /* don't match the file we started with */
769 if (wcscmp(Search
->Search
, L
"*") == 0)
776 /* otherwise, try to match the exact file name */
777 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
778 UnicodeString
.Buffer
= FileName
;
779 UnicodeString
.Buffer
[0] = 0;
780 UnicodeString
.Length
= 0;
781 UnicodeString
.MaximumLength
= sizeof(FileName
);
782 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
783 if (wcscmp(Search
->Search
, UnicodeString
.Buffer
) == 0)
789 /* if we make it here we found no match, so move to the next file */
791 if (Search
->Index
>= PCABHeader
->FileCount
)
793 /* we have reached the end of this cabinet */
794 DPRINT("End of cabinet reached\n");
795 return CAB_STATUS_NOFILE
;
798 Search
->File
= (PCFFILE
)(strchr((char *)(Search
->File
+ 1), 0) + 1);
801 DPRINT("Found file %s\n", Search
->File
->FileName
);
802 return CAB_STATUS_SUCCESS
;
809 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
814 * FUNCTION: Extracts a file from the cabinet
816 * Search = Pointer to PCAB_SEARCH structure used to locate the file
818 * Status of operation
821 CabinetExtractFile(PCAB_SEARCH Search
)
823 ULONG Size
; // remaining file bytes to decompress
824 ULONG CurrentOffset
; // current uncompressed offset within the folder
825 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
826 LONG RemainingBlock
; // remaining comp data in the block
828 HANDLE DestFileSection
;
829 PVOID DestFileBuffer
; // mapped view of dest file
830 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
831 PCFDATA CFData
; // current data block
834 WCHAR DestName
[MAX_PATH
];
836 UNICODE_STRING UnicodeString
;
837 ANSI_STRING AnsiString
;
838 IO_STATUS_BLOCK IoStatusBlock
;
839 OBJECT_ATTRIBUTES ObjectAttributes
;
840 FILE_BASIC_INFORMATION FileBasic
;
841 PCFFOLDER CurrentFolder
;
842 LARGE_INTEGER MaxDestFileSize
;
843 LONG InputLength
, OutputLength
;
846 if (wcscmp(Search
->Cabinet
, CabinetName
) != 0)
848 /* the file is not in the current cabinet */
849 DPRINT("File is not in this cabinet (%S != %S)\n",
850 Search
->Cabinet
, CabinetName
);
851 return CAB_STATUS_NOFILE
;
854 /* look up the folder that the file specifies */
855 if (Search
->File
->FolderIndex
== 0xFFFD ||
856 Search
->File
->FolderIndex
== 0xFFFF)
858 /* folder is continued from previous cabinet,
859 that shouldn't happen here */
860 return CAB_STATUS_NOFILE
;
862 else if (Search
->File
->FolderIndex
== 0xFFFE)
864 /* folder is the last in this cabinet and continues into next */
865 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
- 1];
869 /* folder is completely contained within this cabinet */
870 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
873 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
876 CabinetSelectCodec(CAB_CODEC_RAW
);
879 CabinetSelectCodec(CAB_CODEC_MSZIP
);
882 return CAB_STATUS_UNSUPPCOMP
;
885 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n",
886 (UINT
)Search
->File
->FileOffset
, (UINT
)Search
->File
->FileSize
);
888 RtlInitAnsiString(&AnsiString
, Search
->File
->FileName
);
889 wcscpy(DestName
, DestPath
);
890 UnicodeString
.MaximumLength
= sizeof(DestName
) - wcslen(DestName
) * sizeof(WCHAR
);
891 UnicodeString
.Buffer
= DestName
+ wcslen(DestName
);
892 UnicodeString
.Length
= 0;
893 RtlAnsiStringToUnicodeString(&UnicodeString
, &AnsiString
, FALSE
);
895 /* Create destination file, fail if it already exists */
896 RtlInitUnicodeString(&UnicodeString
, DestName
);
898 InitializeObjectAttributes(&ObjectAttributes
,
900 OBJ_CASE_INSENSITIVE
,
903 NtStatus
= NtCreateFile(&DestFile
,
904 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
908 FILE_ATTRIBUTE_NORMAL
,
911 FILE_SYNCHRONOUS_IO_NONALERT
,
914 if (!NT_SUCCESS(NtStatus
))
916 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
918 /* If file exists, ask to overwrite file */
919 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
921 /* Create destination file, overwrite if it already exists */
922 NtStatus
= NtCreateFile(&DestFile
,
923 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
927 FILE_ATTRIBUTE_NORMAL
,
930 FILE_SYNCHRONOUS_IO_ALERT
,
933 if (!NT_SUCCESS(NtStatus
))
935 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName
, NtStatus
);
936 return CAB_STATUS_CANNOT_CREATE
;
941 DPRINT("File (%S) exists\n", DestName
);
942 return CAB_STATUS_FILE_EXISTS
;
946 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
947 NtStatus
= NtCreateSection(&DestFileSection
,
955 if (!NT_SUCCESS(NtStatus
))
957 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
958 Status
= CAB_STATUS_NOMEMORY
;
964 NtStatus
= NtMapViewOfSection(DestFileSection
,
973 if (!NT_SUCCESS(NtStatus
))
975 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
976 Status
= CAB_STATUS_NOMEMORY
;
977 goto CloseDestFileSection
;
980 CurrentDestBuffer
= DestFileBuffer
;
981 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
,
982 Search
->File
->FileTime
,
985 DPRINT("DosDateTimeToFileTime() failed\n");
986 Status
= CAB_STATUS_CANNOT_WRITE
;
990 NtStatus
= NtQueryInformationFile(DestFile
,
993 sizeof(FILE_BASIC_INFORMATION
),
994 FileBasicInformation
);
995 if (!NT_SUCCESS(NtStatus
))
997 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus
);
1001 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1003 NtStatus
= NtSetInformationFile(DestFile
,
1006 sizeof(FILE_BASIC_INFORMATION
),
1007 FileBasicInformation
);
1008 if (!NT_SUCCESS(NtStatus
))
1010 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus
);
1014 SetAttributesOnFile(Search
->File
, DestFile
);
1016 /* Call extract event handler */
1017 if (ExtractHandler
!= NULL
)
1019 ExtractHandler(Search
->File
, DestName
);
1023 CFData
= Search
->CFData
;
1025 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
1027 CurrentOffset
= Search
->Offset
;
1028 while (CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
1030 /* walk the data blocks until we reach
1031 the one containing the start of the file */
1032 CurrentOffset
+= CFData
->UncompSize
;
1033 CFData
= (PCFDATA
)((char *)(CFData
+ 1) + DataReserved
+ CFData
->CompSize
);
1036 Search
->CFData
= CFData
;
1037 Search
->Offset
= CurrentOffset
;
1039 /* now decompress and discard any data in
1040 the block before the start of the file */
1042 /* start of comp data */
1043 CurrentBuffer
= ((unsigned char *)(CFData
+ 1)) + DataReserved
;
1044 RemainingBlock
= CFData
->CompSize
;
1045 InputLength
= RemainingBlock
;
1047 while (CurrentOffset
< Search
->File
->FileOffset
)
1049 /* compute remaining uncomp bytes to start
1050 of file, bounded by sizeof junk */
1051 OutputLength
= Search
->File
->FileOffset
- CurrentOffset
;
1052 if (OutputLength
> (LONG
)sizeof(Junk
))
1053 OutputLength
= sizeof (Junk
);
1055 /* negate to signal NOT end of block */
1056 OutputLength
= -OutputLength
;
1057 CodecUncompress(Junk
, CurrentBuffer
, &InputLength
, &OutputLength
);
1058 /* add the uncomp bytes extracted to current folder offset */
1059 CurrentOffset
+= OutputLength
;
1060 /* add comp bytes consumed to CurrentBuffer */
1061 CurrentBuffer
+= InputLength
;
1062 /* subtract bytes consumed from bytes remaining in block */
1063 RemainingBlock
-= InputLength
;
1064 /* neg for resume decompression of the same block */
1065 InputLength
= -RemainingBlock
;
1068 /* now CurrentBuffer points to the first comp byte
1069 of the file, so we can begin decompressing */
1071 /* Size = remaining uncomp bytes of the file to decompress */
1072 Size
= Search
->File
->FileSize
;
1075 OutputLength
= Size
;
1076 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n",
1077 CurrentBuffer
, RemainingBlock
, Size
);
1079 Status
= CodecUncompress(CurrentDestBuffer
,
1084 if (Status
!= CS_SUCCESS
)
1086 DPRINT("Cannot uncompress block\n");
1087 if (Status
== CS_NOMEMORY
)
1088 Status
= CAB_STATUS_NOMEMORY
;
1089 Status
= CAB_STATUS_INVALID_CAB
;
1093 /* advance dest buffer by bytes produced */
1094 CurrentDestBuffer
= (PVOID
)((ULONG_PTR
)CurrentDestBuffer
+ OutputLength
);
1095 /* advance src buffer by bytes consumed */
1096 CurrentBuffer
+= InputLength
;
1097 /* reduce remaining file bytes by bytes produced */
1098 Size
-= OutputLength
;
1099 /* reduce remaining block size by bytes consumed */
1100 RemainingBlock
-= InputLength
;
1101 if (RemainingBlock
== 0)
1103 /* used up this block, move on to the next */
1104 DPRINT("Out of block data\n");
1105 CFData
= (PCFDATA
)CurrentBuffer
;
1106 RemainingBlock
= CFData
->CompSize
;
1107 CurrentBuffer
= (unsigned char *)(CFData
+ 1) + DataReserved
;
1108 InputLength
= RemainingBlock
;
1112 Status
= CAB_STATUS_SUCCESS
;
1115 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1117 CloseDestFileSection
:
1118 NtClose(DestFileSection
);
1127 * FUNCTION: Selects codec engine to use
1129 * Id = Codec identifier
1132 CabinetSelectCodec(ULONG Id
)
1139 CodecSelected
= FALSE
;
1145 CodecUncompress
= RawCodecUncompress
;
1147 case CAB_CODEC_MSZIP
:
1148 CodecUncompress
= MSZipCodecUncompress
;
1155 CodecSelected
= TRUE
;
1159 * FUNCTION: Set event handlers
1161 * Overwrite = Handler called when a file is to be overwritten
1162 * Extract = Handler called when a file is to be extracted
1163 * DiskChange = Handler called when changing the disk
1166 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1167 PCABINET_EXTRACT Extract
,
1168 PCABINET_DISK_CHANGE DiskChange
)
1170 OverwriteHandler
= Overwrite
;
1171 ExtractHandler
= Extract
;
1172 DiskChangeHandler
= DiskChange
;
1176 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1179 CabinetGetCabinetReservedArea(PULONG Size
)
1181 if (CabinetReservedArea
!= NULL
)
1185 *Size
= CabinetReserved
;
1188 return CabinetReservedArea
;