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
22 #define SEEK_CURRENT 1
27 typedef struct __DOSTIME
35 typedef struct __DOSDATE
42 static WCHAR CabinetName
[256]; // Filename of current cabinet
43 static WCHAR CabinetPrev
[256]; // Filename of previous cabinet
44 static WCHAR DiskPrev
[256]; // Label of cabinet in file CabinetPrev
45 static WCHAR CabinetNext
[256]; // Filename of next cabinet
46 static WCHAR DiskNext
[256]; // Label of cabinet in file CabinetNext
47 static ULONG FolderUncompSize
= 0; // Uncompressed size of folder
48 static ULONG BytesLeftInBlock
= 0; // Number of bytes left in current block
49 static WCHAR DestPath
[MAX_PATH
];
50 static HANDLE FileHandle
;
51 static HANDLE FileSectionHandle
;
52 static PUCHAR FileBuffer
;
53 static ULONG DestFileSize
;
54 static ULONG FileSize
;
55 static BOOL FileOpen
= FALSE
;
56 static PCFHEADER PCABHeader
;
57 static PCFFOLDER CabinetFolders
;
58 static ULONG CabinetReserved
= 0;
59 static ULONG FolderReserved
= 0;
60 static ULONG DataReserved
= 0;
62 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
63 static BOOL CodecSelected
= FALSE
;
64 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
65 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
66 static PCABINET_EXTRACT ExtractHandler
= NULL
;
67 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
68 static z_stream ZStream
;
69 static PVOID CabinetReservedArea
= NULL
;
72 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
76 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
79 void* calloc(size_t _nmemb
, size_t _size
)
81 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
87 RawCodecUncompress(PVOID OutputBuffer
,
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 LONG In
= abs(*InputLength
), Out
= abs(*OutputLength
);
103 memcpy(OutputBuffer
, InputBuffer
, In
< Out
? In
: Out
);
104 *InputLength
= *OutputLength
= In
< Out
? In
: Out
;
112 MSZipCodecUncompress(PVOID OutputBuffer
,
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
130 DPRINT("MSZipCodecUncompress( OutputBuffer = %x, InputBuffer = %x, InputLength = %d, OutputLength = %d.\n", OutputBuffer
, InputBuffer
, *InputLength
, *OutputLength
);
131 if( *InputLength
> 0 )
133 Magic
= *((PUSHORT
)InputBuffer
);
135 if (Magic
!= MSZIP_MAGIC
)
137 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
141 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
142 ZStream
.avail_in
= *InputLength
- 2;
143 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
144 ZStream
.avail_out
= abs(*OutputLength
);
146 /* WindowBits is passed < 0 to tell that there is no zlib header.
147 * Note that in this case inflate *requires* an extra "dummy" byte
148 * after the compressed stream in order to complete decompression and
149 * return Z_STREAM_END.
151 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
154 DPRINT("inflateInit2() returned (%d).\n", Status
);
157 ZStream
.total_in
= 2;
160 ZStream
.avail_in
= -*InputLength
;
161 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
162 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
163 ZStream
.avail_out
= abs(*OutputLength
);
164 ZStream
.total_in
= 0;
166 ZStream
.total_out
= 0;
167 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
168 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
170 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
171 if (Status
== Z_MEM_ERROR
)
176 if( *OutputLength
> 0 )
178 Status
= inflateEnd(&ZStream
);
181 DPRINT("inflateEnd() returned (%d).\n", Status
);
185 *OutputLength
= ZStream
.total_out
;
186 *InputLength
= ZStream
.total_in
;
192 /* Memory functions */
194 voidpf
MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
196 return (voidpf
)RtlAllocateHeap (ProcessHeap
, 0, items
* size
);
199 void MSZipFree (voidpf opaque
, voidpf address
)
201 RtlFreeHeap(ProcessHeap
, 0, address
);
205 ConvertSystemTimeToFileTime(
206 CONST SYSTEMTIME
* lpSystemTime
,
207 LPFILETIME lpFileTime
)
209 TIME_FIELDS TimeFields
;
210 LARGE_INTEGER liTime
;
212 TimeFields
.Year
= lpSystemTime
->wYear
;
213 TimeFields
.Month
= lpSystemTime
->wMonth
;
214 TimeFields
.Day
= lpSystemTime
->wDay
;
215 TimeFields
.Hour
= lpSystemTime
->wHour
;
216 TimeFields
.Minute
= lpSystemTime
->wMinute
;
217 TimeFields
.Second
= lpSystemTime
->wSecond
;
218 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
220 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
222 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
223 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
231 ConvertDosDateTimeToFileTime(
234 LPFILETIME lpFileTime
)
236 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
237 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
238 SYSTEMTIME SystemTime
;
240 if (lpFileTime
== NULL
)
243 SystemTime
.wMilliseconds
= 0;
244 SystemTime
.wSecond
= pdtime
->Second
;
245 SystemTime
.wMinute
= pdtime
->Minute
;
246 SystemTime
.wHour
= pdtime
->Hour
;
248 SystemTime
.wDay
= pddate
->Day
;
249 SystemTime
.wMonth
= pddate
->Month
;
250 SystemTime
.wYear
= 1980 + pddate
->Year
;
252 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
259 GetFileName(PWCHAR Path
)
261 * FUNCTION: Returns a pointer to file name
263 * Path = Pointer to string with pathname
265 * Pointer to filename
274 if (Path
[i
- 1] == L
'\\') j
= i
;
281 RemoveFileName(PWCHAR Path
)
283 * FUNCTION: Removes a file name from a path
285 * Path = Pointer to string with path
292 FileName
= GetFileName(Path
+ i
);
294 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
296 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
303 SetAttributesOnFile(PCFFILE File
, HANDLE hFile
)
305 * FUNCTION: Sets attributes on a file
307 * File = Pointer to CFFILE node for file
309 * Status of operation
312 FILE_BASIC_INFORMATION FileBasic
;
313 IO_STATUS_BLOCK IoStatusBlock
;
315 ULONG Attributes
= 0;
317 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
318 Attributes
|= FILE_ATTRIBUTE_READONLY
;
320 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
321 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
323 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
324 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
326 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
327 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
329 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
330 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
332 NtStatus
= NtQueryInformationFile(hFile
,
335 sizeof(FILE_BASIC_INFORMATION
),
336 FileBasicInformation
);
337 if (!NT_SUCCESS(NtStatus
))
339 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
343 FileBasic
.FileAttributes
= Attributes
;
345 NtStatus
= NtSetInformationFile(hFile
,
348 sizeof(FILE_BASIC_INFORMATION
),
349 FileBasicInformation
);
350 if (!NT_SUCCESS(NtStatus
))
352 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
356 return NT_SUCCESS(NtStatus
);
362 * FUNCTION: Closes the current cabinet
364 * Status of operation
369 NtUnmapViewOfSection( NtCurrentProcess(), FileBuffer
);
370 NtClose( FileSectionHandle
);
371 NtClose( FileHandle
);
380 CabinetInitialize(VOID
)
382 * FUNCTION: Initialize archiver
385 ZStream
.zalloc
= MSZipAlloc
;
386 ZStream
.zfree
= MSZipFree
;
387 ZStream
.opaque
= (voidpf
)0;
390 wcscpy(DestPath
, L
"");
392 CodecId
= CAB_CODEC_RAW
;
393 CodecSelected
= TRUE
;
395 FolderUncompSize
= 0;
396 BytesLeftInBlock
= 0;
400 CabinetReservedArea
= NULL
;
408 * FUNCTION: Cleanup archiver
416 CabinetNormalizePath(PWCHAR Path
,
419 * FUNCTION: Normalizes a path
421 * Path = Pointer to string with pathname
422 * Length = Number of characters in Path
424 * TRUE if there was enough room in Path, or FALSE
430 if ((n
= wcslen(Path
)) &&
431 (Path
[n
- 1] != L
'\\') &&
432 (OK
= ((n
+ 1) < Length
)))
442 CabinetGetCabinetName()
444 * FUNCTION: Returns pointer to cabinet file name
446 * Pointer to string with name of cabinet
454 CabinetSetCabinetName(PWCHAR FileName
)
456 * FUNCTION: Sets cabinet file name
458 * FileName = Pointer to string with name of cabinet
461 wcscpy(CabinetName
, FileName
);
466 CabinetSetDestinationPath(PWCHAR DestinationPath
)
468 * FUNCTION: Sets destination path
470 * DestinationPath = Pointer to string with name of destination path
473 wcscpy(DestPath
, DestinationPath
);
474 if (wcslen(DestPath
) > 0)
475 CabinetNormalizePath(DestPath
, MAX_PATH
);
480 CabinetGetDestinationPath()
482 * FUNCTION: Returns destination path
484 * Pointer to string with name of destination path
494 * FUNCTION: Opens a cabinet file
496 * Status of operation
500 UNICODE_STRING ustring
;
505 OBJECT_ATTRIBUTES ObjectAttributes
;
506 IO_STATUS_BLOCK IoStatusBlock
;
507 UNICODE_STRING FileName
;
511 RtlInitUnicodeString(&FileName
,
514 InitializeObjectAttributes(&ObjectAttributes
,
516 OBJ_CASE_INSENSITIVE
,
520 NtStatus
= NtOpenFile(&FileHandle
,
521 GENERIC_READ
| SYNCHRONIZE
,
525 FILE_SYNCHRONOUS_IO_NONALERT
);
526 if (!NT_SUCCESS(NtStatus
))
528 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
529 return CAB_STATUS_CANNOT_OPEN
;
533 NtStatus
= NtCreateSection(&FileSectionHandle
,
540 if(!NT_SUCCESS(NtStatus
))
542 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
543 return CAB_STATUS_NOMEMORY
;
547 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
549 (PVOID
*)&FileBuffer
,
557 if(!NT_SUCCESS(NtStatus
))
559 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
560 return CAB_STATUS_NOMEMORY
;
562 DPRINT( "Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
563 PCABHeader
= (PCFHEADER
)FileBuffer
;
566 if(FileSize
<= sizeof(CFHEADER
) ||
567 PCABHeader
->Signature
!= CAB_SIGNATURE
||
568 PCABHeader
->Version
!= CAB_VERSION
||
569 PCABHeader
->FolderCount
== 0 ||
570 PCABHeader
->FileCount
== 0 ||
571 PCABHeader
->FileTableOffset
< sizeof(CFHEADER
))
574 DPRINT("File has invalid header.\n");
575 return CAB_STATUS_INVALID_CAB
;
579 Buffer
= (PUCHAR
)(PCABHeader
+1);
580 /* Read/skip any reserved bytes */
581 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
583 CabinetReserved
= *(PUSHORT
)Buffer
;
585 FolderReserved
= *Buffer
;
587 DataReserved
= *Buffer
;
589 if (CabinetReserved
> 0)
591 CabinetReservedArea
= Buffer
;
592 Buffer
+= CabinetReserved
;
596 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
598 /* The previous cabinet file is in the same directory as the current */
599 wcscpy(CabinetPrev
, CabinetName
);
600 RemoveFileName(CabinetPrev
);
601 CabinetNormalizePath(CabinetPrev
, 256);
602 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
603 ustring
.Length
= wcslen( CabinetPrev
);
604 ustring
.Buffer
= CabinetPrev
+ ustring
.Length
;
605 ustring
.MaximumLength
= sizeof( CabinetPrev
) - ustring
.Length
;
606 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
607 Buffer
+= astring
.Length
+ 1;
609 /* Read label of prev disk */
610 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
612 ustring
.Buffer
= DiskPrev
;
613 ustring
.MaximumLength
= sizeof( DiskPrev
);
614 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
615 Buffer
+= astring
.Length
+ 1;
619 wcscpy(CabinetPrev
, L
"");
620 wcscpy(DiskPrev
, L
"");
623 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
625 /* The next cabinet file is in the same directory as the previous */
626 wcscpy(CabinetNext
, CabinetName
);
627 RemoveFileName(CabinetNext
);
628 CabinetNormalizePath(CabinetNext
, 256);
629 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
630 ustring
.Length
= wcslen( CabinetNext
);
631 ustring
.Buffer
= CabinetNext
+ ustring
.Length
;
632 ustring
.MaximumLength
= sizeof( CabinetNext
) - ustring
.Length
;
633 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
634 Buffer
+= astring
.Length
+ 1;
636 /* Read label of next disk */
637 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
639 ustring
.Buffer
= DiskNext
;
640 ustring
.MaximumLength
= sizeof( DiskNext
);
641 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
642 Buffer
+= astring
.Length
+ 1;
646 wcscpy(CabinetNext
, L
"");
647 wcscpy(DiskNext
, L
"");
649 CabinetFolders
= (PCFFOLDER
)Buffer
;
651 DPRINT( "CabinetOpen returning SUCCESS\n" );
652 return CAB_STATUS_SUCCESS
;
659 * FUNCTION: Closes the cabinet file
672 CabinetFindFirst(PWCHAR FileName
,
675 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
677 * FileName = Pointer to search criteria
678 * Search = Pointer to search structure
680 * Status of operation
683 DPRINT( "CabinetFindFirst( FileName = %S )\n", FileName
);
684 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
685 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
687 return CabinetFindNext(Search
);
692 CabinetFindNext(PCAB_SEARCH Search
)
694 * FUNCTION: Finds next file in the cabinet that matches a search criteria
696 * Search = Pointer to search structure
698 * Status of operation
703 ANSI_STRING AnsiString
;
704 UNICODE_STRING UnicodeString
;
705 WCHAR FileName
[MAX_PATH
];
707 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
708 Search
->File
= 0; // restart search of cabinet has changed since last find
711 // starting new search or cabinet
712 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
716 else Prev
= Search
->File
;
719 // look at each file in the archive and see if we found a match
720 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
722 // skip files continued from previous cab
723 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
724 (char *)(Search
->File
+ 1), Search
->File
->FileOffset
, LastFileOffset
);
727 // FIXME: check for match against search criteria
728 if( Search
->File
!= Prev
)
730 // don't match the file we started with
731 if( wcscmp( Search
->Search
, L
"*" ) == 0 )
737 // otherwise, try to match the exact file name
738 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
739 UnicodeString
.Buffer
= FileName
;
740 UnicodeString
.Length
= 0;
741 UnicodeString
.MaximumLength
= sizeof( FileName
);
742 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
743 if( wcscmp( Search
->Search
, UnicodeString
.Buffer
) == 0 )
748 // if we make it here we found no match, so move to the next file
750 if( Search
->Index
>= PCABHeader
->FileCount
)
752 // we have reached the end of this cabinet, try to open the next
753 DPRINT( "End of cabinet reached\n" );
754 if (wcslen(DiskNext
) > 0)
758 CabinetSetCabinetName(CabinetNext
);
759 wcscpy( Search
->Cabinet
, CabinetName
);
761 if (DiskChangeHandler
!= NULL
)
763 DiskChangeHandler(CabinetNext
, DiskNext
);
766 Status
= CabinetOpen();
767 if (Status
!= CAB_STATUS_SUCCESS
)
773 return CAB_STATUS_NOFILE
;
775 // starting new search or cabinet
776 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
780 else Search
->File
= (PCFFILE
)(strchr( (char *)(Search
->File
+ 1), 0 ) + 1);
782 DPRINT( "Found file %s\n", Search
->File
->FileName
);
783 return CAB_STATUS_SUCCESS
;
789 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
792 ULONG
CabinetExtractFile( PCAB_SEARCH Search
)
794 * FUNCTION: Extracts a file from the cabinet
796 * Search = Pointer to PCAB_SEARCH structure used to locate the file
798 * Status of operation
801 ULONG Size
; // remaining file bytes to decompress
802 ULONG CurrentOffset
; // current uncompressed offset within the folder
803 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
804 LONG RemainingBlock
; // remaining comp data in the block
806 HANDLE DestFileSection
;
807 PVOID DestFileBuffer
; // mapped view of dest file
808 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
809 PCFDATA CFData
; // current data block
812 WCHAR DestName
[MAX_PATH
];
814 UNICODE_STRING UnicodeString
;
815 ANSI_STRING AnsiString
;
816 IO_STATUS_BLOCK IoStatusBlock
;
817 OBJECT_ATTRIBUTES ObjectAttributes
;
818 FILE_BASIC_INFORMATION FileBasic
;
819 PCFFOLDER CurrentFolder
;
820 LARGE_INTEGER MaxDestFileSize
;
821 LONG InputLength
, OutputLength
;
824 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
826 // the file is not in the current cabinet
827 DPRINT( "File is not in this cabinet ( %S != %S )\n", Search
->Cabinet
, CabinetName
);
828 return CAB_STATUS_NOFILE
;
830 // look up the folder that the file specifies
831 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
833 // folder is continued from previous cabinet, that shouldn't happen here
834 return CAB_STATUS_NOFILE
;
836 else if( Search
->File
->FolderIndex
== 0xFFFE )
838 // folder is the last in this cabinet and continues into next
839 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
-1];
842 // folder is completely contained within this cabinet
843 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
845 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
848 CabinetSelectCodec(CAB_CODEC_RAW
);
851 CabinetSelectCodec(CAB_CODEC_MSZIP
);
854 return CAB_STATUS_UNSUPPCOMP
;
857 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)).\n",
858 (UINT
)Search
->File
->FileOffset
,
859 (UINT
)Search
->File
->FileSize
);
860 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
861 wcscpy( DestName
, DestPath
);
862 UnicodeString
.MaximumLength
= sizeof( DestName
) - wcslen( DestName
);
863 UnicodeString
.Buffer
= DestName
+ wcslen( DestName
);
864 UnicodeString
.Length
= 0;
865 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
867 /* Create destination file, fail if it already exists */
868 RtlInitUnicodeString(&UnicodeString
,
872 InitializeObjectAttributes(&ObjectAttributes
,
874 OBJ_CASE_INSENSITIVE
,
878 NtStatus
= NtCreateFile(&DestFile
,
879 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
883 FILE_ATTRIBUTE_NORMAL
,
886 FILE_SYNCHRONOUS_IO_NONALERT
,
889 if (!NT_SUCCESS(NtStatus
))
891 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
893 /* If file exists, ask to overwrite file */
894 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
896 /* Create destination file, overwrite if it already exists */
897 NtStatus
= NtCreateFile(&DestFile
,
898 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
902 FILE_ATTRIBUTE_NORMAL
,
905 FILE_SYNCHRONOUS_IO_ALERT
,
908 if (!NT_SUCCESS(NtStatus
))
910 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
911 return CAB_STATUS_CANNOT_CREATE
;
916 DPRINT("File (%S) exists.\n", DestName
);
917 return CAB_STATUS_FILE_EXISTS
;
920 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
921 NtStatus
= NtCreateSection(&DestFileSection
,
928 if(!NT_SUCCESS(NtStatus
))
930 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
931 Status
= CAB_STATUS_NOMEMORY
;
936 NtStatus
= NtMapViewOfSection(DestFileSection
,
946 if(!NT_SUCCESS(NtStatus
))
948 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
949 Status
= CAB_STATUS_NOMEMORY
;
950 goto CloseDestFileSection
;
952 CurrentDestBuffer
= DestFileBuffer
;
953 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
, Search
->File
->FileTime
, &FileTime
))
955 DPRINT("DosDateTimeToFileTime() failed.\n");
956 Status
= CAB_STATUS_CANNOT_WRITE
;
960 NtStatus
= NtQueryInformationFile(DestFile
,
963 sizeof(FILE_BASIC_INFORMATION
),
964 FileBasicInformation
);
965 if (!NT_SUCCESS(NtStatus
))
967 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
971 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
973 NtStatus
= NtSetInformationFile(DestFile
,
976 sizeof(FILE_BASIC_INFORMATION
),
977 FileBasicInformation
);
978 if (!NT_SUCCESS(NtStatus
))
980 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
984 SetAttributesOnFile(Search
->File
, DestFile
);
986 /* Call extract event handler */
987 if (ExtractHandler
!= NULL
)
989 ExtractHandler(Search
->File
, DestName
);
991 // find the starting block of the file
992 // start with the first data block of the folder
993 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
995 while( CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
997 // walk the data blocks until we reach the one containing the start of the file
998 CurrentOffset
+= CFData
->UncompSize
;
999 CFData
= (PCFDATA
)((char *)(CFData
+1) + DataReserved
+ CFData
->CompSize
);
1001 // now decompress and discard any data in the block before the start of the file
1002 CurrentBuffer
= ((unsigned char *)(CFData
+1)) + DataReserved
; // start of comp data
1003 RemainingBlock
= CFData
->CompSize
;
1004 InputLength
= RemainingBlock
;
1005 while( CurrentOffset
< Search
->File
->FileOffset
)
1007 // compute remaining uncomp bytes to start of file, bounded by sizeof junk
1008 OutputLength
= Search
->File
->FileOffset
- CurrentOffset
;
1009 if( OutputLength
> sizeof( Junk
) )
1010 OutputLength
= sizeof( Junk
);
1011 OutputLength
= -OutputLength
; // negate to signal NOT end of block
1012 CodecUncompress( Junk
,
1016 CurrentOffset
+= OutputLength
; // add the uncomp bytes extracted to current folder offset
1017 CurrentBuffer
+= InputLength
; // add comp bytes consumed to CurrentBuffer
1018 RemainingBlock
-= InputLength
; // subtract bytes consumed from bytes remaining in block
1019 InputLength
= -RemainingBlock
; // neg for resume decompression of the same block
1021 // now CurrentBuffer points to the first comp byte of the file, so we can begin decompressing
1023 Size
= Search
->File
->FileSize
; // Size = remaining uncomp bytes of the file to decompress
1026 OutputLength
= Size
;
1027 DPRINT( "Decompressing block at %x with RemainingBlock = %d, Size = %d\n", CurrentBuffer
, RemainingBlock
, Size
);
1028 Status
= CodecUncompress(CurrentDestBuffer
,
1032 if (Status
!= CS_SUCCESS
)
1034 DPRINT("Cannot uncompress block.\n");
1035 if(Status
== CS_NOMEMORY
)
1036 Status
= CAB_STATUS_NOMEMORY
;
1037 Status
= CAB_STATUS_INVALID_CAB
;
1040 CurrentDestBuffer
+= OutputLength
; // advance dest buffer by bytes produced
1041 CurrentBuffer
+= InputLength
; // advance src buffer by bytes consumed
1042 Size
-= OutputLength
; // reduce remaining file bytes by bytes produced
1043 RemainingBlock
-= InputLength
; // reduce remaining block size by bytes consumed
1044 if( RemainingBlock
== 0 )
1046 // used up this block, move on to the next
1047 DPRINT( "Out of block data\n" );
1048 CFData
= (PCFDATA
)CurrentBuffer
;
1049 RemainingBlock
= CFData
->CompSize
;
1050 CurrentBuffer
= ((unsigned char *)(CFData
+1) + DataReserved
);
1051 InputLength
= RemainingBlock
;
1054 Status
= CAB_STATUS_SUCCESS
;
1056 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1057 CloseDestFileSection
:
1058 NtClose(DestFileSection
);
1067 CabinetSelectCodec(ULONG Id
)
1069 * FUNCTION: Selects codec engine to use
1071 * Id = Codec identifier
1079 CodecSelected
= FALSE
;
1085 CodecUncompress
= RawCodecUncompress
;
1087 case CAB_CODEC_MSZIP
:
1088 CodecUncompress
= MSZipCodecUncompress
;
1095 CodecSelected
= TRUE
;
1100 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1101 PCABINET_EXTRACT Extract
,
1102 PCABINET_DISK_CHANGE DiskChange
)
1104 * FUNCTION: Set event handlers
1106 * Overwrite = Handler called when a file is to be overwritten
1107 * Extract = Handler called when a file is to be extracted
1108 * DiskChange = Handler called when changing the disk
1111 OverwriteHandler
= Overwrite
;
1112 ExtractHandler
= Extract
;
1113 DiskChangeHandler
= DiskChange
;
1118 CabinetGetCabinetReservedArea(PULONG Size
)
1120 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1123 if (CabinetReservedArea
!= NULL
)
1127 *Size
= CabinetReserved
;
1129 return CabinetReservedArea
;