2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: apps/cabman/cabinet.cpp
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * NOTES: Define CAB_READ_ONLY for read only version
9 * CSH 21/03-2001 Created
11 * - Checksum of datablocks should be calculated
12 * - EXTRACT.EXE complains if a disk is created manually
13 * - Folders that are created manually and span disks will result in a damaged cabinet
27 VOID
DumpBuffer(PVOID Buffer
, DWORD Size
)
32 /* Create file, overwrite if it already exists */
33 FileHandle
= CreateFile("dump.bin", // Create this file
34 GENERIC_WRITE
, // Open for writing
37 CREATE_ALWAYS
, // Create or overwrite
38 FILE_ATTRIBUTE_NORMAL
, // Normal file
39 NULL
); // No attribute template
40 if (FileHandle
== INVALID_HANDLE_VALUE
) {
41 DPRINT(MID_TRACE
, ("ERROR OPENING '%d'.\n", (UINT
)GetLastError()));
45 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
)) {
46 DPRINT(MID_TRACE
, ("ERROR WRITING '%d'.\n", (UINT
)GetLastError()));
49 CloseHandle(FileHandle
);
57 CCFDATAStorage::CCFDATAStorage()
59 * FUNCTION: Default constructor
66 CCFDATAStorage::~CCFDATAStorage()
68 * FUNCTION: Default destructor
75 ULONG
CCFDATAStorage::Create(LPTSTR FileName
)
77 * FUNCTION: Creates the file
79 * FileName = Pointer to name of file
84 TCHAR FullName
[MAX_PATH
];
88 if (GetTempPath(MAX_PATH
, FullName
) == 0)
89 return CAB_STATUS_CANNOT_CREATE
;
91 lstrcat(FullName
, FileName
);
93 /* Create file, overwrite if it already exists */
94 FileHandle
= CreateFile(FullName
, // Create this file
95 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
98 CREATE_ALWAYS
, // Create or overwrite
99 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
100 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
101 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
102 NULL
); // No attribute template
103 if (FileHandle
== INVALID_HANDLE_VALUE
) {
104 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (UINT
)GetLastError()));
105 return CAB_STATUS_CANNOT_CREATE
;
110 return CAB_STATUS_SUCCESS
;
114 ULONG
CCFDATAStorage::Destroy()
116 * FUNCTION: Destroys the file
118 * Status of operation
123 CloseHandle(FileHandle
);
127 return CAB_STATUS_SUCCESS
;
131 ULONG
CCFDATAStorage::Truncate()
133 * FUNCTION: Truncate the scratch file to zero bytes
135 * Status of operation
138 if (!SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
))
139 return CAB_STATUS_FAILURE
;
140 if (!SetEndOfFile(FileHandle
))
141 return CAB_STATUS_FAILURE
;
142 return CAB_STATUS_SUCCESS
;
146 ULONG
CCFDATAStorage::Position()
148 * FUNCTION: Returns current position in file
153 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
157 ULONG
CCFDATAStorage::Seek(LONG Position
)
159 * FUNCTION: Seeks to an absolute position
161 * Position = Absolute position to seek to
163 * Status of operation
166 if (SetFilePointer(FileHandle
,
169 FILE_BEGIN
) == 0xFFFFFFFF)
170 return CAB_STATUS_FAILURE
;
172 return CAB_STATUS_SUCCESS
;
176 ULONG
CCFDATAStorage::ReadBlock(PCFDATA Data
, PVOID Buffer
, PDWORD BytesRead
)
178 * FUNCTION: Reads a CFDATA block from the file
180 * Data = Pointer to CFDATA block for the buffer
181 * Buffer = Pointer to buffer to store data read
182 * BytesWritten = Pointer to buffer to write number of bytes read
184 * Status of operation
187 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, BytesRead
, NULL
))
188 return CAB_STATUS_CANNOT_READ
;
190 return CAB_STATUS_SUCCESS
;
194 ULONG
CCFDATAStorage::WriteBlock(PCFDATA Data
, PVOID Buffer
, PDWORD BytesWritten
)
196 * FUNCTION: Writes a CFDATA block to the file
198 * Data = Pointer to CFDATA block for the buffer
199 * Buffer = Pointer to buffer with data to write
200 * BytesWritten = Pointer to buffer to write number of bytes written
202 * Status of operation
205 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, BytesWritten
, NULL
))
206 return CAB_STATUS_CANNOT_WRITE
;
208 return CAB_STATUS_SUCCESS
;
211 #endif /* CAB_READ_ONLY */
218 * FUNCTION: Default constructor
222 lstrcpy(DestPath
, "");
224 FolderListHead
= NULL
;
225 FolderListTail
= NULL
;
229 Codec
= new CRawCodec();
230 CodecId
= CAB_CODEC_RAW
;
231 CodecSelected
= TRUE
;
236 BlockIsSplit
= FALSE
;
239 FolderUncompSize
= 0;
240 BytesLeftInBlock
= 0;
242 CurrentDataNode
= NULL
;
246 CCabinet::~CCabinet()
248 * FUNCTION: Default destructor
256 LPTSTR
CCabinet::GetFileName(LPTSTR Path
)
258 * FUNCTION: Returns a pointer to file name
260 * Path = Pointer to string with pathname
262 * Pointer to filename
267 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
270 if (Path
[i
- 1] == '\\') j
= i
;
276 VOID
CCabinet::RemoveFileName(LPTSTR Path
)
278 * FUNCTION: Removes a file name from a path
280 * Path = Pointer to string with path
286 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
287 FileName
= GetFileName(Path
+ i
);
289 if ((FileName
!= (Path
+ i
)) && (FileName
[-1] == '\\'))
291 if ((FileName
== (Path
+ i
)) && (FileName
[0] == '\\'))
297 BOOL
CCabinet::NormalizePath(LPTSTR Path
,
300 * FUNCTION: Normalizes a path
302 * Path = Pointer to string with pathname
303 * Length = Number of bytes in Path
305 * TRUE if there was enough room in Path, or FALSE
311 if ((n
= lstrlen(Path
)) &&
312 (Path
[n
- 1] != '\\') &&
313 (OK
= ((n
+ 1) < Length
))) {
321 LPTSTR
CCabinet::GetCabinetName()
323 * FUNCTION: Returns pointer to cabinet file name
325 * Pointer to string with name of cabinet
332 VOID
CCabinet::SetCabinetName(LPTSTR FileName
)
334 * FUNCTION: Sets cabinet file name
336 * FileName = Pointer to string with name of cabinet
339 lstrcpy(CabinetName
, FileName
);
343 VOID
CCabinet::SetDestinationPath(LPTSTR DestinationPath
)
345 * FUNCTION: Sets destination path
347 * DestinationPath = Pointer to string with name of destination path
350 lstrcpy(DestPath
, DestinationPath
);
351 if (lstrlen(DestPath
) > 0)
352 NormalizePath(DestPath
, MAX_PATH
);
356 LPTSTR
CCabinet::GetDestinationPath()
358 * FUNCTION: Returns destination path
360 * Pointer to string with name of destination path
367 DWORD
CCabinet::GetCurrentDiskNumber()
369 * FUNCTION: Returns current disk number
371 * Current disk number
374 return CurrentDiskNumber
;
378 ULONG
CCabinet::Open()
380 * FUNCTION: Opens a cabinet file
382 * Status of operation
385 PCFFOLDER_NODE FolderNode
;
393 OutputBuffer
= HeapAlloc(GetProcessHeap(),
394 0, CAB_BLOCKSIZE
+ 12); // This should be enough
396 return CAB_STATUS_NOMEMORY
;
398 FileHandle
= CreateFile(CabinetName
, // Open this file
399 GENERIC_READ
, // Open for reading
400 FILE_SHARE_READ
, // Share for reading
402 OPEN_EXISTING
, // Existing file only
403 FILE_ATTRIBUTE_NORMAL
, // Normal file
404 NULL
); // No attribute template
406 if (FileHandle
== INVALID_HANDLE_VALUE
) {
407 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
408 return CAB_STATUS_CANNOT_OPEN
;
413 /* Load CAB header */
414 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
415 != CAB_STATUS_SUCCESS
) {
416 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
417 return CAB_STATUS_INVALID_CAB
;
421 if ((BytesRead
!= sizeof(CFHEADER
)) ||
422 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
423 (CABHeader
.Version
!= CAB_VERSION
) ||
424 (CABHeader
.FolderCount
== 0 ) ||
425 (CABHeader
.FileCount
== 0 ) ||
426 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
428 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
429 return CAB_STATUS_INVALID_CAB
;
434 /* Read/skip any reserved bytes */
435 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
436 if ((Status
= ReadBlock(&Size
, sizeof(DWORD
), &BytesRead
))
437 != CAB_STATUS_SUCCESS
) {
438 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
439 return CAB_STATUS_INVALID_CAB
;
441 CabinetReserved
= Size
& 0xFFFF;
442 FolderReserved
= (Size
>> 16) & 0xFF;
443 DataReserved
= (Size
>> 24) & 0xFF;
445 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
446 if (GetLastError() != NO_ERROR
) {
447 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
448 return CAB_STATUS_NOMEMORY
;
452 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
453 /* Read name of previous cabinet */
454 Status
= ReadString(CabinetPrev
, 256);
455 if (Status
!= CAB_STATUS_SUCCESS
)
457 /* Read label of previous disk */
458 Status
= ReadString(DiskPrev
, 256);
459 if (Status
!= CAB_STATUS_SUCCESS
)
462 lstrcpy(CabinetPrev
, "");
463 lstrcpy(DiskPrev
, "");
466 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
467 /* Read name of next cabinet */
468 Status
= ReadString(CabinetNext
, 256);
469 if (Status
!= CAB_STATUS_SUCCESS
)
471 /* Read label of next disk */
472 Status
= ReadString(DiskNext
, 256);
473 if (Status
!= CAB_STATUS_SUCCESS
)
476 lstrcpy(CabinetNext
, "");
477 lstrcpy(DiskNext
, "");
480 /* Read all folders */
481 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++) {
482 FolderNode
= NewFolderNode();
484 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
485 return CAB_STATUS_NOMEMORY
;
489 FolderNode
->UncompOffset
= FolderUncompSize
;
491 FolderNode
->Index
= Index
;
493 if ((Status
= ReadBlock(&FolderNode
->Folder
,
494 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
) {
495 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
496 return CAB_STATUS_INVALID_CAB
;
500 /* Read file entries */
501 Status
= ReadFileTable();
502 if (Status
!= CAB_STATUS_SUCCESS
) {
503 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (UINT
)Status
));
507 /* Read data blocks for all folders */
508 FolderNode
= FolderListHead
;
509 while (FolderNode
!= NULL
) {
510 Status
= ReadDataBlocks(FolderNode
);
511 if (Status
!= CAB_STATUS_SUCCESS
) {
512 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (UINT
)Status
));
515 FolderNode
= FolderNode
->Next
;
518 return CAB_STATUS_SUCCESS
;
522 VOID
CCabinet::Close()
524 * FUNCTION: Closes the cabinet file
528 CloseHandle(FileHandle
);
534 ULONG
CCabinet::FindFirst(LPTSTR FileName
,
537 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
539 * FileName = Pointer to search criteria
540 * Search = Pointer to search structure
542 * Status of operation
545 RestartSearch
= FALSE
;
546 strncpy(Search
->Search
, FileName
, MAX_PATH
);
547 Search
->Next
= FileListHead
;
548 return FindNext(Search
);
552 ULONG
CCabinet::FindNext(PCAB_SEARCH Search
)
554 * FUNCTION: Finds next file in the cabinet that matches a search criteria
556 * Search = Pointer to search structure
558 * Status of operation
564 Search
->Next
= FileListHead
;
566 /* Skip split files already extracted */
567 while ((Search
->Next
) &&
568 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
569 (Search
->Next
->File
.FileOffset
<= LastFileOffset
)) {
570 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
571 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
572 Search
->Next
= Search
->Next
->Next
;
575 RestartSearch
= FALSE
;
578 /* FIXME: Check search criteria */
581 if (lstrlen(DiskNext
) > 0) {
584 SetCabinetName(CabinetNext
);
586 OnDiskChange(CabinetNext
, DiskNext
);
589 if (Status
!= CAB_STATUS_SUCCESS
)
592 Search
->Next
= FileListHead
;
594 return CAB_STATUS_NOFILE
;
596 return CAB_STATUS_NOFILE
;
599 Search
->File
= &Search
->Next
->File
;
600 Search
->FileName
= Search
->Next
->FileName
;
601 Search
->Next
= Search
->Next
->Next
;
602 return CAB_STATUS_SUCCESS
;
606 ULONG
CCabinet::ExtractFile(LPTSTR FileName
)
608 * FUNCTION: Extracts a file from the cabinet
610 * FileName = Pointer to buffer with name of file
612 * Status of operation
622 DWORD TotalBytesRead
;
625 PUCHAR CurrentBuffer
;
632 TCHAR DestName
[MAX_PATH
];
633 TCHAR TempName
[MAX_PATH
];
635 Status
= LocateFile(FileName
, &File
);
636 if (Status
!= CAB_STATUS_SUCCESS
) {
637 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (UINT
)Status
));
641 LastFileOffset
= File
->File
.FileOffset
;
643 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
) {
645 SelectCodec(CAB_CODEC_RAW
);
648 SelectCodec(CAB_CODEC_MSZIP
);
651 return CAB_STATUS_UNSUPPCOMP
;
654 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
655 (UINT
)File
->File
.FileOffset
,
656 (UINT
)File
->File
.FileSize
,
657 (UINT
)File
->DataBlock
->AbsoluteOffset
,
658 (UINT
)File
->DataBlock
->UncompOffset
));
660 lstrcpy(DestName
, DestPath
);
661 lstrcat(DestName
, FileName
);
663 /* Create destination file, fail if it already exists */
664 DestFile
= CreateFile(DestName
, // Create this file
665 GENERIC_WRITE
, // Open for writing
668 CREATE_NEW
, // New file only
669 FILE_ATTRIBUTE_NORMAL
, // Normal file
670 NULL
); // No attribute template
671 if (DestFile
== INVALID_HANDLE_VALUE
) {
672 /* If file exists, ask to overwrite file */
673 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
674 (OnOverwrite(&File
->File
, FileName
))) {
675 /* Create destination file, overwrite if it already exists */
676 DestFile
= CreateFile(DestName
, // Create this file
677 GENERIC_WRITE
, // Open for writing
680 TRUNCATE_EXISTING
, // Truncate the file
681 FILE_ATTRIBUTE_NORMAL
, // Normal file
682 NULL
); // No attribute template
683 if (DestFile
== INVALID_HANDLE_VALUE
)
684 return CAB_STATUS_CANNOT_CREATE
;
686 if (Status
== ERROR_FILE_EXISTS
)
687 return CAB_STATUS_FILE_EXISTS
;
689 return CAB_STATUS_CANNOT_CREATE
;
693 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
)) {
694 CloseHandle(DestFile
);
695 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%d).\n", GetLastError()));
696 return CAB_STATUS_CANNOT_WRITE
;
699 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
701 SetAttributesOnFile(File
);
703 Buffer
= (PUCHAR
)HeapAlloc(GetProcessHeap(),
704 0, CAB_BLOCKSIZE
+ 12); // This should be enough
706 CloseHandle(DestFile
);
707 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
708 return CAB_STATUS_NOMEMORY
;
711 /* Call OnExtract event handler */
712 OnExtract(&File
->File
, FileName
);
714 /* Search to start of file */
715 Offset
= SetFilePointer(FileHandle
,
716 File
->DataBlock
->AbsoluteOffset
,
719 if (GetLastError() != NO_ERROR
) {
720 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
721 return CAB_STATUS_INVALID_CAB
;
724 Size
= File
->File
.FileSize
;
725 Offset
= File
->File
.FileOffset
;
726 CurrentOffset
= File
->DataBlock
->UncompOffset
;
730 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
733 DPRINT(MAX_TRACE
, ("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
734 File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, Offset
, Size
,
737 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0)) {
739 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
));
741 CurrentBuffer
= Buffer
;
744 DPRINT(MAX_TRACE
, ("Size (%d bytes).\n", Size
));
746 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
747 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
748 CloseHandle(DestFile
);
749 HeapFree(GetProcessHeap(), 0, Buffer
);
750 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
751 return CAB_STATUS_INVALID_CAB
;
754 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
755 (UINT
)CFData
.Checksum
,
756 (UINT
)CFData
.CompSize
,
757 (UINT
)CFData
.UncompSize
,
758 (UINT
)SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
)));
760 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
762 BytesToRead
= CFData
.CompSize
;
764 DPRINT(MAX_TRACE
, ("Read: (0x%X,0x%X).\n",
765 CurrentBuffer
, Buffer
));
767 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
768 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
769 CloseHandle(DestFile
);
770 HeapFree(GetProcessHeap(), 0, Buffer
);
771 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
772 return CAB_STATUS_INVALID_CAB
;
775 /* FIXME: Does not work with files generated by makecab.exe */
777 if (CFData.Checksum != 0) {
778 DWORD Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
779 if (Checksum != CFData.Checksum) {
780 CloseHandle(DestFile);
781 HeapFree(GetProcessHeap(), 0, Buffer);
782 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
783 Checksum, CFData.Checksum));
784 return CAB_STATUS_INVALID_CAB;
788 TotalBytesRead
+= BytesRead
;
790 CurrentBuffer
+= BytesRead
;
792 if (CFData
.UncompSize
== 0) {
793 if (lstrlen(DiskNext
) == 0)
794 return CAB_STATUS_NOFILE
;
796 /* CloseCabinet() will destroy all file entries so in case
797 FileName refers to the FileName field of a CFFOLDER_NODE
798 structure, we have to save a copy of the filename */
799 lstrcpy(TempName
, FileName
);
803 SetCabinetName(CabinetNext
);
805 OnDiskChange(CabinetNext
, DiskNext
);
808 if (Status
!= CAB_STATUS_SUCCESS
)
811 /* The first data block of the file will not be
812 found as it is located in the previous file */
813 Status
= LocateFile(TempName
, &File
);
814 if (Status
== CAB_STATUS_NOFILE
) {
815 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (UINT
)Status
));
819 /* The file is continued in the first data block in the folder */
820 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
822 /* Search to start of file */
823 SetFilePointer(FileHandle
,
824 File
->DataBlock
->AbsoluteOffset
,
827 if (GetLastError() != NO_ERROR
) {
828 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
829 return CAB_STATUS_INVALID_CAB
;
832 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
833 (UINT
)File
->File
.FileOffset
,
834 (UINT
)File
->File
.FileSize
,
835 (UINT
)File
->DataBlock
->AbsoluteOffset
,
836 (UINT
)File
->DataBlock
->UncompOffset
));
838 CurrentDataNode
= File
->DataBlock
;
841 RestartSearch
= TRUE
;
843 } while (CFData
.UncompSize
== 0);
845 DPRINT(MAX_TRACE
, ("TotalBytesRead (%d).\n", TotalBytesRead
));
847 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
848 if (Status
!= CS_SUCCESS
) {
849 CloseHandle(DestFile
);
850 HeapFree(GetProcessHeap(), 0, Buffer
);
851 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
852 if (Status
== CS_NOMEMORY
)
853 return CAB_STATUS_NOMEMORY
;
854 return CAB_STATUS_INVALID_CAB
;
857 if (BytesToWrite
!= CFData
.UncompSize
) {
858 DPRINT(MID_TRACE
, ("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
859 BytesToWrite
, CFData
.UncompSize
));
860 return CAB_STATUS_INVALID_CAB
;
863 BytesLeftInBlock
= BytesToWrite
;
865 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (UINT
)ReuseBlock
));
867 BytesToWrite
= BytesLeftInBlock
;
869 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%X.\n",
870 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
871 CurrentDataNode
->Data
.CompSize
));
873 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
874 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
875 CloseHandle(DestFile
);
876 HeapFree(GetProcessHeap(), 0, Buffer
);
877 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
878 return CAB_STATUS_INVALID_CAB
;
881 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
882 CFData
.CompSize
, CFData
.UncompSize
));
884 /* Go to next data block */
885 SetFilePointer(FileHandle
,
886 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
887 CurrentDataNode
->Data
.CompSize
,
890 if (GetLastError() != NO_ERROR
) {
891 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
892 return CAB_STATUS_INVALID_CAB
;
899 BytesSkipped
= (Offset
- CurrentOffset
);
903 BytesToWrite
-= BytesSkipped
;
905 if (Size
< BytesToWrite
)
908 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
912 (UINT
)BytesSkipped
, (UINT
)Skip
,
915 if (!WriteFile(DestFile
, (PVOID
)((ULONG
)OutputBuffer
+ BytesSkipped
),
916 BytesToWrite
, &BytesWritten
, NULL
) ||
917 (BytesToWrite
!= BytesWritten
)) {
919 DPRINT(MIN_TRACE
, ("Status 0x%X.\n", GetLastError()));
921 CloseHandle(DestFile
);
922 HeapFree(GetProcessHeap(), 0, Buffer
);
923 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
924 return CAB_STATUS_CANNOT_WRITE
;
926 Size
-= BytesToWrite
;
928 CurrentOffset
+= BytesToWrite
;
930 /* Don't skip any more bytes */
935 CloseHandle(DestFile
);
937 HeapFree(GetProcessHeap(), 0, Buffer
);
939 return CAB_STATUS_SUCCESS
;
943 VOID
CCabinet::SelectCodec(ULONG Id
)
945 * FUNCTION: Selects codec engine to use
947 * Id = Codec identifier
954 CodecSelected
= FALSE
;
960 Codec
= new CRawCodec();
962 case CAB_CODEC_MSZIP
:
963 Codec
= new CMSZipCodec();
970 CodecSelected
= TRUE
;
974 #ifndef CAB_READ_ONLY
976 /* CAB write methods */
978 ULONG
CCabinet::NewCabinet()
980 * FUNCTION: Creates a new cabinet
982 * Status of operation
987 CurrentDiskNumber
= 0;
989 OutputBuffer
= HeapAlloc(GetProcessHeap(), 0, CAB_BLOCKSIZE
+ 12); // This should be enough
990 InputBuffer
= HeapAlloc(GetProcessHeap(), 0, CAB_BLOCKSIZE
+ 12); // This should be enough
991 if ((!OutputBuffer
) || (!InputBuffer
)) {
992 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
993 return CAB_STATUS_NOMEMORY
;
995 CurrentIBuffer
= InputBuffer
;
996 CurrentIBufferSize
= 0;
998 CABHeader
.Signature
= CAB_SIGNATURE
;
999 CABHeader
.Reserved1
= 0; // Not used
1000 CABHeader
.CabinetSize
= 0; // Not yet known
1001 CABHeader
.Reserved2
= 0; // Not used
1002 CABHeader
.Reserved3
= 0; // Not used
1003 CABHeader
.Version
= CAB_VERSION
;
1004 CABHeader
.FolderCount
= 0; // Not yet known
1005 CABHeader
.FileCount
= 0; // Not yet known
1006 CABHeader
.Flags
= 0; // Not yet known
1007 // FIXME: Should be random
1008 CABHeader
.SetID
= 0x534F;
1009 CABHeader
.CabinetNumber
= 0;
1012 TotalFolderSize
= 0;
1015 DiskSize
= sizeof(CFHEADER
);
1017 InitCabinetHeader();
1019 // NextFolderNumber is 0-based
1020 NextFolderNumber
= 0;
1022 CurrentFolderNode
= NULL
;
1023 Status
= NewFolder();
1024 if (Status
!= CAB_STATUS_SUCCESS
)
1027 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1029 ScratchFile
= new CCFDATAStorage
;
1031 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1032 return CAB_STATUS_NOMEMORY
;
1035 Status
= ScratchFile
->Create("~CAB.tmp");
1037 CreateNewFolder
= FALSE
;
1039 CreateNewDisk
= FALSE
;
1041 PrevCabinetNumber
= 0;
1047 ULONG
CCabinet::NewDisk()
1049 * FUNCTION: Forces a new disk to be created
1051 * Status of operation
1054 // NextFolderNumber is 0-based
1055 NextFolderNumber
= 1;
1057 CreateNewDisk
= FALSE
;
1059 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1061 InitCabinetHeader();
1063 CurrentFolderNode
->TotalFolderSize
= 0;
1065 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1067 return CAB_STATUS_SUCCESS
;
1071 ULONG
CCabinet::NewFolder()
1073 * FUNCTION: Forces a new folder to be created
1075 * Status of operation
1078 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1080 CurrentFolderNode
= NewFolderNode();
1081 if (!CurrentFolderNode
) {
1082 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1083 return CAB_STATUS_NOMEMORY
;
1088 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1090 case CAB_CODEC_MSZIP
:
1091 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1094 return CAB_STATUS_UNSUPPCOMP
;
1097 /* FIXME: This won't work if no files are added to the new folder */
1099 DiskSize
+= sizeof(CFFOLDER
);
1101 TotalFolderSize
+= sizeof(CFFOLDER
);
1105 CABHeader
.FolderCount
++;
1109 return CAB_STATUS_SUCCESS
;
1113 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1115 * FUNCTION: Writes a file to the scratch file
1117 * FileNode = Pointer to file node
1119 * Status of operation
1127 if (!ContinueFile
) {
1128 /* Try to open file */
1129 SourceFile
= CreateFile(
1130 FileNode
->FileName
, // Open this file
1131 GENERIC_READ
, // Open for reading
1132 FILE_SHARE_READ
, // Share for reading
1133 NULL
, // No security
1134 OPEN_EXISTING
, // File must exist
1135 FILE_ATTRIBUTE_NORMAL
, // Normal file
1136 NULL
); // No attribute template
1137 if (SourceFile
== INVALID_HANDLE_VALUE
) {
1138 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1139 return CAB_STATUS_NOFILE
;
1142 if (CreateNewFolder
) {
1143 /* There is always a new folder after
1144 a split file is completely stored */
1145 Status
= NewFolder();
1146 if (Status
!= CAB_STATUS_SUCCESS
)
1148 CreateNewFolder
= FALSE
;
1151 /* Call OnAdd event handler */
1152 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1154 TotalBytesLeft
= FileNode
->File
.FileSize
;
1156 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1157 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1158 FileNode
->File
.FileControlID
= NextFolderNumber
- 1;
1159 CurrentFolderNode
->Commit
= TRUE
;
1160 PrevCabinetNumber
= CurrentDiskNumber
;
1162 Size
= sizeof(CFFILE
) + lstrlen(GetFileName(FileNode
->FileName
)) + 1;
1163 CABHeader
.FileTableOffset
+= Size
;
1164 TotalFileSize
+= Size
;
1168 FileNode
->Commit
= TRUE
;
1170 if (TotalBytesLeft
> 0) {
1172 if (TotalBytesLeft
> (DWORD
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1173 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1175 BytesToRead
= TotalBytesLeft
;
1177 if ((!ReadFile(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
, NULL
)
1178 != CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
1179 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%d) BytesRead (%d) CurrentIBufferSize (%d).\n",
1180 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1181 return CAB_STATUS_INVALID_CAB
;
1184 (PUCHAR
)CurrentIBuffer
+= BytesRead
;
1186 CurrentIBufferSize
+= (WORD
)BytesRead
;
1188 if (CurrentIBufferSize
== CAB_BLOCKSIZE
) {
1189 Status
= WriteDataBlock();
1190 if (Status
!= CAB_STATUS_SUCCESS
)
1193 TotalBytesLeft
-= BytesRead
;
1194 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1197 if (TotalBytesLeft
== 0) {
1198 CloseHandle(SourceFile
);
1199 FileNode
->Delete
= TRUE
;
1201 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) {
1202 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1203 CurrentFolderNode
->Delete
= TRUE
;
1205 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1206 Status
= WriteDataBlock();
1207 if (Status
!= CAB_STATUS_SUCCESS
)
1211 CreateNewFolder
= TRUE
;
1214 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1215 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1217 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1220 return CAB_STATUS_SUCCESS
;
1224 ULONG
CCabinet::WriteDisk(DWORD MoreDisks
)
1226 * FUNCTION: Forces the current disk to be written
1228 * MoreDisks = TRUE if there is one or more disks after this disk
1230 * Status of operation
1233 PCFFILE_NODE FileNode
;
1236 ContinueFile
= FALSE
;
1237 FileNode
= FileListHead
;
1238 while (FileNode
!= NULL
) {
1240 Status
= WriteFileToScratchStorage(FileNode
);
1241 if (Status
!= CAB_STATUS_SUCCESS
)
1243 if (CreateNewDisk
) {
1244 /* A data block could span more than two
1245 disks if MaxDiskSize is very small */
1246 while (CreateNewDisk
) {
1247 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1252 ContinueFile
= TRUE
;
1253 CreateNewDisk
= FALSE
;
1255 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%d) CurrentOBufferSize (%d).\n",
1256 CurrentIBufferSize
, CurrentOBufferSize
));
1258 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1259 Status
= WriteDataBlock();
1260 if (Status
!= CAB_STATUS_SUCCESS
)
1265 ContinueFile
= FALSE
;
1266 FileNode
= FileNode
->Next
;
1270 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1271 /* A data block could span more than two
1272 disks if MaxDiskSize is very small */
1274 ASSERT(CreateNewDisk
== FALSE
);
1277 if (CreateNewDisk
) {
1278 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1282 CreateNewDisk
= FALSE
;
1284 ASSERT(FileNode
== FileListHead
);
1287 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1288 Status
= WriteDataBlock();
1289 if (Status
!= CAB_STATUS_SUCCESS
)
1292 } while (CreateNewDisk
);
1294 CommitDisk(MoreDisks
);
1296 return CAB_STATUS_SUCCESS
;
1300 ULONG
CCabinet::CommitDisk(DWORD MoreDisks
)
1302 * FUNCTION: Commits the current disk
1304 * MoreDisks = TRUE if there is one or more disks after this disk
1306 * Status of operation
1309 PCFFOLDER_NODE FolderNode
;
1312 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1314 /* Create file, fail if it already exists */
1315 FileHandle
= CreateFile(CabinetName
, // Create this file
1316 GENERIC_WRITE
, // Open for writing
1318 NULL
, // No security
1319 CREATE_NEW
, // New file only
1320 FILE_ATTRIBUTE_NORMAL
, // Normal file
1321 NULL
); // No attribute template
1322 if (FileHandle
== INVALID_HANDLE_VALUE
) {
1324 /* If file exists, ask to overwrite file */
1325 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1326 (OnOverwrite(NULL
, CabinetName
))) {
1328 /* Create cabinet file, overwrite if it already exists */
1329 FileHandle
= CreateFile(CabinetName
, // Create this file
1330 GENERIC_WRITE
, // Open for writing
1332 NULL
, // No security
1333 TRUNCATE_EXISTING
, // Truncate the file
1334 FILE_ATTRIBUTE_NORMAL
, // Normal file
1335 NULL
); // No attribute template
1336 if (FileHandle
== INVALID_HANDLE_VALUE
)
1337 return CAB_STATUS_CANNOT_CREATE
;
1339 if (Status
== ERROR_FILE_EXISTS
)
1340 return CAB_STATUS_FILE_EXISTS
;
1342 return CAB_STATUS_CANNOT_CREATE
;
1346 WriteCabinetHeader(MoreDisks
);
1348 Status
= WriteFolderEntries();
1349 if (Status
!= CAB_STATUS_SUCCESS
)
1352 /* Write file entries */
1355 /* Write data blocks */
1356 FolderNode
= FolderListHead
;
1357 while (FolderNode
!= NULL
) {
1358 if (FolderNode
->Commit
) {
1359 Status
= CommitDataBlocks(FolderNode
);
1360 if (Status
!= CAB_STATUS_SUCCESS
)
1362 /* Remove data blocks for folder */
1363 DestroyDataNodes(FolderNode
);
1365 FolderNode
= FolderNode
->Next
;
1368 CloseHandle(FileHandle
);
1370 ScratchFile
->Truncate();
1372 return CAB_STATUS_SUCCESS
;
1376 ULONG
CCabinet::CloseDisk()
1378 * FUNCTION: Closes the current disk
1380 * Status of operation
1383 DestroyDeletedFileNodes();
1385 /* Destroy folder nodes that are completely stored */
1386 DestroyDeletedFolderNodes();
1388 CurrentDiskNumber
++;
1390 return CAB_STATUS_SUCCESS
;
1394 ULONG
CCabinet::CloseCabinet()
1396 * FUNCTION: Closes the current cabinet
1398 * Status of operation
1401 PCFFOLDER_NODE PrevNode
;
1402 PCFFOLDER_NODE NextNode
;
1407 DestroyFolderNodes();
1410 HeapFree(GetProcessHeap(), 0, InputBuffer
);
1415 HeapFree(GetProcessHeap(), 0, OutputBuffer
);
1416 OutputBuffer
= NULL
;
1422 Status
= ScratchFile
->Destroy();
1427 return CAB_STATUS_SUCCESS
;
1431 ULONG
CCabinet::AddFile(LPTSTR FileName
)
1433 * FUNCTION: Adds a file to the current disk
1435 * FileName = Pointer to string with file name (full path)
1437 * Status of operation
1442 PCFFILE_NODE FileNode
;
1444 FileNode
= NewFileNode();
1446 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1447 return CAB_STATUS_NOMEMORY
;
1450 FileNode
->FolderNode
= CurrentFolderNode
;
1452 FileNode
->FileName
= (LPTSTR
)HeapAlloc(GetProcessHeap(),
1453 0, lstrlen(FileName
) + 1);
1454 lstrcpy(FileNode
->FileName
, FileName
);
1456 /* Try to open file */
1457 SrcFile
= CreateFile(
1458 FileNode
->FileName
, // Open this file
1459 GENERIC_READ
, // Open for reading
1460 FILE_SHARE_READ
, // Share for reading
1461 NULL
, // No security
1462 OPEN_EXISTING
, // File must exist
1463 FILE_ATTRIBUTE_NORMAL
, // Normal file
1464 NULL
); // No attribute template
1465 if (SrcFile
== INVALID_HANDLE_VALUE
) {
1466 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1467 return CAB_STATUS_CANNOT_OPEN
;
1470 /* FIXME: Check for and handle large files (>= 2GB) */
1471 FileNode
->File
.FileSize
= GetFileSize(SrcFile
, NULL
);
1472 if (GetLastError() != NO_ERROR
) {
1473 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1474 return CAB_STATUS_CANNOT_READ
;
1477 if (GetFileTime(SrcFile
, NULL
, &FileTime
, NULL
))
1478 FileTimeToDosDateTime(&FileTime
,
1479 &FileNode
->File
.FileDate
,
1480 &FileNode
->File
.FileTime
);
1482 GetAttributesOnFile(FileNode
);
1484 CloseHandle(SrcFile
);
1486 return CAB_STATUS_SUCCESS
;
1490 VOID
CCabinet::SetMaxDiskSize(DWORD Size
)
1492 * FUNCTION: Sets the maximum size of the current disk
1494 * Size = Maximum size of current disk (0 means no maximum size)
1500 #endif /* CAB_READ_ONLY */
1503 /* Default event handlers */
1505 BOOL
CCabinet::OnOverwrite(PCFFILE File
,
1508 * FUNCTION: Called when extracting a file and it already exists
1510 * File = Pointer to CFFILE for file being extracted
1511 * FileName = Pointer to buffer with name of file (full path)
1513 * TRUE if the file should be overwritten, FALSE if not
1520 VOID
CCabinet::OnExtract(PCFFILE File
,
1523 * FUNCTION: Called just before extracting a file
1525 * File = Pointer to CFFILE for file being extracted
1526 * FileName = Pointer to buffer with name of file (full path)
1532 VOID
CCabinet::OnDiskChange(LPTSTR CabinetName
,
1535 * FUNCTION: Called when a new disk is to be processed
1537 * CabinetName = Pointer to buffer with name of cabinet
1538 * DiskLabel = Pointer to buffer with label of disk
1544 #ifndef CAB_READ_ONLY
1546 VOID
CCabinet::OnAdd(PCFFILE File
,
1549 * FUNCTION: Called just before adding a file to a cabinet
1551 * File = Pointer to CFFILE for file being added
1552 * FileName = Pointer to buffer with name of file (full path)
1558 BOOL
CCabinet::OnDiskLabel(ULONG Number
, LPTSTR Label
)
1560 * FUNCTION: Called when a disk needs a label
1562 * Number = Cabinet number that needs a label
1563 * Label = Pointer to buffer to place label of disk
1565 * TRUE if a disk label was returned, FALSE if not
1572 BOOL
CCabinet::OnCabinetName(ULONG Number
, LPTSTR Name
)
1574 * FUNCTION: Called when a cabinet needs a name
1576 * Number = Disk number that needs a name
1577 * Name = Pointer to buffer to place name of cabinet
1579 * TRUE if a cabinet name was returned, FALSE if not
1585 #endif /* CAB_READ_ONLY */
1587 PCFFOLDER_NODE
CCabinet::LocateFolderNode(DWORD Index
)
1589 * FUNCTION: Locates a folder node
1591 * Index = Folder index
1593 * Pointer to folder node or NULL if the folder node was not found
1596 PCFFOLDER_NODE Node
;
1599 case CAB_FILE_SPLIT
:
1600 return FolderListTail
;
1602 case CAB_FILE_CONTINUED
:
1603 case CAB_FILE_PREV_NEXT
:
1604 return FolderListHead
;
1607 Node
= FolderListHead
;
1608 while (Node
!= NULL
) {
1609 if (Node
->Index
== Index
)
1617 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
1619 * FUNCTION: Returns the absolute offset of a file
1621 * File = Pointer to CFFILE_NODE structure for file
1623 * Status of operation
1628 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1629 (LPTSTR
)File
->FileName
,
1630 (UINT
)File
->File
.FileOffset
,
1631 (UINT
)File
->File
.FileSize
));
1633 Node
= CurrentFolderNode
->DataListHead
;
1634 while (Node
!= NULL
) {
1636 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1637 (UINT
)Node
->UncompOffset
,
1638 (UINT
)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
1639 (UINT
)Node
->Data
.UncompSize
));
1641 /* Node->Data.UncompSize will be 0 if the block is split
1642 (ie. it is the last block in this cabinet) */
1643 if ((Node
->Data
.UncompSize
== 0) ||
1644 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
1645 (File
->File
.FileOffset
< Node
->UncompOffset
+
1646 Node
->Data
.UncompSize
))) {
1647 File
->DataBlock
= Node
;
1648 return CAB_STATUS_SUCCESS
;
1653 return CAB_STATUS_INVALID_CAB
;
1657 ULONG
CCabinet::LocateFile(LPTSTR FileName
,
1660 * FUNCTION: Locates a file in the cabinet
1662 * FileName = Pointer to string with name of file to locate
1663 * File = Address of pointer to CFFILE_NODE structure to fill
1665 * Status of operation
1667 * Current folder is set to the folder of the file
1673 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
1675 Node
= FileListHead
;
1676 while (Node
!= NULL
) {
1677 if (lstrcmpi(FileName
, Node
->FileName
) == 0) {
1679 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
1680 if (!CurrentFolderNode
) {
1681 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
1682 (UINT
)Node
->File
.FileControlID
));
1683 return CAB_STATUS_INVALID_CAB
;
1686 if (Node
->DataBlock
== NULL
) {
1687 Status
= GetAbsoluteOffset(Node
);
1689 Status
= CAB_STATUS_SUCCESS
;
1695 return CAB_STATUS_NOFILE
;
1699 ULONG
CCabinet::ReadString(LPTSTR String
, DWORD MaxLength
)
1701 * FUNCTION: Reads a NULL-terminated string from the cabinet
1703 * String = Pointer to buffer to place string
1704 * MaxLength = Maximum length of string
1706 * Status of operation
1718 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
1721 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
1722 return CAB_STATUS_INVALID_CAB
;
1725 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
1726 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
)) {
1727 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
1728 return CAB_STATUS_INVALID_CAB
;
1731 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++) {
1732 if (String
[Size
] == '\0') {
1738 Offset
+= BytesRead
;
1742 /* Back up some bytes */
1743 Size
= (BytesRead
- Size
) - 1;
1744 SetFilePointer(FileHandle
, -(LONG
)Size
, NULL
, FILE_CURRENT
);
1745 if (GetLastError() != NO_ERROR
) {
1746 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1747 return CAB_STATUS_INVALID_CAB
;
1749 return CAB_STATUS_SUCCESS
;
1753 ULONG
CCabinet::ReadFileTable()
1755 * FUNCTION: Reads the file table from the cabinet file
1757 * Status of operation
1765 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%X).\n",
1766 CABHeader
.FileTableOffset
));
1768 /* Seek to file table */
1769 SetFilePointer(FileHandle
, CABHeader
.FileTableOffset
, NULL
, FILE_BEGIN
);
1770 if (GetLastError() != NO_ERROR
) {
1771 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1772 return CAB_STATUS_INVALID_CAB
;
1775 for (i
= 0; i
< CABHeader
.FileCount
; i
++) {
1776 File
= NewFileNode();
1778 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1779 return CAB_STATUS_NOMEMORY
;
1782 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
1783 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
1784 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
1785 return CAB_STATUS_INVALID_CAB
;
1788 File
->FileName
= (LPTSTR
)HeapAlloc(GetProcessHeap(), 0, MAX_PATH
);
1789 if (!File
->FileName
) {
1790 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1791 return CAB_STATUS_NOMEMORY
;
1794 /* Read file name */
1795 Status
= ReadString(File
->FileName
, MAX_PATH
);
1796 if (Status
!= CAB_STATUS_SUCCESS
)
1799 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
1800 (LPTSTR
)File
->FileName
,
1801 (UINT
)File
->File
.FileOffset
,
1802 (UINT
)File
->File
.FileSize
,
1803 (UINT
)File
->File
.FileControlID
));
1806 return CAB_STATUS_SUCCESS
;
1810 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
1812 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1814 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1816 * Status of operation
1819 DWORD AbsoluteOffset
;
1826 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1827 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
1829 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
1830 UncompOffset
= FolderNode
->UncompOffset
;
1832 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++) {
1833 Node
= NewDataNode(FolderNode
);
1835 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1836 return CAB_STATUS_NOMEMORY
;
1839 /* Seek to data block */
1840 SetFilePointer(FileHandle
, AbsoluteOffset
, NULL
, FILE_BEGIN
);
1841 if (GetLastError() != NO_ERROR
) {
1842 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1843 return CAB_STATUS_INVALID_CAB
;
1846 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
1847 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
1848 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (UINT
)Status
));
1849 return CAB_STATUS_INVALID_CAB
;
1852 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1853 (UINT
)AbsoluteOffset
,
1855 (UINT
)Node
->Data
.Checksum
,
1856 (UINT
)Node
->Data
.CompSize
,
1857 (UINT
)Node
->Data
.UncompSize
));
1859 Node
->AbsoluteOffset
= AbsoluteOffset
;
1860 Node
->UncompOffset
= UncompOffset
;
1862 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
1863 UncompOffset
+= Node
->Data
.UncompSize
;
1866 FolderUncompSize
= UncompOffset
;
1868 return CAB_STATUS_SUCCESS
;
1872 PCFFOLDER_NODE
CCabinet::NewFolderNode()
1874 * FUNCTION: Creates a new folder node
1876 * Pointer to node if there was enough free memory available, otherwise NULL
1879 PCFFOLDER_NODE Node
;
1881 Node
= (PCFFOLDER_NODE
)HeapAlloc(GetProcessHeap(),
1882 0, sizeof(CFFOLDER_NODE
));
1886 ZeroMemory(Node
, sizeof(CFFOLDER_NODE
));
1888 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
1890 Node
->Prev
= FolderListTail
;
1892 if (FolderListTail
!= NULL
) {
1893 FolderListTail
->Next
= Node
;
1895 FolderListHead
= Node
;
1897 FolderListTail
= Node
;
1903 PCFFILE_NODE
CCabinet::NewFileNode()
1905 * FUNCTION: Creates a new file node
1907 * FolderNode = Pointer to folder node to bind file to
1909 * Pointer to node if there was enough free memory available, otherwise NULL
1914 Node
= (PCFFILE_NODE
)HeapAlloc(GetProcessHeap(),
1915 0, sizeof(CFFILE_NODE
));
1919 ZeroMemory(Node
, sizeof(CFFILE_NODE
));
1921 Node
->Prev
= FileListTail
;
1923 if (FileListTail
!= NULL
) {
1924 FileListTail
->Next
= Node
;
1926 FileListHead
= Node
;
1928 FileListTail
= Node
;
1934 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
1936 * FUNCTION: Creates a new data block node
1938 * FolderNode = Pointer to folder node to bind data block to
1940 * Pointer to node if there was enough free memory available, otherwise NULL
1945 Node
= (PCFDATA_NODE
)HeapAlloc(GetProcessHeap(),
1946 0, sizeof(CFDATA_NODE
));
1950 ZeroMemory(Node
, sizeof(CFDATA_NODE
));
1952 Node
->Prev
= FolderNode
->DataListTail
;
1954 if (FolderNode
->DataListTail
!= NULL
) {
1955 FolderNode
->DataListTail
->Next
= Node
;
1957 FolderNode
->DataListHead
= Node
;
1959 FolderNode
->DataListTail
= Node
;
1965 VOID
CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
1967 * FUNCTION: Destroys data block nodes bound to a folder node
1969 * FolderNode = Pointer to folder node
1972 PCFDATA_NODE PrevNode
;
1973 PCFDATA_NODE NextNode
;
1975 NextNode
= FolderNode
->DataListHead
;
1976 while (NextNode
!= NULL
) {
1977 PrevNode
= NextNode
->Next
;
1978 HeapFree(GetProcessHeap(), 0, NextNode
);
1979 NextNode
= PrevNode
;
1981 FolderNode
->DataListHead
= NULL
;
1982 FolderNode
->DataListTail
= NULL
;
1986 VOID
CCabinet::DestroyFileNodes()
1988 * FUNCTION: Destroys file nodes
1990 * FolderNode = Pointer to folder node
1993 PCFFILE_NODE PrevNode
;
1994 PCFFILE_NODE NextNode
;
1996 NextNode
= FileListHead
;
1997 while (NextNode
!= NULL
) {
1998 PrevNode
= NextNode
->Next
;
1999 if (NextNode
->FileName
)
2000 HeapFree(GetProcessHeap(), 0, NextNode
->FileName
);
2001 HeapFree(GetProcessHeap(), 0, NextNode
);
2002 NextNode
= PrevNode
;
2004 FileListHead
= NULL
;
2005 FileListTail
= NULL
;
2009 VOID
CCabinet::DestroyDeletedFileNodes()
2011 * FUNCTION: Destroys file nodes that are marked for deletion
2014 PCFFILE_NODE CurNode
;
2015 PCFFILE_NODE NextNode
;
2017 CurNode
= FileListHead
;
2018 while (CurNode
!= NULL
) {
2019 NextNode
= CurNode
->Next
;
2021 if (CurNode
->Delete
) {
2022 if (CurNode
->Prev
!= NULL
) {
2023 CurNode
->Prev
->Next
= CurNode
->Next
;
2025 FileListHead
= CurNode
->Next
;
2027 FileListHead
->Prev
= NULL
;
2030 if (CurNode
->Next
!= NULL
) {
2031 CurNode
->Next
->Prev
= CurNode
->Prev
;
2033 FileListTail
= CurNode
->Prev
;
2035 FileListTail
->Next
= NULL
;
2038 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2040 TotalFileSize
-= (sizeof(CFFILE
) + lstrlen(GetFileName(CurNode
->FileName
)) + 1);
2042 if (CurNode
->FileName
)
2043 HeapFree(GetProcessHeap(), 0, CurNode
->FileName
);
2044 HeapFree(GetProcessHeap(), 0, CurNode
);
2051 VOID
CCabinet::DestroyFolderNodes()
2053 * FUNCTION: Destroys folder nodes
2056 PCFFOLDER_NODE PrevNode
;
2057 PCFFOLDER_NODE NextNode
;
2059 NextNode
= FolderListHead
;
2060 while (NextNode
!= NULL
) {
2061 PrevNode
= NextNode
->Next
;
2062 DestroyDataNodes(NextNode
);
2063 HeapFree(GetProcessHeap(), 0, NextNode
);
2064 NextNode
= PrevNode
;
2066 FolderListHead
= NULL
;
2067 FolderListTail
= NULL
;
2071 VOID
CCabinet::DestroyDeletedFolderNodes()
2073 * FUNCTION: Destroys folder nodes that are marked for deletion
2076 PCFFOLDER_NODE CurNode
;
2077 PCFFOLDER_NODE NextNode
;
2079 CurNode
= FolderListHead
;
2080 while (CurNode
!= NULL
) {
2081 NextNode
= CurNode
->Next
;
2083 if (CurNode
->Delete
) {
2084 if (CurNode
->Prev
!= NULL
) {
2085 CurNode
->Prev
->Next
= CurNode
->Next
;
2087 FolderListHead
= CurNode
->Next
;
2089 FolderListHead
->Prev
= NULL
;
2092 if (CurNode
->Next
!= NULL
) {
2093 CurNode
->Next
->Prev
= CurNode
->Prev
;
2095 FolderListTail
= CurNode
->Prev
;
2097 FolderListTail
->Next
= NULL
;
2100 DestroyDataNodes(CurNode
);
2101 HeapFree(GetProcessHeap(), 0, CurNode
);
2103 TotalFolderSize
-= sizeof(CFFOLDER
);
2110 ULONG
CCabinet::ComputeChecksum(PVOID Buffer
,
2114 * FUNCTION: Computes checksum for data block
2116 * Buffer = Pointer to data buffer
2117 * Size = Length of data buffer
2118 * Seed = Previously computed checksum
2120 * Checksum of buffer
2123 INT UlongCount
; // Number of ULONGs in block
2124 ULONG Checksum
; // Checksum accumulator
2128 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2129 won't accept checksums computed by this routine */
2131 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%X) Size (%d)\n", (UINT
)Buffer
, Size
));
2133 UlongCount
= Size
/ 4; // Number of ULONGs
2134 Checksum
= Seed
; // Init checksum
2135 pb
= (PBYTE
)Buffer
; // Start at front of data block
2137 /* Checksum integral multiple of ULONGs */
2138 while (UlongCount
-- > 0) {
2139 /* NOTE: Build ULONG in big/little-endian independent manner */
2140 ul
= *pb
++; // Get low-order byte
2141 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2142 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2143 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2145 Checksum
^= ul
; // Update checksum
2148 /* Checksum remainder bytes */
2152 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2154 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2156 ul
|= *pb
++; // Get low-order byte
2160 Checksum
^= ul
; // Update checksum
2162 /* Return computed checksum */
2167 ULONG
CCabinet::ReadBlock(PVOID Buffer
,
2171 * FUNCTION: Read a block of data from file
2173 * Buffer = Pointer to data buffer
2174 * Size = Length of data buffer
2175 * BytesRead = Pointer to DWORD that on return will contain
2176 * number of bytes read
2178 * Status of operation
2181 if (!ReadFile(FileHandle
, Buffer
, Size
, BytesRead
, NULL
))
2182 return CAB_STATUS_INVALID_CAB
;
2183 return CAB_STATUS_SUCCESS
;
2186 #ifndef CAB_READ_ONLY
2188 ULONG
CCabinet::InitCabinetHeader()
2190 * FUNCTION: Initializes cabinet header and optional fields
2192 * Status of operation
2195 PCFFOLDER_NODE FolderNode
;
2196 PCFFILE_NODE FileNode
;
2200 CABHeader
.FileTableOffset
= 0; // Not known yet
2201 CABHeader
.FolderCount
= 0; // Not known yet
2202 CABHeader
.FileCount
= 0; // Not known yet
2203 CABHeader
.Flags
= 0; // Not known yet
2205 CABHeader
.CabinetNumber
= CurrentDiskNumber
;
2207 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
))) {
2208 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2209 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2210 lstrcpy(CabinetPrev
, "");
2213 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
)) {
2214 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2215 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2216 lstrcpy(DiskNext
, "");
2221 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2223 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2225 /* Calculate size of name of previous cabinet */
2226 TotalSize
+= lstrlen(CabinetPrev
) + 1;
2228 /* Calculate size of label of previous disk */
2229 TotalSize
+= lstrlen(DiskPrev
) + 1;
2232 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2234 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2236 /* Calculate size of name of next cabinet */
2237 Size
= lstrlen(CabinetNext
) + 1;
2239 NextFieldsSize
= Size
;
2241 /* Calculate size of label of next disk */
2242 Size
= lstrlen(DiskNext
) + 1;
2244 NextFieldsSize
+= Size
;
2248 DiskSize
+= TotalSize
;
2250 /* FIXME: Add cabinet reserved area size */
2251 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2253 return CAB_STATUS_SUCCESS
;
2257 ULONG
CCabinet::WriteCabinetHeader(BOOL MoreDisks
)
2259 * FUNCTION: Writes the cabinet header and optional fields
2261 * MoreDisks = TRUE if next cabinet name should be included
2263 * Status of operation
2266 PCFFOLDER_NODE FolderNode
;
2267 PCFFILE_NODE FileNode
;
2272 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2273 Size
= TotalHeaderSize
;
2275 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2276 DiskSize
-= NextFieldsSize
;
2277 Size
= TotalHeaderSize
- NextFieldsSize
;
2280 /* Set absolute folder offsets */
2281 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2282 CABHeader
.FolderCount
= 0;
2283 FolderNode
= FolderListHead
;
2284 while (FolderNode
!= NULL
) {
2285 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2287 BytesWritten
+= FolderNode
->TotalFolderSize
;
2289 CABHeader
.FolderCount
++;
2291 FolderNode
= FolderNode
->Next
;
2294 /* Set absolute offset of file table */
2295 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2297 /* Count number of files to be committed */
2298 CABHeader
.FileCount
= 0;
2299 FileNode
= FileListHead
;
2300 while (FileNode
!= NULL
) {
2301 if (FileNode
->Commit
)
2302 CABHeader
.FileCount
++;
2303 FileNode
= FileNode
->Next
;
2306 CABHeader
.CabinetSize
= DiskSize
;
2309 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
)) {
2310 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2311 return CAB_STATUS_CANNOT_WRITE
;
2314 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2316 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2318 /* Write name of previous cabinet */
2319 Size
= lstrlen(CabinetPrev
) + 1;
2320 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
)) {
2321 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2322 return CAB_STATUS_CANNOT_WRITE
;
2325 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2327 /* Write label of previous disk */
2328 Size
= lstrlen(DiskPrev
) + 1;
2329 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
)) {
2330 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2331 return CAB_STATUS_CANNOT_WRITE
;
2335 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2337 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2339 /* Write name of next cabinet */
2340 Size
= lstrlen(CabinetNext
) + 1;
2341 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
)) {
2342 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2343 return CAB_STATUS_CANNOT_WRITE
;
2346 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2348 /* Write label of next disk */
2349 Size
= lstrlen(DiskNext
) + 1;
2350 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
)) {
2351 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2352 return CAB_STATUS_CANNOT_WRITE
;
2356 return CAB_STATUS_SUCCESS
;
2360 ULONG
CCabinet::WriteFolderEntries()
2362 * FUNCTION: Writes folder entries
2364 * Status of operation
2367 PCFFOLDER_NODE FolderNode
;
2370 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2372 FolderNode
= FolderListHead
;
2373 while (FolderNode
!= NULL
) {
2374 if (FolderNode
->Commit
) {
2376 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
2377 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
2379 if (!WriteFile(FileHandle
,
2380 &FolderNode
->Folder
,
2384 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2385 return CAB_STATUS_CANNOT_WRITE
;
2388 FolderNode
= FolderNode
->Next
;
2391 return CAB_STATUS_SUCCESS
;
2395 ULONG
CCabinet::WriteFileEntries()
2397 * FUNCTION: Writes file entries for all files
2399 * Status of operation
2406 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
2408 File
= FileListHead
;
2409 while (File
!= NULL
) {
2411 /* Remove any continued files that ends in this disk */
2412 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
2413 File
->Delete
= TRUE
;
2415 /* The file could end in the last (split) block and should therefore
2416 appear in the next disk too */
2418 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
2419 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
)) {
2420 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
2421 File
->Delete
= FALSE
;
2425 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%d) FileName (%s).\n",
2426 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
2428 if (!WriteFile(FileHandle
,
2433 return CAB_STATUS_CANNOT_WRITE
;
2435 if (!WriteFile(FileHandle
,
2436 GetFileName(File
->FileName
),
2437 lstrlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
2438 return CAB_STATUS_CANNOT_WRITE
;
2441 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
2448 return CAB_STATUS_SUCCESS
;
2452 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
2454 * FUNCTION: Writes data blocks to the cabinet
2456 * FolderNode = Pointer to folder node containing the data blocks
2458 * Status of operation
2461 PCFDATA_NODE DataNode
;
2466 DataNode
= FolderNode
->DataListHead
;
2467 if (DataNode
!= NULL
)
2468 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
2470 while (DataNode
!= NULL
) {
2471 DPRINT(MAX_TRACE
, ("Reading block at (0x%X) CompSize (%d) UncompSize (%d).\n",
2472 DataNode
->ScratchFilePosition
,
2473 DataNode
->Data
.CompSize
,
2474 DataNode
->Data
.UncompSize
));
2476 /* InputBuffer is free for us to use here, so we use it and avoid a
2477 memory allocation. OutputBuffer can't be used here because it may
2478 still contain valid data (if a data block spans two or more disks) */
2479 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
2480 if (Status
!= CAB_STATUS_SUCCESS
) {
2481 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (UINT
)Status
));
2485 if (!WriteFile(FileHandle
, &DataNode
->Data
,
2486 sizeof(CFDATA
), &BytesWritten
, NULL
)) {
2487 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2488 return CAB_STATUS_CANNOT_WRITE
;
2491 if (!WriteFile(FileHandle
, InputBuffer
,
2492 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
)) {
2493 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2494 return CAB_STATUS_CANNOT_WRITE
;
2497 DataNode
= DataNode
->Next
;
2499 return CAB_STATUS_SUCCESS
;
2503 ULONG
CCabinet::WriteDataBlock()
2505 * FUNCTION: Writes the current data block to the scratch file
2507 * Status of operation
2512 PCFDATA_NODE DataNode
;
2514 if (!BlockIsSplit
) {
2515 Status
= Codec
->Compress(OutputBuffer
,
2520 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%d) TotalCompSize(%d).\n",
2521 CurrentIBufferSize
, TotalCompSize
));
2523 CurrentOBuffer
= OutputBuffer
;
2524 CurrentOBufferSize
= TotalCompSize
;
2527 DataNode
= NewDataNode(CurrentFolderNode
);
2529 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2530 return CAB_STATUS_NOMEMORY
;
2533 DiskSize
+= sizeof(CFDATA
);
2535 if (MaxDiskSize
> 0)
2536 /* Disk size is limited */
2537 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
2539 BlockIsSplit
= FALSE
;
2542 DataNode
->Data
.CompSize
= (WORD
)(MaxDiskSize
- DiskSize
);
2543 DataNode
->Data
.UncompSize
= 0;
2544 CreateNewDisk
= TRUE
;
2546 DataNode
->Data
.CompSize
= (WORD
)CurrentOBufferSize
;
2547 DataNode
->Data
.UncompSize
= (WORD
)CurrentIBufferSize
;
2550 DataNode
->Data
.Checksum
= 0;
2551 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
2553 // FIXME: MAKECAB.EXE does not like this checksum algorithm
2554 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
2556 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2557 (UINT
)DataNode
->Data
.Checksum
,
2558 (UINT
)DataNode
->Data
.CompSize
,
2559 (UINT
)DataNode
->Data
.UncompSize
));
2561 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
2562 CurrentOBuffer
, &BytesWritten
);
2563 if (Status
!= CAB_STATUS_SUCCESS
)
2566 DiskSize
+= BytesWritten
;
2568 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
2569 CurrentFolderNode
->Folder
.DataBlockCount
++;
2571 (PUCHAR
)CurrentOBuffer
+= DataNode
->Data
.CompSize
;
2572 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
2574 LastBlockStart
+= DataNode
->Data
.UncompSize
;
2576 if (!BlockIsSplit
) {
2577 CurrentIBufferSize
= 0;
2578 CurrentIBuffer
= InputBuffer
;
2581 return CAB_STATUS_SUCCESS
;
2585 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
2587 * FUNCTION: Returns attributes on a file
2589 * File = Pointer to CFFILE node for file
2591 * Status of operation
2596 Attributes
= GetFileAttributes(File
->FileName
);
2597 if (Attributes
== -1)
2598 return CAB_STATUS_CANNOT_READ
;
2600 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
2601 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
2603 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
2604 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
2606 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
2607 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
2609 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
2610 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
2612 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
2613 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
2615 return CAB_STATUS_SUCCESS
;
2619 ULONG
CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
2621 * FUNCTION: Sets attributes on a file
2623 * File = Pointer to CFFILE node for file
2625 * Status of operation
2628 DWORD Attributes
= 0;
2630 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
2631 Attributes
|= FILE_ATTRIBUTE_READONLY
;
2633 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
2634 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
2636 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
2637 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
2639 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
2640 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
2642 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
2643 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
2645 SetFileAttributes(File
->FileName
, Attributes
);
2647 return CAB_STATUS_SUCCESS
;
2650 #endif /* CAB_READ_ONLY */