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
21 #define SEEK_CURRENT 1
26 typedef struct __DOSTIME
34 typedef struct __DOSDATE
41 static WCHAR CabinetName
[256]; // Filename of current cabinet
42 static WCHAR CabinetPrev
[256]; // Filename of previous cabinet
43 static WCHAR DiskPrev
[256]; // Label of cabinet in file CabinetPrev
44 static WCHAR CabinetNext
[256]; // Filename of next cabinet
45 static WCHAR DiskNext
[256]; // Label of cabinet in file CabinetNext
46 static ULONG FolderUncompSize
= 0; // Uncompressed size of folder
47 static ULONG BytesLeftInBlock
= 0; // Number of bytes left in current block
48 static BOOL ReuseBlock
= FALSE
;
49 static WCHAR DestPath
[MAX_PATH
];
50 static HANDLE FileHandle
;
51 static BOOL FileOpen
= FALSE
;
52 static CFHEADER CABHeader
;
53 static ULONG CabinetReserved
= 0;
54 static ULONG FolderReserved
= 0;
55 static ULONG DataReserved
= 0;
56 static PCFFOLDER_NODE FolderListHead
= NULL
;
57 static PCFFOLDER_NODE FolderListTail
= NULL
;
58 static PCFFOLDER_NODE CurrentFolderNode
= NULL
;
59 static PCFDATA_NODE CurrentDataNode
= NULL
;
60 static PCFFILE_NODE FileListHead
= NULL
;
61 static PCFFILE_NODE FileListTail
= NULL
;
63 static PCABINET_CODEC_UNCOMPRESS CodecUncompress
= NULL
;
64 static BOOL CodecSelected
= FALSE
;
65 static PVOID InputBuffer
= NULL
;
66 static PVOID CurrentIBuffer
= NULL
; // Current offset in input buffer
67 static ULONG CurrentIBufferSize
= 0; // Bytes left in input buffer
68 static PVOID OutputBuffer
= NULL
;
69 static PVOID CurrentOBuffer
= NULL
; // Current offset in output buffer
70 static ULONG CurrentOBufferSize
= 0; // Bytes left in output buffer
71 static BOOL RestartSearch
= FALSE
;
72 static ULONG LastFileOffset
= 0; // Uncompressed offset of last extracted file
73 static PCABINET_OVERWRITE OverwriteHandler
= NULL
;
74 static PCABINET_EXTRACT ExtractHandler
= NULL
;
75 static PCABINET_DISK_CHANGE DiskChangeHandler
= NULL
;
76 static z_stream ZStream
;
77 static PVOID CabinetReservedArea
= NULL
;
80 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
84 RtlFreeHeap(ProcessHeap
, 0, _ptr
);
87 void* calloc(size_t _nmemb
, size_t _size
)
89 return (void*)RtlAllocateHeap (ProcessHeap
, HEAP_ZERO_MEMORY
, _size
);
95 RawCodecUncompress(PVOID OutputBuffer
,
100 * FUNCTION: Uncompresses data in a buffer
102 * OutputBuffer = Pointer to buffer to place uncompressed data
103 * InputBuffer = Pointer to buffer with data to be uncompressed
104 * InputLength = Length of input buffer
105 * OutputLength = Address of buffer to place size of uncompressed data
108 memcpy(OutputBuffer
, InputBuffer
, InputLength
);
109 *OutputLength
= InputLength
;
117 MSZipCodecUncompress(PVOID OutputBuffer
,
122 * FUNCTION: Uncompresses data in a buffer
124 * OutputBuffer = Pointer to buffer to place uncompressed data
125 * InputBuffer = Pointer to buffer with data to be uncompressed
126 * InputLength = Length of input buffer
127 * OutputLength = Address of buffer to place size of uncompressed data
133 DPRINT("InputLength (%d).\n", InputLength
);
135 Magic
= *((PUSHORT
)InputBuffer
);
137 if (Magic
!= MSZIP_MAGIC
)
139 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic
);
143 ZStream
.next_in
= (PUCHAR
)((ULONG
)InputBuffer
+ 2);
144 ZStream
.avail_in
= InputLength
- 2;
145 ZStream
.next_out
= (PUCHAR
)OutputBuffer
;
146 ZStream
.avail_out
= CAB_BLOCKSIZE
+ 12;
148 /* WindowBits is passed < 0 to tell that there is no zlib header.
149 * Note that in this case inflate *requires* an extra "dummy" byte
150 * after the compressed stream in order to complete decompression and
151 * return Z_STREAM_END.
153 Status
= inflateInit2(&ZStream
, -MAX_WBITS
);
156 DPRINT("inflateInit2() returned (%d).\n", Status
);
160 while ((ZStream
.total_out
< CAB_BLOCKSIZE
+ 12) &&
161 (ZStream
.total_in
< InputLength
- 2))
163 Status
= inflate(&ZStream
, Z_NO_FLUSH
);
164 if (Status
== Z_STREAM_END
) break;
167 DPRINT("inflate() returned (%d) (%s).\n", Status
, ZStream
.msg
);
168 if (Status
== Z_MEM_ERROR
)
174 *OutputLength
= ZStream
.total_out
;
176 Status
= inflateEnd(&ZStream
);
179 DPRINT("inflateEnd() returned (%d).\n", Status
);
187 /* Memory functions */
189 voidpf
MSZipAlloc(voidpf opaque
, uInt items
, uInt size
)
191 return (voidpf
)RtlAllocateHeap (ProcessHeap
, 0, items
* size
);
194 void MSZipFree (voidpf opaque
, voidpf address
)
196 RtlFreeHeap(ProcessHeap
, 0, address
);
201 SeekInFile(HANDLE hFile
,
202 LONG lDistanceToMove
,
203 PLONG lpDistanceToMoveHigh
,
207 FILE_POSITION_INFORMATION FilePosition
;
208 FILE_STANDARD_INFORMATION FileStandart
;
210 IO_STATUS_BLOCK IoStatusBlock
;
211 LARGE_INTEGER Distance
;
213 DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n",
214 hFile
,lDistanceToMove
,dwMoveMethod
);
216 Distance
.u
.LowPart
= lDistanceToMove
;
217 if (lpDistanceToMoveHigh
)
219 Distance
.u
.HighPart
= *lpDistanceToMoveHigh
;
221 else if (lDistanceToMove
>= 0)
223 Distance
.u
.HighPart
= 0;
227 Distance
.u
.HighPart
= -1;
230 if (dwMoveMethod
== SEEK_CURRENT
)
232 NtQueryInformationFile(hFile
,
235 sizeof(FILE_POSITION_INFORMATION
),
236 FilePositionInformation
);
237 FilePosition
.CurrentByteOffset
.QuadPart
+= Distance
.QuadPart
;
239 else if (dwMoveMethod
== SEEK_END
)
241 NtQueryInformationFile(hFile
,
244 sizeof(FILE_STANDARD_INFORMATION
),
245 FileStandardInformation
);
246 FilePosition
.CurrentByteOffset
.QuadPart
=
247 FileStandart
.EndOfFile
.QuadPart
+ Distance
.QuadPart
;
249 else if ( dwMoveMethod
== SEEK_BEGIN
)
251 FilePosition
.CurrentByteOffset
.QuadPart
= Distance
.QuadPart
;
254 // DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart);
256 errCode
= NtSetInformationFile(hFile
,
259 sizeof(FILE_POSITION_INFORMATION
),
260 FilePositionInformation
);
261 if (!NT_SUCCESS(errCode
))
270 if (lpDistanceToMoveHigh
!= NULL
)
272 *lpDistanceToMoveHigh
= FilePosition
.CurrentByteOffset
.u
.HighPart
;
276 *Status
= STATUS_SUCCESS
;
278 return FilePosition
.CurrentByteOffset
.u
.LowPart
;
283 ConvertSystemTimeToFileTime(
284 CONST SYSTEMTIME
* lpSystemTime
,
285 LPFILETIME lpFileTime
)
287 TIME_FIELDS TimeFields
;
288 LARGE_INTEGER liTime
;
290 TimeFields
.Year
= lpSystemTime
->wYear
;
291 TimeFields
.Month
= lpSystemTime
->wMonth
;
292 TimeFields
.Day
= lpSystemTime
->wDay
;
293 TimeFields
.Hour
= lpSystemTime
->wHour
;
294 TimeFields
.Minute
= lpSystemTime
->wMinute
;
295 TimeFields
.Second
= lpSystemTime
->wSecond
;
296 TimeFields
.Milliseconds
= lpSystemTime
->wMilliseconds
;
298 if (RtlTimeFieldsToTime(&TimeFields
, &liTime
))
300 lpFileTime
->dwLowDateTime
= liTime
.u
.LowPart
;
301 lpFileTime
->dwHighDateTime
= liTime
.u
.HighPart
;
309 ConvertDosDateTimeToFileTime(
312 LPFILETIME lpFileTime
)
314 PDOSTIME pdtime
= (PDOSTIME
) &wFatTime
;
315 PDOSDATE pddate
= (PDOSDATE
) &wFatDate
;
316 SYSTEMTIME SystemTime
;
318 if (lpFileTime
== NULL
)
321 SystemTime
.wMilliseconds
= 0;
322 SystemTime
.wSecond
= pdtime
->Second
;
323 SystemTime
.wMinute
= pdtime
->Minute
;
324 SystemTime
.wHour
= pdtime
->Hour
;
326 SystemTime
.wDay
= pddate
->Day
;
327 SystemTime
.wMonth
= pddate
->Month
;
328 SystemTime
.wYear
= 1980 + pddate
->Year
;
330 ConvertSystemTimeToFileTime(&SystemTime
,lpFileTime
);
337 GetFileName(PWCHAR Path
)
339 * FUNCTION: Returns a pointer to file name
341 * Path = Pointer to string with pathname
343 * Pointer to filename
352 if (Path
[i
- 1] == L
'\\') j
= i
;
359 RemoveFileName(PWCHAR Path
)
361 * FUNCTION: Removes a file name from a path
363 * Path = Pointer to string with path
370 FileName
= GetFileName(Path
+ i
);
372 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == L
'\\'))
374 if ((FileName
== (Path
+ i
)) && (FileName
[0] == L
'\\'))
381 SetAttributesOnFile(PCFFILE_NODE File
, HANDLE hFile
)
383 * FUNCTION: Sets attributes on a file
385 * File = Pointer to CFFILE node for file
387 * Status of operation
390 FILE_BASIC_INFORMATION FileBasic
;
391 IO_STATUS_BLOCK IoStatusBlock
;
393 ULONG Attributes
= 0;
395 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
396 Attributes
|= FILE_ATTRIBUTE_READONLY
;
398 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
399 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
401 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
402 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
404 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
405 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
407 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
408 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
410 NtStatus
= NtQueryInformationFile(hFile
,
413 sizeof(FILE_BASIC_INFORMATION
),
414 FileBasicInformation
);
415 if (!NT_SUCCESS(NtStatus
))
417 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
421 FileBasic
.FileAttributes
= Attributes
;
423 NtStatus
= NtSetInformationFile(hFile
,
426 sizeof(FILE_BASIC_INFORMATION
),
427 FileBasicInformation
);
428 if (!NT_SUCCESS(NtStatus
))
430 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
434 return NT_SUCCESS(NtStatus
);
439 ReadBlock(PVOID Buffer
,
443 * FUNCTION: Read a block of data from file
445 * Buffer = Pointer to data buffer
446 * Size = Length of data buffer
447 * BytesRead = Pointer to ULONG that on return will contain
448 * number of bytes read
450 * Status of operation
453 IO_STATUS_BLOCK IoStatusBlock
;
456 NtStatus
= NtReadFile(FileHandle
,
465 if (!NT_SUCCESS(NtStatus
))
467 DPRINT("ReadBlock for %d bytes failed (%x)\n", Size
, NtStatus
);
469 return CAB_STATUS_INVALID_CAB
;
472 return CAB_STATUS_SUCCESS
;
476 static PCFFOLDER_NODE
479 * FUNCTION: Creates a new folder node
481 * Pointer to node if there was enough free memory available, otherwise NULL
486 Node
= (PCFFOLDER_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFOLDER_NODE
));
490 RtlZeroMemory(Node
, sizeof(CFFOLDER_NODE
));
492 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
494 Node
->Prev
= FolderListTail
;
496 if (FolderListTail
!= NULL
)
498 FolderListTail
->Next
= Node
;
502 FolderListHead
= Node
;
504 FolderListTail
= Node
;
513 * FUNCTION: Creates a new file node
515 * FolderNode = Pointer to folder node to bind file to
517 * Pointer to node if there was enough free memory available, otherwise NULL
522 Node
= (PCFFILE_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFFILE_NODE
));
526 RtlZeroMemory(Node
, sizeof(CFFILE_NODE
));
528 Node
->Prev
= FileListTail
;
530 if (FileListTail
!= NULL
)
532 FileListTail
->Next
= Node
;
545 NewDataNode(PCFFOLDER_NODE FolderNode
)
547 * FUNCTION: Creates a new data block node
549 * FolderNode = Pointer to folder node to bind data block to
551 * Pointer to node if there was enough free memory available, otherwise NULL
556 Node
= (PCFDATA_NODE
)RtlAllocateHeap (ProcessHeap
, 0, sizeof(CFDATA_NODE
));
560 RtlZeroMemory(Node
, sizeof(CFDATA_NODE
));
562 Node
->Prev
= FolderNode
->DataListTail
;
564 if (FolderNode
->DataListTail
!= NULL
)
566 FolderNode
->DataListTail
->Next
= Node
;
570 FolderNode
->DataListHead
= Node
;
572 FolderNode
->DataListTail
= Node
;
579 DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
581 * FUNCTION: Destroys data block nodes bound to a folder node
583 * FolderNode = Pointer to folder node
586 PCFDATA_NODE PrevNode
;
587 PCFDATA_NODE NextNode
;
589 NextNode
= FolderNode
->DataListHead
;
590 while (NextNode
!= NULL
)
592 PrevNode
= NextNode
->Next
;
593 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
596 FolderNode
->DataListHead
= NULL
;
597 FolderNode
->DataListTail
= NULL
;
604 * FUNCTION: Destroys file nodes
606 * FolderNode = Pointer to folder node
609 PCFFILE_NODE PrevNode
;
610 PCFFILE_NODE NextNode
;
612 NextNode
= FileListHead
;
613 while (NextNode
!= NULL
)
615 PrevNode
= NextNode
->Next
;
616 if (NextNode
->FileName
)
617 RtlFreeHeap(ProcessHeap
, 0, NextNode
->FileName
);
618 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
627 DestroyDeletedFileNodes()
629 * FUNCTION: Destroys file nodes that are marked for deletion
632 PCFFILE_NODE CurNode
;
633 PCFFILE_NODE NextNode
;
635 CurNode
= FileListHead
;
636 while (CurNode
!= NULL
)
638 NextNode
= CurNode
->Next
;
642 if (CurNode
->Prev
!= NULL
)
644 CurNode
->Prev
->Next
= CurNode
->Next
;
648 FileListHead
= CurNode
->Next
;
650 FileListHead
->Prev
= NULL
;
653 if (CurNode
->Next
!= NULL
)
655 CurNode
->Next
->Prev
= CurNode
->Prev
;
659 FileListTail
= CurNode
->Prev
;
661 FileListTail
->Next
= NULL
;
664 DPRINT("Deleting file: '%S'\n", CurNode
->FileName
);
666 if (CurNode
->FileName
)
667 RtlFreeHeap(ProcessHeap
, 0, CurNode
->FileName
);
668 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
678 * FUNCTION: Destroys folder nodes
681 PCFFOLDER_NODE PrevNode
;
682 PCFFOLDER_NODE NextNode
;
684 NextNode
= FolderListHead
;
685 while (NextNode
!= NULL
)
687 PrevNode
= NextNode
->Next
;
688 DestroyDataNodes(NextNode
);
689 RtlFreeHeap(ProcessHeap
, 0, NextNode
);
692 FolderListHead
= NULL
;
693 FolderListTail
= NULL
;
698 DestroyDeletedFolderNodes()
700 * FUNCTION: Destroys folder nodes that are marked for deletion
703 PCFFOLDER_NODE CurNode
;
704 PCFFOLDER_NODE NextNode
;
706 CurNode
= FolderListHead
;
707 while (CurNode
!= NULL
)
709 NextNode
= CurNode
->Next
;
713 if (CurNode
->Prev
!= NULL
)
715 CurNode
->Prev
->Next
= CurNode
->Next
;
719 FolderListHead
= CurNode
->Next
;
721 FolderListHead
->Prev
= NULL
;
724 if (CurNode
->Next
!= NULL
)
726 CurNode
->Next
->Prev
= CurNode
->Prev
;
730 FolderListTail
= CurNode
->Prev
;
732 FolderListTail
->Next
= NULL
;
735 DestroyDataNodes(CurNode
);
736 RtlFreeHeap(ProcessHeap
, 0, CurNode
);
743 static PCFFOLDER_NODE
744 LocateFolderNode(ULONG Index
)
746 * FUNCTION: Locates a folder node
748 * Index = Folder index
750 * Pointer to folder node or NULL if the folder node was not found
758 return FolderListTail
;
760 case CAB_FILE_CONTINUED
:
761 case CAB_FILE_PREV_NEXT
:
762 return FolderListHead
;
765 Node
= FolderListHead
;
768 if (Node
->Index
== Index
)
777 GetAbsoluteOffset(PCFFILE_NODE File
)
779 * FUNCTION: Returns the absolute offset of a file
781 * File = Pointer to CFFILE_NODE structure for file
783 * Status of operation
788 DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n",
789 (PWCHAR
)File
->FileName
,
790 (UINT
)File
->File
.FileOffset
,
791 (UINT
)File
->File
.FileSize
);
793 Node
= CurrentFolderNode
->DataListHead
;
796 DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
797 (UINT
)Node
->UncompOffset
,
798 (UINT
)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
799 (UINT
)Node
->Data
.UncompSize
);
801 /* Node->Data.UncompSize will be 0 if the block is split
802 (ie. it is the last block in this cabinet) */
803 if ((Node
->Data
.UncompSize
== 0) ||
804 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
805 (File
->File
.FileOffset
< Node
->UncompOffset
+
806 Node
->Data
.UncompSize
)))
808 File
->DataBlock
= Node
;
809 return CAB_STATUS_SUCCESS
;
814 return CAB_STATUS_INVALID_CAB
;
819 LocateFile(PWCHAR FileName
,
822 * FUNCTION: Locates a file in the cabinet
824 * FileName = Pointer to string with name of file to locate
825 * File = Address of pointer to CFFILE_NODE structure to fill
827 * Status of operation
829 * Current folder is set to the folder of the file
835 DPRINT("FileName '%S'\n", FileName
);
840 if (_wcsicmp(FileName
, Node
->FileName
) == 0)
842 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
843 if (!CurrentFolderNode
)
845 DPRINT("Folder with index number (%d) not found.\n",
846 (UINT
)Node
->File
.FileControlID
);
847 return CAB_STATUS_INVALID_CAB
;
850 if (Node
->DataBlock
== NULL
)
852 Status
= GetAbsoluteOffset(Node
);
855 Status
= CAB_STATUS_SUCCESS
;
861 return CAB_STATUS_NOFILE
;
866 ReadString(PWCHAR String
, ULONG MaxLength
)
868 * FUNCTION: Reads a NULL-terminated string from the cabinet
870 * String = Pointer to buffer to place string
871 * MaxLength = Maximum length of string
873 * Status of operation
890 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
894 DPRINT("Too long a filename.\n");
895 return CAB_STATUS_INVALID_CAB
;
898 Status
= ReadBlock((PCFDATA
)&buf
[Offset
], Size
, &BytesRead
);
899 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
))
901 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
902 return CAB_STATUS_INVALID_CAB
;
905 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++)
907 if (buf
[Size
] == '\0')
917 /* Back up some bytes */
918 Size
= (BytesRead
- Size
) - 1;
919 SeekInFile(FileHandle
, -(LONG
)Size
, NULL
, SEEK_CURRENT
, &NtStatus
);
920 if (!NT_SUCCESS(NtStatus
))
922 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
923 return CAB_STATUS_INVALID_CAB
;
926 RtlInitAnsiString(&as
, buf
);
928 us
.MaximumLength
= MaxLength
* sizeof(WCHAR
);
931 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
933 return CAB_STATUS_SUCCESS
;
940 * FUNCTION: Reads the file table from the cabinet file
942 * Status of operation
951 DPRINT("Reading file table at absolute offset (0x%X).\n",
952 CABHeader
.FileTableOffset
);
954 /* Seek to file table */
955 SeekInFile(FileHandle
, CABHeader
.FileTableOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
956 if (!NT_SUCCESS(NtStatus
))
958 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
959 return CAB_STATUS_INVALID_CAB
;
962 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
964 File
= NewFileNode();
967 DPRINT("Insufficient memory.\n");
968 return CAB_STATUS_NOMEMORY
;
971 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
972 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
973 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
974 return CAB_STATUS_INVALID_CAB
;
977 File
->FileName
= (PWCHAR
)RtlAllocateHeap(ProcessHeap
, 0, MAX_PATH
* sizeof(WCHAR
));
980 DPRINT("Insufficient memory.\n");
981 return CAB_STATUS_NOMEMORY
;
985 Status
= ReadString(File
->FileName
, MAX_PATH
);
986 if (Status
!= CAB_STATUS_SUCCESS
)
989 DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
990 (PWCHAR
)File
->FileName
,
991 (UINT
)File
->File
.FileOffset
,
992 (UINT
)File
->File
.FileSize
,
993 (UINT
)File
->File
.FileControlID
);
995 return CAB_STATUS_SUCCESS
;
1000 ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
1002 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1004 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1006 * Status of operation
1009 ULONG AbsoluteOffset
;
1017 DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1018 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
);
1020 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
1021 UncompOffset
= FolderNode
->UncompOffset
;
1023 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
1025 Node
= NewDataNode(FolderNode
);
1028 DPRINT("Insufficient memory.\n");
1029 return CAB_STATUS_NOMEMORY
;
1032 /* Seek to data block */
1033 SeekInFile(FileHandle
, AbsoluteOffset
, NULL
, SEEK_BEGIN
, &NtStatus
);
1034 if (!NT_SUCCESS(NtStatus
))
1036 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1037 return CAB_STATUS_INVALID_CAB
;
1040 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
1041 &BytesRead
)) != CAB_STATUS_SUCCESS
)
1043 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1044 return CAB_STATUS_INVALID_CAB
;
1047 DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1048 (UINT
)AbsoluteOffset
,
1050 (UINT
)Node
->Data
.Checksum
,
1051 (UINT
)Node
->Data
.CompSize
,
1052 (UINT
)Node
->Data
.UncompSize
);
1054 Node
->AbsoluteOffset
= AbsoluteOffset
;
1055 Node
->UncompOffset
= UncompOffset
;
1057 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
1058 UncompOffset
+= Node
->Data
.UncompSize
;
1061 FolderUncompSize
= UncompOffset
;
1063 return CAB_STATUS_SUCCESS
;
1068 ComputeChecksum(PVOID Buffer
,
1072 * FUNCTION: Computes checksum for data block
1074 * Buffer = Pointer to data buffer
1075 * Size = Length of data buffer
1076 * Seed = Previously computed checksum
1078 * Checksum of buffer
1081 INT UlongCount
; // Number of ULONGs in block
1082 ULONG Checksum
; // Checksum accumulator
1086 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
1087 won't accept checksums computed by this routine */
1089 DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT
)Buffer
, Size
);
1091 UlongCount
= Size
/ 4; // Number of ULONGs
1092 Checksum
= Seed
; // Init checksum
1093 pb
= (PBYTE
)Buffer
; // Start at front of data block
1095 /* Checksum integral multiple of ULONGs */
1096 while (UlongCount
-- > 0)
1098 /* NOTE: Build ULONG in big/little-endian independent manner */
1099 ul
= *pb
++; // Get low-order byte
1100 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1101 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
1102 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
1104 Checksum
^= ul
; // Update checksum
1107 /* Checksum remainder bytes */
1112 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
1114 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
1116 ul
|= *pb
++; // Get low-order byte
1120 Checksum
^= ul
; // Update checksum
1122 /* Return computed checksum */
1130 * FUNCTION: Closes the current cabinet
1132 * Status of operation
1135 PCFFOLDER_NODE PrevNode
;
1136 PCFFOLDER_NODE NextNode
;
1141 DestroyFolderNodes();
1145 RtlFreeHeap(ProcessHeap
, 0, InputBuffer
);
1151 RtlFreeHeap(ProcessHeap
, 0, OutputBuffer
);
1152 OutputBuffer
= NULL
;
1155 NtClose(FileHandle
);
1162 * FUNCTION: Initialize archiver
1165 ZStream
.zalloc
= MSZipAlloc
;
1166 ZStream
.zfree
= MSZipFree
;
1167 ZStream
.opaque
= (voidpf
)0;
1170 wcscpy(DestPath
, L
"");
1172 FolderListHead
= NULL
;
1173 FolderListTail
= NULL
;
1174 FileListHead
= NULL
;
1175 FileListTail
= NULL
;
1177 CodecId
= CAB_CODEC_RAW
;
1178 CodecSelected
= TRUE
;
1180 OutputBuffer
= NULL
;
1181 CurrentOBuffer
= NULL
;
1182 CurrentOBufferSize
= 0;
1184 CurrentIBuffer
= NULL
;
1185 CurrentIBufferSize
= 0;
1187 FolderUncompSize
= 0;
1188 BytesLeftInBlock
= 0;
1189 CabinetReserved
= 0;
1193 CurrentFolderNode
= NULL
;
1194 CurrentDataNode
= NULL
;
1195 CabinetReservedArea
= NULL
;
1196 RestartSearch
= FALSE
;
1204 * FUNCTION: Cleanup archiver
1212 CabinetNormalizePath(PWCHAR Path
,
1215 * FUNCTION: Normalizes a path
1217 * Path = Pointer to string with pathname
1218 * Length = Number of characters in Path
1220 * TRUE if there was enough room in Path, or FALSE
1226 if ((n
= wcslen(Path
)) &&
1227 (Path
[n
- 1] != L
'\\') &&
1228 (OK
= ((n
+ 1) < Length
)))
1238 CabinetGetCabinetName()
1240 * FUNCTION: Returns pointer to cabinet file name
1242 * Pointer to string with name of cabinet
1250 CabinetSetCabinetName(PWCHAR FileName
)
1252 * FUNCTION: Sets cabinet file name
1254 * FileName = Pointer to string with name of cabinet
1257 wcscpy(CabinetName
, FileName
);
1262 CabinetSetDestinationPath(PWCHAR DestinationPath
)
1264 * FUNCTION: Sets destination path
1266 * DestinationPath = Pointer to string with name of destination path
1269 wcscpy(DestPath
, DestinationPath
);
1270 if (wcslen(DestPath
) > 0)
1271 CabinetNormalizePath(DestPath
, MAX_PATH
);
1276 CabinetGetDestinationPath()
1278 * FUNCTION: Returns destination path
1280 * Pointer to string with name of destination path
1290 * FUNCTION: Opens a cabinet file
1292 * Status of operation
1295 WCHAR CabinetFileName
[256];
1296 PCFFOLDER_NODE FolderNode
;
1302 OBJECT_ATTRIBUTES ObjectAttributes
;
1303 IO_STATUS_BLOCK IoStatusBlock
;
1304 UNICODE_STRING FileName
;
1309 OutputBuffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1311 return CAB_STATUS_NOMEMORY
;
1313 RtlInitUnicodeString(&FileName
,
1316 InitializeObjectAttributes(&ObjectAttributes
,
1318 OBJ_CASE_INSENSITIVE
,
1322 NtStatus
= NtOpenFile(&FileHandle
,
1327 FILE_SYNCHRONOUS_IO_ALERT
);
1328 if (!NT_SUCCESS(NtStatus
))
1330 DPRINT("Cannot open file (%S) (%x).\n", CabinetName
, NtStatus
);
1331 return CAB_STATUS_CANNOT_OPEN
;
1336 /* Load CAB header */
1337 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1339 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1340 return CAB_STATUS_INVALID_CAB
;
1344 if ((BytesRead
!= sizeof(CFHEADER
)) ||
1345 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
1346 (CABHeader
.Version
!= CAB_VERSION
) ||
1347 (CABHeader
.FolderCount
== 0 ) ||
1348 (CABHeader
.FileCount
== 0 ) ||
1349 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
1351 DPRINT("File has invalid header.\n");
1352 return CAB_STATUS_INVALID_CAB
;
1357 /* Read/skip any reserved bytes */
1358 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
1360 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1362 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1363 return CAB_STATUS_INVALID_CAB
;
1365 CabinetReserved
= Size
& 0xFFFF;
1366 FolderReserved
= (Size
>> 16) & 0xFF;
1367 DataReserved
= (Size
>> 24) & 0xFF;
1369 if (CabinetReserved
> 0)
1371 CabinetReservedArea
= RtlAllocateHeap(ProcessHeap
, 0, CabinetReserved
);
1372 if (!CabinetReservedArea
)
1374 return CAB_STATUS_NOMEMORY
;
1377 if ((Status
= ReadBlock(CabinetReservedArea
, CabinetReserved
, &BytesRead
)) != CAB_STATUS_SUCCESS
)
1379 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1380 return CAB_STATUS_INVALID_CAB
;
1384 SeekInFile(FileHandle
, CabinetReserved
, NULL
, SEEK_CURRENT
, &NtStatus
);
1385 if (!NT_SUCCESS(NtStatus
))
1387 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1388 return CAB_STATUS_INVALID_CAB
;
1393 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
1395 /* Read name of previous cabinet */
1396 Status
= ReadString(CabinetFileName
, 256);
1397 if (Status
!= CAB_STATUS_SUCCESS
)
1400 /* The previous cabinet file is in the same directory as the current */
1401 wcscpy(CabinetPrev
, CabinetName
);
1402 RemoveFileName(CabinetPrev
);
1403 CabinetNormalizePath(CabinetPrev
, 256);
1404 wcscat(CabinetPrev
, CabinetFileName
);
1406 /* Read label of previous disk */
1407 Status
= ReadString(DiskPrev
, 256);
1408 if (Status
!= CAB_STATUS_SUCCESS
)
1413 wcscpy(CabinetPrev
, L
"");
1414 wcscpy(DiskPrev
, L
"");
1417 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
1419 /* Read name of next cabinet */
1420 Status
= ReadString(CabinetFileName
, 256);
1421 if (Status
!= CAB_STATUS_SUCCESS
)
1424 /* The next cabinet file is in the same directory as the previous */
1425 wcscpy(CabinetNext
, CabinetName
);
1426 RemoveFileName(CabinetNext
);
1427 CabinetNormalizePath(CabinetNext
, 256);
1428 wcscat(CabinetNext
, CabinetFileName
);
1430 /* Read label of next disk */
1431 Status
= ReadString(DiskNext
, 256);
1432 if (Status
!= CAB_STATUS_SUCCESS
)
1437 wcscpy(CabinetNext
, L
"");
1438 wcscpy(DiskNext
, L
"");
1441 /* Read all folders */
1442 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
1444 FolderNode
= NewFolderNode();
1447 DPRINT("Insufficient resources.\n");
1448 return CAB_STATUS_NOMEMORY
;
1452 FolderNode
->UncompOffset
= FolderUncompSize
;
1454 FolderNode
->Index
= Index
;
1456 if ((Status
= ReadBlock(&FolderNode
->Folder
,
1457 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
1459 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1460 return CAB_STATUS_INVALID_CAB
;
1464 /* Read file entries */
1465 Status
= ReadFileTable();
1466 if (Status
!= CAB_STATUS_SUCCESS
)
1468 DPRINT("ReadFileTable() failed (%d).\n", (UINT
)Status
);
1472 /* Read data blocks for all folders */
1473 FolderNode
= FolderListHead
;
1474 while (FolderNode
!= NULL
)
1476 Status
= ReadDataBlocks(FolderNode
);
1477 if (Status
!= CAB_STATUS_SUCCESS
)
1479 DPRINT("ReadDataBlocks() failed (%d).\n", (UINT
)Status
);
1482 FolderNode
= FolderNode
->Next
;
1485 return CAB_STATUS_SUCCESS
;
1492 * FUNCTION: Closes the cabinet file
1499 if (CabinetReservedArea
!= NULL
)
1501 RtlFreeHeap(ProcessHeap
, 0, CabinetReservedArea
);
1502 CabinetReservedArea
= NULL
;
1511 CabinetFindFirst(PWCHAR FileName
,
1514 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
1516 * FileName = Pointer to search criteria
1517 * Search = Pointer to search structure
1519 * Status of operation
1522 RestartSearch
= FALSE
;
1523 wcsncpy(Search
->Search
, FileName
, MAX_PATH
);
1524 Search
->Next
= FileListHead
;
1525 return CabinetFindNext(Search
);
1530 CabinetFindNext(PCAB_SEARCH Search
)
1532 * FUNCTION: Finds next file in the cabinet that matches a search criteria
1534 * Search = Pointer to search structure
1536 * Status of operation
1543 Search
->Next
= FileListHead
;
1545 /* Skip split files already extracted */
1546 while ((Search
->Next
) &&
1547 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
1548 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
1550 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
1551 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
);
1552 Search
->Next
= Search
->Next
->Next
;
1555 RestartSearch
= FALSE
;
1558 /* FIXME: Check search criteria */
1562 if (wcslen(DiskNext
) > 0)
1566 CabinetSetCabinetName(CabinetNext
);
1568 if (DiskChangeHandler
!= NULL
)
1570 DiskChangeHandler(CabinetNext
, DiskNext
);
1573 Status
= CabinetOpen();
1574 if (Status
!= CAB_STATUS_SUCCESS
)
1577 Search
->Next
= FileListHead
;
1579 return CAB_STATUS_NOFILE
;
1583 return CAB_STATUS_NOFILE
;
1587 Search
->File
= &Search
->Next
->File
;
1588 Search
->FileName
= Search
->Next
->FileName
;
1589 Search
->Next
= Search
->Next
->Next
;
1590 return CAB_STATUS_SUCCESS
;
1595 CabinetExtractFile(PWCHAR FileName
)
1597 * FUNCTION: Extracts a file from the cabinet
1599 * FileName = Pointer to buffer with name of file
1601 * Status of operation
1611 ULONG TotalBytesRead
;
1612 ULONG CurrentOffset
;
1614 PUCHAR CurrentBuffer
;
1621 WCHAR DestName
[MAX_PATH
];
1622 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 /* Create destination file, fail if it already exists */
1660 RtlInitUnicodeString(&UnicodeString
,
1663 InitializeObjectAttributes(&ObjectAttributes
,
1665 OBJ_CASE_INSENSITIVE
,
1669 NtStatus
= NtCreateFile(&DestFile
,
1674 FILE_ATTRIBUTE_NORMAL
,
1677 FILE_SYNCHRONOUS_IO_ALERT
,
1680 if (!NT_SUCCESS(NtStatus
))
1682 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName
, NtStatus
);
1684 /* If file exists, ask to overwrite file */
1685 if (OverwriteHandler
== NULL
|| OverwriteHandler(&File
->File
, FileName
))
1687 /* Create destination file, overwrite if it already exists */
1688 NtStatus
= NtCreateFile(&DestFile
,
1693 FILE_ATTRIBUTE_NORMAL
,
1696 FILE_SYNCHRONOUS_IO_ALERT
,
1699 if (!NT_SUCCESS(NtStatus
))
1701 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName
, NtStatus
);
1702 return CAB_STATUS_CANNOT_CREATE
;
1707 DPRINT("File (%S) exists.\n", DestName
);
1708 return CAB_STATUS_FILE_EXISTS
;
1712 if (!ConvertDosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1715 DPRINT("DosDateTimeToFileTime() failed.\n");
1716 return CAB_STATUS_CANNOT_WRITE
;
1719 NtStatus
= NtQueryInformationFile(DestFile
,
1722 sizeof(FILE_BASIC_INFORMATION
),
1723 FileBasicInformation
);
1724 if (!NT_SUCCESS(Status
))
1726 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus
);
1730 memcpy(&FileBasic
.LastAccessTime
, &FileTime
, sizeof(FILETIME
));
1732 NtStatus
= NtSetInformationFile(DestFile
,
1735 sizeof(FILE_BASIC_INFORMATION
),
1736 FileBasicInformation
);
1737 if (!NT_SUCCESS(NtStatus
))
1739 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus
);
1743 SetAttributesOnFile(File
, DestFile
);
1745 Buffer
= RtlAllocateHeap(ProcessHeap
, 0, CAB_BLOCKSIZE
+ 12); // This should be enough
1749 DPRINT("Insufficient memory.\n");
1750 return CAB_STATUS_NOMEMORY
;
1753 /* Call extract event handler */
1754 if (ExtractHandler
!= NULL
)
1756 ExtractHandler(&File
->File
, FileName
);
1759 /* Search to start of file */
1760 Offset
= SeekInFile(FileHandle
,
1761 File
->DataBlock
->AbsoluteOffset
,
1765 if (!NT_SUCCESS(NtStatus
))
1767 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1768 return CAB_STATUS_INVALID_CAB
;
1771 Size
= File
->File
.FileSize
;
1772 Offset
= File
->File
.FileOffset
;
1773 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1777 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1782 DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1783 File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, Offset
, Size
,
1786 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1788 DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1790 CurrentBuffer
= Buffer
;
1794 DPRINT("Size (%d bytes).\n", Size
);
1796 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1797 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1800 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1801 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1802 return CAB_STATUS_INVALID_CAB
;
1805 DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
1806 (UINT
)CFData
.Checksum
,
1807 (UINT
)CFData
.CompSize
,
1808 (UINT
)CFData
.UncompSize
,
1809 (UINT
)SeekInFile(FileHandle
, 0, NULL
, SEEK_CURRENT
, &NtStatus
));
1811 //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1813 BytesToRead
= CFData
.CompSize
;
1815 DPRINT("Read: (0x%X,0x%X).\n",
1816 CurrentBuffer
, Buffer
);
1818 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1819 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1822 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1823 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1824 return CAB_STATUS_INVALID_CAB
;
1827 /* FIXME: Does not work with files generated by makecab.exe */
1829 if (CFData.Checksum != 0)
1831 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1832 if (Checksum != CFData.Checksum)
1835 RtlFreeHeap(ProcessHeap, 0, Buffer);
1836 DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n",
1837 Checksum, CFData.Checksum);
1838 return CAB_STATUS_INVALID_CAB;
1842 TotalBytesRead
+= BytesRead
;
1844 CurrentBuffer
+= BytesRead
;
1846 if (CFData
.UncompSize
== 0)
1848 if (wcslen(DiskNext
) == 0)
1849 return CAB_STATUS_NOFILE
;
1851 /* CloseCabinet() will destroy all file entries so in case
1852 FileName refers to the FileName field of a CFFOLDER_NODE
1853 structure, we have to save a copy of the filename */
1854 wcscpy(TempName
, FileName
);
1858 CabinetSetCabinetName(CabinetNext
);
1860 if (DiskChangeHandler
!= NULL
)
1862 DiskChangeHandler(CabinetNext
, DiskNext
);
1865 Status
= CabinetOpen();
1866 if (Status
!= CAB_STATUS_SUCCESS
)
1869 /* The first data block of the file will not be
1870 found as it is located in the previous file */
1871 Status
= LocateFile(TempName
, &File
);
1872 if (Status
== CAB_STATUS_NOFILE
)
1874 DPRINT("Cannot locate file (%d).\n", (UINT
)Status
);
1878 /* The file is continued in the first data block in the folder */
1879 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1881 /* Search to start of file */
1882 SeekInFile(FileHandle
,
1883 File
->DataBlock
->AbsoluteOffset
,
1887 if (!NT_SUCCESS(NtStatus
))
1889 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1890 return CAB_STATUS_INVALID_CAB
;
1893 DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1894 (UINT
)File
->File
.FileOffset
,
1895 (UINT
)File
->File
.FileSize
,
1896 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1897 (UINT
)File
->DataBlock
->UncompOffset
);
1899 CurrentDataNode
= File
->DataBlock
;
1902 RestartSearch
= TRUE
;
1904 } while (CFData
.UncompSize
== 0);
1906 DPRINT("TotalBytesRead (%d).\n", TotalBytesRead
);
1908 Status
= CodecUncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1909 if (Status
!= CS_SUCCESS
)
1912 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1913 DPRINT("Cannot uncompress block.\n");
1914 if (Status
== CS_NOMEMORY
)
1915 return CAB_STATUS_NOMEMORY
;
1916 return CAB_STATUS_INVALID_CAB
;
1919 if (BytesToWrite
!= CFData
.UncompSize
)
1921 DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
1922 BytesToWrite
, CFData
.UncompSize
);
1923 return CAB_STATUS_INVALID_CAB
;
1926 BytesLeftInBlock
= BytesToWrite
;
1930 DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
);
1932 BytesToWrite
= BytesLeftInBlock
;
1934 DPRINT("Seeking to absolute offset 0x%X.\n",
1935 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1936 CurrentDataNode
->Data
.CompSize
);
1938 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1939 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1942 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1943 DPRINT("Cannot read from file (%d).\n", (UINT
)Status
);
1944 return CAB_STATUS_INVALID_CAB
;
1947 DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1948 CFData
.CompSize
, CFData
.UncompSize
);
1950 /* Go to next data block */
1951 SeekInFile(FileHandle
,
1952 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1953 CurrentDataNode
->Data
.CompSize
,
1957 if (!NT_SUCCESS(NtStatus
))
1959 DPRINT("SeekInFile() failed (%x).\n", NtStatus
);
1960 return CAB_STATUS_INVALID_CAB
;
1967 BytesSkipped
= (Offset
- CurrentOffset
);
1971 BytesToWrite
-= BytesSkipped
;
1973 if (Size
< BytesToWrite
)
1974 BytesToWrite
= Size
;
1976 DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1978 (UINT
)CurrentOffset
,
1980 (UINT
)BytesSkipped
, (UINT
)Skip
,
1983 // if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1984 // BytesToWrite, &BytesWritten, NULL) ||
1985 // (BytesToWrite != BytesWritten))
1987 NtStatus
= NtWriteFile(DestFile
,
1992 (PVOID
)((ULONG
)OutputBuffer
+ BytesSkipped
),
1996 BytesWritten
= BytesToWrite
;
1997 if (!NT_SUCCESS(NtStatus
))
1999 DPRINT("Status 0x%X.\n", NtStatus
);
2002 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2003 DPRINT("Cannot write to file.\n");
2004 return CAB_STATUS_CANNOT_WRITE
;
2006 Size
-= BytesToWrite
;
2008 CurrentOffset
+= BytesToWrite
;
2010 /* Don't skip any more bytes */
2017 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
2019 return CAB_STATUS_SUCCESS
;
2024 CabinetSelectCodec(ULONG Id
)
2026 * FUNCTION: Selects codec engine to use
2028 * Id = Codec identifier
2036 CodecSelected
= FALSE
;
2042 CodecUncompress
= RawCodecUncompress
;
2044 case CAB_CODEC_MSZIP
:
2045 CodecUncompress
= MSZipCodecUncompress
;
2052 CodecSelected
= TRUE
;
2057 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite
,
2058 PCABINET_EXTRACT Extract
,
2059 PCABINET_DISK_CHANGE DiskChange
)
2061 * FUNCTION: Set event handlers
2063 * Overwrite = Handler called when a file is to be overwritten
2064 * Extract = Handler called when a file is to be extracted
2065 * DiskChange = Handler called when changing the disk
2068 OverwriteHandler
= Overwrite
;
2069 ExtractHandler
= Extract
;
2070 DiskChangeHandler
= DiskChange
;
2075 CabinetGetCabinetReservedArea(PULONG Size
)
2077 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
2080 if (CabinetReservedArea
!= NULL
)
2084 *Size
= CabinetReserved
;
2086 return CabinetReservedArea
;