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 ULONG DestFileSize
;
50 static ULONG 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 */
70 void __cdecl
free(void* _ptr
)
72 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
75 void* __cdecl
calloc(size_t _nmemb
, size_t _size
)
77 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
83 RawCodecUncompress(PVOID OutputBuffer
,
88 * FUNCTION: Uncompresses data in a buffer
90 * OutputBuffer = Pointer to buffer to place uncompressed data
91 * InputBuffer = Pointer to buffer with data to be uncompressed
92 * InputLength = Length of input buffer before, and amount consumed after
93 * Negative to indicate that this is not the start of a new block
94 * OutputLength = Length of output buffer before, amount filled after
95 * Negative to indicate that this is not the end of the block
98 LONG In
= abs(*InputLength
), Out
= abs(*OutputLength
);
99 memcpy(OutputBuffer
, InputBuffer
, In
< Out
? In
: Out
);
100 *InputLength
= *OutputLength
= In
< Out
? In
: Out
;
108 MSZipCodecUncompress(PVOID OutputBuffer
,
113 * FUNCTION: Uncompresses data in a buffer
115 * OutputBuffer = Pointer to buffer to place uncompressed data
116 * InputBuffer = Pointer to buffer with data to be uncompressed
117 * InputLength = Length of input buffer before, and amount consumed after
118 * Negative to indicate that this is not the start of a new block
119 * OutputLength = Length of output buffer before, amount filled after
120 * Negative to indicate that this is not the end of the block
126 DPRINT("MSZipCodecUncompress( OutputBuffer = %x, InputBuffer = %x, InputLength = %d, OutputLength = %d.\n", OutputBuffer
, InputBuffer
, *InputLength
, *OutputLength
);
127 if( *InputLength
> 0 )
129 Magic
= *((PUSHORT
)InputBuffer
);
131 if (Magic
!= MSZIP_MAGIC
)
133 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
137 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
138 ZStream
.avail_in
= *InputLength
- 2;
139 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
140 ZStream
.avail_out
= abs(*OutputLength
);
142 /* WindowBits is passed < 0 to tell that there is no zlib header.
143 * Note that in this case inflate *requires* an extra "dummy" byte
144 * after the compressed stream in order to complete decompression and
145 * return Z_STREAM_END.
147 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
150 DPRINT("inflateInit2() returned (%d).\n", Status
);
153 ZStream
.total_in
= 2;
156 ZStream
.avail_in
= -*InputLength
;
157 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
158 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
159 ZStream
.avail_out
= abs(*OutputLength
);
160 ZStream
.total_in
= 0;
162 ZStream
.total_out
= 0;
163 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
164 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
166 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
167 if (Status
== Z_MEM_ERROR
)
172 if( *OutputLength
> 0 )
174 Status
= inflateEnd(&ZStream
);
177 DPRINT("inflateEnd() returned (%d).\n", Status
);
181 *OutputLength
= ZStream
.total_out
;
182 *InputLength
= ZStream
.total_in
;
188 /* Memory functions */
190 voidpf
MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
192 return (voidpf
)RtlAllocateHeap (ProcessHeap
, 0, items
* size
);
195 void MSZipFree (voidpf opaque
, voidpf address
)
197 RtlFreeHeap(ProcessHeap
, 0, address
);
201 ConvertSystemTimeToFileTime(
202 CONST SYSTEMTIME
* lpSystemTime
,
203 LPFILETIME lpFileTime
)
205 TIME_FIELDS TimeFields
;
206 LARGE_INTEGER liTime
;
208 TimeFields
.Year
= lpSystemTime
->wYear
;
209 TimeFields
.Month
= lpSystemTime
->wMonth
;
210 TimeFields
.Day
= lpSystemTime
->wDay
;
211 TimeFields
.Hour
= lpSystemTime
->wHour
;
212 TimeFields
.Minute
= lpSystemTime
->wMinute
;
213 TimeFields
.Second
= lpSystemTime
->wSecond
;
214 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
216 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
218 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
219 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
227 ConvertDosDateTimeToFileTime(
230 LPFILETIME lpFileTime
)
232 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
233 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
234 SYSTEMTIME SystemTime
;
236 if (lpFileTime
== NULL
)
239 SystemTime
.wMilliseconds
= 0;
240 SystemTime
.wSecond
= pdtime
->Second
;
241 SystemTime
.wMinute
= pdtime
->Minute
;
242 SystemTime
.wHour
= pdtime
->Hour
;
244 SystemTime
.wDay
= pddate
->Day
;
245 SystemTime
.wMonth
= pddate
->Month
;
246 SystemTime
.wYear
= 1980 + pddate
->Year
;
248 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
255 GetFileName(PWCHAR Path
)
257 * FUNCTION: Returns a pointer to file name
259 * Path = Pointer to string with pathname
261 * Pointer to filename
270 if (Path
[i
- 1] == L
'\\') j
= i
;
277 RemoveFileName(PWCHAR Path
)
279 * FUNCTION: Removes a file name from a path
281 * Path = Pointer to string with path
288 FileName
= GetFileName(Path
+ i
);
290 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
292 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
299 SetAttributesOnFile(PCFFILE File
, HANDLE hFile
)
301 * FUNCTION: Sets attributes on a file
303 * File = Pointer to CFFILE node for file
305 * Status of operation
308 FILE_BASIC_INFORMATION FileBasic
;
309 IO_STATUS_BLOCK IoStatusBlock
;
311 ULONG Attributes
= 0;
313 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
314 Attributes
|= FILE_ATTRIBUTE_READONLY
;
316 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
317 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
319 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
320 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
322 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
323 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
325 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
326 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
328 NtStatus
= NtQueryInformationFile(hFile
,
331 sizeof(FILE_BASIC_INFORMATION
),
332 FileBasicInformation
);
333 if (!NT_SUCCESS(NtStatus
))
335 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
339 FileBasic
.FileAttributes
= Attributes
;
341 NtStatus
= NtSetInformationFile(hFile
,
344 sizeof(FILE_BASIC_INFORMATION
),
345 FileBasicInformation
);
346 if (!NT_SUCCESS(NtStatus
))
348 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
352 return NT_SUCCESS(NtStatus
);
358 * FUNCTION: Closes the current cabinet
360 * Status of operation
365 NtUnmapViewOfSection( NtCurrentProcess(), FileBuffer
);
366 NtClose( FileSectionHandle
);
367 NtClose( FileHandle
);
376 CabinetInitialize(VOID
)
378 * FUNCTION: Initialize archiver
381 ZStream
.zalloc
= MSZipAlloc
;
382 ZStream
.zfree
= MSZipFree
;
383 ZStream
.opaque
= (voidpf
)0;
386 wcscpy(DestPath
, L
"");
388 CodecId
= CAB_CODEC_RAW
;
389 CodecSelected
= TRUE
;
391 FolderUncompSize
= 0;
392 BytesLeftInBlock
= 0;
396 CabinetReservedArea
= NULL
;
404 * FUNCTION: Cleanup archiver
412 CabinetNormalizePath(PWCHAR Path
,
415 * FUNCTION: Normalizes a path
417 * Path = Pointer to string with pathname
418 * Length = Number of characters in Path
420 * TRUE if there was enough room in Path, or FALSE
426 if ((n
= wcslen(Path
)) &&
427 (Path
[n
- 1] != L
'\\') &&
428 (OK
= ((n
+ 1) < Length
)))
438 CabinetGetCabinetName()
440 * FUNCTION: Returns pointer to cabinet file name
442 * Pointer to string with name of cabinet
450 CabinetSetCabinetName(PWCHAR FileName
)
452 * FUNCTION: Sets cabinet file name
454 * FileName = Pointer to string with name of cabinet
457 wcscpy(CabinetName
, FileName
);
462 CabinetSetDestinationPath(PWCHAR DestinationPath
)
464 * FUNCTION: Sets destination path
466 * DestinationPath = Pointer to string with name of destination path
469 wcscpy(DestPath
, DestinationPath
);
470 if (wcslen(DestPath
) > 0)
471 CabinetNormalizePath(DestPath
, MAX_PATH
);
476 CabinetGetDestinationPath()
478 * FUNCTION: Returns destination path
480 * Pointer to string with name of destination path
490 * FUNCTION: Opens a cabinet file
492 * Status of operation
496 UNICODE_STRING ustring
;
501 OBJECT_ATTRIBUTES ObjectAttributes
;
502 IO_STATUS_BLOCK IoStatusBlock
;
503 UNICODE_STRING FileName
;
507 RtlInitUnicodeString(&FileName
,
510 InitializeObjectAttributes(&ObjectAttributes
,
512 OBJ_CASE_INSENSITIVE
,
516 NtStatus
= NtOpenFile(&FileHandle
,
517 GENERIC_READ
| SYNCHRONIZE
,
521 FILE_SYNCHRONOUS_IO_NONALERT
);
522 if (!NT_SUCCESS(NtStatus
))
524 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
525 return CAB_STATUS_CANNOT_OPEN
;
529 NtStatus
= NtCreateSection(&FileSectionHandle
,
536 if(!NT_SUCCESS(NtStatus
))
538 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
539 return CAB_STATUS_NOMEMORY
;
543 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
545 (PVOID
*)&FileBuffer
,
553 if(!NT_SUCCESS(NtStatus
))
555 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
556 return CAB_STATUS_NOMEMORY
;
558 DPRINT( "Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
559 PCABHeader
= (PCFHEADER
)FileBuffer
;
562 if(FileSize
<= sizeof(CFHEADER
) ||
563 PCABHeader
->Signature
!= CAB_SIGNATURE
||
564 PCABHeader
->Version
!= CAB_VERSION
||
565 PCABHeader
->FolderCount
== 0 ||
566 PCABHeader
->FileCount
== 0 ||
567 PCABHeader
->FileTableOffset
< sizeof(CFHEADER
))
570 DPRINT("File has invalid header.\n");
571 return CAB_STATUS_INVALID_CAB
;
575 Buffer
= (PUCHAR
)(PCABHeader
+1);
576 /* Read/skip any reserved bytes */
577 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
579 CabinetReserved
= *(PUSHORT
)Buffer
;
581 FolderReserved
= *Buffer
;
583 DataReserved
= *Buffer
;
585 if (CabinetReserved
> 0)
587 CabinetReservedArea
= Buffer
;
588 Buffer
+= CabinetReserved
;
592 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
594 /* The previous cabinet file is in the same directory as the current */
595 wcscpy(CabinetPrev
, CabinetName
);
596 RemoveFileName(CabinetPrev
);
597 CabinetNormalizePath(CabinetPrev
, 256);
598 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
599 ustring
.Length
= wcslen( CabinetPrev
);
600 ustring
.Buffer
= CabinetPrev
+ ustring
.Length
;
601 ustring
.MaximumLength
= sizeof( CabinetPrev
) - ustring
.Length
;
602 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
603 Buffer
+= astring
.Length
+ 1;
605 /* Read label of prev disk */
606 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
608 ustring
.Buffer
= DiskPrev
;
609 ustring
.MaximumLength
= sizeof( DiskPrev
);
610 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
611 Buffer
+= astring
.Length
+ 1;
615 wcscpy(CabinetPrev
, L
"");
616 wcscpy(DiskPrev
, L
"");
619 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
621 /* The next cabinet file is in the same directory as the previous */
622 wcscpy(CabinetNext
, CabinetName
);
623 RemoveFileName(CabinetNext
);
624 CabinetNormalizePath(CabinetNext
, 256);
625 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
626 ustring
.Length
= wcslen( CabinetNext
);
627 ustring
.Buffer
= CabinetNext
+ ustring
.Length
;
628 ustring
.MaximumLength
= sizeof( CabinetNext
) - ustring
.Length
;
629 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
630 Buffer
+= astring
.Length
+ 1;
632 /* Read label of next disk */
633 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
635 ustring
.Buffer
= DiskNext
;
636 ustring
.MaximumLength
= sizeof( DiskNext
);
637 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
638 Buffer
+= astring
.Length
+ 1;
642 wcscpy(CabinetNext
, L
"");
643 wcscpy(DiskNext
, L
"");
645 CabinetFolders
= (PCFFOLDER
)Buffer
;
647 DPRINT( "CabinetOpen returning SUCCESS\n" );
648 return CAB_STATUS_SUCCESS
;
655 * FUNCTION: Closes the cabinet file
668 CabinetFindFirst(PWCHAR FileName
,
671 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
673 * FileName = Pointer to search criteria
674 * Search = Pointer to search structure
676 * Status of operation
679 DPRINT( "CabinetFindFirst( FileName = %S )\n", FileName
);
680 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
681 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
683 return CabinetFindNext(Search
);
688 CabinetFindNext(PCAB_SEARCH Search
)
690 * FUNCTION: Finds next file in the cabinet that matches a search criteria
692 * Search = Pointer to search structure
694 * Status of operation
699 ANSI_STRING AnsiString
;
700 UNICODE_STRING UnicodeString
;
701 WCHAR FileName
[MAX_PATH
];
703 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
704 Search
->File
= 0; // restart search of cabinet has changed since last find
707 // starting new search or cabinet
708 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
712 else Prev
= Search
->File
;
715 // look at each file in the archive and see if we found a match
716 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
718 // skip files continued from previous cab
719 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
720 (char *)(Search
->File
+ 1), Search
->File
->FileOffset
, LastFileOffset
);
723 // FIXME: check for match against search criteria
724 if( Search
->File
!= Prev
)
726 // don't match the file we started with
727 if( wcscmp( Search
->Search
, L
"*" ) == 0 )
733 // otherwise, try to match the exact file name
734 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
735 UnicodeString
.Buffer
= FileName
;
736 UnicodeString
.Length
= 0;
737 UnicodeString
.MaximumLength
= sizeof( FileName
);
738 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
739 if( wcscmp( Search
->Search
, UnicodeString
.Buffer
) == 0 )
744 // if we make it here we found no match, so move to the next file
746 if( Search
->Index
>= PCABHeader
->FileCount
)
748 // we have reached the end of this cabinet, try to open the next
749 DPRINT( "End of cabinet reached\n" );
750 if (wcslen(DiskNext
) > 0)
754 CabinetSetCabinetName(CabinetNext
);
755 wcscpy( Search
->Cabinet
, CabinetName
);
757 if (DiskChangeHandler
!= NULL
)
759 DiskChangeHandler(CabinetNext
, DiskNext
);
762 Status
= CabinetOpen();
763 if (Status
!= CAB_STATUS_SUCCESS
)
769 return CAB_STATUS_NOFILE
;
771 // starting new search or cabinet
772 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
776 else Search
->File
= (PCFFILE
)(strchr( (char *)(Search
->File
+ 1), 0 ) + 1);
778 DPRINT( "Found file %s\n", Search
->File
->FileName
);
779 return CAB_STATUS_SUCCESS
;
785 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
788 ULONG
CabinetExtractFile( PCAB_SEARCH Search
)
790 * FUNCTION: Extracts a file from the cabinet
792 * Search = Pointer to PCAB_SEARCH structure used to locate the file
794 * Status of operation
797 ULONG Size
; // remaining file bytes to decompress
798 ULONG CurrentOffset
; // current uncompressed offset within the folder
799 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
800 LONG RemainingBlock
; // remaining comp data in the block
802 HANDLE DestFileSection
;
803 PVOID DestFileBuffer
; // mapped view of dest file
804 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
805 PCFDATA CFData
; // current data block
808 WCHAR DestName
[MAX_PATH
];
810 UNICODE_STRING UnicodeString
;
811 ANSI_STRING AnsiString
;
812 IO_STATUS_BLOCK IoStatusBlock
;
813 OBJECT_ATTRIBUTES ObjectAttributes
;
814 FILE_BASIC_INFORMATION FileBasic
;
815 PCFFOLDER CurrentFolder
;
816 LARGE_INTEGER MaxDestFileSize
;
817 LONG InputLength
, OutputLength
;
820 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
822 // the file is not in the current cabinet
823 DPRINT( "File is not in this cabinet ( %S != %S )\n", Search
->Cabinet
, CabinetName
);
824 return CAB_STATUS_NOFILE
;
826 // look up the folder that the file specifies
827 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
829 // folder is continued from previous cabinet, that shouldn't happen here
830 return CAB_STATUS_NOFILE
;
832 else if( Search
->File
->FolderIndex
== 0xFFFE )
834 // folder is the last in this cabinet and continues into next
835 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
-1];
838 // folder is completely contained within this cabinet
839 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
841 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
844 CabinetSelectCodec(CAB_CODEC_RAW
);
847 CabinetSelectCodec(CAB_CODEC_MSZIP
);
850 return CAB_STATUS_UNSUPPCOMP
;
853 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)).\n",
854 (UINT
)Search
->File
->FileOffset
,
855 (UINT
)Search
->File
->FileSize
);
856 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
857 wcscpy( DestName
, DestPath
);
858 UnicodeString
.MaximumLength
= sizeof( DestName
) - wcslen( DestName
);
859 UnicodeString
.Buffer
= DestName
+ wcslen( DestName
);
860 UnicodeString
.Length
= 0;
861 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
863 /* Create destination file, fail if it already exists */
864 RtlInitUnicodeString(&UnicodeString
,
868 InitializeObjectAttributes(&ObjectAttributes
,
870 OBJ_CASE_INSENSITIVE
,
874 NtStatus
= NtCreateFile(&DestFile
,
875 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
879 FILE_ATTRIBUTE_NORMAL
,
882 FILE_SYNCHRONOUS_IO_NONALERT
,
885 if (!NT_SUCCESS(NtStatus
))
887 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
889 /* If file exists, ask to overwrite file */
890 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
892 /* Create destination file, overwrite if it already exists */
893 NtStatus
= NtCreateFile(&DestFile
,
894 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
898 FILE_ATTRIBUTE_NORMAL
,
901 FILE_SYNCHRONOUS_IO_ALERT
,
904 if (!NT_SUCCESS(NtStatus
))
906 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
907 return CAB_STATUS_CANNOT_CREATE
;
912 DPRINT("File (%S) exists.\n", DestName
);
913 return CAB_STATUS_FILE_EXISTS
;
916 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
917 NtStatus
= NtCreateSection(&DestFileSection
,
924 if(!NT_SUCCESS(NtStatus
))
926 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
927 Status
= CAB_STATUS_NOMEMORY
;
932 NtStatus
= NtMapViewOfSection(DestFileSection
,
942 if(!NT_SUCCESS(NtStatus
))
944 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
945 Status
= CAB_STATUS_NOMEMORY
;
946 goto CloseDestFileSection
;
948 CurrentDestBuffer
= DestFileBuffer
;
949 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
, Search
->File
->FileTime
, &FileTime
))
951 DPRINT("DosDateTimeToFileTime() failed.\n");
952 Status
= CAB_STATUS_CANNOT_WRITE
;
956 NtStatus
= NtQueryInformationFile(DestFile
,
959 sizeof(FILE_BASIC_INFORMATION
),
960 FileBasicInformation
);
961 if (!NT_SUCCESS(NtStatus
))
963 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
967 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
969 NtStatus
= NtSetInformationFile(DestFile
,
972 sizeof(FILE_BASIC_INFORMATION
),
973 FileBasicInformation
);
974 if (!NT_SUCCESS(NtStatus
))
976 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
980 SetAttributesOnFile(Search
->File
, DestFile
);
982 /* Call extract event handler */
983 if (ExtractHandler
!= NULL
)
985 ExtractHandler(Search
->File
, DestName
);
987 // find the starting block of the file
988 // start with the first data block of the folder
989 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
991 while( CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
993 // walk the data blocks until we reach the one containing the start of the file
994 CurrentOffset
+= CFData
->UncompSize
;
995 CFData
= (PCFDATA
)((char *)(CFData
+1) + DataReserved
+ CFData
->CompSize
);
997 // now decompress and discard any data in the block before the start of the file
998 CurrentBuffer
= ((unsigned char *)(CFData
+1)) + DataReserved
; // start of comp data
999 RemainingBlock
= CFData
->CompSize
;
1000 InputLength
= RemainingBlock
;
1001 while( CurrentOffset
< Search
->File
->FileOffset
)
1003 // compute remaining uncomp bytes to start of file, bounded by sizeof junk
1004 OutputLength
= Search
->File
->FileOffset
- CurrentOffset
;
1005 if( OutputLength
> (LONG
)sizeof( Junk
) )
1006 OutputLength
= sizeof( Junk
);
1007 OutputLength
= -OutputLength
; // negate to signal NOT end of block
1008 CodecUncompress( Junk
,
1012 CurrentOffset
+= OutputLength
; // add the uncomp bytes extracted to current folder offset
1013 CurrentBuffer
+= InputLength
; // add comp bytes consumed to CurrentBuffer
1014 RemainingBlock
-= InputLength
; // subtract bytes consumed from bytes remaining in block
1015 InputLength
= -RemainingBlock
; // neg for resume decompression of the same block
1017 // now CurrentBuffer points to the first comp byte of the file, so we can begin decompressing
1019 Size
= Search
->File
->FileSize
; // Size = remaining uncomp bytes of the file to decompress
1022 OutputLength
= Size
;
1023 DPRINT( "Decompressing block at %x with RemainingBlock = %d, Size = %d\n", CurrentBuffer
, RemainingBlock
, Size
);
1024 Status
= CodecUncompress(CurrentDestBuffer
,
1028 if (Status
!= CS_SUCCESS
)
1030 DPRINT("Cannot uncompress block.\n");
1031 if(Status
== CS_NOMEMORY
)
1032 Status
= CAB_STATUS_NOMEMORY
;
1033 Status
= CAB_STATUS_INVALID_CAB
;
1036 CurrentDestBuffer
= (PVOID
)((ULONG_PTR
)CurrentDestBuffer
+ OutputLength
); // advance dest buffer by bytes produced
1037 CurrentBuffer
+= InputLength
; // advance src buffer by bytes consumed
1038 Size
-= OutputLength
; // reduce remaining file bytes by bytes produced
1039 RemainingBlock
-= InputLength
; // reduce remaining block size by bytes consumed
1040 if( RemainingBlock
== 0 )
1042 // used up this block, move on to the next
1043 DPRINT( "Out of block data\n" );
1044 CFData
= (PCFDATA
)CurrentBuffer
;
1045 RemainingBlock
= CFData
->CompSize
;
1046 CurrentBuffer
= ((unsigned char *)(CFData
+1) + DataReserved
);
1047 InputLength
= RemainingBlock
;
1050 Status
= CAB_STATUS_SUCCESS
;
1052 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1053 CloseDestFileSection
:
1054 NtClose(DestFileSection
);
1063 CabinetSelectCodec(ULONG Id
)
1065 * FUNCTION: Selects codec engine to use
1067 * Id = Codec identifier
1075 CodecSelected
= FALSE
;
1081 CodecUncompress
= RawCodecUncompress
;
1083 case CAB_CODEC_MSZIP
:
1084 CodecUncompress
= MSZipCodecUncompress
;
1091 CodecSelected
= TRUE
;
1096 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1097 PCABINET_EXTRACT Extract
,
1098 PCABINET_DISK_CHANGE DiskChange
)
1100 * FUNCTION: Set event handlers
1102 * Overwrite = Handler called when a file is to be overwritten
1103 * Extract = Handler called when a file is to be extracted
1104 * DiskChange = Handler called when changing the disk
1107 OverwriteHandler
= Overwrite
;
1108 ExtractHandler
= Extract
;
1109 DiskChangeHandler
= DiskChange
;
1114 CabinetGetCabinetReservedArea(PULONG Size
)
1116 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1119 if (CabinetReservedArea
!= NULL
)
1123 *Size
= CabinetReserved
;
1125 return CabinetReservedArea
;