2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cxx
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()
120 * FUNCTION: Creates the file
122 * FileName = Pointer to name of file
124 * Status of operation
128 char tmpPath
[MAX_PATH
];
130 ASSERT(!FileCreated
);
133 if (GetTempPath(MAX_PATH
, tmpPath
) == 0)
134 return CAB_STATUS_CANNOT_CREATE
;
135 if(GetTempFileName(tmpPath
, "cab", 0, FullName
) == 0)
136 return CAB_STATUS_CANNOT_CREATE
;
138 /* Create file, overwrite if it already exists */
139 FileHandle
= CreateFile(FullName
, // Create this file
140 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
143 CREATE_ALWAYS
, // Create or overwrite
144 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
145 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
146 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
147 NULL
); // No attribute template
148 if (FileHandle
== INVALID_HANDLE_VALUE
)
150 DPRINT(MID_TRACE
, ("ERROR '%u'.\n", (UINT
)GetLastError()));
151 return CAB_STATUS_CANNOT_CREATE
;
154 /*if (tmpnam(FullName) == NULL)*/
155 if ((FileHandle
= tmpfile()) == NULL
)
156 return CAB_STATUS_CANNOT_CREATE
;
158 FileHandle = fopen(FullName, "w+b");
159 if (FileHandle == NULL) {
160 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno));
161 return CAB_STATUS_CANNOT_CREATE;
168 return CAB_STATUS_SUCCESS
;
172 ULONG
CCFDATAStorage::Destroy()
174 * FUNCTION: Destroys the file
176 * Status of operation
181 CloseFile(FileHandle
);
185 return CAB_STATUS_SUCCESS
;
189 ULONG
CCFDATAStorage::Truncate()
191 * FUNCTION: Truncate the scratch file to zero bytes
193 * Status of operation
197 if( SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
198 return CAB_STATUS_FAILURE
;
199 if (!SetEndOfFile(FileHandle
))
200 return CAB_STATUS_FAILURE
;
203 FileHandle
= tmpfile();
204 if (FileHandle
== NULL
)
206 DPRINT(MID_TRACE
, ("ERROR '%i'.\n", errno
));
207 return CAB_STATUS_FAILURE
;
210 return CAB_STATUS_SUCCESS
;
214 ULONG
CCFDATAStorage::Position()
216 * FUNCTION: Returns current position in file
222 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
224 return (ULONG
)ftell(FileHandle
);
229 ULONG
CCFDATAStorage::Seek(LONG Position
)
231 * FUNCTION: Seeks to an absolute position
233 * Position = Absolute position to seek to
235 * Status of operation
239 if( SetFilePointer(FileHandle
,
242 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
243 return CAB_STATUS_FAILURE
;
245 return CAB_STATUS_SUCCESS
;
247 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0)
248 return CAB_STATUS_FAILURE
;
250 return CAB_STATUS_SUCCESS
;
255 ULONG
CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, PULONG BytesRead
)
257 * FUNCTION: Reads a CFDATA block from the file
259 * Data = Pointer to CFDATA block for the buffer
260 * Buffer = Pointer to buffer to store data read
261 * BytesWritten = Pointer to buffer to write number of bytes read
263 * Status of operation
267 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesRead
, NULL
))
268 return CAB_STATUS_CANNOT_READ
;
271 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
272 if (*BytesRead
!= Data
->CompSize
)
273 return CAB_STATUS_CANNOT_READ
;
275 return CAB_STATUS_SUCCESS
;
279 ULONG
CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, PULONG BytesWritten
)
281 * FUNCTION: Writes a CFDATA block to the file
283 * Data = Pointer to CFDATA block for the buffer
284 * Buffer = Pointer to buffer with data to write
285 * BytesWritten = Pointer to buffer to write number of bytes written
287 * Status of operation
291 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesWritten
, NULL
))
292 return CAB_STATUS_CANNOT_WRITE
;
294 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
295 if (*BytesWritten
!= Data
->CompSize
)
296 return CAB_STATUS_CANNOT_WRITE
;
298 return CAB_STATUS_SUCCESS
;
301 #endif /* CAB_READ_ONLY */
308 * FUNCTION: Default constructor
317 *CabinetReservedFile
= '\0';
320 CabinetReservedFileBuffer
= NULL
;
321 CabinetReservedFileSize
= 0;
323 FolderListHead
= NULL
;
324 FolderListTail
= NULL
;
327 CriteriaListHead
= NULL
;
328 CriteriaListTail
= NULL
;
332 CodecSelected
= false;
337 BlockIsSplit
= false;
340 FolderUncompSize
= 0;
341 BytesLeftInBlock
= 0;
343 CurrentDataNode
= NULL
;
347 CCabinet::~CCabinet()
349 * FUNCTION: Default destructor
352 if (CabinetReservedFileBuffer
!= NULL
)
354 FreeMemory(CabinetReservedFileBuffer
);
355 CabinetReservedFileBuffer
= NULL
;
356 CabinetReservedFileSize
= 0;
363 bool CCabinet::IsSeparator(char Char
)
365 * FUNCTION: Determines if a character is a separator
367 * Char = Character to check
369 * Whether it is a separator
372 if ((Char
== '\\') || (Char
== '/'))
378 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
380 * FUNCTION: Replaces \ or / with the one used by the host environment
382 * Path = Pointer to string with pathname
383 * Allocate = Specifies whether to allocate memory for the new
384 * string or to change the existing buffer
386 * Pointer to new path
393 newpath
= strdup(Path
);
409 newpath
[i
] = Path
[i
];
419 char* CCabinet::GetFileName(char* Path
)
421 * FUNCTION: Returns a pointer to file name
423 * Path = Pointer to string with pathname
425 * Pointer to filename
430 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
433 if (IsSeparator(Path
[i
- 1]))
440 void CCabinet::RemoveFileName(char* Path
)
442 * FUNCTION: Removes a file name from a path
444 * Path = Pointer to string with path
450 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
451 FileName
= GetFileName(Path
+ i
);
453 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
455 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
461 bool CCabinet::NormalizePath(char* Path
,
464 * FUNCTION: Normalizes a path
466 * Path = Pointer to string with pathname
467 * Length = Number of bytes in Path
469 * true if there was enough room in Path, or false
475 if ((n
= (ULONG
)strlen(Path
)) &&
476 (!IsSeparator(Path
[n
- 1])) &&
477 (OK
= ((n
+ 1) < Length
)))
479 Path
[n
] = DIR_SEPARATOR_CHAR
;
486 char* CCabinet::GetCabinetName()
488 * FUNCTION: Returns pointer to cabinet file name
490 * Pointer to string with name of cabinet
497 void CCabinet::SetCabinetName(char* FileName
)
499 * FUNCTION: Sets cabinet file name
501 * FileName = Pointer to string with name of cabinet
504 strcpy(CabinetName
, FileName
);
508 void CCabinet::SetDestinationPath(char* DestinationPath
)
510 * FUNCTION: Sets destination path
512 * DestinationPath = Pointer to string with name of destination path
515 strcpy(DestPath
, DestinationPath
);
516 ConvertPath(DestPath
, false);
517 if (strlen(DestPath
) > 0)
518 NormalizePath(DestPath
, PATH_MAX
);
521 ULONG
CCabinet::AddSearchCriteria(char* SearchCriteria
)
523 * FUNCTION: Adds a criteria to the search criteria list
525 * SearchCriteria = String with the search criteria to add
527 * Status of operation
530 PSEARCH_CRITERIA Criteria
;
532 // Add the criteria to the list of search criteria
533 Criteria
= (PSEARCH_CRITERIA
)AllocateMemory(sizeof(SEARCH_CRITERIA
));
536 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
537 return CAB_STATUS_NOMEMORY
;
540 Criteria
->Prev
= CriteriaListTail
;
541 Criteria
->Next
= NULL
;
544 CriteriaListTail
->Next
= Criteria
;
546 CriteriaListHead
= Criteria
;
548 CriteriaListTail
= Criteria
;
550 // Set the actual criteria string
551 Criteria
->Search
= (char*)AllocateMemory(strlen(SearchCriteria
) + 1);
552 if (!Criteria
->Search
)
554 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
555 return CAB_STATUS_NOMEMORY
;
558 strcpy(Criteria
->Search
, SearchCriteria
);
560 return CAB_STATUS_SUCCESS
;
563 void CCabinet::DestroySearchCriteria()
565 * FUNCTION: Destroys the list with the search criteria
568 PSEARCH_CRITERIA Criteria
;
569 PSEARCH_CRITERIA NextCriteria
;
571 Criteria
= CriteriaListHead
;
575 NextCriteria
= Criteria
->Next
;
577 FreeMemory(Criteria
->Search
);
578 FreeMemory(Criteria
);
580 Criteria
= NextCriteria
;
583 CriteriaListHead
= NULL
;
584 CriteriaListTail
= NULL
;
587 bool CCabinet::HasSearchCriteria()
589 * FUNCTION: Returns whether we have search criteria
591 * Whether we have search criteria or not.
594 return (CriteriaListHead
!= NULL
);
597 bool CCabinet::SetCompressionCodec(char* CodecName
)
599 * FUNCTION: Selects the codec to use for compression
601 * CodecName = Pointer to a string with the name of the codec
604 if( !strcasecmp(CodecName
, "raw") )
605 SelectCodec(CAB_CODEC_RAW
);
606 else if( !strcasecmp(CodecName
, "mszip") )
607 SelectCodec(CAB_CODEC_MSZIP
);
610 printf("ERROR: Invalid codec specified!\n");
617 char* CCabinet::GetDestinationPath()
619 * FUNCTION: Returns destination path
621 * Pointer to string with name of destination path
628 bool CCabinet::SetCabinetReservedFile(char* FileName
)
630 * FUNCTION: Sets cabinet reserved file
632 * FileName = Pointer to string with name of cabinet reserved file
635 FILEHANDLE FileHandle
;
637 char* ConvertedFileName
;
639 ConvertedFileName
= ConvertPath(FileName
, true);
641 FileHandle
= CreateFile(ConvertedFileName
, // Open this file
642 GENERIC_READ
, // Open for reading
643 FILE_SHARE_READ
, // Share for reading
645 OPEN_EXISTING
, // Existing file only
646 FILE_ATTRIBUTE_NORMAL
, // Normal file
647 NULL
); // No attribute template
648 free(ConvertedFileName
);
649 if (FileHandle
== INVALID_HANDLE_VALUE
)
651 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
655 FileHandle
= fopen(ConvertedFileName
, "rb");
656 free(ConvertedFileName
);
657 if (FileHandle
== NULL
)
659 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
664 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
665 if (CabinetReservedFileSize
== (ULONG
)-1)
667 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
671 if (CabinetReservedFileSize
== 0)
673 CloseFile(FileHandle
);
677 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
678 if (!CabinetReservedFileBuffer
)
680 CloseFile(FileHandle
);
684 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
))
686 CloseFile(FileHandle
);
690 CloseFile(FileHandle
);
692 strcpy(CabinetReservedFile
, FileName
);
698 char* CCabinet::GetCabinetReservedFile()
700 * FUNCTION: Returns cabionet reserved file
702 * Pointer to string with name of cabinet reserved file
705 return CabinetReservedFile
;
709 ULONG
CCabinet::GetCurrentDiskNumber()
711 * FUNCTION: Returns current disk number
713 * Current disk number
716 return CurrentDiskNumber
;
720 ULONG
CCabinet::Open()
722 * FUNCTION: Opens a cabinet file
724 * Status of operation
727 PCFFOLDER_NODE FolderNode
;
736 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
738 return CAB_STATUS_NOMEMORY
;
741 FileHandle
= CreateFile(CabinetName
, // Open this file
742 GENERIC_READ
, // Open for reading
743 FILE_SHARE_READ
, // Share for reading
745 OPEN_EXISTING
, // Existing file only
746 FILE_ATTRIBUTE_NORMAL
, // Normal file
747 NULL
); // No attribute template
749 if (FileHandle
== INVALID_HANDLE_VALUE
)
751 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
752 return CAB_STATUS_CANNOT_OPEN
;
755 FileHandle
= fopen(CabinetName
, "rb");
756 if (FileHandle
== NULL
)
758 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
759 return CAB_STATUS_CANNOT_OPEN
;
765 /* Load CAB header */
766 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
767 != CAB_STATUS_SUCCESS
)
769 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
770 return CAB_STATUS_INVALID_CAB
;
774 if ((BytesRead
!= sizeof(CFHEADER
)) ||
775 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
776 (CABHeader
.Version
!= CAB_VERSION
) ||
777 (CABHeader
.FolderCount
== 0 ) ||
778 (CABHeader
.FileCount
== 0 ) ||
779 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
)))
782 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
783 return CAB_STATUS_INVALID_CAB
;
788 /* Read/skip any reserved bytes */
789 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
791 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
))
792 != CAB_STATUS_SUCCESS
)
794 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
795 return CAB_STATUS_INVALID_CAB
;
797 CabinetReserved
= Size
& 0xFFFF;
798 FolderReserved
= (Size
>> 16) & 0xFF;
799 DataReserved
= (Size
>> 24) & 0xFF;
802 if (SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
804 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
805 return CAB_STATUS_FAILURE
;
808 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0)
810 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
811 return CAB_STATUS_FAILURE
;
816 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
818 /* Read name of previous cabinet */
819 Status
= ReadString(CabinetPrev
, 256);
820 if (Status
!= CAB_STATUS_SUCCESS
)
822 /* Read label of previous disk */
823 Status
= ReadString(DiskPrev
, 256);
824 if (Status
!= CAB_STATUS_SUCCESS
)
829 strcpy(CabinetPrev
, "");
830 strcpy(DiskPrev
, "");
833 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
835 /* Read name of next cabinet */
836 Status
= ReadString(CabinetNext
, 256);
837 if (Status
!= CAB_STATUS_SUCCESS
)
839 /* Read label of next disk */
840 Status
= ReadString(DiskNext
, 256);
841 if (Status
!= CAB_STATUS_SUCCESS
)
846 strcpy(CabinetNext
, "");
847 strcpy(DiskNext
, "");
850 /* Read all folders */
851 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
853 FolderNode
= NewFolderNode();
856 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
857 return CAB_STATUS_NOMEMORY
;
861 FolderNode
->UncompOffset
= FolderUncompSize
;
863 FolderNode
->Index
= Index
;
865 if ((Status
= ReadBlock(&FolderNode
->Folder
,
866 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
868 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
869 return CAB_STATUS_INVALID_CAB
;
873 /* Read file entries */
874 Status
= ReadFileTable();
875 if (Status
!= CAB_STATUS_SUCCESS
)
877 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%u).\n", (UINT
)Status
));
881 /* Read data blocks for all folders */
882 FolderNode
= FolderListHead
;
883 while (FolderNode
!= NULL
)
885 Status
= ReadDataBlocks(FolderNode
);
886 if (Status
!= CAB_STATUS_SUCCESS
)
888 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%u).\n", (UINT
)Status
));
891 FolderNode
= FolderNode
->Next
;
894 return CAB_STATUS_SUCCESS
;
898 void CCabinet::Close()
900 * FUNCTION: Closes the cabinet file
905 CloseFile(FileHandle
);
911 ULONG
CCabinet::FindFirst(PCAB_SEARCH Search
)
913 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
915 * Search = Pointer to search structure
917 * Status of operation
920 RestartSearch
= false;
921 Search
->Next
= FileListHead
;
922 return FindNext(Search
);
926 ULONG
CCabinet::FindNext(PCAB_SEARCH Search
)
928 * FUNCTION: Finds next file in the cabinet that matches a search criteria
930 * Search = Pointer to search structure
932 * Status of operation
936 PSEARCH_CRITERIA Criteria
;
941 Search
->Next
= FileListHead
;
943 /* Skip split files already extracted */
944 while ((Search
->Next
) &&
945 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
946 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
948 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
949 Search
->Next
->FileName
, (UINT
)Search
->Next
->File
.FileOffset
, (UINT
)LastFileOffset
));
950 Search
->Next
= Search
->Next
->Next
;
953 RestartSearch
= false;
956 /* Check each search criteria against each file */
959 // Some features (like displaying cabinets) don't require search criteria, so we can just break here.
960 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx".
961 if(!CriteriaListHead
)
964 Criteria
= CriteriaListHead
;
968 if(MatchFileNamePattern(Search
->Next
->FileName
, Criteria
->Search
))
974 Criteria
= Criteria
->Next
;
980 Search
->Next
= Search
->Next
->Next
;
985 if (strlen(DiskNext
) > 0)
989 SetCabinetName(CabinetNext
);
991 OnDiskChange(CabinetNext
, DiskNext
);
994 if (Status
!= CAB_STATUS_SUCCESS
)
997 Search
->Next
= FileListHead
;
999 return CAB_STATUS_NOFILE
;
1002 return CAB_STATUS_NOFILE
;
1005 Search
->File
= &Search
->Next
->File
;
1006 Search
->FileName
= Search
->Next
->FileName
;
1007 Search
->Next
= Search
->Next
->Next
;
1008 return CAB_STATUS_SUCCESS
;
1012 ULONG
CCabinet::ExtractFile(char* FileName
)
1014 * FUNCTION: Extracts a file from the cabinet
1016 * FileName = Pointer to buffer with name of file
1018 * Status of operation
1028 ULONG TotalBytesRead
;
1029 ULONG CurrentOffset
;
1031 PUCHAR CurrentBuffer
;
1032 FILEHANDLE DestFile
;
1040 CHAR DestName
[PATH_MAX
];
1041 CHAR TempName
[PATH_MAX
];
1043 Status
= LocateFile(FileName
, &File
);
1044 if (Status
!= CAB_STATUS_SUCCESS
)
1046 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1050 LastFileOffset
= File
->File
.FileOffset
;
1052 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
1055 SelectCodec(CAB_CODEC_RAW
);
1058 case CAB_COMP_MSZIP
:
1059 SelectCodec(CAB_CODEC_MSZIP
);
1063 return CAB_STATUS_UNSUPPCOMP
;
1066 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1067 (UINT
)File
->File
.FileOffset
,
1068 (UINT
)File
->File
.FileSize
,
1069 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1070 (UINT
)File
->DataBlock
->UncompOffset
));
1072 strcpy(DestName
, DestPath
);
1073 strcat(DestName
, FileName
);
1075 /* Create destination file, fail if it already exists */
1077 DestFile
= CreateFile(DestName
, // Create this file
1078 GENERIC_WRITE
, // Open for writing
1080 NULL
, // No security
1081 CREATE_NEW
, // New file only
1082 FILE_ATTRIBUTE_NORMAL
, // Normal file
1083 NULL
); // No attribute template
1084 if (DestFile
== INVALID_HANDLE_VALUE
)
1086 /* If file exists, ask to overwrite file */
1087 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1088 (OnOverwrite(&File
->File
, FileName
)))
1090 /* Create destination file, overwrite if it already exists */
1091 DestFile
= CreateFile(DestName
, // Create this file
1092 GENERIC_WRITE
, // Open for writing
1094 NULL
, // No security
1095 TRUNCATE_EXISTING
, // Truncate the file
1096 FILE_ATTRIBUTE_NORMAL
, // Normal file
1097 NULL
); // No attribute template
1098 if (DestFile
== INVALID_HANDLE_VALUE
)
1099 return CAB_STATUS_CANNOT_CREATE
;
1103 if (Status
== ERROR_FILE_EXISTS
)
1104 return CAB_STATUS_FILE_EXISTS
;
1106 return CAB_STATUS_CANNOT_CREATE
;
1110 DestFile
= fopen(DestName
, "rb");
1111 if (DestFile
!= NULL
)
1114 /* If file exists, ask to overwrite file */
1115 if (OnOverwrite(&File
->File
, FileName
))
1117 DestFile
= fopen(DestName
, "w+b");
1118 if (DestFile
== NULL
)
1119 return CAB_STATUS_CANNOT_CREATE
;
1122 return CAB_STATUS_FILE_EXISTS
;
1126 DestFile
= fopen(DestName
, "w+b");
1127 if (DestFile
== NULL
)
1128 return CAB_STATUS_CANNOT_CREATE
;
1132 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1134 CloseFile(DestFile
);
1135 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%u).\n", (UINT
)GetLastError()));
1136 return CAB_STATUS_CANNOT_WRITE
;
1139 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
1141 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1143 SetAttributesOnFile(DestName
, File
->File
.Attributes
);
1145 Buffer
= (PUCHAR
)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1148 CloseFile(DestFile
);
1149 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1150 return CAB_STATUS_NOMEMORY
;
1153 /* Call OnExtract event handler */
1154 OnExtract(&File
->File
, FileName
);
1156 /* Search to start of file */
1158 Offset
= SetFilePointer(FileHandle
,
1159 File
->DataBlock
->AbsoluteOffset
,
1162 if (Offset
== INVALID_SET_FILE_POINTER
)
1164 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1165 CloseFile(DestFile
);
1167 return CAB_STATUS_INVALID_CAB
;
1170 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1172 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1173 CloseFile(DestFile
);
1175 return CAB_STATUS_FAILURE
;
1177 Offset
= ftell(FileHandle
);
1180 Size
= File
->File
.FileSize
;
1181 Offset
= File
->File
.FileOffset
;
1182 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1186 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1191 DPRINT(MAX_TRACE
, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1192 (UINT
)File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, (UINT
)Offset
, (UINT
)Size
,
1193 (UINT
)BytesLeftInBlock
));
1195 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1197 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1199 CurrentBuffer
= Buffer
;
1203 DPRINT(MAX_TRACE
, ("Size (%u bytes).\n", (UINT
)Size
));
1205 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1206 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1208 CloseFile(DestFile
);
1210 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1211 return CAB_STATUS_INVALID_CAB
;
1214 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n",
1215 (UINT
)CFData
.Checksum
,
1217 CFData
.UncompSize
));
1219 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1221 BytesToRead
= CFData
.CompSize
;
1223 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1224 (unsigned long)CurrentBuffer
, (unsigned long)Buffer
));
1226 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1227 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1229 CloseFile(DestFile
);
1231 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1232 return CAB_STATUS_INVALID_CAB
;
1235 /* FIXME: Does not work with files generated by makecab.exe */
1237 if (CFData.Checksum != 0)
1239 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1240 if (Checksum != CFData.Checksum)
1242 CloseFile(DestFile);
1244 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1245 Checksum, CFData.Checksum));
1246 return CAB_STATUS_INVALID_CAB;
1250 TotalBytesRead
+= BytesRead
;
1252 CurrentBuffer
+= BytesRead
;
1254 if (CFData
.UncompSize
== 0)
1256 if (strlen(DiskNext
) == 0)
1258 CloseFile(DestFile
);
1260 return CAB_STATUS_NOFILE
;
1263 /* CloseCabinet() will destroy all file entries so in case
1264 FileName refers to the FileName field of a CFFOLDER_NODE
1265 structure, we have to save a copy of the filename */
1266 strcpy(TempName
, FileName
);
1270 SetCabinetName(CabinetNext
);
1272 OnDiskChange(CabinetNext
, DiskNext
);
1275 if (Status
!= CAB_STATUS_SUCCESS
)
1277 CloseFile(DestFile
);
1282 /* The first data block of the file will not be
1283 found as it is located in the previous file */
1284 Status
= LocateFile(TempName
, &File
);
1285 if (Status
== CAB_STATUS_NOFILE
)
1287 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1288 CloseFile(DestFile
);
1293 /* The file is continued in the first data block in the folder */
1294 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1296 /* Search to start of file */
1298 if( SetFilePointer(FileHandle
,
1299 File
->DataBlock
->AbsoluteOffset
,
1301 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1303 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1304 CloseFile(DestFile
);
1306 return CAB_STATUS_INVALID_CAB
;
1309 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1311 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1312 CloseFile(DestFile
);
1314 return CAB_STATUS_INVALID_CAB
;
1318 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1319 (UINT
)File
->File
.FileOffset
,
1320 (UINT
)File
->File
.FileSize
,
1321 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1322 (UINT
)File
->DataBlock
->UncompOffset
));
1324 CurrentDataNode
= File
->DataBlock
;
1327 RestartSearch
= true;
1329 } while (CFData
.UncompSize
== 0);
1331 DPRINT(MAX_TRACE
, ("TotalBytesRead (%u).\n", (UINT
)TotalBytesRead
));
1333 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1334 if (Status
!= CS_SUCCESS
)
1336 CloseFile(DestFile
);
1338 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1339 if (Status
== CS_NOMEMORY
)
1340 return CAB_STATUS_NOMEMORY
;
1341 return CAB_STATUS_INVALID_CAB
;
1344 if (BytesToWrite
!= CFData
.UncompSize
)
1346 DPRINT(MID_TRACE
, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n",
1347 (UINT
)BytesToWrite
, CFData
.UncompSize
));
1348 CloseFile(DestFile
);
1350 return CAB_STATUS_INVALID_CAB
;
1353 BytesLeftInBlock
= BytesToWrite
;
1357 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1359 BytesToWrite
= BytesLeftInBlock
;
1361 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%X.\n",
1362 (UINT
)(CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) + CurrentDataNode
->Data
.CompSize
)));
1364 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1365 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1367 CloseFile(DestFile
);
1369 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1370 return CAB_STATUS_INVALID_CAB
;
1373 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1374 CFData
.CompSize
, CFData
.UncompSize
));
1376 /* Go to next data block */
1378 if( SetFilePointer(FileHandle
,
1379 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1380 CurrentDataNode
->Data
.CompSize
,
1382 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1384 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1385 CloseFile(DestFile
);
1387 return CAB_STATUS_INVALID_CAB
;
1390 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1391 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1393 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1394 CloseFile(DestFile
);
1396 return CAB_STATUS_INVALID_CAB
;
1404 BytesSkipped
= (Offset
- CurrentOffset
);
1408 BytesToWrite
-= BytesSkipped
;
1410 if (Size
< BytesToWrite
)
1411 BytesToWrite
= Size
;
1413 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n",
1415 (UINT
)CurrentOffset
,
1417 (UINT
)BytesSkipped
, (UINT
)Skip
,
1421 if (!WriteFile(DestFile
, (void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1422 BytesToWrite
, (LPDWORD
)&BytesWritten
, NULL
) ||
1423 (BytesToWrite
!= BytesWritten
))
1425 DPRINT(MIN_TRACE
, ("Status 0x%X.\n", (UINT
)GetLastError()));
1427 BytesWritten
= BytesToWrite
;
1428 if (fwrite((void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1429 BytesToWrite
, 1, DestFile
) < 1)
1432 CloseFile(DestFile
);
1434 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1435 return CAB_STATUS_CANNOT_WRITE
;
1437 Size
-= BytesToWrite
;
1439 CurrentOffset
+= BytesToWrite
;
1441 /* Don't skip any more bytes */
1446 CloseFile(DestFile
);
1450 return CAB_STATUS_SUCCESS
;
1453 bool CCabinet::IsCodecSelected()
1455 * FUNCTION: Returns the value of CodecSelected
1457 * Whether a codec is selected
1460 return CodecSelected
;
1463 void CCabinet::SelectCodec(LONG Id
)
1465 * FUNCTION: Selects codec engine to use
1467 * Id = Codec identifier
1475 CodecSelected
= false;
1482 Codec
= new CRawCodec();
1485 case CAB_CODEC_MSZIP
:
1486 Codec
= new CMSZipCodec();
1494 CodecSelected
= true;
1498 #ifndef CAB_READ_ONLY
1500 /* CAB write methods */
1502 ULONG
CCabinet::NewCabinet()
1504 * FUNCTION: Creates a new cabinet
1506 * Status of operation
1511 CurrentDiskNumber
= 0;
1513 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1514 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1515 if ((!OutputBuffer
) || (!InputBuffer
))
1517 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1518 return CAB_STATUS_NOMEMORY
;
1520 CurrentIBuffer
= InputBuffer
;
1521 CurrentIBufferSize
= 0;
1523 CABHeader
.Signature
= CAB_SIGNATURE
;
1524 CABHeader
.Reserved1
= 0; // Not used
1525 CABHeader
.CabinetSize
= 0; // Not yet known
1526 CABHeader
.Reserved2
= 0; // Not used
1527 CABHeader
.Reserved3
= 0; // Not used
1528 CABHeader
.Version
= CAB_VERSION
;
1529 CABHeader
.FolderCount
= 0; // Not yet known
1530 CABHeader
.FileCount
= 0; // Not yet known
1531 CABHeader
.Flags
= 0; // Not yet known
1532 // FIXME: Should be random
1533 CABHeader
.SetID
= 0x534F;
1534 CABHeader
.CabinetNumber
= 0;
1537 TotalFolderSize
= 0;
1540 DiskSize
= sizeof(CFHEADER
);
1542 InitCabinetHeader();
1544 // NextFolderNumber is 0-based
1545 NextFolderNumber
= 0;
1547 CurrentFolderNode
= NULL
;
1548 Status
= NewFolder();
1549 if (Status
!= CAB_STATUS_SUCCESS
)
1552 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1554 ScratchFile
= new CCFDATAStorage
;
1557 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1558 return CAB_STATUS_NOMEMORY
;
1561 Status
= ScratchFile
->Create();
1563 CreateNewFolder
= false;
1565 CreateNewDisk
= false;
1567 PrevCabinetNumber
= 0;
1573 ULONG
CCabinet::NewDisk()
1575 * FUNCTION: Forces a new disk to be created
1577 * Status of operation
1580 // NextFolderNumber is 0-based
1581 NextFolderNumber
= 1;
1583 CreateNewDisk
= false;
1585 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1587 InitCabinetHeader();
1589 CurrentFolderNode
->TotalFolderSize
= 0;
1591 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1593 return CAB_STATUS_SUCCESS
;
1597 ULONG
CCabinet::NewFolder()
1599 * FUNCTION: Forces a new folder to be created
1601 * Status of operation
1604 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1606 CurrentFolderNode
= NewFolderNode();
1607 if (!CurrentFolderNode
)
1609 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1610 return CAB_STATUS_NOMEMORY
;
1615 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1618 case CAB_CODEC_MSZIP
:
1619 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1623 return CAB_STATUS_UNSUPPCOMP
;
1626 /* FIXME: This won't work if no files are added to the new folder */
1628 DiskSize
+= sizeof(CFFOLDER
);
1630 TotalFolderSize
+= sizeof(CFFOLDER
);
1634 CABHeader
.FolderCount
++;
1638 return CAB_STATUS_SUCCESS
;
1642 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1644 * FUNCTION: Writes a file to the scratch file
1646 * FileNode = Pointer to file node
1648 * Status of operation
1658 /* Try to open file */
1660 SourceFile
= CreateFile(
1661 FileNode
->FileName
, // Open this file
1662 GENERIC_READ
, // Open for reading
1663 FILE_SHARE_READ
, // Share for reading
1664 NULL
, // No security
1665 OPEN_EXISTING
, // File must exist
1666 FILE_ATTRIBUTE_NORMAL
, // Normal file
1667 NULL
); // No attribute template
1668 if (SourceFile
== INVALID_HANDLE_VALUE
)
1670 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1671 return CAB_STATUS_NOFILE
;
1674 SourceFile
= fopen(FileNode
->FileName
, "rb");
1675 if (SourceFile
== NULL
)
1677 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1678 return CAB_STATUS_NOFILE
;
1682 if (CreateNewFolder
)
1684 /* There is always a new folder after
1685 a split file is completely stored */
1686 Status
= NewFolder();
1687 if (Status
!= CAB_STATUS_SUCCESS
)
1689 CreateNewFolder
= false;
1692 /* Call OnAdd event handler */
1693 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1695 TotalBytesLeft
= FileNode
->File
.FileSize
;
1697 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1698 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1699 FileNode
->File
.FileControlID
= (USHORT
)(NextFolderNumber
- 1);
1700 CurrentFolderNode
->Commit
= true;
1701 PrevCabinetNumber
= CurrentDiskNumber
;
1703 Size
= sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(FileNode
->FileName
)) + 1;
1704 CABHeader
.FileTableOffset
+= Size
;
1705 TotalFileSize
+= Size
;
1709 FileNode
->Commit
= true;
1711 if (TotalBytesLeft
> 0)
1715 if (TotalBytesLeft
> (ULONG
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1716 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1718 BytesToRead
= TotalBytesLeft
;
1720 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1722 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n",
1723 (UINT
)BytesToRead
, (UINT
)BytesRead
, (UINT
)CurrentIBufferSize
));
1724 return CAB_STATUS_INVALID_CAB
;
1727 CurrentIBuffer
= (unsigned char*)CurrentIBuffer
+ BytesRead
;
1728 CurrentIBufferSize
+= (USHORT
)BytesRead
;
1730 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1732 Status
= WriteDataBlock();
1733 if (Status
!= CAB_STATUS_SUCCESS
)
1736 TotalBytesLeft
-= BytesRead
;
1737 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1740 if (TotalBytesLeft
== 0)
1742 CloseFile(SourceFile
);
1743 FileNode
->Delete
= true;
1745 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1747 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1748 CurrentFolderNode
->Delete
= true;
1750 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1752 Status
= WriteDataBlock();
1753 if (Status
!= CAB_STATUS_SUCCESS
)
1757 CreateNewFolder
= true;
1762 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1763 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1765 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1768 return CAB_STATUS_SUCCESS
;
1772 ULONG
CCabinet::WriteDisk(ULONG MoreDisks
)
1774 * FUNCTION: Forces the current disk to be written
1776 * MoreDisks = true if there is one or more disks after this disk
1778 * Status of operation
1781 PCFFILE_NODE FileNode
;
1784 ContinueFile
= false;
1785 FileNode
= FileListHead
;
1786 while (FileNode
!= NULL
)
1788 Status
= WriteFileToScratchStorage(FileNode
);
1789 if (Status
!= CAB_STATUS_SUCCESS
)
1794 /* A data block could span more than two
1795 disks if MaxDiskSize is very small */
1796 while (CreateNewDisk
)
1798 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1803 ContinueFile
= true;
1804 CreateNewDisk
= false;
1806 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1807 (UINT
)CurrentIBufferSize
, (UINT
)CurrentOBufferSize
));
1809 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1811 Status
= WriteDataBlock();
1812 if (Status
!= CAB_STATUS_SUCCESS
)
1819 ContinueFile
= false;
1820 FileNode
= FileNode
->Next
;
1824 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1826 /* A data block could span more than two
1827 disks if MaxDiskSize is very small */
1829 ASSERT(CreateNewDisk
== false);
1835 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1839 CreateNewDisk
= false;
1841 ASSERT(FileNode
== FileListHead
);
1844 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1846 Status
= WriteDataBlock();
1847 if (Status
!= CAB_STATUS_SUCCESS
)
1850 } while (CreateNewDisk
);
1852 CommitDisk(MoreDisks
);
1854 return CAB_STATUS_SUCCESS
;
1858 ULONG
CCabinet::CommitDisk(ULONG MoreDisks
)
1860 * FUNCTION: Commits the current disk
1862 * MoreDisks = true if there is one or more disks after this disk
1864 * Status of operation
1867 PCFFOLDER_NODE FolderNode
;
1870 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1872 /* Create file, fail if it already exists */
1874 FileHandle
= CreateFile(CabinetName
, // Create this file
1875 GENERIC_WRITE
, // Open for writing
1877 NULL
, // No security
1878 CREATE_NEW
, // New file only
1879 FILE_ATTRIBUTE_NORMAL
, // Normal file
1880 NULL
); // No attribute template
1881 if (FileHandle
== INVALID_HANDLE_VALUE
)
1884 /* If file exists, ask to overwrite file */
1885 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1886 (OnOverwrite(NULL
, CabinetName
)))
1889 /* Create cabinet file, overwrite if it already exists */
1890 FileHandle
= CreateFile(CabinetName
, // Create this file
1891 GENERIC_WRITE
, // Open for writing
1893 NULL
, // No security
1894 TRUNCATE_EXISTING
, // Truncate the file
1895 FILE_ATTRIBUTE_NORMAL
, // Normal file
1896 NULL
); // No attribute template
1897 if (FileHandle
== INVALID_HANDLE_VALUE
)
1898 return CAB_STATUS_CANNOT_CREATE
;
1902 if (Status
== ERROR_FILE_EXISTS
)
1903 return CAB_STATUS_FILE_EXISTS
;
1905 return CAB_STATUS_CANNOT_CREATE
;
1909 FileHandle
= fopen(CabinetName
, "rb");
1910 if (FileHandle
!= NULL
)
1913 /* If file exists, ask to overwrite file */
1914 if (OnOverwrite(NULL
, CabinetName
))
1916 FileHandle
= fopen(CabinetName
, "w+b");
1917 if (FileHandle
== NULL
)
1918 return CAB_STATUS_CANNOT_CREATE
;
1921 return CAB_STATUS_FILE_EXISTS
;
1926 FileHandle
= fopen(CabinetName
, "w+b");
1927 if (FileHandle
== NULL
)
1928 return CAB_STATUS_CANNOT_CREATE
;
1932 WriteCabinetHeader(MoreDisks
!= 0);
1934 Status
= WriteFolderEntries();
1935 if (Status
!= CAB_STATUS_SUCCESS
)
1938 /* Write file entries */
1941 /* Write data blocks */
1942 FolderNode
= FolderListHead
;
1943 while (FolderNode
!= NULL
)
1945 if (FolderNode
->Commit
)
1947 Status
= CommitDataBlocks(FolderNode
);
1948 if (Status
!= CAB_STATUS_SUCCESS
)
1950 /* Remove data blocks for folder */
1951 DestroyDataNodes(FolderNode
);
1953 FolderNode
= FolderNode
->Next
;
1956 CloseFile(FileHandle
);
1958 ScratchFile
->Truncate();
1960 return CAB_STATUS_SUCCESS
;
1964 ULONG
CCabinet::CloseDisk()
1966 * FUNCTION: Closes the current disk
1968 * Status of operation
1971 DestroyDeletedFileNodes();
1973 /* Destroy folder nodes that are completely stored */
1974 DestroyDeletedFolderNodes();
1976 CurrentDiskNumber
++;
1978 return CAB_STATUS_SUCCESS
;
1982 ULONG
CCabinet::CloseCabinet()
1984 * FUNCTION: Closes the current cabinet
1986 * Status of operation
1993 DestroyFolderNodes();
1997 FreeMemory(InputBuffer
);
2003 FreeMemory(OutputBuffer
);
2004 OutputBuffer
= NULL
;
2011 Status
= ScratchFile
->Destroy();
2016 return CAB_STATUS_SUCCESS
;
2020 ULONG
CCabinet::AddFile(char* FileName
)
2022 * FUNCTION: Adds a file to the current disk
2024 * FileName = Pointer to string with file name (full path)
2026 * Status of operation
2030 PCFFILE_NODE FileNode
;
2033 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
2036 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2037 return CAB_STATUS_NOMEMORY
;
2039 strcpy(NewFileName
, FileName
);
2040 ConvertPath(NewFileName
, false);
2042 /* Try to open file */
2044 SrcFile
= CreateFile(
2045 NewFileName
, // Open this file
2046 GENERIC_READ
, // Open for reading
2047 FILE_SHARE_READ
, // Share for reading
2048 NULL
, // No security
2049 OPEN_EXISTING
, // File must exist
2050 FILE_ATTRIBUTE_NORMAL
, // Normal file
2051 NULL
); // No attribute template
2052 if (SrcFile
== INVALID_HANDLE_VALUE
)
2054 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2055 FreeMemory(NewFileName
);
2056 return CAB_STATUS_CANNOT_OPEN
;
2059 SrcFile
= fopen(NewFileName
, "rb");
2060 if (SrcFile
== NULL
)
2062 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2063 FreeMemory(NewFileName
);
2064 return CAB_STATUS_CANNOT_OPEN
;
2068 FileNode
= NewFileNode();
2071 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2072 FreeMemory(NewFileName
);
2074 return CAB_STATUS_NOMEMORY
;
2077 FileNode
->FolderNode
= CurrentFolderNode
;
2078 FileNode
->FileName
= NewFileName
;
2080 /* FIXME: Check for and handle large files (>= 2GB) */
2081 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
2082 if (FileNode
->File
.FileSize
== (ULONG
)-1)
2084 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
2086 return CAB_STATUS_CANNOT_READ
;
2089 if (GetFileTimes(SrcFile
, FileNode
) != CAB_STATUS_SUCCESS
)
2091 DPRINT(MIN_TRACE
, ("Cannot read file times.\n"));
2093 return CAB_STATUS_CANNOT_READ
;
2096 if (GetAttributesOnFile(FileNode
) != CAB_STATUS_SUCCESS
)
2098 DPRINT(MIN_TRACE
, ("Cannot read file attributes.\n"));
2100 return CAB_STATUS_CANNOT_READ
;
2105 return CAB_STATUS_SUCCESS
;
2108 bool CCabinet::CreateSimpleCabinet()
2110 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2115 char szFilePath
[PATH_MAX
];
2116 char szFile
[PATH_MAX
];
2117 PSEARCH_CRITERIA Criteria
;
2122 WIN32_FIND_DATA FindFileData
;
2129 // Initialize a new cabinet
2130 Status
= NewCabinet();
2131 if (Status
!= CAB_STATUS_SUCCESS
)
2133 DPRINT(MIN_TRACE
, ("Cannot create cabinet (%u).\n", (UINT
)Status
));
2137 // Add each file in the criteria list
2138 Criteria
= CriteriaListHead
;
2142 // Store the file path with a trailing slash in szFilePath
2143 ConvertPath(Criteria
->Search
, false);
2144 pszFile
= strrchr(Criteria
->Search
, DIR_SEPARATOR_CHAR
);
2148 // Set the pointer to the start of the file name, not the slash
2151 strncpy(szFilePath
, Criteria
->Search
, pszFile
- Criteria
->Search
);
2152 szFilePath
[pszFile
- Criteria
->Search
] = 0;
2156 pszFile
= Criteria
->Search
;
2161 // needed for opendir()
2162 strcpy(szFilePath
, "./");
2167 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2168 hFind
= FindFirstFile(Criteria
->Search
, &FindFileData
);
2170 // Don't stop if a search criteria is not found
2171 if(hFind
== INVALID_HANDLE_VALUE
&& GetLastError() != ERROR_FILE_NOT_FOUND
)
2173 DPRINT(MIN_TRACE
, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria
->Search
, (UINT
)GetLastError()));
2179 if(!(FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
2181 strcpy(szFile
, szFilePath
);
2182 strcat(szFile
, FindFileData
.cFileName
);
2184 Status
= AddFile(szFile
);
2186 if(Status
!= CAB_STATUS_SUCCESS
)
2188 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2193 while(FindNextFile(hFind
, &FindFileData
));
2197 // 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
2198 dirp
= opendir(szFilePath
);
2202 while( (dp
= readdir(dirp
)) )
2204 strcpy(szFile
, szFilePath
);
2205 strcat(szFile
, dp
->d_name
);
2207 if(stat(szFile
, &stbuf
) == 0)
2209 if(stbuf
.st_mode
!= S_IFDIR
)
2211 if(MatchFileNamePattern(dp
->d_name
, pszFile
))
2213 Status
= AddFile(szFile
);
2215 if(Status
!= CAB_STATUS_SUCCESS
)
2217 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2225 DPRINT(MIN_TRACE
, ("stat failed, error code is %i\n", errno
));
2234 Criteria
= Criteria
->Next
;
2237 Status
= WriteDisk(false);
2238 if (Status
== CAB_STATUS_SUCCESS
)
2239 Status
= CloseDisk();
2240 if (Status
!= CAB_STATUS_SUCCESS
)
2242 DPRINT(MIN_TRACE
, ("Cannot write disk (%u).\n", (UINT
)Status
));
2251 DestroySearchCriteria();
2255 void CCabinet::SetMaxDiskSize(ULONG Size
)
2257 * FUNCTION: Sets the maximum size of the current disk
2259 * Size = Maximum size of current disk (0 means no maximum size)
2265 #endif /* CAB_READ_ONLY */
2268 /* Default event handlers */
2270 bool CCabinet::OnOverwrite(PCFFILE File
,
2273 * FUNCTION: Called when extracting a file and it already exists
2275 * File = Pointer to CFFILE for file being extracted
2276 * FileName = Pointer to buffer with name of file (full path)
2278 * true if the file should be overwritten, false if not
2285 void CCabinet::OnExtract(PCFFILE File
,
2288 * FUNCTION: Called just before extracting a file
2290 * File = Pointer to CFFILE for file being extracted
2291 * FileName = Pointer to buffer with name of file (full path)
2297 void CCabinet::OnDiskChange(char* CabinetName
,
2300 * FUNCTION: Called when a new disk is to be processed
2302 * CabinetName = Pointer to buffer with name of cabinet
2303 * DiskLabel = Pointer to buffer with label of disk
2309 #ifndef CAB_READ_ONLY
2311 void CCabinet::OnAdd(PCFFILE File
,
2314 * FUNCTION: Called just before adding a file to a cabinet
2316 * File = Pointer to CFFILE for file being added
2317 * FileName = Pointer to buffer with name of file (full path)
2323 bool CCabinet::OnDiskLabel(ULONG Number
, char* Label
)
2325 * FUNCTION: Called when a disk needs a label
2327 * Number = Cabinet number that needs a label
2328 * Label = Pointer to buffer to place label of disk
2330 * true if a disk label was returned, false if not
2337 bool CCabinet::OnCabinetName(ULONG Number
, char* Name
)
2339 * FUNCTION: Called when a cabinet needs a name
2341 * Number = Disk number that needs a name
2342 * Name = Pointer to buffer to place name of cabinet
2344 * true if a cabinet name was returned, false if not
2350 #endif /* CAB_READ_ONLY */
2352 PCFFOLDER_NODE
CCabinet::LocateFolderNode(ULONG Index
)
2354 * FUNCTION: Locates a folder node
2356 * Index = Folder index
2358 * Pointer to folder node or NULL if the folder node was not found
2361 PCFFOLDER_NODE Node
;
2365 case CAB_FILE_SPLIT
:
2366 return FolderListTail
;
2368 case CAB_FILE_CONTINUED
:
2369 case CAB_FILE_PREV_NEXT
:
2370 return FolderListHead
;
2373 Node
= FolderListHead
;
2374 while (Node
!= NULL
)
2376 if (Node
->Index
== Index
)
2384 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2386 * FUNCTION: Returns the absolute offset of a file
2388 * File = Pointer to CFFILE_NODE structure for file
2390 * Status of operation
2395 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2397 (UINT
)File
->File
.FileOffset
,
2398 (UINT
)File
->File
.FileSize
));
2400 Node
= CurrentFolderNode
->DataListHead
;
2401 while (Node
!= NULL
)
2403 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2404 (UINT
)Node
->UncompOffset
,
2405 (UINT
)(Node
->UncompOffset
+ Node
->Data
.UncompSize
),
2406 (UINT
)Node
->Data
.UncompSize
));
2408 /* Node->Data.UncompSize will be 0 if the block is split
2409 (ie. it is the last block in this cabinet) */
2410 if ((Node
->Data
.UncompSize
== 0) ||
2411 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2412 (File
->File
.FileOffset
< Node
->UncompOffset
+
2413 Node
->Data
.UncompSize
)))
2415 File
->DataBlock
= Node
;
2416 return CAB_STATUS_SUCCESS
;
2421 return CAB_STATUS_INVALID_CAB
;
2425 ULONG
CCabinet::LocateFile(char* FileName
,
2428 * FUNCTION: Locates a file in the cabinet
2430 * FileName = Pointer to string with name of file to locate
2431 * File = Address of pointer to CFFILE_NODE structure to fill
2433 * Status of operation
2435 * Current folder is set to the folder of the file
2441 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2443 Node
= FileListHead
;
2444 while (Node
!= NULL
)
2446 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2448 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2449 if (!CurrentFolderNode
)
2451 DPRINT(MID_TRACE
, ("Folder with index number (%u) not found.\n",
2452 Node
->File
.FileControlID
));
2453 return CAB_STATUS_INVALID_CAB
;
2456 if (Node
->DataBlock
== NULL
)
2457 Status
= GetAbsoluteOffset(Node
);
2459 Status
= CAB_STATUS_SUCCESS
;
2466 return CAB_STATUS_NOFILE
;
2470 ULONG
CCabinet::ReadString(char* String
, LONG MaxLength
)
2472 * FUNCTION: Reads a NULL-terminated string from the cabinet
2474 * String = Pointer to buffer to place string
2475 * MaxLength = Maximum length of string
2477 * Status of operation
2487 Status
= ReadBlock(String
, MaxLength
, &BytesRead
);
2488 if (Status
!= CAB_STATUS_SUCCESS
)
2490 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2491 return CAB_STATUS_INVALID_CAB
;
2494 // Find the terminating NULL character
2495 for (Size
= 0; Size
< MaxLength
; Size
++)
2497 if (String
[Size
] == '\0')
2506 DPRINT(MIN_TRACE
, ("Filename in the cabinet file is too long.\n"));
2507 return CAB_STATUS_INVALID_CAB
;
2510 // Compute the offset of the next CFFILE.
2511 // 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.
2512 // + 1 to skip the terminating NULL character as well.
2513 Size
= -(MaxLength
- Size
) + 1;
2516 if( SetFilePointer(FileHandle
,
2519 FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
2521 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2522 return CAB_STATUS_INVALID_CAB
;
2525 if (fseek(FileHandle
, (off_t
)Size
, SEEK_CUR
) != 0)
2527 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2528 return CAB_STATUS_INVALID_CAB
;
2531 return CAB_STATUS_SUCCESS
;
2535 ULONG
CCabinet::ReadFileTable()
2537 * FUNCTION: Reads the file table from the cabinet file
2539 * Status of operation
2547 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%X).\n",
2548 (UINT
)CABHeader
.FileTableOffset
));
2550 /* Seek to file table */
2552 if( SetFilePointer(FileHandle
,
2553 CABHeader
.FileTableOffset
,
2555 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2557 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2558 return CAB_STATUS_INVALID_CAB
;
2561 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2563 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2564 return CAB_STATUS_INVALID_CAB
;
2568 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2570 File
= NewFileNode();
2573 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2574 return CAB_STATUS_NOMEMORY
;
2577 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2578 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2580 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2581 return CAB_STATUS_INVALID_CAB
;
2584 File
->FileName
= (char*)AllocateMemory(PATH_MAX
);
2585 if (!File
->FileName
)
2587 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2588 return CAB_STATUS_NOMEMORY
;
2591 /* Read file name */
2592 Status
= ReadString(File
->FileName
, PATH_MAX
);
2593 if (Status
!= CAB_STATUS_SUCCESS
)
2596 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2598 (UINT
)File
->File
.FileOffset
,
2599 (UINT
)File
->File
.FileSize
,
2600 File
->File
.FileControlID
));
2603 return CAB_STATUS_SUCCESS
;
2607 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2609 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2611 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2613 * Status of operation
2616 ULONG AbsoluteOffset
;
2623 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2624 (UINT
)FolderNode
->Index
, (UINT
)FolderNode
->Folder
.DataOffset
));
2626 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2627 UncompOffset
= FolderNode
->UncompOffset
;
2629 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2631 Node
= NewDataNode(FolderNode
);
2634 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2635 return CAB_STATUS_NOMEMORY
;
2638 /* Seek to data block */
2640 if( SetFilePointer(FileHandle
,
2643 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2645 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2646 return CAB_STATUS_INVALID_CAB
;
2649 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2651 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2652 return CAB_STATUS_INVALID_CAB
;
2656 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2657 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2659 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2660 return CAB_STATUS_INVALID_CAB
;
2663 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2664 (UINT
)AbsoluteOffset
,
2666 (UINT
)Node
->Data
.Checksum
,
2667 Node
->Data
.CompSize
,
2668 Node
->Data
.UncompSize
));
2670 Node
->AbsoluteOffset
= AbsoluteOffset
;
2671 Node
->UncompOffset
= UncompOffset
;
2673 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2674 UncompOffset
+= Node
->Data
.UncompSize
;
2677 FolderUncompSize
= UncompOffset
;
2679 return CAB_STATUS_SUCCESS
;
2683 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2685 * FUNCTION: Creates a new folder node
2687 * Pointer to node if there was enough free memory available, otherwise NULL
2690 PCFFOLDER_NODE Node
;
2692 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2696 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2698 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2700 Node
->Prev
= FolderListTail
;
2702 if (FolderListTail
!= NULL
)
2703 FolderListTail
->Next
= Node
;
2705 FolderListHead
= Node
;
2707 FolderListTail
= Node
;
2713 PCFFILE_NODE
CCabinet::NewFileNode()
2715 * FUNCTION: Creates a new file node
2717 * FolderNode = Pointer to folder node to bind file to
2719 * Pointer to node if there was enough free memory available, otherwise NULL
2724 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2728 memset(Node
, 0, sizeof(CFFILE_NODE
));
2730 Node
->Prev
= FileListTail
;
2732 if (FileListTail
!= NULL
)
2733 FileListTail
->Next
= Node
;
2735 FileListHead
= Node
;
2737 FileListTail
= Node
;
2743 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2745 * FUNCTION: Creates a new data block node
2747 * FolderNode = Pointer to folder node to bind data block to
2749 * Pointer to node if there was enough free memory available, otherwise NULL
2754 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2758 memset(Node
, 0, sizeof(CFDATA_NODE
));
2760 Node
->Prev
= FolderNode
->DataListTail
;
2762 if (FolderNode
->DataListTail
!= NULL
)
2763 FolderNode
->DataListTail
->Next
= Node
;
2765 FolderNode
->DataListHead
= Node
;
2767 FolderNode
->DataListTail
= Node
;
2773 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2775 * FUNCTION: Destroys data block nodes bound to a folder node
2777 * FolderNode = Pointer to folder node
2780 PCFDATA_NODE PrevNode
;
2781 PCFDATA_NODE NextNode
;
2783 NextNode
= FolderNode
->DataListHead
;
2784 while (NextNode
!= NULL
)
2786 PrevNode
= NextNode
->Next
;
2787 FreeMemory(NextNode
);
2788 NextNode
= PrevNode
;
2790 FolderNode
->DataListHead
= NULL
;
2791 FolderNode
->DataListTail
= NULL
;
2795 void CCabinet::DestroyFileNodes()
2797 * FUNCTION: Destroys file nodes
2800 PCFFILE_NODE PrevNode
;
2801 PCFFILE_NODE NextNode
;
2803 NextNode
= FileListHead
;
2804 while (NextNode
!= NULL
)
2806 PrevNode
= NextNode
->Next
;
2807 if (NextNode
->FileName
)
2808 FreeMemory(NextNode
->FileName
);
2809 FreeMemory(NextNode
);
2810 NextNode
= PrevNode
;
2812 FileListHead
= NULL
;
2813 FileListTail
= NULL
;
2817 void CCabinet::DestroyDeletedFileNodes()
2819 * FUNCTION: Destroys file nodes that are marked for deletion
2822 PCFFILE_NODE CurNode
;
2823 PCFFILE_NODE NextNode
;
2825 CurNode
= FileListHead
;
2826 while (CurNode
!= NULL
)
2828 NextNode
= CurNode
->Next
;
2830 if (CurNode
->Delete
)
2832 if (CurNode
->Prev
!= NULL
)
2833 CurNode
->Prev
->Next
= CurNode
->Next
;
2836 FileListHead
= CurNode
->Next
;
2838 FileListHead
->Prev
= NULL
;
2841 if (CurNode
->Next
!= NULL
)
2842 CurNode
->Next
->Prev
= CurNode
->Prev
;
2845 FileListTail
= CurNode
->Prev
;
2847 FileListTail
->Next
= NULL
;
2850 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2852 TotalFileSize
-= (sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(CurNode
->FileName
)) + 1);
2854 if (CurNode
->FileName
)
2855 FreeMemory(CurNode
->FileName
);
2856 FreeMemory(CurNode
);
2863 void CCabinet::DestroyFolderNodes()
2865 * FUNCTION: Destroys folder nodes
2868 PCFFOLDER_NODE PrevNode
;
2869 PCFFOLDER_NODE NextNode
;
2871 NextNode
= FolderListHead
;
2872 while (NextNode
!= NULL
)
2874 PrevNode
= NextNode
->Next
;
2875 DestroyDataNodes(NextNode
);
2876 FreeMemory(NextNode
);
2877 NextNode
= PrevNode
;
2879 FolderListHead
= NULL
;
2880 FolderListTail
= NULL
;
2884 void CCabinet::DestroyDeletedFolderNodes()
2886 * FUNCTION: Destroys folder nodes that are marked for deletion
2889 PCFFOLDER_NODE CurNode
;
2890 PCFFOLDER_NODE NextNode
;
2892 CurNode
= FolderListHead
;
2893 while (CurNode
!= NULL
)
2895 NextNode
= CurNode
->Next
;
2897 if (CurNode
->Delete
)
2899 if (CurNode
->Prev
!= NULL
)
2900 CurNode
->Prev
->Next
= CurNode
->Next
;
2903 FolderListHead
= CurNode
->Next
;
2905 FolderListHead
->Prev
= NULL
;
2908 if (CurNode
->Next
!= NULL
)
2909 CurNode
->Next
->Prev
= CurNode
->Prev
;
2912 FolderListTail
= CurNode
->Prev
;
2914 FolderListTail
->Next
= NULL
;
2917 DestroyDataNodes(CurNode
);
2918 FreeMemory(CurNode
);
2920 TotalFolderSize
-= sizeof(CFFOLDER
);
2927 ULONG
CCabinet::ComputeChecksum(void* Buffer
,
2931 * FUNCTION: Computes checksum for data block
2933 * Buffer = Pointer to data buffer
2934 * Size = Length of data buffer
2935 * Seed = Previously computed checksum
2937 * Checksum of buffer
2940 int UlongCount
; // Number of ULONGs in block
2941 ULONG Checksum
; // Checksum accumulator
2945 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2946 won't accept checksums computed by this routine */
2948 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer
, (UINT
)Size
));
2950 UlongCount
= Size
/ 4; // Number of ULONGs
2951 Checksum
= Seed
; // Init checksum
2952 pb
= (unsigned char*)Buffer
; // Start at front of data block
2954 /* Checksum integral multiple of ULONGs */
2955 while (UlongCount
-- > 0)
2957 /* NOTE: Build ULONG in big/little-endian independent manner */
2958 ul
= *pb
++; // Get low-order byte
2959 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2960 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2961 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2963 Checksum
^= ul
; // Update checksum
2966 /* Checksum remainder bytes */
2971 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2973 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2975 ul
|= *pb
++; // Get low-order byte
2979 Checksum
^= ul
; // Update checksum
2981 /* Return computed checksum */
2986 ULONG
CCabinet::ReadBlock(void* Buffer
,
2990 * FUNCTION: Read a block of data from file
2992 * Buffer = Pointer to data buffer
2993 * Size = Length of data buffer
2994 * BytesRead = Pointer to ULONG that on return will contain
2995 * number of bytes read
2997 * Status of operation
3000 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
3001 return CAB_STATUS_INVALID_CAB
;
3002 return CAB_STATUS_SUCCESS
;
3005 bool CCabinet::MatchFileNamePattern(char* FileName
, char* Pattern
)
3007 * FUNCTION: Matches a wildcard character pattern against a file
3009 * FileName = The file name to check
3010 * Pattern = The pattern
3012 * Whether the pattern matches the file
3015 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
3016 * Adapted from code written by Ingo Wilken.
3017 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
3020 char* retryPattern
= NULL
;
3021 char* retryFileName
= NULL
;
3024 while (*FileName
|| *Pattern
)
3031 retryPattern
= Pattern
;
3032 retryFileName
= FileName
;
3036 if (*FileName
++ == '\0')
3042 if (*FileName
== ch
)
3051 Pattern
= retryPattern
;
3052 FileName
= ++retryFileName
;
3066 #ifndef CAB_READ_ONLY
3068 ULONG
CCabinet::InitCabinetHeader()
3070 * FUNCTION: Initializes cabinet header and optional fields
3072 * Status of operation
3078 CABHeader
.FileTableOffset
= 0; // Not known yet
3079 CABHeader
.FolderCount
= 0; // Not known yet
3080 CABHeader
.FileCount
= 0; // Not known yet
3081 CABHeader
.Flags
= 0; // Not known yet
3083 CABHeader
.CabinetNumber
= (USHORT
)CurrentDiskNumber
;
3085 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
3087 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
3088 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
3089 strcpy(CabinetPrev
, "");
3092 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
3094 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3095 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
3096 strcpy(DiskNext
, "");
3101 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3104 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3106 /* Calculate size of name of previous cabinet */
3107 TotalSize
+= (ULONG
)strlen(CabinetPrev
) + 1;
3109 /* Calculate size of label of previous disk */
3110 TotalSize
+= (ULONG
)strlen(DiskPrev
) + 1;
3113 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3116 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3118 /* Calculate size of name of next cabinet */
3119 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3121 NextFieldsSize
= Size
;
3123 /* Calculate size of label of next disk */
3124 Size
= (ULONG
)strlen(DiskNext
) + 1;
3126 NextFieldsSize
+= Size
;
3131 /* Add cabinet reserved area size if present */
3132 if (CabinetReservedFileSize
> 0)
3134 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
3135 TotalSize
+= CabinetReservedFileSize
;
3136 TotalSize
+= sizeof(ULONG
); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3139 DiskSize
+= TotalSize
;
3141 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
3143 return CAB_STATUS_SUCCESS
;
3147 ULONG
CCabinet::WriteCabinetHeader(bool MoreDisks
)
3149 * FUNCTION: Writes the cabinet header and optional fields
3151 * MoreDisks = true if next cabinet name should be included
3153 * Status of operation
3156 PCFFOLDER_NODE FolderNode
;
3157 PCFFILE_NODE FileNode
;
3163 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3164 Size
= TotalHeaderSize
;
3168 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
3169 DiskSize
-= NextFieldsSize
;
3170 Size
= TotalHeaderSize
- NextFieldsSize
;
3173 /* Set absolute folder offsets */
3174 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
3175 CABHeader
.FolderCount
= 0;
3176 FolderNode
= FolderListHead
;
3177 while (FolderNode
!= NULL
)
3179 FolderNode
->Folder
.DataOffset
= BytesWritten
;
3181 BytesWritten
+= FolderNode
->TotalFolderSize
;
3183 CABHeader
.FolderCount
++;
3185 FolderNode
= FolderNode
->Next
;
3188 /* Set absolute offset of file table */
3189 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
3191 /* Count number of files to be committed */
3192 CABHeader
.FileCount
= 0;
3193 FileNode
= FileListHead
;
3194 while (FileNode
!= NULL
)
3196 if (FileNode
->Commit
)
3197 CABHeader
.FileCount
++;
3198 FileNode
= FileNode
->Next
;
3201 CABHeader
.CabinetSize
= DiskSize
;
3205 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), (LPDWORD
)&BytesWritten
, NULL
))
3207 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3208 return CAB_STATUS_CANNOT_WRITE
;
3211 BytesWritten
= sizeof(CFHEADER
);
3212 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
3214 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3215 return CAB_STATUS_CANNOT_WRITE
;
3219 /* Write per-cabinet reserved area if present */
3220 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
3224 ReservedSize
= CabinetReservedFileSize
& 0xffff;
3225 ReservedSize
|= (0 << 16); /* Folder reserved area size */
3226 ReservedSize
|= (0 << 24); /* Folder reserved area size */
3228 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(ULONG
), (LPDWORD
)&BytesWritten
, NULL
))
3230 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3231 return CAB_STATUS_CANNOT_WRITE
;
3234 BytesWritten
= sizeof(ULONG
);
3235 if (fwrite(&ReservedSize
, sizeof(ULONG
), 1, FileHandle
) < 1)
3237 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3238 return CAB_STATUS_CANNOT_WRITE
;
3243 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, (LPDWORD
)&BytesWritten
, NULL
))
3245 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3246 return CAB_STATUS_CANNOT_WRITE
;
3249 BytesWritten
= CabinetReservedFileSize
;
3250 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
3252 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3253 return CAB_STATUS_CANNOT_WRITE
;
3258 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3260 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3262 /* Write name of previous cabinet */
3263 Size
= (ULONG
)strlen(CabinetPrev
) + 1;
3265 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3267 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3268 return CAB_STATUS_CANNOT_WRITE
;
3271 BytesWritten
= Size
;
3272 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
3274 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3275 return CAB_STATUS_CANNOT_WRITE
;
3279 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
3281 /* Write label of previous disk */
3282 Size
= (ULONG
)strlen(DiskPrev
) + 1;
3284 if (!WriteFile(FileHandle
, DiskPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3286 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3287 return CAB_STATUS_CANNOT_WRITE
;
3290 BytesWritten
= Size
;
3291 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1)
3293 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3294 return CAB_STATUS_CANNOT_WRITE
;
3299 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3301 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3303 /* Write name of next cabinet */
3304 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3306 if (!WriteFile(FileHandle
, CabinetNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3308 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3309 return CAB_STATUS_CANNOT_WRITE
;
3312 BytesWritten
= Size
;
3313 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
3315 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3316 return CAB_STATUS_CANNOT_WRITE
;
3320 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
3322 /* Write label of next disk */
3323 Size
= (ULONG
)strlen(DiskNext
) + 1;
3325 if (!WriteFile(FileHandle
, DiskNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3327 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3328 return CAB_STATUS_CANNOT_WRITE
;
3331 BytesWritten
= Size
;
3332 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1)
3334 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3335 return CAB_STATUS_CANNOT_WRITE
;
3340 return CAB_STATUS_SUCCESS
;
3344 ULONG
CCabinet::WriteFolderEntries()
3346 * FUNCTION: Writes folder entries
3348 * Status of operation
3351 PCFFOLDER_NODE FolderNode
;
3354 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
3356 FolderNode
= FolderListHead
;
3357 while (FolderNode
!= NULL
)
3359 if (FolderNode
->Commit
)
3361 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3362 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, (UINT
)FolderNode
->Folder
.DataOffset
));
3365 if (!WriteFile(FileHandle
,
3366 &FolderNode
->Folder
,
3368 (LPDWORD
)&BytesWritten
,
3371 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3372 return CAB_STATUS_CANNOT_WRITE
;
3375 BytesWritten
= sizeof(CFFOLDER
);
3376 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3378 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3379 return CAB_STATUS_CANNOT_WRITE
;
3383 FolderNode
= FolderNode
->Next
;
3386 return CAB_STATUS_SUCCESS
;
3390 ULONG
CCabinet::WriteFileEntries()
3392 * FUNCTION: Writes file entries for all files
3394 * Status of operation
3399 bool SetCont
= false;
3401 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3403 File
= FileListHead
;
3404 while (File
!= NULL
)
3408 /* Remove any continued files that ends in this disk */
3409 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3410 File
->Delete
= true;
3412 /* The file could end in the last (split) block and should therefore
3413 appear in the next disk too */
3415 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3416 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3418 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3419 File
->Delete
= false;
3423 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3424 File
->File
.FileControlID
, (UINT
)File
->File
.FileOffset
, (UINT
)File
->File
.FileSize
, File
->FileName
));
3427 if (!WriteFile(FileHandle
,
3430 (LPDWORD
)&BytesWritten
,
3432 return CAB_STATUS_CANNOT_WRITE
;
3434 BytesWritten
= sizeof(CFFILE
);
3435 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3437 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3438 return CAB_STATUS_CANNOT_WRITE
;
3443 if (!WriteFile(FileHandle
,
3444 GetFileName(File
->FileName
),
3445 (DWORD
)strlen(GetFileName(File
->FileName
)) + 1,
3446 (LPDWORD
)&BytesWritten
,
3448 return CAB_STATUS_CANNOT_WRITE
;
3450 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3451 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3453 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3454 return CAB_STATUS_CANNOT_WRITE
;
3460 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3467 return CAB_STATUS_SUCCESS
;
3471 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3473 * FUNCTION: Writes data blocks to the cabinet
3475 * FolderNode = Pointer to folder node containing the data blocks
3477 * Status of operation
3480 PCFDATA_NODE DataNode
;
3485 DataNode
= FolderNode
->DataListHead
;
3486 if (DataNode
!= NULL
)
3487 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3489 while (DataNode
!= NULL
)
3491 DPRINT(MAX_TRACE
, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3492 (UINT
)DataNode
->ScratchFilePosition
,
3493 DataNode
->Data
.CompSize
,
3494 DataNode
->Data
.UncompSize
));
3496 /* InputBuffer is free for us to use here, so we use it and avoid a
3497 memory allocation. OutputBuffer can't be used here because it may
3498 still contain valid data (if a data block spans two or more disks) */
3499 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3500 if (Status
!= CAB_STATUS_SUCCESS
)
3502 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%u).\n", (UINT
)Status
));
3507 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3508 sizeof(CFDATA
), (LPDWORD
)&BytesWritten
, NULL
))
3510 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3511 return CAB_STATUS_CANNOT_WRITE
;
3514 BytesWritten
= sizeof(CFDATA
);
3515 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3517 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3518 return CAB_STATUS_CANNOT_WRITE
;
3523 if (!WriteFile(FileHandle
, InputBuffer
,
3524 DataNode
->Data
.CompSize
, (LPDWORD
)&BytesWritten
, NULL
))
3526 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3527 return CAB_STATUS_CANNOT_WRITE
;
3530 BytesWritten
= DataNode
->Data
.CompSize
;
3531 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3533 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3534 return CAB_STATUS_CANNOT_WRITE
;
3538 DataNode
= DataNode
->Next
;
3540 return CAB_STATUS_SUCCESS
;
3544 ULONG
CCabinet::WriteDataBlock()
3546 * FUNCTION: Writes the current data block to the scratch file
3548 * Status of operation
3553 PCFDATA_NODE DataNode
;
3557 Status
= Codec
->Compress(OutputBuffer
,
3562 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3563 (UINT
)CurrentIBufferSize
, (UINT
)TotalCompSize
));
3565 CurrentOBuffer
= OutputBuffer
;
3566 CurrentOBufferSize
= TotalCompSize
;
3569 DataNode
= NewDataNode(CurrentFolderNode
);
3572 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3573 return CAB_STATUS_NOMEMORY
;
3576 DiskSize
+= sizeof(CFDATA
);
3578 if (MaxDiskSize
> 0)
3579 /* Disk size is limited */
3580 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3582 BlockIsSplit
= false;
3586 DataNode
->Data
.CompSize
= (USHORT
)(MaxDiskSize
- DiskSize
);
3587 DataNode
->Data
.UncompSize
= 0;
3588 CreateNewDisk
= true;
3592 DataNode
->Data
.CompSize
= (USHORT
)CurrentOBufferSize
;
3593 DataNode
->Data
.UncompSize
= (USHORT
)CurrentIBufferSize
;
3596 DataNode
->Data
.Checksum
= 0;
3597 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3599 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3600 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3602 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3603 (UINT
)DataNode
->Data
.Checksum
,
3604 DataNode
->Data
.CompSize
,
3605 DataNode
->Data
.UncompSize
));
3607 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3608 CurrentOBuffer
, &BytesWritten
);
3609 if (Status
!= CAB_STATUS_SUCCESS
)
3612 DiskSize
+= BytesWritten
;
3614 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3615 CurrentFolderNode
->Folder
.DataBlockCount
++;
3617 CurrentOBuffer
= (unsigned char*)CurrentOBuffer
+ DataNode
->Data
.CompSize
;
3618 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3620 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3624 CurrentIBufferSize
= 0;
3625 CurrentIBuffer
= InputBuffer
;
3628 return CAB_STATUS_SUCCESS
;
3631 #if !defined(_WIN32)
3633 void CCabinet::ConvertDateAndTime(time_t* Time
,
3637 * FUNCTION: Returns file times of a file
3639 * FileHandle = File handle of file to get file times from
3640 * File = Pointer to CFFILE node for file
3642 * Status of operation
3647 timedef
= localtime(Time
);
3649 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3650 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3651 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3653 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3654 | ((timedef
->tm_mon
+ 1) << 5)
3655 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3657 *DosTime
= (timedef
->tm_sec
<< 0)
3658 | (timedef
->tm_min
<< 5)
3659 | (timedef
->tm_hour
<< 11);
3665 ULONG
CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3667 * FUNCTION: Returns file times of a file
3669 * FileHandle = File handle of file to get file times from
3670 * File = Pointer to CFFILE node for file
3672 * Status of operation
3678 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3679 FileTimeToDosDateTime(&FileTime
,
3680 &File
->File
.FileDate
,
3681 &File
->File
.FileTime
);
3686 // Check for an absolute path
3687 if (IsSeparator(File
->FileName
[0]))
3688 strcpy(buf
, File
->FileName
);
3691 if (!getcwd(buf
, sizeof(buf
)))
3692 return CAB_STATUS_CANNOT_READ
;
3693 strcat(buf
, DIR_SEPARATOR_STRING
);
3694 strcat(buf
, File
->FileName
);
3697 if (stat(buf
, &stbuf
) == -1)
3698 return CAB_STATUS_CANNOT_READ
;
3700 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3702 return CAB_STATUS_SUCCESS
;
3706 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3708 * FUNCTION: Returns attributes on a file
3710 * File = Pointer to CFFILE node for file
3712 * Status of operation
3718 Attributes
= GetFileAttributes(File
->FileName
);
3719 if (Attributes
== -1)
3720 return CAB_STATUS_CANNOT_READ
;
3722 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3723 // The IDs for these attributes are the same in the CAB file and under Windows
3724 // If the file has any other attributes, strip them off by the logical AND.
3725 File
->File
.Attributes
= (USHORT
)(Attributes
& 0x37);
3730 // Check for an absolute path
3731 if (IsSeparator(File
->FileName
[0]))
3732 strcpy(buf
, File
->FileName
);
3735 if (!getcwd(buf
, sizeof(buf
)))
3736 return CAB_STATUS_CANNOT_READ
;
3737 strcat(buf
, DIR_SEPARATOR_STRING
);
3738 strcat(buf
, File
->FileName
);
3741 if (stat(buf
, &stbuf
) == -1)
3742 return CAB_STATUS_CANNOT_READ
;
3745 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3746 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3747 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3750 if (stbuf
.st_mode
& S_IFDIR
)
3751 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3753 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3756 return CAB_STATUS_SUCCESS
;
3760 ULONG
CCabinet::SetAttributesOnFile(char* FileName
, USHORT FileAttributes
)
3762 * FUNCTION: Sets attributes on a file
3764 * FileName = File name with path
3765 * FileAttributes = Attributes of that file
3767 * Status of operation
3771 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3772 // The IDs for these attributes are the same in the CAB file and under Windows
3773 // If the file has any other attributes, strip them off by the logical AND.
3774 SetFileAttributes(FileName
, (DWORD
)(FileAttributes
& 0x37));
3776 return CAB_STATUS_SUCCESS
;
3778 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3779 return CAB_STATUS_SUCCESS
;
3783 #endif /* CAB_READ_ONLY */