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 BOOL ReuseBlock
= FALSE
;
50 static WCHAR DestPath
[MAX_PATH
];
51 static HANDLE FileHandle
;
52 static BOOL FileOpen
= FALSE
;
53 static CFHEADER CABHeader
;
54 static ULONG CabinetReserved
= 0;
55 static ULONG FolderReserved
= 0;
56 static ULONG DataReserved
= 0;
57 static PCFFOLDER_NODE FolderListHead
= NULL
;
58 static PCFFOLDER_NODE FolderListTail
= NULL
;
59 static PCFFOLDER_NODE CurrentFolderNode
= NULL
;
60 static PCFDATA_NODE CurrentDataNode
= NULL
;
61 static PCFFILE_NODE FileListHead
= NULL
;
62 static PCFFILE_NODE FileListTail
= NULL
;
64 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
65 static BOOL CodecSelected
= FALSE
;
66 static PVOID InputBuffer
= NULL
;
67 static PVOID CurrentIBuffer
= NULL
; // Current offset in input buffer
68 static ULONG CurrentIBufferSize
= 0; // Bytes left in input buffer
69 static PVOID OutputBuffer
= NULL
;
70 static PVOID CurrentOBuffer
= NULL
; // Current offset in output buffer
71 static ULONG CurrentOBufferSize
= 0; // Bytes left in output buffer
72 static BOOL RestartSearch
= FALSE
;
73 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
74 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
75 static PCABINET_EXTRACT ExtractHandler
= NULL
;
76 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
77 static z_stream ZStream
;
78 static PVOID CabinetReservedArea
= NULL
;
81 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
85 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
88 void* calloc(size_t _nmemb
, size_t _size
)
90 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
96 RawCodecUncompress(PVOID OutputBuffer
,
101 * FUNCTION: Uncompresses data in a buffer
103 * OutputBuffer = Pointer to buffer to place uncompressed data
104 * InputBuffer = Pointer to buffer with data to be uncompressed
105 * InputLength = Length of input buffer
106 * OutputLength = Address of buffer to place size of uncompressed data
109 memcpy(OutputBuffer
, InputBuffer
, InputLength
);
110 *OutputLength
= InputLength
;
118 MSZipCodecUncompress(PVOID OutputBuffer
,
123 * FUNCTION: Uncompresses data in a buffer
125 * OutputBuffer = Pointer to buffer to place uncompressed data
126 * InputBuffer = Pointer to buffer with data to be uncompressed
127 * InputLength = Length of input buffer
128 * OutputLength = Address of buffer to place size of uncompressed data
134 DPRINT("InputLength (%d).\n", InputLength
);
136 Magic
= *((PUSHORT
)InputBuffer
);
138 if (Magic
!= MSZIP_MAGIC
)
140 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
144 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
145 ZStream
.avail_in
= InputLength
- 2;
146 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
147 ZStream
.avail_out
= CAB_BLOCKSIZE
+ 12;
149 /* WindowBits is passed < 0 to tell that there is no zlib header.
150 * Note that in this case inflate *requires* an extra "dummy" byte
151 * after the compressed stream in order to complete decompression and
152 * return Z_STREAM_END.
154 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
157 DPRINT("inflateInit2() returned (%d).\n", Status
);
161 while ((ZStream
.total_out
< CAB_BLOCKSIZE
+ 12) &&
162 (ZStream
.total_in
< InputLength
- 2))
164 Status
= inflate(&ZStream
, Z_NO_FLUSH
);
165 if (Status
== Z_STREAM_END
) break;
168 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
169 if (Status
== Z_MEM_ERROR
)
175 *OutputLength
= ZStream
.total_out
;
177 Status
= inflateEnd(&ZStream
);
180 DPRINT("inflateEnd() returned (%d).\n", Status
);
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
);
202 SeekInFile(HANDLE hFile
,
203 LONG lDistanceToMove
,
204 PLONG lpDistanceToMoveHigh
,
208 FILE_POSITION_INFORMATION FilePosition
;
209 FILE_STANDARD_INFORMATION FileStandard
;
211 IO_STATUS_BLOCK IoStatusBlock
;
212 LARGE_INTEGER Distance
;
214 DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n",
215 hFile
,lDistanceToMove
,dwMoveMethod
);
217 Distance
.u
.LowPart
= lDistanceToMove
;
218 if (lpDistanceToMoveHigh
)
220 Distance
.u
.HighPart
= *lpDistanceToMoveHigh
;
222 else if (lDistanceToMove
>= 0)
224 Distance
.u
.HighPart
= 0;
228 Distance
.u
.HighPart
= -1;
231 if (dwMoveMethod
== SEEK_CURRENT
)
233 NtQueryInformationFile(hFile
,
236 sizeof(FILE_POSITION_INFORMATION
),
237 FilePositionInformation
);
238 FilePosition
.CurrentByteOffset
.QuadPart
+= Distance
.QuadPart
;
240 else if (dwMoveMethod
== SEEK_END
)
242 NtQueryInformationFile(hFile
,
245 sizeof(FILE_STANDARD_INFORMATION
),
246 FileStandardInformation
);
247 FilePosition
.CurrentByteOffset
.QuadPart
=
248 FileStandard
.EndOfFile
.QuadPart
+ Distance
.QuadPart
;
250 else if ( dwMoveMethod
== SEEK_BEGIN
)
252 FilePosition
.CurrentByteOffset
.QuadPart
= Distance
.QuadPart
;
255 // DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart);
257 errCode
= NtSetInformationFile(hFile
,
260 sizeof(FILE_POSITION_INFORMATION
),
261 FilePositionInformation
);
262 if (!NT_SUCCESS(errCode
))
271 if (lpDistanceToMoveHigh
!= NULL
)
273 *lpDistanceToMoveHigh
= FilePosition
.CurrentByteOffset
.u
.HighPart
;
277 *Status
= STATUS_SUCCESS
;
279 return FilePosition
.CurrentByteOffset
.u
.LowPart
;
284 ConvertSystemTimeToFileTime(
285 CONST SYSTEMTIME
* lpSystemTime
,
286 LPFILETIME lpFileTime
)
288 TIME_FIELDS TimeFields
;
289 LARGE_INTEGER liTime
;
291 TimeFields
.Year
= lpSystemTime
->wYear
;
292 TimeFields
.Month
= lpSystemTime
->wMonth
;
293 TimeFields
.Day
= lpSystemTime
->wDay
;
294 TimeFields
.Hour
= lpSystemTime
->wHour
;
295 TimeFields
.Minute
= lpSystemTime
->wMinute
;
296 TimeFields
.Second
= lpSystemTime
->wSecond
;
297 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
299 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
301 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
302 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
310 ConvertDosDateTimeToFileTime(
313 LPFILETIME lpFileTime
)
315 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
316 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
317 SYSTEMTIME SystemTime
;
319 if (lpFileTime
== NULL
)
322 SystemTime
.wMilliseconds
= 0;
323 SystemTime
.wSecond
= pdtime
->Second
;
324 SystemTime
.wMinute
= pdtime
->Minute
;
325 SystemTime
.wHour
= pdtime
->Hour
;
327 SystemTime
.wDay
= pddate
->Day
;
328 SystemTime
.wMonth
= pddate
->Month
;
329 SystemTime
.wYear
= 1980 + pddate
->Year
;
331 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
338 GetFileName(PWCHAR Path
)
340 * FUNCTION: Returns a pointer to file name
342 * Path = Pointer to string with pathname
344 * Pointer to filename
353 if (Path
[i
- 1] == L
'\\') j
= i
;
360 RemoveFileName(PWCHAR Path
)
362 * FUNCTION: Removes a file name from a path
364 * Path = Pointer to string with path
371 FileName
= GetFileName(Path
+ i
);
373 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
375 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
382 SetAttributesOnFile(PCFFILE_NODE File
, HANDLE hFile
)
384 * FUNCTION: Sets attributes on a file
386 * File = Pointer to CFFILE node for file
388 * Status of operation
391 FILE_BASIC_INFORMATION FileBasic
;
392 IO_STATUS_BLOCK IoStatusBlock
;
394 ULONG Attributes
= 0;
396 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
397 Attributes
|= FILE_ATTRIBUTE_READONLY
;
399 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
400 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
402 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
403 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
405 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
406 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
408 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
409 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
411 NtStatus
= NtQueryInformationFile(hFile
,
414 sizeof(FILE_BASIC_INFORMATION
),
415 FileBasicInformation
);
416 if (!NT_SUCCESS(NtStatus
))
418 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
422 FileBasic
.FileAttributes
= Attributes
;
424 NtStatus
= NtSetInformationFile(hFile
,
427 sizeof(FILE_BASIC_INFORMATION
),
428 FileBasicInformation
);
429 if (!NT_SUCCESS(NtStatus
))
431 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
435 return NT_SUCCESS(NtStatus
);
440 ReadBlock(PVOID Buffer
,
444 * FUNCTION: Read a block of data from file
446 * Buffer = Pointer to data buffer
447 * Size = Length of data buffer
448 * BytesRead = Pointer to ULONG that on return will contain
449 * number of bytes read
451 * Status of operation
454 IO_STATUS_BLOCK IoStatusBlock
;
457 NtStatus
= NtReadFile(FileHandle
,
466 if (!NT_SUCCESS(NtStatus
))
468 DPRINT("ReadBlock for %d bytes failed (%x)\n", Size
, NtStatus
);
470 return CAB_STATUS_INVALID_CAB
;
473 return CAB_STATUS_SUCCESS
;
477 static PCFFOLDER_NODE
480 * FUNCTION: Creates a new folder node
482 * Pointer to node if there was enough free memory available, otherwise NULL
487 Node
= (PCFFOLDER_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFOLDER_NODE
));
491 RtlZeroMemory(Node
, sizeof(CFFOLDER_NODE
));
493 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
495 Node
->Prev
= FolderListTail
;
497 if (FolderListTail
!= NULL
)
499 FolderListTail
->Next
= Node
;
503 FolderListHead
= Node
;
505 FolderListTail
= Node
;
514 * FUNCTION: Creates a new file node
516 * FolderNode = Pointer to folder node to bind file to
518 * Pointer to node if there was enough free memory available, otherwise NULL
523 Node
= (PCFFILE_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFILE_NODE
));
527 RtlZeroMemory(Node
, sizeof(CFFILE_NODE
));
529 Node
->Prev
= FileListTail
;
531 if (FileListTail
!= NULL
)
533 FileListTail
->Next
= Node
;
546 NewDataNode(PCFFOLDER_NODE FolderNode
)
548 * FUNCTION: Creates a new data block node
550 * FolderNode = Pointer to folder node to bind data block to
552 * Pointer to node if there was enough free memory available, otherwise NULL
557 Node
= (PCFDATA_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFDATA_NODE
));
561 RtlZeroMemory(Node
, sizeof(CFDATA_NODE
));
563 Node
->Prev
= FolderNode
->DataListTail
;
565 if (FolderNode
->DataListTail
!= NULL
)
567 FolderNode
->DataListTail
->Next
= Node
;
571 FolderNode
->DataListHead
= Node
;
573 FolderNode
->DataListTail
= Node
;
580 DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
582 * FUNCTION: Destroys data block nodes bound to a folder node
584 * FolderNode = Pointer to folder node
587 PCFDATA_NODE PrevNode
;
588 PCFDATA_NODE NextNode
;
590 NextNode
= FolderNode
->DataListHead
;
591 while (NextNode
!= NULL
)
593 PrevNode
= NextNode
->Next
;
594 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
597 FolderNode
->DataListHead
= NULL
;
598 FolderNode
->DataListTail
= NULL
;
605 * FUNCTION: Destroys file nodes
607 * FolderNode = Pointer to folder node
610 PCFFILE_NODE PrevNode
;
611 PCFFILE_NODE NextNode
;
613 NextNode
= FileListHead
;
614 while (NextNode
!= NULL
)
616 PrevNode
= NextNode
->Next
;
617 if (NextNode
->FileName
)
618 RtlFreeHeap(ProcessHeap
, 0, NextNode
->FileName
);
619 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
629 DestroyDeletedFileNodes()
631 * FUNCTION: Destroys file nodes that are marked for deletion
634 PCFFILE_NODE CurNode
;
635 PCFFILE_NODE NextNode
;
637 CurNode
= FileListHead
;
638 while (CurNode
!= NULL
)
640 NextNode
= CurNode
->Next
;
644 if (CurNode
->Prev
!= NULL
)
646 CurNode
->Prev
->Next
= CurNode
->Next
;
650 FileListHead
= CurNode
->Next
;
652 FileListHead
->Prev
= NULL
;
655 if (CurNode
->Next
!= NULL
)
657 CurNode
->Next
->Prev
= CurNode
->Prev
;
661 FileListTail
= CurNode
->Prev
;
663 FileListTail
->Next
= NULL
;
666 DPRINT("Deleting file: '%S'\n", CurNode
->FileName
);
668 if (CurNode
->FileName
)
669 RtlFreeHeap(ProcessHeap
, 0, CurNode
->FileName
);
670 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
680 * FUNCTION: Destroys folder nodes
683 PCFFOLDER_NODE PrevNode
;
684 PCFFOLDER_NODE NextNode
;
686 NextNode
= FolderListHead
;
687 while (NextNode
!= NULL
)
689 PrevNode
= NextNode
->Next
;
690 DestroyDataNodes(NextNode
);
691 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
694 FolderListHead
= NULL
;
695 FolderListTail
= NULL
;
700 DestroyDeletedFolderNodes()
702 * FUNCTION: Destroys folder nodes that are marked for deletion
705 PCFFOLDER_NODE CurNode
;
706 PCFFOLDER_NODE NextNode
;
708 CurNode
= FolderListHead
;
709 while (CurNode
!= NULL
)
711 NextNode
= CurNode
->Next
;
715 if (CurNode
->Prev
!= NULL
)
717 CurNode
->Prev
->Next
= CurNode
->Next
;
721 FolderListHead
= CurNode
->Next
;
723 FolderListHead
->Prev
= NULL
;
726 if (CurNode
->Next
!= NULL
)
728 CurNode
->Next
->Prev
= CurNode
->Prev
;
732 FolderListTail
= CurNode
->Prev
;
734 FolderListTail
->Next
= NULL
;
737 DestroyDataNodes(CurNode
);
738 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
745 static PCFFOLDER_NODE
746 LocateFolderNode(ULONG Index
)
748 * FUNCTION: Locates a folder node
750 * Index = Folder index
752 * Pointer to folder node or NULL if the folder node was not found
760 return FolderListTail
;
762 case CAB_FILE_CONTINUED
:
763 case CAB_FILE_PREV_NEXT
:
764 return FolderListHead
;
767 Node
= FolderListHead
;
770 if (Node
->Index
== Index
)
779 GetAbsoluteOffset(PCFFILE_NODE File
)
781 * FUNCTION: Returns the absolute offset of a file
783 * File = Pointer to CFFILE_NODE structure for file
785 * Status of operation
790 DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n",
791 (PWCHAR
)File
->FileName
,
792 (UINT
)File
->File
.FileOffset
,
793 (UINT
)File
->File
.FileSize
);
795 Node
= CurrentFolderNode
->DataListHead
;
798 DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
799 (UINT
)Node
->UncompOffset
,
800 (UINT
)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
801 (UINT
)Node
->Data
.UncompSize
);
803 /* Node->Data.UncompSize will be 0 if the block is split
804 (ie. it is the last block in this cabinet) */
805 if ((Node
->Data
.UncompSize
== 0) ||
806 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
807 (File
->File
.FileOffset
< Node
->UncompOffset
+
808 Node
->Data
.UncompSize
)))
810 File
->DataBlock
= Node
;
811 return CAB_STATUS_SUCCESS
;
816 return CAB_STATUS_INVALID_CAB
;
821 LocateFile(PWCHAR FileName
,
824 * FUNCTION: Locates a file in the cabinet
826 * FileName = Pointer to string with name of file to locate
827 * File = Address of pointer to CFFILE_NODE structure to fill
829 * Status of operation
831 * Current folder is set to the folder of the file
837 DPRINT("FileName '%S'\n", FileName
);
842 if (_wcsicmp(FileName
, Node
->FileName
) == 0)
844 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
845 if (!CurrentFolderNode
)
847 DPRINT("Folder with index number (%d) not found.\n",
848 (UINT
)Node
->File
.FileControlID
);
849 return CAB_STATUS_INVALID_CAB
;
852 if (Node
->DataBlock
== NULL
)
854 Status
= GetAbsoluteOffset(Node
);
857 Status
= CAB_STATUS_SUCCESS
;
863 return CAB_STATUS_NOFILE
;
868 ReadString(PWCHAR String
, ULONG MaxLength
)
870 * FUNCTION: Reads a NULL-terminated string from the cabinet
872 * String = Pointer to buffer to place string
873 * MaxLength = Maximum length of string
875 * Status of operation
892 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
896 DPRINT("Too long a filename.\n");
897 return CAB_STATUS_INVALID_CAB
;
900 Status
= ReadBlock((PCFDATA
)&buf
[Offset
], Size
, &BytesRead
);
901 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
))
903 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
904 return CAB_STATUS_INVALID_CAB
;
907 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++)
909 if (buf
[Size
] == '\0')
919 /* Back up some bytes */
920 Size
= (BytesRead
- Size
) - 1;
921 SeekInFile(FileHandle
, -(LONG
)Size
, NULL
, SEEK_CURRENT
, &NtStatus
);
922 if (!NT_SUCCESS(NtStatus
))
924 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
925 return CAB_STATUS_INVALID_CAB
;
928 RtlInitAnsiString(&as
, buf
);
930 us
.MaximumLength
= MaxLength
* sizeof(WCHAR
);
933 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
935 return CAB_STATUS_SUCCESS
;
942 * FUNCTION: Reads the file table from the cabinet file
944 * Status of operation
953 DPRINT("Reading file table at absolute offset (0x%X).\n",
954 CABHeader
.FileTableOffset
);
956 /* Seek to file table */
957 SeekInFile(FileHandle
, CABHeader
.FileTableOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
958 if (!NT_SUCCESS(NtStatus
))
960 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
961 return CAB_STATUS_INVALID_CAB
;
964 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
966 File
= NewFileNode();
969 DPRINT("Insufficient memory.\n");
970 return CAB_STATUS_NOMEMORY
;
973 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
974 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
975 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
976 return CAB_STATUS_INVALID_CAB
;
979 File
->FileName
= (PWCHAR
)RtlAllocateHeap(ProcessHeap
, 0, MAX_PATH
* sizeof(WCHAR
));
982 DPRINT("Insufficient memory.\n");
983 return CAB_STATUS_NOMEMORY
;
987 Status
= ReadString(File
->FileName
, MAX_PATH
);
988 if (Status
!= CAB_STATUS_SUCCESS
)
991 DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
992 (PWCHAR
)File
->FileName
,
993 (UINT
)File
->File
.FileOffset
,
994 (UINT
)File
->File
.FileSize
,
995 (UINT
)File
->File
.FileControlID
);
997 return CAB_STATUS_SUCCESS
;
1002 ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
1004 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1006 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1008 * Status of operation
1011 ULONG AbsoluteOffset
;
1019 DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1020 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
);
1022 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
1023 UncompOffset
= FolderNode
->UncompOffset
;
1025 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
1027 Node
= NewDataNode(FolderNode
);
1030 DPRINT("Insufficient memory.\n");
1031 return CAB_STATUS_NOMEMORY
;
1034 /* Seek to data block */
1035 SeekInFile(FileHandle
, AbsoluteOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
1036 if (!NT_SUCCESS(NtStatus
))
1038 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1039 return CAB_STATUS_INVALID_CAB
;
1042 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
1043 &BytesRead
)) != CAB_STATUS_SUCCESS
)
1045 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1046 return CAB_STATUS_INVALID_CAB
;
1049 DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1050 (UINT
)AbsoluteOffset
,
1052 (UINT
)Node
->Data
.Checksum
,
1053 (UINT
)Node
->Data
.CompSize
,
1054 (UINT
)Node
->Data
.UncompSize
);
1056 Node
->AbsoluteOffset
= AbsoluteOffset
;
1057 Node
->UncompOffset
= UncompOffset
;
1059 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
1060 UncompOffset
+= Node
->Data
.UncompSize
;
1063 FolderUncompSize
= UncompOffset
;
1065 return CAB_STATUS_SUCCESS
;
1070 ComputeChecksum(PVOID Buffer
,
1074 * FUNCTION: Computes checksum for data block
1076 * Buffer = Pointer to data buffer
1077 * Size = Length of data buffer
1078 * Seed = Previously computed checksum
1080 * Checksum of buffer
1083 INT UlongCount
; // Number of ULONGs in block
1084 ULONG Checksum
; // Checksum accumulator
1088 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
1089 won't accept checksums computed by this routine */
1091 DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT
)Buffer
, Size
);
1093 UlongCount
= Size
/ 4; // Number of ULONGs
1094 Checksum
= Seed
; // Init checksum
1095 pb
= (PBYTE
)Buffer
; // Start at front of data block
1097 /* Checksum integral multiple of ULONGs */
1098 while (UlongCount
-- > 0)
1100 /* NOTE: Build ULONG in big/little-endian independent manner */
1101 ul
= *pb
++; // Get low-order byte
1102 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1103 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
1104 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
1106 Checksum
^= ul
; // Update checksum
1109 /* Checksum remainder bytes */
1114 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
1116 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1118 ul
|= *pb
++; // Get low-order byte
1122 Checksum
^= ul
; // Update checksum
1124 /* Return computed checksum */
1132 * FUNCTION: Closes the current cabinet
1134 * Status of operation
1139 DestroyFolderNodes();
1143 RtlFreeHeap(ProcessHeap
, 0, InputBuffer
);
1149 RtlFreeHeap(ProcessHeap
, 0, OutputBuffer
);
1150 OutputBuffer
= NULL
;
1153 NtClose(FileHandle
);
1159 CabinetInitialize(VOID
)
1161 * FUNCTION: Initialize archiver
1164 ZStream
.zalloc
= MSZipAlloc
;
1165 ZStream
.zfree
= MSZipFree
;
1166 ZStream
.opaque
= (voidpf
)0;
1169 wcscpy(DestPath
, L
"");
1171 FolderListHead
= NULL
;
1172 FolderListTail
= NULL
;
1173 FileListHead
= NULL
;
1174 FileListTail
= NULL
;
1176 CodecId
= CAB_CODEC_RAW
;
1177 CodecSelected
= TRUE
;
1179 OutputBuffer
= NULL
;
1180 CurrentOBuffer
= NULL
;
1181 CurrentOBufferSize
= 0;
1183 CurrentIBuffer
= NULL
;
1184 CurrentIBufferSize
= 0;
1186 FolderUncompSize
= 0;
1187 BytesLeftInBlock
= 0;
1188 CabinetReserved
= 0;
1192 CurrentFolderNode
= NULL
;
1193 CurrentDataNode
= NULL
;
1194 CabinetReservedArea
= NULL
;
1195 RestartSearch
= FALSE
;
1201 CabinetCleanup(VOID
)
1203 * FUNCTION: Cleanup archiver
1211 CabinetNormalizePath(PWCHAR Path
,
1214 * FUNCTION: Normalizes a path
1216 * Path = Pointer to string with pathname
1217 * Length = Number of characters in Path
1219 * TRUE if there was enough room in Path, or FALSE
1225 if ((n
= wcslen(Path
)) &&
1226 (Path
[n
- 1] != L
'\\') &&
1227 (OK
= ((n
+ 1) < Length
)))
1237 CabinetGetCabinetName()
1239 * FUNCTION: Returns pointer to cabinet file name
1241 * Pointer to string with name of cabinet
1249 CabinetSetCabinetName(PWCHAR FileName
)
1251 * FUNCTION: Sets cabinet file name
1253 * FileName = Pointer to string with name of cabinet
1256 wcscpy(CabinetName
, FileName
);
1261 CabinetSetDestinationPath(PWCHAR DestinationPath
)
1263 * FUNCTION: Sets destination path
1265 * DestinationPath = Pointer to string with name of destination path
1268 wcscpy(DestPath
, DestinationPath
);
1269 if (wcslen(DestPath
) > 0)
1270 CabinetNormalizePath(DestPath
, MAX_PATH
);
1275 CabinetGetDestinationPath()
1277 * FUNCTION: Returns destination path
1279 * Pointer to string with name of destination path
1289 * FUNCTION: Opens a cabinet file
1291 * Status of operation
1294 WCHAR CabinetFileName
[256];
1295 PCFFOLDER_NODE FolderNode
;
1301 OBJECT_ATTRIBUTES ObjectAttributes
;
1302 IO_STATUS_BLOCK IoStatusBlock
;
1303 UNICODE_STRING FileName
;
1308 OutputBuffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1310 return CAB_STATUS_NOMEMORY
;
1312 RtlInitUnicodeString(&FileName
,
1315 InitializeObjectAttributes(&ObjectAttributes
,
1317 OBJ_CASE_INSENSITIVE
,
1321 NtStatus
= NtOpenFile(&FileHandle
,
1326 FILE_SYNCHRONOUS_IO_NONALERT
);
1327 if (!NT_SUCCESS(NtStatus
))
1329 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
1330 return CAB_STATUS_CANNOT_OPEN
;
1335 /* Load CAB header */
1336 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1338 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1339 return CAB_STATUS_INVALID_CAB
;
1343 if ((BytesRead
!= sizeof(CFHEADER
)) ||
1344 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
1345 (CABHeader
.Version
!= CAB_VERSION
) ||
1346 (CABHeader
.FolderCount
== 0 ) ||
1347 (CABHeader
.FileCount
== 0 ) ||
1348 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
1350 DPRINT("File has invalid header.\n");
1351 return CAB_STATUS_INVALID_CAB
;
1356 /* Read/skip any reserved bytes */
1357 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
1359 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1361 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1362 return CAB_STATUS_INVALID_CAB
;
1364 CabinetReserved
= Size
& 0xFFFF;
1365 FolderReserved
= (Size
>> 16) & 0xFF;
1366 DataReserved
= (Size
>> 24) & 0xFF;
1368 if (CabinetReserved
> 0)
1370 CabinetReservedArea
= RtlAllocateHeap(ProcessHeap
, 0, CabinetReserved
);
1371 if (!CabinetReservedArea
)
1373 return CAB_STATUS_NOMEMORY
;
1376 if ((Status
= ReadBlock(CabinetReservedArea
, CabinetReserved
, &BytesRead
)) != CAB_STATUS_SUCCESS
)
1378 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1379 return CAB_STATUS_INVALID_CAB
;
1383 SeekInFile(FileHandle
, CabinetReserved
, NULL
, SEEK_CURRENT
, &NtStatus
);
1384 if (!NT_SUCCESS(NtStatus
))
1386 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1387 return CAB_STATUS_INVALID_CAB
;
1392 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
1394 /* Read name of previous cabinet */
1395 Status
= ReadString(CabinetFileName
, 256);
1396 if (Status
!= CAB_STATUS_SUCCESS
)
1399 /* The previous cabinet file is in the same directory as the current */
1400 wcscpy(CabinetPrev
, CabinetName
);
1401 RemoveFileName(CabinetPrev
);
1402 CabinetNormalizePath(CabinetPrev
, 256);
1403 wcscat(CabinetPrev
, CabinetFileName
);
1405 /* Read label of previous disk */
1406 Status
= ReadString(DiskPrev
, 256);
1407 if (Status
!= CAB_STATUS_SUCCESS
)
1412 wcscpy(CabinetPrev
, L
"");
1413 wcscpy(DiskPrev
, L
"");
1416 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
1418 /* Read name of next cabinet */
1419 Status
= ReadString(CabinetFileName
, 256);
1420 if (Status
!= CAB_STATUS_SUCCESS
)
1423 /* The next cabinet file is in the same directory as the previous */
1424 wcscpy(CabinetNext
, CabinetName
);
1425 RemoveFileName(CabinetNext
);
1426 CabinetNormalizePath(CabinetNext
, 256);
1427 wcscat(CabinetNext
, CabinetFileName
);
1429 /* Read label of next disk */
1430 Status
= ReadString(DiskNext
, 256);
1431 if (Status
!= CAB_STATUS_SUCCESS
)
1436 wcscpy(CabinetNext
, L
"");
1437 wcscpy(DiskNext
, L
"");
1440 /* Read all folders */
1441 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
1443 FolderNode
= NewFolderNode();
1446 DPRINT("Insufficient resources.\n");
1447 return CAB_STATUS_NOMEMORY
;
1451 FolderNode
->UncompOffset
= FolderUncompSize
;
1453 FolderNode
->Index
= Index
;
1455 if ((Status
= ReadBlock(&FolderNode
->Folder
,
1456 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1458 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1459 return CAB_STATUS_INVALID_CAB
;
1463 /* Read file entries */
1464 Status
= ReadFileTable();
1465 if (Status
!= CAB_STATUS_SUCCESS
)
1467 DPRINT("ReadFileTable() failed (%d).\n", (UINT
)Status
);
1471 /* Read data blocks for all folders */
1472 FolderNode
= FolderListHead
;
1473 while (FolderNode
!= NULL
)
1475 Status
= ReadDataBlocks(FolderNode
);
1476 if (Status
!= CAB_STATUS_SUCCESS
)
1478 DPRINT("ReadDataBlocks() failed (%d).\n", (UINT
)Status
);
1481 FolderNode
= FolderNode
->Next
;
1484 return CAB_STATUS_SUCCESS
;
1491 * FUNCTION: Closes the cabinet file
1498 if (CabinetReservedArea
!= NULL
)
1500 RtlFreeHeap(ProcessHeap
, 0, CabinetReservedArea
);
1501 CabinetReservedArea
= NULL
;
1510 CabinetFindFirst(PWCHAR FileName
,
1513 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
1515 * FileName = Pointer to search criteria
1516 * Search = Pointer to search structure
1518 * Status of operation
1521 RestartSearch
= FALSE
;
1522 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
1523 Search
->Next
= FileListHead
;
1524 return CabinetFindNext(Search
);
1529 CabinetFindNext(PCAB_SEARCH Search
)
1531 * FUNCTION: Finds next file in the cabinet that matches a search criteria
1533 * Search = Pointer to search structure
1535 * Status of operation
1542 Search
->Next
= FileListHead
;
1544 /* Skip split files already extracted */
1545 while ((Search
->Next
) &&
1546 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
1547 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
1549 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
1550 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
);
1551 Search
->Next
= Search
->Next
->Next
;
1554 RestartSearch
= FALSE
;
1557 /* FIXME: Check search criteria */
1561 if (wcslen(DiskNext
) > 0)
1565 CabinetSetCabinetName(CabinetNext
);
1567 if (DiskChangeHandler
!= NULL
)
1569 DiskChangeHandler(CabinetNext
, DiskNext
);
1572 Status
= CabinetOpen();
1573 if (Status
!= CAB_STATUS_SUCCESS
)
1576 Search
->Next
= FileListHead
;
1578 return CAB_STATUS_NOFILE
;
1582 return CAB_STATUS_NOFILE
;
1586 Search
->File
= &Search
->Next
->File
;
1587 Search
->FileName
= Search
->Next
->FileName
;
1588 Search
->Next
= Search
->Next
->Next
;
1589 return CAB_STATUS_SUCCESS
;
1594 CabinetExtractFile(PWCHAR FileName
)
1596 * FUNCTION: Extracts a file from the cabinet
1598 * FileName = Pointer to buffer with name of file
1600 * Status of operation
1610 ULONG TotalBytesRead
;
1611 ULONG CurrentOffset
;
1613 PUCHAR CurrentBuffer
;
1620 WCHAR DestName
[MAX_PATH
];
1621 WCHAR TempName
[MAX_PATH
];
1624 UNICODE_STRING UnicodeString
;
1625 IO_STATUS_BLOCK IoStatusBlock
;
1626 OBJECT_ATTRIBUTES ObjectAttributes
;
1627 FILE_BASIC_INFORMATION FileBasic
;
1629 Status
= LocateFile(FileName
, &File
);
1630 if (Status
!= CAB_STATUS_SUCCESS
)
1632 DPRINT("Cannot locate file (%d).\n", (UINT
)Status
);
1636 LastFileOffset
= File
->File
.FileOffset
;
1638 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
1641 CabinetSelectCodec(CAB_CODEC_RAW
);
1643 case CAB_COMP_MSZIP
:
1644 CabinetSelectCodec(CAB_CODEC_MSZIP
);
1647 return CAB_STATUS_UNSUPPCOMP
;
1650 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1651 (UINT
)File
->File
.FileOffset
,
1652 (UINT
)File
->File
.FileSize
,
1653 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1654 (UINT
)File
->DataBlock
->UncompOffset
);
1656 wcscpy(DestName
, DestPath
);
1657 wcscat(DestName
, FileName
);
1659 while (NULL
!= (s
= wcsstr(DestName
, L
"\\.\\")))
1661 memmove(s
, s
+ 2, (wcslen(s
+ 2) + 1) *sizeof(WCHAR
));
1664 /* Create destination file, fail if it already exists */
1665 RtlInitUnicodeString(&UnicodeString
,
1669 InitializeObjectAttributes(&ObjectAttributes
,
1671 OBJ_CASE_INSENSITIVE
,
1675 NtStatus
= NtCreateFile(&DestFile
,
1676 GENERIC_WRITE
|FILE_READ_ATTRIBUTES
,
1680 FILE_ATTRIBUTE_NORMAL
,
1683 FILE_SYNCHRONOUS_IO_NONALERT
,
1686 if (!NT_SUCCESS(NtStatus
))
1688 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
1690 /* If file exists, ask to overwrite file */
1691 if (OverwriteHandler
== NULL
|| OverwriteHandler(&File
->File
, FileName
))
1693 /* Create destination file, overwrite if it already exists */
1694 NtStatus
= NtCreateFile(&DestFile
,
1695 GENERIC_WRITE
|FILE_READ_ATTRIBUTES
,
1699 FILE_ATTRIBUTE_NORMAL
,
1702 FILE_SYNCHRONOUS_IO_ALERT
,
1705 if (!NT_SUCCESS(NtStatus
))
1707 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
1708 return CAB_STATUS_CANNOT_CREATE
;
1713 DPRINT("File (%S) exists.\n", DestName
);
1714 return CAB_STATUS_FILE_EXISTS
;
1718 if (!ConvertDosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1721 DPRINT("DosDateTimeToFileTime() failed.\n");
1722 return CAB_STATUS_CANNOT_WRITE
;
1725 NtStatus
= NtQueryInformationFile(DestFile
,
1728 sizeof(FILE_BASIC_INFORMATION
),
1729 FileBasicInformation
);
1730 if (!NT_SUCCESS(Status
))
1732 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
1736 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1738 NtStatus
= NtSetInformationFile(DestFile
,
1741 sizeof(FILE_BASIC_INFORMATION
),
1742 FileBasicInformation
);
1743 if (!NT_SUCCESS(NtStatus
))
1745 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
1749 SetAttributesOnFile(File
, DestFile
);
1751 Buffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1755 DPRINT("Insufficient memory.\n");
1756 return CAB_STATUS_NOMEMORY
;
1759 /* Call extract event handler */
1760 if (ExtractHandler
!= NULL
)
1762 ExtractHandler(&File
->File
, FileName
);
1765 /* Search to start of file */
1766 Offset
= SeekInFile(FileHandle
,
1767 File
->DataBlock
->AbsoluteOffset
,
1771 if (!NT_SUCCESS(NtStatus
))
1773 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1774 return CAB_STATUS_INVALID_CAB
;
1777 Size
= File
->File
.FileSize
;
1778 Offset
= File
->File
.FileOffset
;
1779 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1783 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1788 DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1789 File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, Offset
, Size
,
1792 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1794 DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1796 CurrentBuffer
= Buffer
;
1800 DPRINT("Size (%d bytes).\n", Size
);
1802 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1803 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1806 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1807 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1808 return CAB_STATUS_INVALID_CAB
;
1811 DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
1812 (UINT
)CFData
.Checksum
,
1813 (UINT
)CFData
.CompSize
,
1814 (UINT
)CFData
.UncompSize
,
1815 (UINT
)SeekInFile(FileHandle
, 0, NULL
, SEEK_CURRENT
, &NtStatus
));
1817 //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1819 BytesToRead
= CFData
.CompSize
;
1821 DPRINT("Read: (0x%X,0x%X).\n",
1822 CurrentBuffer
, Buffer
);
1824 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1825 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1828 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1829 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1830 return CAB_STATUS_INVALID_CAB
;
1833 /* FIXME: Does not work with files generated by makecab.exe */
1835 if (CFData.Checksum != 0)
1837 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1838 if (Checksum != CFData.Checksum)
1841 RtlFreeHeap(ProcessHeap, 0, Buffer);
1842 DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n",
1843 Checksum, CFData.Checksum);
1844 return CAB_STATUS_INVALID_CAB;
1848 TotalBytesRead
+= BytesRead
;
1850 CurrentBuffer
+= BytesRead
;
1852 if (CFData
.UncompSize
== 0)
1854 if (wcslen(DiskNext
) == 0)
1855 return CAB_STATUS_NOFILE
;
1857 /* CloseCabinet() will destroy all file entries so in case
1858 FileName refers to the FileName field of a CFFOLDER_NODE
1859 structure, we have to save a copy of the filename */
1860 wcscpy(TempName
, FileName
);
1864 CabinetSetCabinetName(CabinetNext
);
1866 if (DiskChangeHandler
!= NULL
)
1868 DiskChangeHandler(CabinetNext
, DiskNext
);
1871 Status
= CabinetOpen();
1872 if (Status
!= CAB_STATUS_SUCCESS
)
1875 /* The first data block of the file will not be
1876 found as it is located in the previous file */
1877 Status
= LocateFile(TempName
, &File
);
1878 if (Status
== CAB_STATUS_NOFILE
)
1880 DPRINT("Cannot locate file (%d).\n", (UINT
)Status
);
1884 /* The file is continued in the first data block in the folder */
1885 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1887 /* Search to start of file */
1888 SeekInFile(FileHandle
,
1889 File
->DataBlock
->AbsoluteOffset
,
1893 if (!NT_SUCCESS(NtStatus
))
1895 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1896 return CAB_STATUS_INVALID_CAB
;
1899 DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1900 (UINT
)File
->File
.FileOffset
,
1901 (UINT
)File
->File
.FileSize
,
1902 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1903 (UINT
)File
->DataBlock
->UncompOffset
);
1905 CurrentDataNode
= File
->DataBlock
;
1908 RestartSearch
= TRUE
;
1910 } while (CFData
.UncompSize
== 0);
1912 DPRINT("TotalBytesRead (%d).\n", TotalBytesRead
);
1914 Status
= CodecUncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1915 if (Status
!= CS_SUCCESS
)
1918 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1919 DPRINT("Cannot uncompress block.\n");
1920 if (Status
== CS_NOMEMORY
)
1921 return CAB_STATUS_NOMEMORY
;
1922 return CAB_STATUS_INVALID_CAB
;
1925 if (BytesToWrite
!= CFData
.UncompSize
)
1927 DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
1928 BytesToWrite
, CFData
.UncompSize
);
1929 return CAB_STATUS_INVALID_CAB
;
1932 BytesLeftInBlock
= BytesToWrite
;
1936 DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1938 BytesToWrite
= BytesLeftInBlock
;
1940 DPRINT("Seeking to absolute offset 0x%X.\n",
1941 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1942 CurrentDataNode
->Data
.CompSize
);
1944 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1945 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1948 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1949 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1950 return CAB_STATUS_INVALID_CAB
;
1953 DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1954 CFData
.CompSize
, CFData
.UncompSize
);
1956 /* Go to next data block */
1957 SeekInFile(FileHandle
,
1958 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1959 CurrentDataNode
->Data
.CompSize
,
1963 if (!NT_SUCCESS(NtStatus
))
1965 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1966 return CAB_STATUS_INVALID_CAB
;
1973 BytesSkipped
= (Offset
- CurrentOffset
);
1977 BytesToWrite
-= BytesSkipped
;
1979 if (Size
< BytesToWrite
)
1980 BytesToWrite
= Size
;
1982 DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1984 (UINT
)CurrentOffset
,
1986 (UINT
)BytesSkipped
, (UINT
)Skip
,
1989 // if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1990 // BytesToWrite, &BytesWritten, NULL) ||
1991 // (BytesToWrite != BytesWritten))
1993 NtStatus
= NtWriteFile(DestFile
,
1998 (PVOID
)((ULONG
)OutputBuffer
+ BytesSkipped
),
2002 BytesWritten
= BytesToWrite
;
2003 if (!NT_SUCCESS(NtStatus
))
2005 DPRINT("Status 0x%X.\n", NtStatus
);
2008 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2009 DPRINT("Cannot write to file.\n");
2010 return CAB_STATUS_CANNOT_WRITE
;
2012 Size
-= BytesToWrite
;
2014 CurrentOffset
+= BytesToWrite
;
2016 /* Don't skip any more bytes */
2023 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2025 return CAB_STATUS_SUCCESS
;
2030 CabinetSelectCodec(ULONG Id
)
2032 * FUNCTION: Selects codec engine to use
2034 * Id = Codec identifier
2042 CodecSelected
= FALSE
;
2048 CodecUncompress
= RawCodecUncompress
;
2050 case CAB_CODEC_MSZIP
:
2051 CodecUncompress
= MSZipCodecUncompress
;
2058 CodecSelected
= TRUE
;
2063 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
2064 PCABINET_EXTRACT Extract
,
2065 PCABINET_DISK_CHANGE DiskChange
)
2067 * FUNCTION: Set event handlers
2069 * Overwrite = Handler called when a file is to be overwritten
2070 * Extract = Handler called when a file is to be extracted
2071 * DiskChange = Handler called when changing the disk
2074 OverwriteHandler
= Overwrite
;
2075 ExtractHandler
= Extract
;
2076 DiskChangeHandler
= DiskChange
;
2081 CabinetGetCabinetReservedArea(PULONG Size
)
2083 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
2086 if (CabinetReservedArea
!= NULL
)
2090 *Size
= CabinetReserved
;
2092 return CabinetReservedArea
;