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 SIZE_T DestFileSize
;
50 static SIZE_T 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 /* round to 16 bytes + alloc at minimum 16 bytes */
71 #define ROUND_SIZE(size) (max(16, ROUND_UP(size, 16)))
73 void* __cdecl
malloc(size_t _size
)
75 size_t nSize
= ROUND_SIZE(_size
);
80 return RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
, nSize
);
84 void __cdecl
free(void* _ptr
)
86 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
89 void* __cdecl
calloc(size_t _nmemb
, size_t _size
)
91 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
97 RawCodecUncompress(PVOID OutputBuffer
,
102 * FUNCTION: Uncompresses data in a buffer
104 * OutputBuffer = Pointer to buffer to place uncompressed data
105 * InputBuffer = Pointer to buffer with data to be uncompressed
106 * InputLength = Length of input buffer before, and amount consumed after
107 * Negative to indicate that this is not the start of a new block
108 * OutputLength = Length of output buffer before, amount filled after
109 * Negative to indicate that this is not the end of the block
112 LONG In
= abs(*InputLength
), Out
= abs(*OutputLength
);
113 memcpy(OutputBuffer
, InputBuffer
, In
< Out
? In
: Out
);
114 *InputLength
= *OutputLength
= In
< Out
? In
: Out
;
122 MSZipCodecUncompress(PVOID OutputBuffer
,
127 * FUNCTION: Uncompresses data in a buffer
129 * OutputBuffer = Pointer to buffer to place uncompressed data
130 * InputBuffer = Pointer to buffer with data to be uncompressed
131 * InputLength = Length of input buffer before, and amount consumed after
132 * Negative to indicate that this is not the start of a new block
133 * OutputLength = Length of output buffer before, amount filled after
134 * Negative to indicate that this is not the end of the block
140 DPRINT("MSZipCodecUncompress( OutputBuffer = %x, InputBuffer = %x, InputLength = %d, OutputLength = %d.\n", OutputBuffer
, InputBuffer
, *InputLength
, *OutputLength
);
141 if( *InputLength
> 0 )
143 Magic
= *((PUSHORT
)InputBuffer
);
145 if (Magic
!= MSZIP_MAGIC
)
147 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
151 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
152 ZStream
.avail_in
= *InputLength
- 2;
153 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
154 ZStream
.avail_out
= abs(*OutputLength
);
156 /* WindowBits is passed < 0 to tell that there is no zlib header.
157 * Note that in this case inflate *requires* an extra "dummy" byte
158 * after the compressed stream in order to complete decompression and
159 * return Z_STREAM_END.
161 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
164 DPRINT("inflateInit2() returned (%d).\n", Status
);
167 ZStream
.total_in
= 2;
170 ZStream
.avail_in
= -*InputLength
;
171 ZStream
.next_in
= (PUCHAR
)InputBuffer
;
172 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
173 ZStream
.avail_out
= abs(*OutputLength
);
174 ZStream
.total_in
= 0;
176 ZStream
.total_out
= 0;
177 Status
= inflate(&ZStream
, Z_SYNC_FLUSH
);
178 if (Status
!= Z_OK
&& Status
!= Z_STREAM_END
)
180 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
181 if (Status
== Z_MEM_ERROR
)
186 if( *OutputLength
> 0 )
188 Status
= inflateEnd(&ZStream
);
191 DPRINT("inflateEnd() returned (%d).\n", Status
);
195 *OutputLength
= ZStream
.total_out
;
196 *InputLength
= ZStream
.total_in
;
202 /* Memory functions */
204 voidpf
MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
206 return (voidpf
)RtlAllocateHeap (ProcessHeap
, 0, items
* size
);
209 void MSZipFree (voidpf opaque
, voidpf address
)
211 RtlFreeHeap(ProcessHeap
, 0, address
);
215 ConvertSystemTimeToFileTime(
216 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(
244 LPFILETIME lpFileTime
)
246 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
247 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
248 SYSTEMTIME SystemTime
;
250 if (lpFileTime
== NULL
)
253 SystemTime
.wMilliseconds
= 0;
254 SystemTime
.wSecond
= pdtime
->Second
;
255 SystemTime
.wMinute
= pdtime
->Minute
;
256 SystemTime
.wHour
= pdtime
->Hour
;
258 SystemTime
.wDay
= pddate
->Day
;
259 SystemTime
.wMonth
= pddate
->Month
;
260 SystemTime
.wYear
= 1980 + pddate
->Year
;
262 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
269 GetFileName(PWCHAR Path
)
271 * FUNCTION: Returns a pointer to file name
273 * Path = Pointer to string with pathname
275 * Pointer to filename
284 if (Path
[i
- 1] == L
'\\') j
= i
;
291 RemoveFileName(PWCHAR Path
)
293 * FUNCTION: Removes a file name from a path
295 * Path = Pointer to string with path
302 FileName
= GetFileName(Path
+ i
);
304 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
306 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
313 SetAttributesOnFile(PCFFILE File
, HANDLE hFile
)
315 * FUNCTION: Sets attributes on a file
317 * File = Pointer to CFFILE node for file
319 * Status of operation
322 FILE_BASIC_INFORMATION FileBasic
;
323 IO_STATUS_BLOCK IoStatusBlock
;
325 ULONG Attributes
= 0;
327 if (File
->Attributes
& CAB_ATTRIB_READONLY
)
328 Attributes
|= FILE_ATTRIBUTE_READONLY
;
330 if (File
->Attributes
& CAB_ATTRIB_HIDDEN
)
331 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
333 if (File
->Attributes
& CAB_ATTRIB_SYSTEM
)
334 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
336 if (File
->Attributes
& CAB_ATTRIB_DIRECTORY
)
337 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
339 if (File
->Attributes
& CAB_ATTRIB_ARCHIVE
)
340 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
342 NtStatus
= NtQueryInformationFile(hFile
,
345 sizeof(FILE_BASIC_INFORMATION
),
346 FileBasicInformation
);
347 if (!NT_SUCCESS(NtStatus
))
349 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
353 FileBasic
.FileAttributes
= Attributes
;
355 NtStatus
= NtSetInformationFile(hFile
,
358 sizeof(FILE_BASIC_INFORMATION
),
359 FileBasicInformation
);
360 if (!NT_SUCCESS(NtStatus
))
362 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
366 return NT_SUCCESS(NtStatus
);
372 * FUNCTION: Closes the current cabinet
374 * Status of operation
379 NtUnmapViewOfSection( NtCurrentProcess(), FileBuffer
);
380 NtClose( FileSectionHandle
);
381 NtClose( FileHandle
);
390 CabinetInitialize(VOID
)
392 * FUNCTION: Initialize archiver
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
;
418 * FUNCTION: Cleanup archiver
426 CabinetNormalizePath(PWCHAR Path
,
429 * FUNCTION: Normalizes a path
431 * Path = Pointer to string with pathname
432 * Length = Number of characters in Path
434 * TRUE if there was enough room in Path, or FALSE
440 if ((n
= wcslen(Path
)) &&
441 (Path
[n
- 1] != L
'\\') &&
442 (OK
= ((n
+ 1) < Length
)))
452 CabinetGetCabinetName()
454 * FUNCTION: Returns pointer to cabinet file name
456 * Pointer to string with name of cabinet
464 CabinetSetCabinetName(PWCHAR FileName
)
466 * FUNCTION: Sets cabinet file name
468 * FileName = Pointer to string with name of cabinet
471 wcscpy(CabinetName
, FileName
);
476 CabinetSetDestinationPath(PWCHAR DestinationPath
)
478 * FUNCTION: Sets destination path
480 * DestinationPath = Pointer to string with name of destination path
483 wcscpy(DestPath
, DestinationPath
);
484 if (wcslen(DestPath
) > 0)
485 CabinetNormalizePath(DestPath
, MAX_PATH
);
490 CabinetGetDestinationPath()
492 * FUNCTION: Returns destination path
494 * Pointer to string with name of destination path
504 * FUNCTION: Opens a cabinet file
506 * Status of operation
510 UNICODE_STRING ustring
;
515 OBJECT_ATTRIBUTES ObjectAttributes
;
516 IO_STATUS_BLOCK IoStatusBlock
;
517 UNICODE_STRING FileName
;
521 RtlInitUnicodeString(&FileName
,
524 InitializeObjectAttributes(&ObjectAttributes
,
526 OBJ_CASE_INSENSITIVE
,
530 NtStatus
= NtOpenFile(&FileHandle
,
531 GENERIC_READ
| SYNCHRONIZE
,
535 FILE_SYNCHRONOUS_IO_NONALERT
);
536 if (!NT_SUCCESS(NtStatus
))
538 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
539 return CAB_STATUS_CANNOT_OPEN
;
543 NtStatus
= NtCreateSection(&FileSectionHandle
,
550 if(!NT_SUCCESS(NtStatus
))
552 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
553 return CAB_STATUS_NOMEMORY
;
557 NtStatus
= NtMapViewOfSection(FileSectionHandle
,
559 (PVOID
*)&FileBuffer
,
567 if(!NT_SUCCESS(NtStatus
))
569 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
570 return CAB_STATUS_NOMEMORY
;
572 DPRINT( "Cabinet file %S opened and mapped to %x\n", CabinetName
, FileBuffer
);
573 PCABHeader
= (PCFHEADER
)FileBuffer
;
576 if(FileSize
<= sizeof(CFHEADER
) ||
577 PCABHeader
->Signature
!= CAB_SIGNATURE
||
578 PCABHeader
->Version
!= CAB_VERSION
||
579 PCABHeader
->FolderCount
== 0 ||
580 PCABHeader
->FileCount
== 0 ||
581 PCABHeader
->FileTableOffset
< sizeof(CFHEADER
))
584 DPRINT("File has invalid header.\n");
585 return CAB_STATUS_INVALID_CAB
;
589 Buffer
= (PUCHAR
)(PCABHeader
+1);
590 /* Read/skip any reserved bytes */
591 if (PCABHeader
->Flags
& CAB_FLAG_RESERVE
)
593 CabinetReserved
= *(PUSHORT
)Buffer
;
595 FolderReserved
= *Buffer
;
597 DataReserved
= *Buffer
;
599 if (CabinetReserved
> 0)
601 CabinetReservedArea
= Buffer
;
602 Buffer
+= CabinetReserved
;
606 if (PCABHeader
->Flags
& CAB_FLAG_HASPREV
)
608 /* The previous cabinet file is in the same directory as the current */
609 wcscpy(CabinetPrev
, CabinetName
);
610 RemoveFileName(CabinetPrev
);
611 CabinetNormalizePath(CabinetPrev
, 256);
612 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
613 ustring
.Length
= wcslen( CabinetPrev
);
614 ustring
.Buffer
= CabinetPrev
+ ustring
.Length
;
615 ustring
.MaximumLength
= sizeof( CabinetPrev
) - ustring
.Length
;
616 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
617 Buffer
+= astring
.Length
+ 1;
619 /* Read label of prev disk */
620 RtlInitAnsiString( &astring
, (LPSTR
)Buffer
);
622 ustring
.Buffer
= DiskPrev
;
623 ustring
.MaximumLength
= sizeof( DiskPrev
);
624 RtlAnsiStringToUnicodeString( &ustring
, &astring
, FALSE
);
625 Buffer
+= astring
.Length
+ 1;
629 wcscpy(CabinetPrev
, L
"");
630 wcscpy(DiskPrev
, L
"");
633 if (PCABHeader
->Flags
& CAB_FLAG_HASNEXT
)
635 /* The next cabinet file is in 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
;
661 DPRINT( "CabinetOpen returning SUCCESS\n" );
662 return CAB_STATUS_SUCCESS
;
669 * FUNCTION: Closes the cabinet file
682 CabinetFindFirst(PWCHAR FileName
,
685 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
687 * FileName = Pointer to search criteria
688 * Search = Pointer to search structure
690 * Status of operation
693 DPRINT( "CabinetFindFirst( FileName = %S )\n", FileName
);
694 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
695 wcsncpy(Search
->Cabinet
, CabinetName
, MAX_PATH
);
697 return CabinetFindNext(Search
);
702 CabinetFindNext(PCAB_SEARCH Search
)
704 * FUNCTION: Finds next file in the cabinet that matches a search criteria
706 * Search = Pointer to search structure
708 * Status of operation
713 ANSI_STRING AnsiString
;
714 UNICODE_STRING UnicodeString
;
715 WCHAR FileName
[MAX_PATH
] = {L
'\0'};
717 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
718 Search
->File
= 0; // restart search of cabinet has changed since last find
721 // starting new search or cabinet
722 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
726 else Prev
= Search
->File
;
729 // look at each file in the archive and see if we found a match
730 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
732 // skip files continued from previous cab
733 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
734 (char *)(Search
->File
+ 1), Search
->File
->FileOffset
, LastFileOffset
);
737 // FIXME: check for match against search criteria
738 if( Search
->File
!= Prev
)
740 // don't match the file we started with
741 if( wcscmp( Search
->Search
, L
"*" ) == 0 )
747 // otherwise, try to match the exact file name
748 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
749 UnicodeString
.Buffer
= FileName
;
750 UnicodeString
.Length
= 0;
751 UnicodeString
.MaximumLength
= sizeof( FileName
);
752 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
753 if( wcscmp( Search
->Search
, UnicodeString
.Buffer
) == 0 )
758 // if we make it here we found no match, so move to the next file
760 if( Search
->Index
>= PCABHeader
->FileCount
)
762 // we have reached the end of this cabinet, try to open the next
763 DPRINT( "End of cabinet reached\n" );
764 if (wcslen(DiskNext
) > 0)
768 CabinetSetCabinetName(CabinetNext
);
769 wcscpy( Search
->Cabinet
, CabinetName
);
771 if (DiskChangeHandler
!= NULL
)
773 DiskChangeHandler(CabinetNext
, DiskNext
);
776 Status
= CabinetOpen();
777 if (Status
!= CAB_STATUS_SUCCESS
)
783 return CAB_STATUS_NOFILE
;
785 // starting new search or cabinet
786 Search
->File
= (PCFFILE
)(FileBuffer
+ PCABHeader
->FileTableOffset
);
790 else Search
->File
= (PCFFILE
)(strchr( (char *)(Search
->File
+ 1), 0 ) + 1);
792 DPRINT( "Found file %s\n", Search
->File
->FileName
);
793 return CAB_STATUS_SUCCESS
;
799 return (int)RtlValidateHeap(ProcessHeap
, 0, 0);
802 ULONG
CabinetExtractFile( PCAB_SEARCH Search
)
804 * FUNCTION: Extracts a file from the cabinet
806 * Search = Pointer to PCAB_SEARCH structure used to locate the file
808 * Status of operation
811 ULONG Size
; // remaining file bytes to decompress
812 ULONG CurrentOffset
; // current uncompressed offset within the folder
813 PUCHAR CurrentBuffer
; // current pointer to compressed data in the block
814 LONG RemainingBlock
; // remaining comp data in the block
816 HANDLE DestFileSection
;
817 PVOID DestFileBuffer
; // mapped view of dest file
818 PVOID CurrentDestBuffer
; // pointer to the current position in the dest view
819 PCFDATA CFData
; // current data block
822 WCHAR DestName
[MAX_PATH
];
824 UNICODE_STRING UnicodeString
;
825 ANSI_STRING AnsiString
;
826 IO_STATUS_BLOCK IoStatusBlock
;
827 OBJECT_ATTRIBUTES ObjectAttributes
;
828 FILE_BASIC_INFORMATION FileBasic
;
829 PCFFOLDER CurrentFolder
;
830 LARGE_INTEGER MaxDestFileSize
;
831 LONG InputLength
, OutputLength
;
834 if( wcscmp( Search
->Cabinet
, CabinetName
) != 0 )
836 // the file is not in the current cabinet
837 DPRINT( "File is not in this cabinet ( %S != %S )\n", Search
->Cabinet
, CabinetName
);
838 return CAB_STATUS_NOFILE
;
840 // look up the folder that the file specifies
841 if( Search
->File
->FolderIndex
== 0xFFFD || Search
->File
->FolderIndex
== 0xFFFF )
843 // folder is continued from previous cabinet, that shouldn't happen here
844 return CAB_STATUS_NOFILE
;
846 else if( Search
->File
->FolderIndex
== 0xFFFE )
848 // folder is the last in this cabinet and continues into next
849 CurrentFolder
= &CabinetFolders
[PCABHeader
->FolderCount
-1];
852 // folder is completely contained within this cabinet
853 CurrentFolder
= &CabinetFolders
[Search
->File
->FolderIndex
];
855 switch (CurrentFolder
->CompressionType
& CAB_COMP_MASK
)
858 CabinetSelectCodec(CAB_CODEC_RAW
);
861 CabinetSelectCodec(CAB_CODEC_MSZIP
);
864 return CAB_STATUS_UNSUPPCOMP
;
867 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)).\n",
868 (UINT
)Search
->File
->FileOffset
,
869 (UINT
)Search
->File
->FileSize
);
870 RtlInitAnsiString( &AnsiString
, Search
->File
->FileName
);
871 wcscpy( DestName
, DestPath
);
872 UnicodeString
.MaximumLength
= sizeof( DestName
) - wcslen( DestName
);
873 UnicodeString
.Buffer
= DestName
+ wcslen( DestName
);
874 UnicodeString
.Length
= 0;
875 RtlAnsiStringToUnicodeString( &UnicodeString
, &AnsiString
, FALSE
);
877 /* Create destination file, fail if it already exists */
878 RtlInitUnicodeString(&UnicodeString
,
882 InitializeObjectAttributes(&ObjectAttributes
,
884 OBJ_CASE_INSENSITIVE
,
888 NtStatus
= NtCreateFile(&DestFile
,
889 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
893 FILE_ATTRIBUTE_NORMAL
,
896 FILE_SYNCHRONOUS_IO_NONALERT
,
899 if (!NT_SUCCESS(NtStatus
))
901 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
903 /* If file exists, ask to overwrite file */
904 if (OverwriteHandler
== NULL
|| OverwriteHandler(Search
->File
, DestName
))
906 /* Create destination file, overwrite if it already exists */
907 NtStatus
= NtCreateFile(&DestFile
,
908 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
912 FILE_ATTRIBUTE_NORMAL
,
915 FILE_SYNCHRONOUS_IO_ALERT
,
918 if (!NT_SUCCESS(NtStatus
))
920 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
921 return CAB_STATUS_CANNOT_CREATE
;
926 DPRINT("File (%S) exists.\n", DestName
);
927 return CAB_STATUS_FILE_EXISTS
;
930 MaxDestFileSize
.QuadPart
= Search
->File
->FileSize
;
931 NtStatus
= NtCreateSection(&DestFileSection
,
938 if(!NT_SUCCESS(NtStatus
))
940 DPRINT("NtCreateSection failed: %x\n", NtStatus
);
941 Status
= CAB_STATUS_NOMEMORY
;
946 NtStatus
= NtMapViewOfSection(DestFileSection
,
956 if(!NT_SUCCESS(NtStatus
))
958 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus
);
959 Status
= CAB_STATUS_NOMEMORY
;
960 goto CloseDestFileSection
;
962 CurrentDestBuffer
= DestFileBuffer
;
963 if (!ConvertDosDateTimeToFileTime(Search
->File
->FileDate
, Search
->File
->FileTime
, &FileTime
))
965 DPRINT("DosDateTimeToFileTime() failed.\n");
966 Status
= CAB_STATUS_CANNOT_WRITE
;
970 NtStatus
= NtQueryInformationFile(DestFile
,
973 sizeof(FILE_BASIC_INFORMATION
),
974 FileBasicInformation
);
975 if (!NT_SUCCESS(NtStatus
))
977 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
981 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
983 NtStatus
= NtSetInformationFile(DestFile
,
986 sizeof(FILE_BASIC_INFORMATION
),
987 FileBasicInformation
);
988 if (!NT_SUCCESS(NtStatus
))
990 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
994 SetAttributesOnFile(Search
->File
, DestFile
);
996 /* Call extract event handler */
997 if (ExtractHandler
!= NULL
)
999 ExtractHandler(Search
->File
, DestName
);
1001 // find the starting block of the file
1002 // start with the first data block of the folder
1003 CFData
= (PCFDATA
)(CabinetFolders
[Search
->File
->FolderIndex
].DataOffset
+ FileBuffer
);
1005 while( CurrentOffset
+ CFData
->UncompSize
<= Search
->File
->FileOffset
)
1007 // walk the data blocks until we reach the one containing the start of the file
1008 CurrentOffset
+= CFData
->UncompSize
;
1009 CFData
= (PCFDATA
)((char *)(CFData
+1) + DataReserved
+ CFData
->CompSize
);
1011 // now decompress and discard any data in the block before the start of the file
1012 CurrentBuffer
= ((unsigned char *)(CFData
+1)) + DataReserved
; // start of comp data
1013 RemainingBlock
= CFData
->CompSize
;
1014 InputLength
= RemainingBlock
;
1015 while( CurrentOffset
< Search
->File
->FileOffset
)
1017 // compute remaining uncomp bytes to start of file, bounded by sizeof junk
1018 OutputLength
= Search
->File
->FileOffset
- CurrentOffset
;
1019 if( OutputLength
> (LONG
)sizeof( Junk
) )
1020 OutputLength
= sizeof( Junk
);
1021 OutputLength
= -OutputLength
; // negate to signal NOT end of block
1022 CodecUncompress( Junk
,
1026 CurrentOffset
+= OutputLength
; // add the uncomp bytes extracted to current folder offset
1027 CurrentBuffer
+= InputLength
; // add comp bytes consumed to CurrentBuffer
1028 RemainingBlock
-= InputLength
; // subtract bytes consumed from bytes remaining in block
1029 InputLength
= -RemainingBlock
; // neg for resume decompression of the same block
1031 // now CurrentBuffer points to the first comp byte of the file, so we can begin decompressing
1033 Size
= Search
->File
->FileSize
; // Size = remaining uncomp bytes of the file to decompress
1036 OutputLength
= Size
;
1037 DPRINT( "Decompressing block at %x with RemainingBlock = %d, Size = %d\n", CurrentBuffer
, RemainingBlock
, Size
);
1038 Status
= CodecUncompress(CurrentDestBuffer
,
1042 if (Status
!= CS_SUCCESS
)
1044 DPRINT("Cannot uncompress block.\n");
1045 if(Status
== CS_NOMEMORY
)
1046 Status
= CAB_STATUS_NOMEMORY
;
1047 Status
= CAB_STATUS_INVALID_CAB
;
1050 CurrentDestBuffer
= (PVOID
)((ULONG_PTR
)CurrentDestBuffer
+ OutputLength
); // advance dest buffer by bytes produced
1051 CurrentBuffer
+= InputLength
; // advance src buffer by bytes consumed
1052 Size
-= OutputLength
; // reduce remaining file bytes by bytes produced
1053 RemainingBlock
-= InputLength
; // reduce remaining block size by bytes consumed
1054 if( RemainingBlock
== 0 )
1056 // used up this block, move on to the next
1057 DPRINT( "Out of block data\n" );
1058 CFData
= (PCFDATA
)CurrentBuffer
;
1059 RemainingBlock
= CFData
->CompSize
;
1060 CurrentBuffer
= ((unsigned char *)(CFData
+1) + DataReserved
);
1061 InputLength
= RemainingBlock
;
1064 Status
= CAB_STATUS_SUCCESS
;
1066 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer
);
1067 CloseDestFileSection
:
1068 NtClose(DestFileSection
);
1077 CabinetSelectCodec(ULONG Id
)
1079 * FUNCTION: Selects codec engine to use
1081 * Id = Codec identifier
1089 CodecSelected
= FALSE
;
1095 CodecUncompress
= RawCodecUncompress
;
1097 case CAB_CODEC_MSZIP
:
1098 CodecUncompress
= MSZipCodecUncompress
;
1105 CodecSelected
= TRUE
;
1110 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
1111 PCABINET_EXTRACT Extract
,
1112 PCABINET_DISK_CHANGE DiskChange
)
1114 * FUNCTION: Set event handlers
1116 * Overwrite = Handler called when a file is to be overwritten
1117 * Extract = Handler called when a file is to be extracted
1118 * DiskChange = Handler called when changing the disk
1121 OverwriteHandler
= Overwrite
;
1122 ExtractHandler
= Extract
;
1123 DiskChangeHandler
= DiskChange
;
1128 CabinetGetCabinetReservedArea(PULONG Size
)
1130 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
1133 if (CabinetReservedArea
!= NULL
)
1137 *Size
= CabinetReserved
;
1139 return CabinetReservedArea
;