2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cpp
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Colin Finck <mail@colinfinck.de>
8 * NOTES: Define CAB_READ_ONLY for read only version
10 * CSH 21/03-2001 Created
11 * CSH 15/08-2003 Made it portable
12 * CF 04/05-2007 Made it compatible with 64-bit operating systems
14 * - Checksum of datablocks should be calculated
15 * - EXTRACT.EXE complains if a disk is created manually
16 * - Folders that are created manually and span disks will result in a damaged cabinet
23 # include <sys/stat.h>
24 # include <sys/types.h>
31 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
32 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
34 ULONG size
= GetFileSize(handle
, NULL
);
35 if (size
== INVALID_FILE_SIZE
)
40 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
41 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
43 return ReadFile(handle
, buffer
, size
, (LPDWORD
)bytesread
, NULL
) != 0;
46 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
47 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
50 fseek(handle
, 0, SEEK_END
);
52 fseek(handle
, 0, SEEK_SET
);
55 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
56 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
58 *bytesread
= fread(buffer
, 1, size
, handle
);
59 return *bytesread
== size
;
68 void DumpBuffer(void* Buffer
, ULONG Size
)
70 FILEHANDLE FileHandle
;
73 /* Create file, overwrite if it already exists */
74 FileHandle
= CreateFile("dump.bin", // Create this file
75 GENERIC_WRITE
, // Open for writing
78 CREATE_ALWAYS
, // Create or overwrite
79 FILE_ATTRIBUTE_NORMAL
, // Normal file
80 NULL
); // No attribute template
81 if (FileHandle
== INVALID_HANDLE_VALUE
)
83 DPRINT(MID_TRACE
, ("ERROR OPENING '%u'.\n", (UINT
)GetLastError()));
87 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
))
89 DPRINT(MID_TRACE
, ("ERROR WRITING '%u'.\n", (UINT
)GetLastError()));
92 CloseFile(FileHandle
);
100 CCFDATAStorage::CCFDATAStorage()
102 * FUNCTION: Default constructor
109 CCFDATAStorage::~CCFDATAStorage()
111 * FUNCTION: Default destructor
114 ASSERT(!FileCreated
);
118 ULONG
CCFDATAStorage::Create(const char* FileName
)
120 * FUNCTION: Creates the file
122 * FileName = Pointer to name of file
124 * Status of operation
127 ASSERT(!FileCreated
);
130 if (GetTempPath(MAX_PATH
, FullName
) == 0)
131 return CAB_STATUS_CANNOT_CREATE
;
133 strcat(FullName
, FileName
);
135 /* Create file, overwrite if it already exists */
136 FileHandle
= CreateFile(FullName
, // Create this file
137 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
140 CREATE_ALWAYS
, // Create or overwrite
141 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
142 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
143 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
144 NULL
); // No attribute template
145 if (FileHandle
== INVALID_HANDLE_VALUE
)
147 DPRINT(MID_TRACE
, ("ERROR '%u'.\n", (UINT
)GetLastError()));
148 return CAB_STATUS_CANNOT_CREATE
;
151 /*if (tmpnam(FullName) == NULL)*/
152 if ((FileHandle
= tmpfile()) == NULL
)
153 return CAB_STATUS_CANNOT_CREATE
;
155 FileHandle = fopen(FullName, "w+b");
156 if (FileHandle == NULL) {
157 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno));
158 return CAB_STATUS_CANNOT_CREATE;
165 return CAB_STATUS_SUCCESS
;
169 ULONG
CCFDATAStorage::Destroy()
171 * FUNCTION: Destroys the file
173 * Status of operation
178 CloseFile(FileHandle
);
182 return CAB_STATUS_SUCCESS
;
186 ULONG
CCFDATAStorage::Truncate()
188 * FUNCTION: Truncate the scratch file to zero bytes
190 * Status of operation
194 if( SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
195 return CAB_STATUS_FAILURE
;
196 if (!SetEndOfFile(FileHandle
))
197 return CAB_STATUS_FAILURE
;
200 FileHandle
= tmpfile();
201 if (FileHandle
== NULL
)
203 DPRINT(MID_TRACE
, ("ERROR '%i'.\n", errno
));
204 return CAB_STATUS_FAILURE
;
207 return CAB_STATUS_SUCCESS
;
211 ULONG
CCFDATAStorage::Position()
213 * FUNCTION: Returns current position in file
219 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
221 return (ULONG
)ftell(FileHandle
);
226 ULONG
CCFDATAStorage::Seek(LONG Position
)
228 * FUNCTION: Seeks to an absolute position
230 * Position = Absolute position to seek to
232 * Status of operation
236 if( SetFilePointer(FileHandle
,
239 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
240 return CAB_STATUS_FAILURE
;
242 return CAB_STATUS_SUCCESS
;
244 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0)
245 return CAB_STATUS_FAILURE
;
247 return CAB_STATUS_SUCCESS
;
252 ULONG
CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, PULONG BytesRead
)
254 * FUNCTION: Reads a CFDATA block from the file
256 * Data = Pointer to CFDATA block for the buffer
257 * Buffer = Pointer to buffer to store data read
258 * BytesWritten = Pointer to buffer to write number of bytes read
260 * Status of operation
264 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesRead
, NULL
))
265 return CAB_STATUS_CANNOT_READ
;
268 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
269 if (*BytesRead
!= Data
->CompSize
)
270 return CAB_STATUS_CANNOT_READ
;
272 return CAB_STATUS_SUCCESS
;
276 ULONG
CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, PULONG BytesWritten
)
278 * FUNCTION: Writes a CFDATA block to the file
280 * Data = Pointer to CFDATA block for the buffer
281 * Buffer = Pointer to buffer with data to write
282 * BytesWritten = Pointer to buffer to write number of bytes written
284 * Status of operation
288 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesWritten
, NULL
))
289 return CAB_STATUS_CANNOT_WRITE
;
291 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
292 if (*BytesWritten
!= Data
->CompSize
)
293 return CAB_STATUS_CANNOT_WRITE
;
295 return CAB_STATUS_SUCCESS
;
298 #endif /* CAB_READ_ONLY */
305 * FUNCTION: Default constructor
314 *CabinetReservedFile
= '\0';
317 CabinetReservedFileBuffer
= NULL
;
318 CabinetReservedFileSize
= 0;
320 FolderListHead
= NULL
;
321 FolderListTail
= NULL
;
324 CriteriaListHead
= NULL
;
325 CriteriaListTail
= NULL
;
329 CodecSelected
= false;
334 BlockIsSplit
= false;
337 FolderUncompSize
= 0;
338 BytesLeftInBlock
= 0;
340 CurrentDataNode
= NULL
;
344 CCabinet::~CCabinet()
346 * FUNCTION: Default destructor
349 if (CabinetReservedFileBuffer
!= NULL
)
351 FreeMemory(CabinetReservedFileBuffer
);
352 CabinetReservedFileBuffer
= NULL
;
353 CabinetReservedFileSize
= 0;
360 bool CCabinet::IsSeparator(char Char
)
362 * FUNCTION: Determines if a character is a separator
364 * Char = Character to check
366 * Whether it is a separator
369 if ((Char
== '\\') || (Char
== '/'))
375 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
377 * FUNCTION: Replaces \ or / with the one used by the host environment
379 * Path = Pointer to string with pathname
380 * Allocate = Specifies whether to allocate memory for the new
381 * string or to change the existing buffer
383 * Pointer to new path
390 newpath
= strdup(Path
);
406 newpath
[i
] = Path
[i
];
416 char* CCabinet::GetFileName(char* Path
)
418 * FUNCTION: Returns a pointer to file name
420 * Path = Pointer to string with pathname
422 * Pointer to filename
427 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
430 if (IsSeparator(Path
[i
- 1]))
437 void CCabinet::RemoveFileName(char* Path
)
439 * FUNCTION: Removes a file name from a path
441 * Path = Pointer to string with path
447 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
448 FileName
= GetFileName(Path
+ i
);
450 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
452 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
458 bool CCabinet::NormalizePath(char* Path
,
461 * FUNCTION: Normalizes a path
463 * Path = Pointer to string with pathname
464 * Length = Number of bytes in Path
466 * true if there was enough room in Path, or false
472 if ((n
= (ULONG
)strlen(Path
)) &&
473 (!IsSeparator(Path
[n
- 1])) &&
474 (OK
= ((n
+ 1) < Length
)))
476 Path
[n
] = DIR_SEPARATOR_CHAR
;
483 char* CCabinet::GetCabinetName()
485 * FUNCTION: Returns pointer to cabinet file name
487 * Pointer to string with name of cabinet
494 void CCabinet::SetCabinetName(char* FileName
)
496 * FUNCTION: Sets cabinet file name
498 * FileName = Pointer to string with name of cabinet
501 strcpy(CabinetName
, FileName
);
505 void CCabinet::SetDestinationPath(char* DestinationPath
)
507 * FUNCTION: Sets destination path
509 * DestinationPath = Pointer to string with name of destination path
512 strcpy(DestPath
, DestinationPath
);
513 ConvertPath(DestPath
, false);
514 if (strlen(DestPath
) > 0)
515 NormalizePath(DestPath
, MAX_PATH
);
518 ULONG
CCabinet::AddSearchCriteria(char* SearchCriteria
)
520 * FUNCTION: Adds a criteria to the search criteria list
522 * SearchCriteria = String with the search criteria to add
524 * Status of operation
527 PSEARCH_CRITERIA Criteria
;
529 // Add the criteria to the list of search criteria
530 Criteria
= (PSEARCH_CRITERIA
)AllocateMemory(sizeof(SEARCH_CRITERIA
));
533 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
534 return CAB_STATUS_NOMEMORY
;
537 Criteria
->Prev
= CriteriaListTail
;
538 Criteria
->Next
= NULL
;
541 CriteriaListTail
->Next
= Criteria
;
543 CriteriaListHead
= Criteria
;
545 CriteriaListTail
= Criteria
;
547 // Set the actual criteria string
548 Criteria
->Search
= (char*)AllocateMemory(strlen(SearchCriteria
) + 1);
549 if (!Criteria
->Search
)
551 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
552 return CAB_STATUS_NOMEMORY
;
555 strcpy(Criteria
->Search
, SearchCriteria
);
557 return CAB_STATUS_SUCCESS
;
560 void CCabinet::DestroySearchCriteria()
562 * FUNCTION: Destroys the list with the search criteria
565 PSEARCH_CRITERIA Criteria
;
566 PSEARCH_CRITERIA NextCriteria
;
568 Criteria
= CriteriaListHead
;
572 NextCriteria
= Criteria
->Next
;
574 FreeMemory(Criteria
->Search
);
575 FreeMemory(Criteria
);
577 Criteria
= NextCriteria
;
580 CriteriaListHead
= NULL
;
581 CriteriaListTail
= NULL
;
584 bool CCabinet::HasSearchCriteria()
586 * FUNCTION: Returns whether we have search criteria
588 * Whether we have search criteria or not.
591 return (CriteriaListHead
!= NULL
);
594 bool CCabinet::SetCompressionCodec(char* CodecName
)
596 * FUNCTION: Selects the codec to use for compression
598 * CodecName = Pointer to a string with the name of the codec
601 if( !strcasecmp(CodecName
, "raw") )
602 SelectCodec(CAB_CODEC_RAW
);
603 else if( !strcasecmp(CodecName
, "mszip") )
604 SelectCodec(CAB_CODEC_MSZIP
);
607 printf("Invalid codec specified!\n");
614 char* CCabinet::GetDestinationPath()
616 * FUNCTION: Returns destination path
618 * Pointer to string with name of destination path
625 bool CCabinet::SetCabinetReservedFile(char* FileName
)
627 * FUNCTION: Sets cabinet reserved file
629 * FileName = Pointer to string with name of cabinet reserved file
632 FILEHANDLE FileHandle
;
636 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
637 GENERIC_READ
, // Open for reading
638 FILE_SHARE_READ
, // Share for reading
640 OPEN_EXISTING
, // Existing file only
641 FILE_ATTRIBUTE_NORMAL
, // Normal file
642 NULL
); // No attribute template
643 if (FileHandle
== INVALID_HANDLE_VALUE
)
645 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
649 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
650 if (FileHandle
== NULL
)
652 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
657 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
658 if (CabinetReservedFileSize
== (ULONG
)-1)
660 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
664 if (CabinetReservedFileSize
== 0)
666 CloseFile(FileHandle
);
670 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
671 if (!CabinetReservedFileBuffer
)
673 CloseFile(FileHandle
);
677 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
))
679 CloseFile(FileHandle
);
683 CloseFile(FileHandle
);
685 strcpy(CabinetReservedFile
, FileName
);
691 char* CCabinet::GetCabinetReservedFile()
693 * FUNCTION: Returns cabionet reserved file
695 * Pointer to string with name of cabinet reserved file
698 return CabinetReservedFile
;
702 ULONG
CCabinet::GetCurrentDiskNumber()
704 * FUNCTION: Returns current disk number
706 * Current disk number
709 return CurrentDiskNumber
;
713 ULONG
CCabinet::Open()
715 * FUNCTION: Opens a cabinet file
717 * Status of operation
720 PCFFOLDER_NODE FolderNode
;
729 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
731 return CAB_STATUS_NOMEMORY
;
734 FileHandle
= CreateFile(CabinetName
, // Open this file
735 GENERIC_READ
, // Open for reading
736 FILE_SHARE_READ
, // Share for reading
738 OPEN_EXISTING
, // Existing file only
739 FILE_ATTRIBUTE_NORMAL
, // Normal file
740 NULL
); // No attribute template
742 if (FileHandle
== INVALID_HANDLE_VALUE
)
744 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
745 return CAB_STATUS_CANNOT_OPEN
;
748 FileHandle
= fopen(CabinetName
, "rb");
749 if (FileHandle
== NULL
)
751 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
752 return CAB_STATUS_CANNOT_OPEN
;
758 /* Load CAB header */
759 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
760 != CAB_STATUS_SUCCESS
)
762 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
763 return CAB_STATUS_INVALID_CAB
;
767 if ((BytesRead
!= sizeof(CFHEADER
)) ||
768 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
769 (CABHeader
.Version
!= CAB_VERSION
) ||
770 (CABHeader
.FolderCount
== 0 ) ||
771 (CABHeader
.FileCount
== 0 ) ||
772 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
)))
775 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
776 return CAB_STATUS_INVALID_CAB
;
781 /* Read/skip any reserved bytes */
782 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
784 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
))
785 != CAB_STATUS_SUCCESS
)
787 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
788 return CAB_STATUS_INVALID_CAB
;
790 CabinetReserved
= Size
& 0xFFFF;
791 FolderReserved
= (Size
>> 16) & 0xFF;
792 DataReserved
= (Size
>> 24) & 0xFF;
795 if (SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
797 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
798 return CAB_STATUS_FAILURE
;
801 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0)
803 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
804 return CAB_STATUS_FAILURE
;
809 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
811 /* Read name of previous cabinet */
812 Status
= ReadString(CabinetPrev
, 256);
813 if (Status
!= CAB_STATUS_SUCCESS
)
815 /* Read label of previous disk */
816 Status
= ReadString(DiskPrev
, 256);
817 if (Status
!= CAB_STATUS_SUCCESS
)
822 strcpy(CabinetPrev
, "");
823 strcpy(DiskPrev
, "");
826 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
828 /* Read name of next cabinet */
829 Status
= ReadString(CabinetNext
, 256);
830 if (Status
!= CAB_STATUS_SUCCESS
)
832 /* Read label of next disk */
833 Status
= ReadString(DiskNext
, 256);
834 if (Status
!= CAB_STATUS_SUCCESS
)
839 strcpy(CabinetNext
, "");
840 strcpy(DiskNext
, "");
843 /* Read all folders */
844 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
846 FolderNode
= NewFolderNode();
849 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
850 return CAB_STATUS_NOMEMORY
;
854 FolderNode
->UncompOffset
= FolderUncompSize
;
856 FolderNode
->Index
= Index
;
858 if ((Status
= ReadBlock(&FolderNode
->Folder
,
859 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
861 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
862 return CAB_STATUS_INVALID_CAB
;
866 /* Read file entries */
867 Status
= ReadFileTable();
868 if (Status
!= CAB_STATUS_SUCCESS
)
870 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%u).\n", (UINT
)Status
));
874 /* Read data blocks for all folders */
875 FolderNode
= FolderListHead
;
876 while (FolderNode
!= NULL
)
878 Status
= ReadDataBlocks(FolderNode
);
879 if (Status
!= CAB_STATUS_SUCCESS
)
881 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%u).\n", (UINT
)Status
));
884 FolderNode
= FolderNode
->Next
;
887 return CAB_STATUS_SUCCESS
;
891 void CCabinet::Close()
893 * FUNCTION: Closes the cabinet file
898 CloseFile(FileHandle
);
904 ULONG
CCabinet::FindFirst(PCAB_SEARCH Search
)
906 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
908 * Search = Pointer to search structure
910 * Status of operation
913 RestartSearch
= false;
914 Search
->Next
= FileListHead
;
915 return FindNext(Search
);
919 ULONG
CCabinet::FindNext(PCAB_SEARCH Search
)
921 * FUNCTION: Finds next file in the cabinet that matches a search criteria
923 * Search = Pointer to search structure
925 * Status of operation
929 PSEARCH_CRITERIA Criteria
;
934 Search
->Next
= FileListHead
;
936 /* Skip split files already extracted */
937 while ((Search
->Next
) &&
938 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
939 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
941 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
942 Search
->Next
->FileName
, (UINT
)Search
->Next
->File
.FileOffset
, (UINT
)LastFileOffset
));
943 Search
->Next
= Search
->Next
->Next
;
946 RestartSearch
= false;
949 /* Check each search criteria against each file */
952 // Some features (like displaying cabinets) don't require search criteria, so we can just break here.
953 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx".
954 if(!CriteriaListHead
)
957 Criteria
= CriteriaListHead
;
961 if(MatchFileNamePattern(Search
->Next
->FileName
, Criteria
->Search
))
967 Criteria
= Criteria
->Next
;
973 Search
->Next
= Search
->Next
->Next
;
978 if (strlen(DiskNext
) > 0)
982 SetCabinetName(CabinetNext
);
984 OnDiskChange(CabinetNext
, DiskNext
);
987 if (Status
!= CAB_STATUS_SUCCESS
)
990 Search
->Next
= FileListHead
;
992 return CAB_STATUS_NOFILE
;
995 return CAB_STATUS_NOFILE
;
998 Search
->File
= &Search
->Next
->File
;
999 Search
->FileName
= Search
->Next
->FileName
;
1000 Search
->Next
= Search
->Next
->Next
;
1001 return CAB_STATUS_SUCCESS
;
1005 ULONG
CCabinet::ExtractFile(char* FileName
)
1007 * FUNCTION: Extracts a file from the cabinet
1009 * FileName = Pointer to buffer with name of file
1011 * Status of operation
1021 ULONG TotalBytesRead
;
1022 ULONG CurrentOffset
;
1024 PUCHAR CurrentBuffer
;
1025 FILEHANDLE DestFile
;
1033 CHAR DestName
[MAX_PATH
];
1034 CHAR TempName
[MAX_PATH
];
1036 Status
= LocateFile(FileName
, &File
);
1037 if (Status
!= CAB_STATUS_SUCCESS
)
1039 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1043 LastFileOffset
= File
->File
.FileOffset
;
1045 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
1048 SelectCodec(CAB_CODEC_RAW
);
1051 case CAB_COMP_MSZIP
:
1052 SelectCodec(CAB_CODEC_MSZIP
);
1056 return CAB_STATUS_UNSUPPCOMP
;
1059 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1060 (UINT
)File
->File
.FileOffset
,
1061 (UINT
)File
->File
.FileSize
,
1062 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1063 (UINT
)File
->DataBlock
->UncompOffset
));
1065 strcpy(DestName
, DestPath
);
1066 strcat(DestName
, FileName
);
1068 /* Create destination file, fail if it already exists */
1070 DestFile
= CreateFile(DestName
, // Create this file
1071 GENERIC_WRITE
, // Open for writing
1073 NULL
, // No security
1074 CREATE_NEW
, // New file only
1075 FILE_ATTRIBUTE_NORMAL
, // Normal file
1076 NULL
); // No attribute template
1077 if (DestFile
== INVALID_HANDLE_VALUE
)
1079 /* If file exists, ask to overwrite file */
1080 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1081 (OnOverwrite(&File
->File
, FileName
)))
1083 /* Create destination file, overwrite if it already exists */
1084 DestFile
= CreateFile(DestName
, // Create this file
1085 GENERIC_WRITE
, // Open for writing
1087 NULL
, // No security
1088 TRUNCATE_EXISTING
, // Truncate the file
1089 FILE_ATTRIBUTE_NORMAL
, // Normal file
1090 NULL
); // No attribute template
1091 if (DestFile
== INVALID_HANDLE_VALUE
)
1092 return CAB_STATUS_CANNOT_CREATE
;
1096 if (Status
== ERROR_FILE_EXISTS
)
1097 return CAB_STATUS_FILE_EXISTS
;
1099 return CAB_STATUS_CANNOT_CREATE
;
1103 DestFile
= fopen(DestName
, "rb");
1104 if (DestFile
!= NULL
)
1107 /* If file exists, ask to overwrite file */
1108 if (OnOverwrite(&File
->File
, FileName
))
1110 DestFile
= fopen(DestName
, "w+b");
1111 if (DestFile
== NULL
)
1112 return CAB_STATUS_CANNOT_CREATE
;
1115 return CAB_STATUS_FILE_EXISTS
;
1119 DestFile
= fopen(DestName
, "w+b");
1120 if (DestFile
== NULL
)
1121 return CAB_STATUS_CANNOT_CREATE
;
1125 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1127 CloseFile(DestFile
);
1128 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%u).\n", (UINT
)GetLastError()));
1129 return CAB_STATUS_CANNOT_WRITE
;
1132 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
1134 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1136 SetAttributesOnFile(DestName
, File
->File
.Attributes
);
1138 Buffer
= (PUCHAR
)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1141 CloseFile(DestFile
);
1142 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1143 return CAB_STATUS_NOMEMORY
;
1146 /* Call OnExtract event handler */
1147 OnExtract(&File
->File
, FileName
);
1149 /* Search to start of file */
1151 Offset
= SetFilePointer(FileHandle
,
1152 File
->DataBlock
->AbsoluteOffset
,
1155 if (Offset
== INVALID_SET_FILE_POINTER
)
1157 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1158 return CAB_STATUS_INVALID_CAB
;
1161 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1163 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1164 return CAB_STATUS_FAILURE
;
1166 Offset
= ftell(FileHandle
);
1169 Size
= File
->File
.FileSize
;
1170 Offset
= File
->File
.FileOffset
;
1171 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1175 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1180 DPRINT(MAX_TRACE
, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1181 (UINT
)File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, (UINT
)Offset
, (UINT
)Size
,
1182 (UINT
)BytesLeftInBlock
));
1184 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1186 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1188 CurrentBuffer
= Buffer
;
1192 DPRINT(MAX_TRACE
, ("Size (%u bytes).\n", (UINT
)Size
));
1194 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1195 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1197 CloseFile(DestFile
);
1199 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1200 return CAB_STATUS_INVALID_CAB
;
1203 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n",
1204 (UINT
)CFData
.Checksum
,
1206 CFData
.UncompSize
));
1208 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1210 BytesToRead
= CFData
.CompSize
;
1212 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1213 (unsigned long)CurrentBuffer
, (unsigned long)Buffer
));
1215 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1216 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1218 CloseFile(DestFile
);
1220 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1221 return CAB_STATUS_INVALID_CAB
;
1224 /* FIXME: Does not work with files generated by makecab.exe */
1226 if (CFData.Checksum != 0)
1228 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1229 if (Checksum != CFData.Checksum)
1231 CloseFile(DestFile);
1233 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1234 Checksum, CFData.Checksum));
1235 return CAB_STATUS_INVALID_CAB;
1239 TotalBytesRead
+= BytesRead
;
1241 CurrentBuffer
+= BytesRead
;
1243 if (CFData
.UncompSize
== 0)
1245 if (strlen(DiskNext
) == 0)
1246 return CAB_STATUS_NOFILE
;
1248 /* CloseCabinet() will destroy all file entries so in case
1249 FileName refers to the FileName field of a CFFOLDER_NODE
1250 structure, we have to save a copy of the filename */
1251 strcpy(TempName
, FileName
);
1255 SetCabinetName(CabinetNext
);
1257 OnDiskChange(CabinetNext
, DiskNext
);
1260 if (Status
!= CAB_STATUS_SUCCESS
)
1263 /* The first data block of the file will not be
1264 found as it is located in the previous file */
1265 Status
= LocateFile(TempName
, &File
);
1266 if (Status
== CAB_STATUS_NOFILE
)
1268 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1272 /* The file is continued in the first data block in the folder */
1273 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1275 /* Search to start of file */
1277 if( SetFilePointer(FileHandle
,
1278 File
->DataBlock
->AbsoluteOffset
,
1280 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1282 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1283 return CAB_STATUS_INVALID_CAB
;
1286 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1288 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1289 return CAB_STATUS_INVALID_CAB
;
1293 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1294 (UINT
)File
->File
.FileOffset
,
1295 (UINT
)File
->File
.FileSize
,
1296 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1297 (UINT
)File
->DataBlock
->UncompOffset
));
1299 CurrentDataNode
= File
->DataBlock
;
1302 RestartSearch
= true;
1304 } while (CFData
.UncompSize
== 0);
1306 DPRINT(MAX_TRACE
, ("TotalBytesRead (%u).\n", (UINT
)TotalBytesRead
));
1308 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1309 if (Status
!= CS_SUCCESS
)
1311 CloseFile(DestFile
);
1313 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1314 if (Status
== CS_NOMEMORY
)
1315 return CAB_STATUS_NOMEMORY
;
1316 return CAB_STATUS_INVALID_CAB
;
1319 if (BytesToWrite
!= CFData
.UncompSize
)
1321 DPRINT(MID_TRACE
, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n",
1322 (UINT
)BytesToWrite
, CFData
.UncompSize
));
1323 return CAB_STATUS_INVALID_CAB
;
1326 BytesLeftInBlock
= BytesToWrite
;
1330 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1332 BytesToWrite
= BytesLeftInBlock
;
1334 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%X.\n",
1335 (UINT
)(CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) + CurrentDataNode
->Data
.CompSize
)));
1337 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1338 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1340 CloseFile(DestFile
);
1342 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1343 return CAB_STATUS_INVALID_CAB
;
1346 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1347 CFData
.CompSize
, CFData
.UncompSize
));
1349 /* Go to next data block */
1351 if( SetFilePointer(FileHandle
,
1352 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1353 CurrentDataNode
->Data
.CompSize
,
1355 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1357 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1358 return CAB_STATUS_INVALID_CAB
;
1361 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1362 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1364 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1365 return CAB_STATUS_INVALID_CAB
;
1373 BytesSkipped
= (Offset
- CurrentOffset
);
1377 BytesToWrite
-= BytesSkipped
;
1379 if (Size
< BytesToWrite
)
1380 BytesToWrite
= Size
;
1382 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n",
1384 (UINT
)CurrentOffset
,
1386 (UINT
)BytesSkipped
, (UINT
)Skip
,
1390 if (!WriteFile(DestFile
, (void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1391 BytesToWrite
, (LPDWORD
)&BytesWritten
, NULL
) ||
1392 (BytesToWrite
!= BytesWritten
))
1394 DPRINT(MIN_TRACE
, ("Status 0x%X.\n", (UINT
)GetLastError()));
1396 BytesWritten
= BytesToWrite
;
1397 if (fwrite((void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1398 BytesToWrite
, 1, DestFile
) < 1)
1401 CloseFile(DestFile
);
1403 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1404 return CAB_STATUS_CANNOT_WRITE
;
1406 Size
-= BytesToWrite
;
1408 CurrentOffset
+= BytesToWrite
;
1410 /* Don't skip any more bytes */
1415 CloseFile(DestFile
);
1419 return CAB_STATUS_SUCCESS
;
1422 bool CCabinet::IsCodecSelected()
1424 * FUNCTION: Returns the value of CodecSelected
1426 * Whether a codec is selected
1429 return CodecSelected
;
1432 void CCabinet::SelectCodec(LONG Id
)
1434 * FUNCTION: Selects codec engine to use
1436 * Id = Codec identifier
1444 CodecSelected
= false;
1451 Codec
= new CRawCodec();
1454 case CAB_CODEC_MSZIP
:
1455 Codec
= new CMSZipCodec();
1463 CodecSelected
= true;
1467 #ifndef CAB_READ_ONLY
1469 /* CAB write methods */
1471 ULONG
CCabinet::NewCabinet()
1473 * FUNCTION: Creates a new cabinet
1475 * Status of operation
1480 CurrentDiskNumber
= 0;
1482 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1483 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1484 if ((!OutputBuffer
) || (!InputBuffer
))
1486 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1487 return CAB_STATUS_NOMEMORY
;
1489 CurrentIBuffer
= InputBuffer
;
1490 CurrentIBufferSize
= 0;
1492 CABHeader
.Signature
= CAB_SIGNATURE
;
1493 CABHeader
.Reserved1
= 0; // Not used
1494 CABHeader
.CabinetSize
= 0; // Not yet known
1495 CABHeader
.Reserved2
= 0; // Not used
1496 CABHeader
.Reserved3
= 0; // Not used
1497 CABHeader
.Version
= CAB_VERSION
;
1498 CABHeader
.FolderCount
= 0; // Not yet known
1499 CABHeader
.FileCount
= 0; // Not yet known
1500 CABHeader
.Flags
= 0; // Not yet known
1501 // FIXME: Should be random
1502 CABHeader
.SetID
= 0x534F;
1503 CABHeader
.CabinetNumber
= 0;
1506 TotalFolderSize
= 0;
1509 DiskSize
= sizeof(CFHEADER
);
1511 InitCabinetHeader();
1513 // NextFolderNumber is 0-based
1514 NextFolderNumber
= 0;
1516 CurrentFolderNode
= NULL
;
1517 Status
= NewFolder();
1518 if (Status
!= CAB_STATUS_SUCCESS
)
1521 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1523 ScratchFile
= new CCFDATAStorage
;
1526 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1527 return CAB_STATUS_NOMEMORY
;
1530 Status
= ScratchFile
->Create("~CAB.tmp");
1532 CreateNewFolder
= false;
1534 CreateNewDisk
= false;
1536 PrevCabinetNumber
= 0;
1542 ULONG
CCabinet::NewDisk()
1544 * FUNCTION: Forces a new disk to be created
1546 * Status of operation
1549 // NextFolderNumber is 0-based
1550 NextFolderNumber
= 1;
1552 CreateNewDisk
= false;
1554 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1556 InitCabinetHeader();
1558 CurrentFolderNode
->TotalFolderSize
= 0;
1560 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1562 return CAB_STATUS_SUCCESS
;
1566 ULONG
CCabinet::NewFolder()
1568 * FUNCTION: Forces a new folder to be created
1570 * Status of operation
1573 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1575 CurrentFolderNode
= NewFolderNode();
1576 if (!CurrentFolderNode
)
1578 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1579 return CAB_STATUS_NOMEMORY
;
1584 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1587 case CAB_CODEC_MSZIP
:
1588 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1592 return CAB_STATUS_UNSUPPCOMP
;
1595 /* FIXME: This won't work if no files are added to the new folder */
1597 DiskSize
+= sizeof(CFFOLDER
);
1599 TotalFolderSize
+= sizeof(CFFOLDER
);
1603 CABHeader
.FolderCount
++;
1607 return CAB_STATUS_SUCCESS
;
1611 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1613 * FUNCTION: Writes a file to the scratch file
1615 * FileNode = Pointer to file node
1617 * Status of operation
1627 /* Try to open file */
1629 SourceFile
= CreateFile(
1630 FileNode
->FileName
, // Open this file
1631 GENERIC_READ
, // Open for reading
1632 FILE_SHARE_READ
, // Share for reading
1633 NULL
, // No security
1634 OPEN_EXISTING
, // File must exist
1635 FILE_ATTRIBUTE_NORMAL
, // Normal file
1636 NULL
); // No attribute template
1637 if (SourceFile
== INVALID_HANDLE_VALUE
)
1639 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1640 return CAB_STATUS_NOFILE
;
1643 SourceFile
= fopen(FileNode
->FileName
, "rb");
1644 if (SourceFile
== NULL
)
1646 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1647 return CAB_STATUS_NOFILE
;
1651 if (CreateNewFolder
)
1653 /* There is always a new folder after
1654 a split file is completely stored */
1655 Status
= NewFolder();
1656 if (Status
!= CAB_STATUS_SUCCESS
)
1658 CreateNewFolder
= false;
1661 /* Call OnAdd event handler */
1662 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1664 TotalBytesLeft
= FileNode
->File
.FileSize
;
1666 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1667 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1668 FileNode
->File
.FileControlID
= (USHORT
)(NextFolderNumber
- 1);
1669 CurrentFolderNode
->Commit
= true;
1670 PrevCabinetNumber
= CurrentDiskNumber
;
1672 Size
= sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(FileNode
->FileName
)) + 1;
1673 CABHeader
.FileTableOffset
+= Size
;
1674 TotalFileSize
+= Size
;
1678 FileNode
->Commit
= true;
1680 if (TotalBytesLeft
> 0)
1684 if (TotalBytesLeft
> (ULONG
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1685 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1687 BytesToRead
= TotalBytesLeft
;
1689 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1691 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n",
1692 (UINT
)BytesToRead
, (UINT
)BytesRead
, (UINT
)CurrentIBufferSize
));
1693 return CAB_STATUS_INVALID_CAB
;
1696 CurrentIBuffer
= (unsigned char*)CurrentIBuffer
+ BytesRead
;
1697 CurrentIBufferSize
+= (USHORT
)BytesRead
;
1699 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1701 Status
= WriteDataBlock();
1702 if (Status
!= CAB_STATUS_SUCCESS
)
1705 TotalBytesLeft
-= BytesRead
;
1706 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1709 if (TotalBytesLeft
== 0)
1711 CloseFile(SourceFile
);
1712 FileNode
->Delete
= true;
1714 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1716 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1717 CurrentFolderNode
->Delete
= true;
1719 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1721 Status
= WriteDataBlock();
1722 if (Status
!= CAB_STATUS_SUCCESS
)
1726 CreateNewFolder
= true;
1731 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1732 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1734 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1737 return CAB_STATUS_SUCCESS
;
1741 ULONG
CCabinet::WriteDisk(ULONG MoreDisks
)
1743 * FUNCTION: Forces the current disk to be written
1745 * MoreDisks = true if there is one or more disks after this disk
1747 * Status of operation
1750 PCFFILE_NODE FileNode
;
1753 ContinueFile
= false;
1754 FileNode
= FileListHead
;
1755 while (FileNode
!= NULL
)
1757 Status
= WriteFileToScratchStorage(FileNode
);
1758 if (Status
!= CAB_STATUS_SUCCESS
)
1763 /* A data block could span more than two
1764 disks if MaxDiskSize is very small */
1765 while (CreateNewDisk
)
1767 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1772 ContinueFile
= true;
1773 CreateNewDisk
= false;
1775 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1776 (UINT
)CurrentIBufferSize
, (UINT
)CurrentOBufferSize
));
1778 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1780 Status
= WriteDataBlock();
1781 if (Status
!= CAB_STATUS_SUCCESS
)
1788 ContinueFile
= false;
1789 FileNode
= FileNode
->Next
;
1793 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1795 /* A data block could span more than two
1796 disks if MaxDiskSize is very small */
1798 ASSERT(CreateNewDisk
== false);
1804 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1808 CreateNewDisk
= false;
1810 ASSERT(FileNode
== FileListHead
);
1813 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1815 Status
= WriteDataBlock();
1816 if (Status
!= CAB_STATUS_SUCCESS
)
1819 } while (CreateNewDisk
);
1821 CommitDisk(MoreDisks
);
1823 return CAB_STATUS_SUCCESS
;
1827 ULONG
CCabinet::CommitDisk(ULONG MoreDisks
)
1829 * FUNCTION: Commits the current disk
1831 * MoreDisks = true if there is one or more disks after this disk
1833 * Status of operation
1836 PCFFOLDER_NODE FolderNode
;
1839 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1841 /* Create file, fail if it already exists */
1843 FileHandle
= CreateFile(CabinetName
, // Create this file
1844 GENERIC_WRITE
, // Open for writing
1846 NULL
, // No security
1847 CREATE_NEW
, // New file only
1848 FILE_ATTRIBUTE_NORMAL
, // Normal file
1849 NULL
); // No attribute template
1850 if (FileHandle
== INVALID_HANDLE_VALUE
)
1853 /* If file exists, ask to overwrite file */
1854 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1855 (OnOverwrite(NULL
, CabinetName
)))
1858 /* Create cabinet file, overwrite if it already exists */
1859 FileHandle
= CreateFile(CabinetName
, // Create this file
1860 GENERIC_WRITE
, // Open for writing
1862 NULL
, // No security
1863 TRUNCATE_EXISTING
, // Truncate the file
1864 FILE_ATTRIBUTE_NORMAL
, // Normal file
1865 NULL
); // No attribute template
1866 if (FileHandle
== INVALID_HANDLE_VALUE
)
1867 return CAB_STATUS_CANNOT_CREATE
;
1871 if (Status
== ERROR_FILE_EXISTS
)
1872 return CAB_STATUS_FILE_EXISTS
;
1874 return CAB_STATUS_CANNOT_CREATE
;
1878 FileHandle
= fopen(CabinetName
, "rb");
1879 if (FileHandle
!= NULL
)
1882 /* If file exists, ask to overwrite file */
1883 if (OnOverwrite(NULL
, CabinetName
))
1885 FileHandle
= fopen(CabinetName
, "w+b");
1886 if (FileHandle
== NULL
)
1887 return CAB_STATUS_CANNOT_CREATE
;
1890 return CAB_STATUS_FILE_EXISTS
;
1895 FileHandle
= fopen(CabinetName
, "w+b");
1896 if (FileHandle
== NULL
)
1897 return CAB_STATUS_CANNOT_CREATE
;
1901 WriteCabinetHeader(MoreDisks
!= 0);
1903 Status
= WriteFolderEntries();
1904 if (Status
!= CAB_STATUS_SUCCESS
)
1907 /* Write file entries */
1910 /* Write data blocks */
1911 FolderNode
= FolderListHead
;
1912 while (FolderNode
!= NULL
)
1914 if (FolderNode
->Commit
)
1916 Status
= CommitDataBlocks(FolderNode
);
1917 if (Status
!= CAB_STATUS_SUCCESS
)
1919 /* Remove data blocks for folder */
1920 DestroyDataNodes(FolderNode
);
1922 FolderNode
= FolderNode
->Next
;
1925 CloseFile(FileHandle
);
1927 ScratchFile
->Truncate();
1929 return CAB_STATUS_SUCCESS
;
1933 ULONG
CCabinet::CloseDisk()
1935 * FUNCTION: Closes the current disk
1937 * Status of operation
1940 DestroyDeletedFileNodes();
1942 /* Destroy folder nodes that are completely stored */
1943 DestroyDeletedFolderNodes();
1945 CurrentDiskNumber
++;
1947 return CAB_STATUS_SUCCESS
;
1951 ULONG
CCabinet::CloseCabinet()
1953 * FUNCTION: Closes the current cabinet
1955 * Status of operation
1962 DestroyFolderNodes();
1966 FreeMemory(InputBuffer
);
1972 FreeMemory(OutputBuffer
);
1973 OutputBuffer
= NULL
;
1980 Status
= ScratchFile
->Destroy();
1985 return CAB_STATUS_SUCCESS
;
1989 ULONG
CCabinet::AddFile(char* FileName
)
1991 * FUNCTION: Adds a file to the current disk
1993 * FileName = Pointer to string with file name (full path)
1995 * Status of operation
1999 PCFFILE_NODE FileNode
;
2002 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
2005 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2006 return CAB_STATUS_NOMEMORY
;
2008 strcpy(NewFileName
, FileName
);
2009 ConvertPath(NewFileName
, false);
2011 /* Try to open file */
2013 SrcFile
= CreateFile(
2014 NewFileName
, // Open this file
2015 GENERIC_READ
, // Open for reading
2016 FILE_SHARE_READ
, // Share for reading
2017 NULL
, // No security
2018 OPEN_EXISTING
, // File must exist
2019 FILE_ATTRIBUTE_NORMAL
, // Normal file
2020 NULL
); // No attribute template
2021 if (SrcFile
== INVALID_HANDLE_VALUE
)
2023 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2024 FreeMemory(NewFileName
);
2025 return CAB_STATUS_CANNOT_OPEN
;
2028 SrcFile
= fopen(NewFileName
, "rb");
2029 if (SrcFile
== NULL
)
2031 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2032 FreeMemory(NewFileName
);
2033 return CAB_STATUS_CANNOT_OPEN
;
2037 FileNode
= NewFileNode();
2040 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2041 FreeMemory(NewFileName
);
2042 return CAB_STATUS_NOMEMORY
;
2045 FileNode
->FolderNode
= CurrentFolderNode
;
2046 FileNode
->FileName
= NewFileName
;
2048 /* FIXME: Check for and handle large files (>= 2GB) */
2049 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
2050 if (FileNode
->File
.FileSize
== (ULONG
)-1)
2052 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
2053 FreeMemory(NewFileName
);
2054 return CAB_STATUS_CANNOT_READ
;
2057 if (GetFileTimes(SrcFile
, FileNode
) != CAB_STATUS_SUCCESS
)
2059 DPRINT(MIN_TRACE
, ("Cannot read file times.\n"));
2060 FreeMemory(NewFileName
);
2061 return CAB_STATUS_CANNOT_READ
;
2064 if (GetAttributesOnFile(FileNode
) != CAB_STATUS_SUCCESS
)
2066 DPRINT(MIN_TRACE
, ("Cannot read file attributes.\n"));
2067 FreeMemory(NewFileName
);
2068 return CAB_STATUS_CANNOT_READ
;
2073 return CAB_STATUS_SUCCESS
;
2076 bool CCabinet::CreateSimpleCabinet()
2078 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2083 char szFilePath
[MAX_PATH
];
2084 char szFile
[MAX_PATH
];
2085 PSEARCH_CRITERIA Criteria
;
2090 WIN32_FIND_DATA FindFileData
;
2097 // Initialize a new cabinet
2098 Status
= NewCabinet();
2099 if (Status
!= CAB_STATUS_SUCCESS
)
2101 DPRINT(MIN_TRACE
, ("Cannot create cabinet (%u).\n", (UINT
)Status
));
2105 // Add each file in the criteria list
2106 Criteria
= CriteriaListHead
;
2110 // Store the file path with a trailing slash in szFilePath
2111 ConvertPath(Criteria
->Search
, false);
2112 pszFile
= strrchr(Criteria
->Search
, DIR_SEPARATOR_CHAR
);
2116 // Set the pointer to the start of the file name, not the slash
2119 strncpy(szFilePath
, Criteria
->Search
, pszFile
- Criteria
->Search
);
2120 szFilePath
[pszFile
- Criteria
->Search
] = 0;
2124 pszFile
= Criteria
->Search
;
2129 // needed for opendir()
2130 strcpy(szFilePath
, "./");
2135 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2136 hFind
= FindFirstFile(Criteria
->Search
, &FindFileData
);
2138 // Don't stop if a search criteria is not found
2139 if(hFind
== INVALID_HANDLE_VALUE
&& GetLastError() != ERROR_FILE_NOT_FOUND
)
2141 DPRINT(MIN_TRACE
, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria
->Search
, (UINT
)GetLastError()));
2147 if(!(FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
2149 strcpy(szFile
, szFilePath
);
2150 strcat(szFile
, FindFileData
.cFileName
);
2152 Status
= AddFile(szFile
);
2154 if(Status
!= CAB_STATUS_SUCCESS
)
2156 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2161 while(FindNextFile(hFind
, &FindFileData
));
2165 // Unix: Use opendir/readdir to loop through all entries, stat to check if it's a file and MatchFileNamePattern to match the file against the pattern
2166 dirp
= opendir(szFilePath
);
2170 while( (dp
= readdir(dirp
)) )
2172 strcpy(szFile
, szFilePath
);
2173 strcat(szFile
, dp
->d_name
);
2175 if(stat(szFile
, &stbuf
) == 0)
2177 if(stbuf
.st_mode
!= S_IFDIR
)
2179 if(MatchFileNamePattern(dp
->d_name
, pszFile
))
2181 Status
= AddFile(szFile
);
2183 if(Status
!= CAB_STATUS_SUCCESS
)
2185 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2193 DPRINT(MIN_TRACE
, ("stat failed, error code is %i\n", errno
));
2202 Criteria
= Criteria
->Next
;
2205 Status
= WriteDisk(false);
2206 if (Status
== CAB_STATUS_SUCCESS
)
2207 Status
= CloseDisk();
2208 if (Status
!= CAB_STATUS_SUCCESS
)
2210 DPRINT(MIN_TRACE
, ("Cannot write disk (%u).\n", (UINT
)Status
));
2218 DestroySearchCriteria();
2222 void CCabinet::SetMaxDiskSize(ULONG Size
)
2224 * FUNCTION: Sets the maximum size of the current disk
2226 * Size = Maximum size of current disk (0 means no maximum size)
2232 #endif /* CAB_READ_ONLY */
2235 /* Default event handlers */
2237 bool CCabinet::OnOverwrite(PCFFILE File
,
2240 * FUNCTION: Called when extracting a file and it already exists
2242 * File = Pointer to CFFILE for file being extracted
2243 * FileName = Pointer to buffer with name of file (full path)
2245 * true if the file should be overwritten, false if not
2252 void CCabinet::OnExtract(PCFFILE File
,
2255 * FUNCTION: Called just before extracting a file
2257 * File = Pointer to CFFILE for file being extracted
2258 * FileName = Pointer to buffer with name of file (full path)
2264 void CCabinet::OnDiskChange(char* CabinetName
,
2267 * FUNCTION: Called when a new disk is to be processed
2269 * CabinetName = Pointer to buffer with name of cabinet
2270 * DiskLabel = Pointer to buffer with label of disk
2276 #ifndef CAB_READ_ONLY
2278 void CCabinet::OnAdd(PCFFILE File
,
2281 * FUNCTION: Called just before adding a file to a cabinet
2283 * File = Pointer to CFFILE for file being added
2284 * FileName = Pointer to buffer with name of file (full path)
2290 bool CCabinet::OnDiskLabel(ULONG Number
, char* Label
)
2292 * FUNCTION: Called when a disk needs a label
2294 * Number = Cabinet number that needs a label
2295 * Label = Pointer to buffer to place label of disk
2297 * true if a disk label was returned, false if not
2304 bool CCabinet::OnCabinetName(ULONG Number
, char* Name
)
2306 * FUNCTION: Called when a cabinet needs a name
2308 * Number = Disk number that needs a name
2309 * Name = Pointer to buffer to place name of cabinet
2311 * true if a cabinet name was returned, false if not
2317 #endif /* CAB_READ_ONLY */
2319 PCFFOLDER_NODE
CCabinet::LocateFolderNode(ULONG Index
)
2321 * FUNCTION: Locates a folder node
2323 * Index = Folder index
2325 * Pointer to folder node or NULL if the folder node was not found
2328 PCFFOLDER_NODE Node
;
2332 case CAB_FILE_SPLIT
:
2333 return FolderListTail
;
2335 case CAB_FILE_CONTINUED
:
2336 case CAB_FILE_PREV_NEXT
:
2337 return FolderListHead
;
2340 Node
= FolderListHead
;
2341 while (Node
!= NULL
)
2343 if (Node
->Index
== Index
)
2351 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2353 * FUNCTION: Returns the absolute offset of a file
2355 * File = Pointer to CFFILE_NODE structure for file
2357 * Status of operation
2362 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2364 (UINT
)File
->File
.FileOffset
,
2365 (UINT
)File
->File
.FileSize
));
2367 Node
= CurrentFolderNode
->DataListHead
;
2368 while (Node
!= NULL
)
2370 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2371 (UINT
)Node
->UncompOffset
,
2372 (UINT
)(Node
->UncompOffset
+ Node
->Data
.UncompSize
),
2373 (UINT
)Node
->Data
.UncompSize
));
2375 /* Node->Data.UncompSize will be 0 if the block is split
2376 (ie. it is the last block in this cabinet) */
2377 if ((Node
->Data
.UncompSize
== 0) ||
2378 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2379 (File
->File
.FileOffset
< Node
->UncompOffset
+
2380 Node
->Data
.UncompSize
)))
2382 File
->DataBlock
= Node
;
2383 return CAB_STATUS_SUCCESS
;
2388 return CAB_STATUS_INVALID_CAB
;
2392 ULONG
CCabinet::LocateFile(char* FileName
,
2395 * FUNCTION: Locates a file in the cabinet
2397 * FileName = Pointer to string with name of file to locate
2398 * File = Address of pointer to CFFILE_NODE structure to fill
2400 * Status of operation
2402 * Current folder is set to the folder of the file
2408 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2410 Node
= FileListHead
;
2411 while (Node
!= NULL
)
2413 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2415 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2416 if (!CurrentFolderNode
)
2418 DPRINT(MID_TRACE
, ("Folder with index number (%u) not found.\n",
2419 Node
->File
.FileControlID
));
2420 return CAB_STATUS_INVALID_CAB
;
2423 if (Node
->DataBlock
== NULL
)
2424 Status
= GetAbsoluteOffset(Node
);
2426 Status
= CAB_STATUS_SUCCESS
;
2433 return CAB_STATUS_NOFILE
;
2437 ULONG
CCabinet::ReadString(char* String
, LONG MaxLength
)
2439 * FUNCTION: Reads a NULL-terminated string from the cabinet
2441 * String = Pointer to buffer to place string
2442 * MaxLength = Maximum length of string
2444 * Status of operation
2454 Status
= ReadBlock(String
, MaxLength
, &BytesRead
);
2455 if (Status
!= CAB_STATUS_SUCCESS
)
2457 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2458 return CAB_STATUS_INVALID_CAB
;
2461 // Find the terminating NULL character
2462 for (Size
= 0; Size
< MaxLength
; Size
++)
2464 if (String
[Size
] == '\0')
2473 DPRINT(MIN_TRACE
, ("Filename in the cabinet file is too long.\n"));
2474 return CAB_STATUS_INVALID_CAB
;
2477 // Compute the offset of the next CFFILE.
2478 // We have to subtract from the current offset here, because we read MaxLength characters above and most-probably the file name isn't MaxLength characters long.
2479 // + 1 to skip the terminating NULL character as well.
2480 Size
= -(MaxLength
- Size
) + 1;
2483 if( SetFilePointer(FileHandle
,
2486 FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
2488 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2489 return CAB_STATUS_INVALID_CAB
;
2492 if (fseek(FileHandle
, (off_t
)Size
, SEEK_CUR
) != 0)
2494 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2495 return CAB_STATUS_INVALID_CAB
;
2498 return CAB_STATUS_SUCCESS
;
2502 ULONG
CCabinet::ReadFileTable()
2504 * FUNCTION: Reads the file table from the cabinet file
2506 * Status of operation
2514 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%X).\n",
2515 (UINT
)CABHeader
.FileTableOffset
));
2517 /* Seek to file table */
2519 if( SetFilePointer(FileHandle
,
2520 CABHeader
.FileTableOffset
,
2522 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2524 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2525 return CAB_STATUS_INVALID_CAB
;
2528 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2530 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2531 return CAB_STATUS_INVALID_CAB
;
2535 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2537 File
= NewFileNode();
2540 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2541 return CAB_STATUS_NOMEMORY
;
2544 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2545 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2547 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2548 return CAB_STATUS_INVALID_CAB
;
2551 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2552 if (!File
->FileName
)
2554 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2555 return CAB_STATUS_NOMEMORY
;
2558 /* Read file name */
2559 Status
= ReadString(File
->FileName
, MAX_PATH
);
2560 if (Status
!= CAB_STATUS_SUCCESS
)
2563 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2565 (UINT
)File
->File
.FileOffset
,
2566 (UINT
)File
->File
.FileSize
,
2567 File
->File
.FileControlID
));
2570 return CAB_STATUS_SUCCESS
;
2574 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2576 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2578 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2580 * Status of operation
2583 ULONG AbsoluteOffset
;
2590 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2591 (UINT
)FolderNode
->Index
, (UINT
)FolderNode
->Folder
.DataOffset
));
2593 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2594 UncompOffset
= FolderNode
->UncompOffset
;
2596 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2598 Node
= NewDataNode(FolderNode
);
2601 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2602 return CAB_STATUS_NOMEMORY
;
2605 /* Seek to data block */
2607 if( SetFilePointer(FileHandle
,
2610 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2612 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2613 return CAB_STATUS_INVALID_CAB
;
2616 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2618 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2619 return CAB_STATUS_INVALID_CAB
;
2623 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2624 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2626 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2627 return CAB_STATUS_INVALID_CAB
;
2630 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2631 (UINT
)AbsoluteOffset
,
2633 (UINT
)Node
->Data
.Checksum
,
2634 Node
->Data
.CompSize
,
2635 Node
->Data
.UncompSize
));
2637 Node
->AbsoluteOffset
= AbsoluteOffset
;
2638 Node
->UncompOffset
= UncompOffset
;
2640 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2641 UncompOffset
+= Node
->Data
.UncompSize
;
2644 FolderUncompSize
= UncompOffset
;
2646 return CAB_STATUS_SUCCESS
;
2650 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2652 * FUNCTION: Creates a new folder node
2654 * Pointer to node if there was enough free memory available, otherwise NULL
2657 PCFFOLDER_NODE Node
;
2659 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2663 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2665 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2667 Node
->Prev
= FolderListTail
;
2669 if (FolderListTail
!= NULL
)
2670 FolderListTail
->Next
= Node
;
2672 FolderListHead
= Node
;
2674 FolderListTail
= Node
;
2680 PCFFILE_NODE
CCabinet::NewFileNode()
2682 * FUNCTION: Creates a new file node
2684 * FolderNode = Pointer to folder node to bind file to
2686 * Pointer to node if there was enough free memory available, otherwise NULL
2691 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2695 memset(Node
, 0, sizeof(CFFILE_NODE
));
2697 Node
->Prev
= FileListTail
;
2699 if (FileListTail
!= NULL
)
2700 FileListTail
->Next
= Node
;
2702 FileListHead
= Node
;
2704 FileListTail
= Node
;
2710 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2712 * FUNCTION: Creates a new data block node
2714 * FolderNode = Pointer to folder node to bind data block to
2716 * Pointer to node if there was enough free memory available, otherwise NULL
2721 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2725 memset(Node
, 0, sizeof(CFDATA_NODE
));
2727 Node
->Prev
= FolderNode
->DataListTail
;
2729 if (FolderNode
->DataListTail
!= NULL
)
2730 FolderNode
->DataListTail
->Next
= Node
;
2732 FolderNode
->DataListHead
= Node
;
2734 FolderNode
->DataListTail
= Node
;
2740 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2742 * FUNCTION: Destroys data block nodes bound to a folder node
2744 * FolderNode = Pointer to folder node
2747 PCFDATA_NODE PrevNode
;
2748 PCFDATA_NODE NextNode
;
2750 NextNode
= FolderNode
->DataListHead
;
2751 while (NextNode
!= NULL
)
2753 PrevNode
= NextNode
->Next
;
2754 FreeMemory(NextNode
);
2755 NextNode
= PrevNode
;
2757 FolderNode
->DataListHead
= NULL
;
2758 FolderNode
->DataListTail
= NULL
;
2762 void CCabinet::DestroyFileNodes()
2764 * FUNCTION: Destroys file nodes
2767 PCFFILE_NODE PrevNode
;
2768 PCFFILE_NODE NextNode
;
2770 NextNode
= FileListHead
;
2771 while (NextNode
!= NULL
)
2773 PrevNode
= NextNode
->Next
;
2774 if (NextNode
->FileName
)
2775 FreeMemory(NextNode
->FileName
);
2776 FreeMemory(NextNode
);
2777 NextNode
= PrevNode
;
2779 FileListHead
= NULL
;
2780 FileListTail
= NULL
;
2784 void CCabinet::DestroyDeletedFileNodes()
2786 * FUNCTION: Destroys file nodes that are marked for deletion
2789 PCFFILE_NODE CurNode
;
2790 PCFFILE_NODE NextNode
;
2792 CurNode
= FileListHead
;
2793 while (CurNode
!= NULL
)
2795 NextNode
= CurNode
->Next
;
2797 if (CurNode
->Delete
)
2799 if (CurNode
->Prev
!= NULL
)
2800 CurNode
->Prev
->Next
= CurNode
->Next
;
2803 FileListHead
= CurNode
->Next
;
2805 FileListHead
->Prev
= NULL
;
2808 if (CurNode
->Next
!= NULL
)
2809 CurNode
->Next
->Prev
= CurNode
->Prev
;
2812 FileListTail
= CurNode
->Prev
;
2814 FileListTail
->Next
= NULL
;
2817 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2819 TotalFileSize
-= (sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(CurNode
->FileName
)) + 1);
2821 if (CurNode
->FileName
)
2822 FreeMemory(CurNode
->FileName
);
2823 FreeMemory(CurNode
);
2830 void CCabinet::DestroyFolderNodes()
2832 * FUNCTION: Destroys folder nodes
2835 PCFFOLDER_NODE PrevNode
;
2836 PCFFOLDER_NODE NextNode
;
2838 NextNode
= FolderListHead
;
2839 while (NextNode
!= NULL
)
2841 PrevNode
= NextNode
->Next
;
2842 DestroyDataNodes(NextNode
);
2843 FreeMemory(NextNode
);
2844 NextNode
= PrevNode
;
2846 FolderListHead
= NULL
;
2847 FolderListTail
= NULL
;
2851 void CCabinet::DestroyDeletedFolderNodes()
2853 * FUNCTION: Destroys folder nodes that are marked for deletion
2856 PCFFOLDER_NODE CurNode
;
2857 PCFFOLDER_NODE NextNode
;
2859 CurNode
= FolderListHead
;
2860 while (CurNode
!= NULL
)
2862 NextNode
= CurNode
->Next
;
2864 if (CurNode
->Delete
)
2866 if (CurNode
->Prev
!= NULL
)
2867 CurNode
->Prev
->Next
= CurNode
->Next
;
2870 FolderListHead
= CurNode
->Next
;
2872 FolderListHead
->Prev
= NULL
;
2875 if (CurNode
->Next
!= NULL
)
2876 CurNode
->Next
->Prev
= CurNode
->Prev
;
2879 FolderListTail
= CurNode
->Prev
;
2881 FolderListTail
->Next
= NULL
;
2884 DestroyDataNodes(CurNode
);
2885 FreeMemory(CurNode
);
2887 TotalFolderSize
-= sizeof(CFFOLDER
);
2894 ULONG
CCabinet::ComputeChecksum(void* Buffer
,
2898 * FUNCTION: Computes checksum for data block
2900 * Buffer = Pointer to data buffer
2901 * Size = Length of data buffer
2902 * Seed = Previously computed checksum
2904 * Checksum of buffer
2907 int UlongCount
; // Number of ULONGs in block
2908 ULONG Checksum
; // Checksum accumulator
2912 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2913 won't accept checksums computed by this routine */
2915 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer
, (UINT
)Size
));
2917 UlongCount
= Size
/ 4; // Number of ULONGs
2918 Checksum
= Seed
; // Init checksum
2919 pb
= (unsigned char*)Buffer
; // Start at front of data block
2921 /* Checksum integral multiple of ULONGs */
2922 while (UlongCount
-- > 0)
2924 /* NOTE: Build ULONG in big/little-endian independent manner */
2925 ul
= *pb
++; // Get low-order byte
2926 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2927 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2928 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2930 Checksum
^= ul
; // Update checksum
2933 /* Checksum remainder bytes */
2938 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2940 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2942 ul
|= *pb
++; // Get low-order byte
2946 Checksum
^= ul
; // Update checksum
2948 /* Return computed checksum */
2953 ULONG
CCabinet::ReadBlock(void* Buffer
,
2957 * FUNCTION: Read a block of data from file
2959 * Buffer = Pointer to data buffer
2960 * Size = Length of data buffer
2961 * BytesRead = Pointer to ULONG that on return will contain
2962 * number of bytes read
2964 * Status of operation
2967 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2968 return CAB_STATUS_INVALID_CAB
;
2969 return CAB_STATUS_SUCCESS
;
2972 bool CCabinet::MatchFileNamePattern(char* FileName
, char* Pattern
)
2974 * FUNCTION: Matches a wildcard character pattern against a file
2976 * FileName = The file name to check
2977 * Pattern = The pattern
2979 * Whether the pattern matches the file
2982 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
2983 * Adapted from code written by Ingo Wilken.
2984 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
2987 char* retryPattern
= NULL
;
2988 char* retryFileName
= NULL
;
2991 while (*FileName
|| *Pattern
)
2998 retryPattern
= Pattern
;
2999 retryFileName
= FileName
;
3003 if (*FileName
++ == '\0')
3009 if (*FileName
== ch
)
3018 Pattern
= retryPattern
;
3019 FileName
= ++retryFileName
;
3033 #ifndef CAB_READ_ONLY
3035 ULONG
CCabinet::InitCabinetHeader()
3037 * FUNCTION: Initializes cabinet header and optional fields
3039 * Status of operation
3045 CABHeader
.FileTableOffset
= 0; // Not known yet
3046 CABHeader
.FolderCount
= 0; // Not known yet
3047 CABHeader
.FileCount
= 0; // Not known yet
3048 CABHeader
.Flags
= 0; // Not known yet
3050 CABHeader
.CabinetNumber
= (USHORT
)CurrentDiskNumber
;
3052 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
3054 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
3055 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
3056 strcpy(CabinetPrev
, "");
3059 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
3061 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3062 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
3063 strcpy(DiskNext
, "");
3068 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3071 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3073 /* Calculate size of name of previous cabinet */
3074 TotalSize
+= (ULONG
)strlen(CabinetPrev
) + 1;
3076 /* Calculate size of label of previous disk */
3077 TotalSize
+= (ULONG
)strlen(DiskPrev
) + 1;
3080 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3083 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3085 /* Calculate size of name of next cabinet */
3086 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3088 NextFieldsSize
= Size
;
3090 /* Calculate size of label of next disk */
3091 Size
= (ULONG
)strlen(DiskNext
) + 1;
3093 NextFieldsSize
+= Size
;
3098 /* Add cabinet reserved area size if present */
3099 if (CabinetReservedFileSize
> 0)
3101 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
3102 TotalSize
+= CabinetReservedFileSize
;
3103 TotalSize
+= sizeof(ULONG
); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3106 DiskSize
+= TotalSize
;
3108 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
3110 return CAB_STATUS_SUCCESS
;
3114 ULONG
CCabinet::WriteCabinetHeader(bool MoreDisks
)
3116 * FUNCTION: Writes the cabinet header and optional fields
3118 * MoreDisks = true if next cabinet name should be included
3120 * Status of operation
3123 PCFFOLDER_NODE FolderNode
;
3124 PCFFILE_NODE FileNode
;
3130 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3131 Size
= TotalHeaderSize
;
3135 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
3136 DiskSize
-= NextFieldsSize
;
3137 Size
= TotalHeaderSize
- NextFieldsSize
;
3140 /* Set absolute folder offsets */
3141 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
3142 CABHeader
.FolderCount
= 0;
3143 FolderNode
= FolderListHead
;
3144 while (FolderNode
!= NULL
)
3146 FolderNode
->Folder
.DataOffset
= BytesWritten
;
3148 BytesWritten
+= FolderNode
->TotalFolderSize
;
3150 CABHeader
.FolderCount
++;
3152 FolderNode
= FolderNode
->Next
;
3155 /* Set absolute offset of file table */
3156 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
3158 /* Count number of files to be committed */
3159 CABHeader
.FileCount
= 0;
3160 FileNode
= FileListHead
;
3161 while (FileNode
!= NULL
)
3163 if (FileNode
->Commit
)
3164 CABHeader
.FileCount
++;
3165 FileNode
= FileNode
->Next
;
3168 CABHeader
.CabinetSize
= DiskSize
;
3172 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), (LPDWORD
)&BytesWritten
, NULL
))
3174 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3175 return CAB_STATUS_CANNOT_WRITE
;
3178 BytesWritten
= sizeof(CFHEADER
);
3179 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
3181 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3182 return CAB_STATUS_CANNOT_WRITE
;
3186 /* Write per-cabinet reserved area if present */
3187 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
3191 ReservedSize
= CabinetReservedFileSize
& 0xffff;
3192 ReservedSize
|= (0 << 16); /* Folder reserved area size */
3193 ReservedSize
|= (0 << 24); /* Folder reserved area size */
3195 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(ULONG
), (LPDWORD
)&BytesWritten
, NULL
))
3197 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3198 return CAB_STATUS_CANNOT_WRITE
;
3201 BytesWritten
= sizeof(ULONG
);
3202 if (fwrite(&ReservedSize
, sizeof(ULONG
), 1, FileHandle
) < 1)
3204 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3205 return CAB_STATUS_CANNOT_WRITE
;
3210 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, (LPDWORD
)&BytesWritten
, NULL
))
3212 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3213 return CAB_STATUS_CANNOT_WRITE
;
3216 BytesWritten
= CabinetReservedFileSize
;
3217 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
3219 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3220 return CAB_STATUS_CANNOT_WRITE
;
3225 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3227 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3229 /* Write name of previous cabinet */
3230 Size
= (ULONG
)strlen(CabinetPrev
) + 1;
3232 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3234 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3235 return CAB_STATUS_CANNOT_WRITE
;
3238 BytesWritten
= Size
;
3239 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
3241 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3242 return CAB_STATUS_CANNOT_WRITE
;
3246 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
3248 /* Write label of previous disk */
3249 Size
= (ULONG
)strlen(DiskPrev
) + 1;
3251 if (!WriteFile(FileHandle
, DiskPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3253 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3254 return CAB_STATUS_CANNOT_WRITE
;
3257 BytesWritten
= Size
;
3258 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1)
3260 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3261 return CAB_STATUS_CANNOT_WRITE
;
3266 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3268 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3270 /* Write name of next cabinet */
3271 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3273 if (!WriteFile(FileHandle
, CabinetNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3275 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3276 return CAB_STATUS_CANNOT_WRITE
;
3279 BytesWritten
= Size
;
3280 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
3282 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3283 return CAB_STATUS_CANNOT_WRITE
;
3287 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
3289 /* Write label of next disk */
3290 Size
= (ULONG
)strlen(DiskNext
) + 1;
3292 if (!WriteFile(FileHandle
, DiskNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3294 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3295 return CAB_STATUS_CANNOT_WRITE
;
3298 BytesWritten
= Size
;
3299 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1)
3301 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3302 return CAB_STATUS_CANNOT_WRITE
;
3307 return CAB_STATUS_SUCCESS
;
3311 ULONG
CCabinet::WriteFolderEntries()
3313 * FUNCTION: Writes folder entries
3315 * Status of operation
3318 PCFFOLDER_NODE FolderNode
;
3321 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
3323 FolderNode
= FolderListHead
;
3324 while (FolderNode
!= NULL
)
3326 if (FolderNode
->Commit
)
3328 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3329 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, (UINT
)FolderNode
->Folder
.DataOffset
));
3332 if (!WriteFile(FileHandle
,
3333 &FolderNode
->Folder
,
3335 (LPDWORD
)&BytesWritten
,
3338 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3339 return CAB_STATUS_CANNOT_WRITE
;
3342 BytesWritten
= sizeof(CFFOLDER
);
3343 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3345 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3346 return CAB_STATUS_CANNOT_WRITE
;
3350 FolderNode
= FolderNode
->Next
;
3353 return CAB_STATUS_SUCCESS
;
3357 ULONG
CCabinet::WriteFileEntries()
3359 * FUNCTION: Writes file entries for all files
3361 * Status of operation
3366 bool SetCont
= false;
3368 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3370 File
= FileListHead
;
3371 while (File
!= NULL
)
3375 /* Remove any continued files that ends in this disk */
3376 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3377 File
->Delete
= true;
3379 /* The file could end in the last (split) block and should therefore
3380 appear in the next disk too */
3382 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3383 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3385 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3386 File
->Delete
= false;
3390 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3391 File
->File
.FileControlID
, (UINT
)File
->File
.FileOffset
, (UINT
)File
->File
.FileSize
, File
->FileName
));
3394 if (!WriteFile(FileHandle
,
3397 (LPDWORD
)&BytesWritten
,
3399 return CAB_STATUS_CANNOT_WRITE
;
3401 BytesWritten
= sizeof(CFFILE
);
3402 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3404 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3405 return CAB_STATUS_CANNOT_WRITE
;
3410 if (!WriteFile(FileHandle
,
3411 GetFileName(File
->FileName
),
3412 (DWORD
)strlen(GetFileName(File
->FileName
)) + 1,
3413 (LPDWORD
)&BytesWritten
,
3415 return CAB_STATUS_CANNOT_WRITE
;
3417 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3418 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3420 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3421 return CAB_STATUS_CANNOT_WRITE
;
3427 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3434 return CAB_STATUS_SUCCESS
;
3438 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3440 * FUNCTION: Writes data blocks to the cabinet
3442 * FolderNode = Pointer to folder node containing the data blocks
3444 * Status of operation
3447 PCFDATA_NODE DataNode
;
3452 DataNode
= FolderNode
->DataListHead
;
3453 if (DataNode
!= NULL
)
3454 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3456 while (DataNode
!= NULL
)
3458 DPRINT(MAX_TRACE
, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3459 (UINT
)DataNode
->ScratchFilePosition
,
3460 DataNode
->Data
.CompSize
,
3461 DataNode
->Data
.UncompSize
));
3463 /* InputBuffer is free for us to use here, so we use it and avoid a
3464 memory allocation. OutputBuffer can't be used here because it may
3465 still contain valid data (if a data block spans two or more disks) */
3466 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3467 if (Status
!= CAB_STATUS_SUCCESS
)
3469 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%u).\n", (UINT
)Status
));
3474 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3475 sizeof(CFDATA
), (LPDWORD
)&BytesWritten
, NULL
))
3477 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3478 return CAB_STATUS_CANNOT_WRITE
;
3481 BytesWritten
= sizeof(CFDATA
);
3482 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3484 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3485 return CAB_STATUS_CANNOT_WRITE
;
3490 if (!WriteFile(FileHandle
, InputBuffer
,
3491 DataNode
->Data
.CompSize
, (LPDWORD
)&BytesWritten
, NULL
))
3493 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3494 return CAB_STATUS_CANNOT_WRITE
;
3497 BytesWritten
= DataNode
->Data
.CompSize
;
3498 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3500 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3501 return CAB_STATUS_CANNOT_WRITE
;
3505 DataNode
= DataNode
->Next
;
3507 return CAB_STATUS_SUCCESS
;
3511 ULONG
CCabinet::WriteDataBlock()
3513 * FUNCTION: Writes the current data block to the scratch file
3515 * Status of operation
3520 PCFDATA_NODE DataNode
;
3524 Status
= Codec
->Compress(OutputBuffer
,
3529 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3530 (UINT
)CurrentIBufferSize
, (UINT
)TotalCompSize
));
3532 CurrentOBuffer
= OutputBuffer
;
3533 CurrentOBufferSize
= TotalCompSize
;
3536 DataNode
= NewDataNode(CurrentFolderNode
);
3539 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3540 return CAB_STATUS_NOMEMORY
;
3543 DiskSize
+= sizeof(CFDATA
);
3545 if (MaxDiskSize
> 0)
3546 /* Disk size is limited */
3547 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3549 BlockIsSplit
= false;
3553 DataNode
->Data
.CompSize
= (USHORT
)(MaxDiskSize
- DiskSize
);
3554 DataNode
->Data
.UncompSize
= 0;
3555 CreateNewDisk
= true;
3559 DataNode
->Data
.CompSize
= (USHORT
)CurrentOBufferSize
;
3560 DataNode
->Data
.UncompSize
= (USHORT
)CurrentIBufferSize
;
3563 DataNode
->Data
.Checksum
= 0;
3564 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3566 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3567 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3569 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3570 (UINT
)DataNode
->Data
.Checksum
,
3571 DataNode
->Data
.CompSize
,
3572 DataNode
->Data
.UncompSize
));
3574 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3575 CurrentOBuffer
, &BytesWritten
);
3576 if (Status
!= CAB_STATUS_SUCCESS
)
3579 DiskSize
+= BytesWritten
;
3581 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3582 CurrentFolderNode
->Folder
.DataBlockCount
++;
3584 CurrentOBuffer
= (unsigned char*)CurrentOBuffer
+ DataNode
->Data
.CompSize
;
3585 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3587 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3591 CurrentIBufferSize
= 0;
3592 CurrentIBuffer
= InputBuffer
;
3595 return CAB_STATUS_SUCCESS
;
3600 void CCabinet::ConvertDateAndTime(time_t* Time
,
3604 * FUNCTION: Returns file times of a file
3606 * FileHandle = File handle of file to get file times from
3607 * File = Pointer to CFFILE node for file
3609 * Status of operation
3614 timedef
= localtime(Time
);
3616 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3617 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3618 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3620 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3621 | ((timedef
->tm_mon
+ 1) << 5)
3622 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3624 *DosTime
= (timedef
->tm_sec
<< 0)
3625 | (timedef
->tm_min
<< 5)
3626 | (timedef
->tm_hour
<< 11);
3632 ULONG
CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3634 * FUNCTION: Returns file times of a file
3636 * FileHandle = File handle of file to get file times from
3637 * File = Pointer to CFFILE node for file
3639 * Status of operation
3645 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3646 FileTimeToDosDateTime(&FileTime
,
3647 &File
->File
.FileDate
,
3648 &File
->File
.FileTime
);
3653 // Check for an absolute path
3654 if (IsSeparator(File
->FileName
[0]))
3655 strcpy(buf
, File
->FileName
);
3658 if (!getcwd(buf
, sizeof(buf
)))
3659 return CAB_STATUS_CANNOT_READ
;
3660 strcat(buf
, DIR_SEPARATOR_STRING
);
3661 strcat(buf
, File
->FileName
);
3664 if (stat(buf
, &stbuf
) == -1)
3665 return CAB_STATUS_CANNOT_READ
;
3667 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3669 return CAB_STATUS_SUCCESS
;
3673 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3675 * FUNCTION: Returns attributes on a file
3677 * File = Pointer to CFFILE node for file
3679 * Status of operation
3685 Attributes
= GetFileAttributes(File
->FileName
);
3686 if (Attributes
== -1)
3687 return CAB_STATUS_CANNOT_READ
;
3689 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3690 // The IDs for these attributes are the same in the CAB file and under Windows
3691 // If the file has any other attributes, strip them off by the logical AND.
3692 File
->File
.Attributes
= (USHORT
)(Attributes
& 0x37);
3697 // Check for an absolute path
3698 if (IsSeparator(File
->FileName
[0]))
3699 strcpy(buf
, File
->FileName
);
3702 if (!getcwd(buf
, sizeof(buf
)))
3703 return CAB_STATUS_CANNOT_READ
;
3704 strcat(buf
, DIR_SEPARATOR_STRING
);
3705 strcat(buf
, File
->FileName
);
3708 if (stat(buf
, &stbuf
) == -1)
3709 return CAB_STATUS_CANNOT_READ
;
3712 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3713 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3714 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3717 if (stbuf
.st_mode
& S_IFDIR
)
3718 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3720 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3723 return CAB_STATUS_SUCCESS
;
3727 ULONG
CCabinet::SetAttributesOnFile(char* FileName
, USHORT FileAttributes
)
3729 * FUNCTION: Sets attributes on a file
3731 * FileName = File name with path
3732 * FileAttributes = Attributes of that file
3734 * Status of operation
3738 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3739 // The IDs for these attributes are the same in the CAB file and under Windows
3740 // If the file has any other attributes, strip them off by the logical AND.
3741 SetFileAttributes(FileName
, (DWORD
)(FileAttributes
& 0x37));
3743 return CAB_STATUS_SUCCESS
;
3745 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3746 return CAB_STATUS_SUCCESS
;
3750 #endif /* CAB_READ_ONLY */