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
);
1166 return CAB_STATUS_INVALID_CAB
;
1169 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1171 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1172 CloseFile(DestFile
);
1173 return CAB_STATUS_FAILURE
;
1175 Offset
= ftell(FileHandle
);
1178 Size
= File
->File
.FileSize
;
1179 Offset
= File
->File
.FileOffset
;
1180 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1184 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1189 DPRINT(MAX_TRACE
, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1190 (UINT
)File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, (UINT
)Offset
, (UINT
)Size
,
1191 (UINT
)BytesLeftInBlock
));
1193 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1195 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1197 CurrentBuffer
= Buffer
;
1201 DPRINT(MAX_TRACE
, ("Size (%u bytes).\n", (UINT
)Size
));
1203 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1204 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1206 CloseFile(DestFile
);
1208 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1209 return CAB_STATUS_INVALID_CAB
;
1212 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n",
1213 (UINT
)CFData
.Checksum
,
1215 CFData
.UncompSize
));
1217 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1219 BytesToRead
= CFData
.CompSize
;
1221 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1222 (unsigned long)CurrentBuffer
, (unsigned long)Buffer
));
1224 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1225 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1227 CloseFile(DestFile
);
1229 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1230 return CAB_STATUS_INVALID_CAB
;
1233 /* FIXME: Does not work with files generated by makecab.exe */
1235 if (CFData.Checksum != 0)
1237 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1238 if (Checksum != CFData.Checksum)
1240 CloseFile(DestFile);
1242 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1243 Checksum, CFData.Checksum));
1244 return CAB_STATUS_INVALID_CAB;
1248 TotalBytesRead
+= BytesRead
;
1250 CurrentBuffer
+= BytesRead
;
1252 if (CFData
.UncompSize
== 0)
1254 if (strlen(DiskNext
) == 0)
1255 return CAB_STATUS_NOFILE
;
1257 /* CloseCabinet() will destroy all file entries so in case
1258 FileName refers to the FileName field of a CFFOLDER_NODE
1259 structure, we have to save a copy of the filename */
1260 strcpy(TempName
, FileName
);
1264 SetCabinetName(CabinetNext
);
1266 OnDiskChange(CabinetNext
, DiskNext
);
1269 if (Status
!= CAB_STATUS_SUCCESS
)
1272 /* The first data block of the file will not be
1273 found as it is located in the previous file */
1274 Status
= LocateFile(TempName
, &File
);
1275 if (Status
== CAB_STATUS_NOFILE
)
1277 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1281 /* The file is continued in the first data block in the folder */
1282 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1284 /* Search to start of file */
1286 if( SetFilePointer(FileHandle
,
1287 File
->DataBlock
->AbsoluteOffset
,
1289 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1291 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1292 return CAB_STATUS_INVALID_CAB
;
1295 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1297 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1298 return CAB_STATUS_INVALID_CAB
;
1302 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1303 (UINT
)File
->File
.FileOffset
,
1304 (UINT
)File
->File
.FileSize
,
1305 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1306 (UINT
)File
->DataBlock
->UncompOffset
));
1308 CurrentDataNode
= File
->DataBlock
;
1311 RestartSearch
= true;
1313 } while (CFData
.UncompSize
== 0);
1315 DPRINT(MAX_TRACE
, ("TotalBytesRead (%u).\n", (UINT
)TotalBytesRead
));
1317 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1318 if (Status
!= CS_SUCCESS
)
1320 CloseFile(DestFile
);
1322 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1323 if (Status
== CS_NOMEMORY
)
1324 return CAB_STATUS_NOMEMORY
;
1325 return CAB_STATUS_INVALID_CAB
;
1328 if (BytesToWrite
!= CFData
.UncompSize
)
1330 DPRINT(MID_TRACE
, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n",
1331 (UINT
)BytesToWrite
, CFData
.UncompSize
));
1332 return CAB_STATUS_INVALID_CAB
;
1335 BytesLeftInBlock
= BytesToWrite
;
1339 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1341 BytesToWrite
= BytesLeftInBlock
;
1343 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%X.\n",
1344 (UINT
)(CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) + CurrentDataNode
->Data
.CompSize
)));
1346 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1347 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1349 CloseFile(DestFile
);
1351 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1352 return CAB_STATUS_INVALID_CAB
;
1355 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1356 CFData
.CompSize
, CFData
.UncompSize
));
1358 /* Go to next data block */
1360 if( SetFilePointer(FileHandle
,
1361 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1362 CurrentDataNode
->Data
.CompSize
,
1364 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1366 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1367 return CAB_STATUS_INVALID_CAB
;
1370 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1371 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1373 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1374 return CAB_STATUS_INVALID_CAB
;
1382 BytesSkipped
= (Offset
- CurrentOffset
);
1386 BytesToWrite
-= BytesSkipped
;
1388 if (Size
< BytesToWrite
)
1389 BytesToWrite
= Size
;
1391 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n",
1393 (UINT
)CurrentOffset
,
1395 (UINT
)BytesSkipped
, (UINT
)Skip
,
1399 if (!WriteFile(DestFile
, (void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1400 BytesToWrite
, (LPDWORD
)&BytesWritten
, NULL
) ||
1401 (BytesToWrite
!= BytesWritten
))
1403 DPRINT(MIN_TRACE
, ("Status 0x%X.\n", (UINT
)GetLastError()));
1405 BytesWritten
= BytesToWrite
;
1406 if (fwrite((void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1407 BytesToWrite
, 1, DestFile
) < 1)
1410 CloseFile(DestFile
);
1412 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1413 return CAB_STATUS_CANNOT_WRITE
;
1415 Size
-= BytesToWrite
;
1417 CurrentOffset
+= BytesToWrite
;
1419 /* Don't skip any more bytes */
1424 CloseFile(DestFile
);
1428 return CAB_STATUS_SUCCESS
;
1431 bool CCabinet::IsCodecSelected()
1433 * FUNCTION: Returns the value of CodecSelected
1435 * Whether a codec is selected
1438 return CodecSelected
;
1441 void CCabinet::SelectCodec(LONG Id
)
1443 * FUNCTION: Selects codec engine to use
1445 * Id = Codec identifier
1453 CodecSelected
= false;
1460 Codec
= new CRawCodec();
1463 case CAB_CODEC_MSZIP
:
1464 Codec
= new CMSZipCodec();
1472 CodecSelected
= true;
1476 #ifndef CAB_READ_ONLY
1478 /* CAB write methods */
1480 ULONG
CCabinet::NewCabinet()
1482 * FUNCTION: Creates a new cabinet
1484 * Status of operation
1489 CurrentDiskNumber
= 0;
1491 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1492 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1493 if ((!OutputBuffer
) || (!InputBuffer
))
1495 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1496 return CAB_STATUS_NOMEMORY
;
1498 CurrentIBuffer
= InputBuffer
;
1499 CurrentIBufferSize
= 0;
1501 CABHeader
.Signature
= CAB_SIGNATURE
;
1502 CABHeader
.Reserved1
= 0; // Not used
1503 CABHeader
.CabinetSize
= 0; // Not yet known
1504 CABHeader
.Reserved2
= 0; // Not used
1505 CABHeader
.Reserved3
= 0; // Not used
1506 CABHeader
.Version
= CAB_VERSION
;
1507 CABHeader
.FolderCount
= 0; // Not yet known
1508 CABHeader
.FileCount
= 0; // Not yet known
1509 CABHeader
.Flags
= 0; // Not yet known
1510 // FIXME: Should be random
1511 CABHeader
.SetID
= 0x534F;
1512 CABHeader
.CabinetNumber
= 0;
1515 TotalFolderSize
= 0;
1518 DiskSize
= sizeof(CFHEADER
);
1520 InitCabinetHeader();
1522 // NextFolderNumber is 0-based
1523 NextFolderNumber
= 0;
1525 CurrentFolderNode
= NULL
;
1526 Status
= NewFolder();
1527 if (Status
!= CAB_STATUS_SUCCESS
)
1530 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1532 ScratchFile
= new CCFDATAStorage
;
1535 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1536 return CAB_STATUS_NOMEMORY
;
1539 Status
= ScratchFile
->Create();
1541 CreateNewFolder
= false;
1543 CreateNewDisk
= false;
1545 PrevCabinetNumber
= 0;
1551 ULONG
CCabinet::NewDisk()
1553 * FUNCTION: Forces a new disk to be created
1555 * Status of operation
1558 // NextFolderNumber is 0-based
1559 NextFolderNumber
= 1;
1561 CreateNewDisk
= false;
1563 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1565 InitCabinetHeader();
1567 CurrentFolderNode
->TotalFolderSize
= 0;
1569 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1571 return CAB_STATUS_SUCCESS
;
1575 ULONG
CCabinet::NewFolder()
1577 * FUNCTION: Forces a new folder to be created
1579 * Status of operation
1582 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1584 CurrentFolderNode
= NewFolderNode();
1585 if (!CurrentFolderNode
)
1587 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1588 return CAB_STATUS_NOMEMORY
;
1593 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1596 case CAB_CODEC_MSZIP
:
1597 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1601 return CAB_STATUS_UNSUPPCOMP
;
1604 /* FIXME: This won't work if no files are added to the new folder */
1606 DiskSize
+= sizeof(CFFOLDER
);
1608 TotalFolderSize
+= sizeof(CFFOLDER
);
1612 CABHeader
.FolderCount
++;
1616 return CAB_STATUS_SUCCESS
;
1620 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1622 * FUNCTION: Writes a file to the scratch file
1624 * FileNode = Pointer to file node
1626 * Status of operation
1636 /* Try to open file */
1638 SourceFile
= CreateFile(
1639 FileNode
->FileName
, // Open this file
1640 GENERIC_READ
, // Open for reading
1641 FILE_SHARE_READ
, // Share for reading
1642 NULL
, // No security
1643 OPEN_EXISTING
, // File must exist
1644 FILE_ATTRIBUTE_NORMAL
, // Normal file
1645 NULL
); // No attribute template
1646 if (SourceFile
== INVALID_HANDLE_VALUE
)
1648 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1649 return CAB_STATUS_NOFILE
;
1652 SourceFile
= fopen(FileNode
->FileName
, "rb");
1653 if (SourceFile
== NULL
)
1655 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1656 return CAB_STATUS_NOFILE
;
1660 if (CreateNewFolder
)
1662 /* There is always a new folder after
1663 a split file is completely stored */
1664 Status
= NewFolder();
1665 if (Status
!= CAB_STATUS_SUCCESS
)
1667 CreateNewFolder
= false;
1670 /* Call OnAdd event handler */
1671 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1673 TotalBytesLeft
= FileNode
->File
.FileSize
;
1675 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1676 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1677 FileNode
->File
.FileControlID
= (USHORT
)(NextFolderNumber
- 1);
1678 CurrentFolderNode
->Commit
= true;
1679 PrevCabinetNumber
= CurrentDiskNumber
;
1681 Size
= sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(FileNode
->FileName
)) + 1;
1682 CABHeader
.FileTableOffset
+= Size
;
1683 TotalFileSize
+= Size
;
1687 FileNode
->Commit
= true;
1689 if (TotalBytesLeft
> 0)
1693 if (TotalBytesLeft
> (ULONG
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1694 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1696 BytesToRead
= TotalBytesLeft
;
1698 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1700 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n",
1701 (UINT
)BytesToRead
, (UINT
)BytesRead
, (UINT
)CurrentIBufferSize
));
1702 return CAB_STATUS_INVALID_CAB
;
1705 CurrentIBuffer
= (unsigned char*)CurrentIBuffer
+ BytesRead
;
1706 CurrentIBufferSize
+= (USHORT
)BytesRead
;
1708 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1710 Status
= WriteDataBlock();
1711 if (Status
!= CAB_STATUS_SUCCESS
)
1714 TotalBytesLeft
-= BytesRead
;
1715 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1718 if (TotalBytesLeft
== 0)
1720 CloseFile(SourceFile
);
1721 FileNode
->Delete
= true;
1723 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1725 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1726 CurrentFolderNode
->Delete
= true;
1728 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1730 Status
= WriteDataBlock();
1731 if (Status
!= CAB_STATUS_SUCCESS
)
1735 CreateNewFolder
= true;
1740 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1741 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1743 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1746 return CAB_STATUS_SUCCESS
;
1750 ULONG
CCabinet::WriteDisk(ULONG MoreDisks
)
1752 * FUNCTION: Forces the current disk to be written
1754 * MoreDisks = true if there is one or more disks after this disk
1756 * Status of operation
1759 PCFFILE_NODE FileNode
;
1762 ContinueFile
= false;
1763 FileNode
= FileListHead
;
1764 while (FileNode
!= NULL
)
1766 Status
= WriteFileToScratchStorage(FileNode
);
1767 if (Status
!= CAB_STATUS_SUCCESS
)
1772 /* A data block could span more than two
1773 disks if MaxDiskSize is very small */
1774 while (CreateNewDisk
)
1776 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1781 ContinueFile
= true;
1782 CreateNewDisk
= false;
1784 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1785 (UINT
)CurrentIBufferSize
, (UINT
)CurrentOBufferSize
));
1787 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1789 Status
= WriteDataBlock();
1790 if (Status
!= CAB_STATUS_SUCCESS
)
1797 ContinueFile
= false;
1798 FileNode
= FileNode
->Next
;
1802 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1804 /* A data block could span more than two
1805 disks if MaxDiskSize is very small */
1807 ASSERT(CreateNewDisk
== false);
1813 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1817 CreateNewDisk
= false;
1819 ASSERT(FileNode
== FileListHead
);
1822 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1824 Status
= WriteDataBlock();
1825 if (Status
!= CAB_STATUS_SUCCESS
)
1828 } while (CreateNewDisk
);
1830 CommitDisk(MoreDisks
);
1832 return CAB_STATUS_SUCCESS
;
1836 ULONG
CCabinet::CommitDisk(ULONG MoreDisks
)
1838 * FUNCTION: Commits the current disk
1840 * MoreDisks = true if there is one or more disks after this disk
1842 * Status of operation
1845 PCFFOLDER_NODE FolderNode
;
1848 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1850 /* Create file, fail if it already exists */
1852 FileHandle
= CreateFile(CabinetName
, // Create this file
1853 GENERIC_WRITE
, // Open for writing
1855 NULL
, // No security
1856 CREATE_NEW
, // New file only
1857 FILE_ATTRIBUTE_NORMAL
, // Normal file
1858 NULL
); // No attribute template
1859 if (FileHandle
== INVALID_HANDLE_VALUE
)
1862 /* If file exists, ask to overwrite file */
1863 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1864 (OnOverwrite(NULL
, CabinetName
)))
1867 /* Create cabinet file, overwrite if it already exists */
1868 FileHandle
= CreateFile(CabinetName
, // Create this file
1869 GENERIC_WRITE
, // Open for writing
1871 NULL
, // No security
1872 TRUNCATE_EXISTING
, // Truncate the file
1873 FILE_ATTRIBUTE_NORMAL
, // Normal file
1874 NULL
); // No attribute template
1875 if (FileHandle
== INVALID_HANDLE_VALUE
)
1876 return CAB_STATUS_CANNOT_CREATE
;
1880 if (Status
== ERROR_FILE_EXISTS
)
1881 return CAB_STATUS_FILE_EXISTS
;
1883 return CAB_STATUS_CANNOT_CREATE
;
1887 FileHandle
= fopen(CabinetName
, "rb");
1888 if (FileHandle
!= NULL
)
1891 /* If file exists, ask to overwrite file */
1892 if (OnOverwrite(NULL
, CabinetName
))
1894 FileHandle
= fopen(CabinetName
, "w+b");
1895 if (FileHandle
== NULL
)
1896 return CAB_STATUS_CANNOT_CREATE
;
1899 return CAB_STATUS_FILE_EXISTS
;
1904 FileHandle
= fopen(CabinetName
, "w+b");
1905 if (FileHandle
== NULL
)
1906 return CAB_STATUS_CANNOT_CREATE
;
1910 WriteCabinetHeader(MoreDisks
!= 0);
1912 Status
= WriteFolderEntries();
1913 if (Status
!= CAB_STATUS_SUCCESS
)
1916 /* Write file entries */
1919 /* Write data blocks */
1920 FolderNode
= FolderListHead
;
1921 while (FolderNode
!= NULL
)
1923 if (FolderNode
->Commit
)
1925 Status
= CommitDataBlocks(FolderNode
);
1926 if (Status
!= CAB_STATUS_SUCCESS
)
1928 /* Remove data blocks for folder */
1929 DestroyDataNodes(FolderNode
);
1931 FolderNode
= FolderNode
->Next
;
1934 CloseFile(FileHandle
);
1936 ScratchFile
->Truncate();
1938 return CAB_STATUS_SUCCESS
;
1942 ULONG
CCabinet::CloseDisk()
1944 * FUNCTION: Closes the current disk
1946 * Status of operation
1949 DestroyDeletedFileNodes();
1951 /* Destroy folder nodes that are completely stored */
1952 DestroyDeletedFolderNodes();
1954 CurrentDiskNumber
++;
1956 return CAB_STATUS_SUCCESS
;
1960 ULONG
CCabinet::CloseCabinet()
1962 * FUNCTION: Closes the current cabinet
1964 * Status of operation
1971 DestroyFolderNodes();
1975 FreeMemory(InputBuffer
);
1981 FreeMemory(OutputBuffer
);
1982 OutputBuffer
= NULL
;
1989 Status
= ScratchFile
->Destroy();
1994 return CAB_STATUS_SUCCESS
;
1998 ULONG
CCabinet::AddFile(char* FileName
)
2000 * FUNCTION: Adds a file to the current disk
2002 * FileName = Pointer to string with file name (full path)
2004 * Status of operation
2008 PCFFILE_NODE FileNode
;
2011 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
2014 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2015 return CAB_STATUS_NOMEMORY
;
2017 strcpy(NewFileName
, FileName
);
2018 ConvertPath(NewFileName
, false);
2020 /* Try to open file */
2022 SrcFile
= CreateFile(
2023 NewFileName
, // Open this file
2024 GENERIC_READ
, // Open for reading
2025 FILE_SHARE_READ
, // Share for reading
2026 NULL
, // No security
2027 OPEN_EXISTING
, // File must exist
2028 FILE_ATTRIBUTE_NORMAL
, // Normal file
2029 NULL
); // No attribute template
2030 if (SrcFile
== INVALID_HANDLE_VALUE
)
2032 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2033 FreeMemory(NewFileName
);
2034 return CAB_STATUS_CANNOT_OPEN
;
2037 SrcFile
= fopen(NewFileName
, "rb");
2038 if (SrcFile
== NULL
)
2040 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2041 FreeMemory(NewFileName
);
2042 return CAB_STATUS_CANNOT_OPEN
;
2046 FileNode
= NewFileNode();
2049 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2050 FreeMemory(NewFileName
);
2052 return CAB_STATUS_NOMEMORY
;
2055 FileNode
->FolderNode
= CurrentFolderNode
;
2056 FileNode
->FileName
= NewFileName
;
2058 /* FIXME: Check for and handle large files (>= 2GB) */
2059 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
2060 if (FileNode
->File
.FileSize
== (ULONG
)-1)
2062 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
2063 FreeMemory(NewFileName
);
2065 return CAB_STATUS_CANNOT_READ
;
2068 if (GetFileTimes(SrcFile
, FileNode
) != CAB_STATUS_SUCCESS
)
2070 DPRINT(MIN_TRACE
, ("Cannot read file times.\n"));
2071 FreeMemory(NewFileName
);
2073 return CAB_STATUS_CANNOT_READ
;
2076 if (GetAttributesOnFile(FileNode
) != CAB_STATUS_SUCCESS
)
2078 DPRINT(MIN_TRACE
, ("Cannot read file attributes.\n"));
2079 FreeMemory(NewFileName
);
2081 return CAB_STATUS_CANNOT_READ
;
2086 return CAB_STATUS_SUCCESS
;
2089 bool CCabinet::CreateSimpleCabinet()
2091 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2096 char szFilePath
[PATH_MAX
];
2097 char szFile
[PATH_MAX
];
2098 PSEARCH_CRITERIA Criteria
;
2103 WIN32_FIND_DATA FindFileData
;
2110 // Initialize a new cabinet
2111 Status
= NewCabinet();
2112 if (Status
!= CAB_STATUS_SUCCESS
)
2114 DPRINT(MIN_TRACE
, ("Cannot create cabinet (%u).\n", (UINT
)Status
));
2118 // Add each file in the criteria list
2119 Criteria
= CriteriaListHead
;
2123 // Store the file path with a trailing slash in szFilePath
2124 ConvertPath(Criteria
->Search
, false);
2125 pszFile
= strrchr(Criteria
->Search
, DIR_SEPARATOR_CHAR
);
2129 // Set the pointer to the start of the file name, not the slash
2132 strncpy(szFilePath
, Criteria
->Search
, pszFile
- Criteria
->Search
);
2133 szFilePath
[pszFile
- Criteria
->Search
] = 0;
2137 pszFile
= Criteria
->Search
;
2142 // needed for opendir()
2143 strcpy(szFilePath
, "./");
2148 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2149 hFind
= FindFirstFile(Criteria
->Search
, &FindFileData
);
2151 // Don't stop if a search criteria is not found
2152 if(hFind
== INVALID_HANDLE_VALUE
&& GetLastError() != ERROR_FILE_NOT_FOUND
)
2154 DPRINT(MIN_TRACE
, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria
->Search
, (UINT
)GetLastError()));
2160 if(!(FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
2162 strcpy(szFile
, szFilePath
);
2163 strcat(szFile
, FindFileData
.cFileName
);
2165 Status
= AddFile(szFile
);
2167 if(Status
!= CAB_STATUS_SUCCESS
)
2169 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2174 while(FindNextFile(hFind
, &FindFileData
));
2178 // 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
2179 dirp
= opendir(szFilePath
);
2183 while( (dp
= readdir(dirp
)) )
2185 strcpy(szFile
, szFilePath
);
2186 strcat(szFile
, dp
->d_name
);
2188 if(stat(szFile
, &stbuf
) == 0)
2190 if(stbuf
.st_mode
!= S_IFDIR
)
2192 if(MatchFileNamePattern(dp
->d_name
, pszFile
))
2194 Status
= AddFile(szFile
);
2196 if(Status
!= CAB_STATUS_SUCCESS
)
2198 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2206 DPRINT(MIN_TRACE
, ("stat failed, error code is %i\n", errno
));
2215 Criteria
= Criteria
->Next
;
2218 Status
= WriteDisk(false);
2219 if (Status
== CAB_STATUS_SUCCESS
)
2220 Status
= CloseDisk();
2221 if (Status
!= CAB_STATUS_SUCCESS
)
2223 DPRINT(MIN_TRACE
, ("Cannot write disk (%u).\n", (UINT
)Status
));
2232 DestroySearchCriteria();
2236 void CCabinet::SetMaxDiskSize(ULONG Size
)
2238 * FUNCTION: Sets the maximum size of the current disk
2240 * Size = Maximum size of current disk (0 means no maximum size)
2246 #endif /* CAB_READ_ONLY */
2249 /* Default event handlers */
2251 bool CCabinet::OnOverwrite(PCFFILE File
,
2254 * FUNCTION: Called when extracting a file and it already exists
2256 * File = Pointer to CFFILE for file being extracted
2257 * FileName = Pointer to buffer with name of file (full path)
2259 * true if the file should be overwritten, false if not
2266 void CCabinet::OnExtract(PCFFILE File
,
2269 * FUNCTION: Called just before extracting a file
2271 * File = Pointer to CFFILE for file being extracted
2272 * FileName = Pointer to buffer with name of file (full path)
2278 void CCabinet::OnDiskChange(char* CabinetName
,
2281 * FUNCTION: Called when a new disk is to be processed
2283 * CabinetName = Pointer to buffer with name of cabinet
2284 * DiskLabel = Pointer to buffer with label of disk
2290 #ifndef CAB_READ_ONLY
2292 void CCabinet::OnAdd(PCFFILE File
,
2295 * FUNCTION: Called just before adding a file to a cabinet
2297 * File = Pointer to CFFILE for file being added
2298 * FileName = Pointer to buffer with name of file (full path)
2304 bool CCabinet::OnDiskLabel(ULONG Number
, char* Label
)
2306 * FUNCTION: Called when a disk needs a label
2308 * Number = Cabinet number that needs a label
2309 * Label = Pointer to buffer to place label of disk
2311 * true if a disk label was returned, false if not
2318 bool CCabinet::OnCabinetName(ULONG Number
, char* Name
)
2320 * FUNCTION: Called when a cabinet needs a name
2322 * Number = Disk number that needs a name
2323 * Name = Pointer to buffer to place name of cabinet
2325 * true if a cabinet name was returned, false if not
2331 #endif /* CAB_READ_ONLY */
2333 PCFFOLDER_NODE
CCabinet::LocateFolderNode(ULONG Index
)
2335 * FUNCTION: Locates a folder node
2337 * Index = Folder index
2339 * Pointer to folder node or NULL if the folder node was not found
2342 PCFFOLDER_NODE Node
;
2346 case CAB_FILE_SPLIT
:
2347 return FolderListTail
;
2349 case CAB_FILE_CONTINUED
:
2350 case CAB_FILE_PREV_NEXT
:
2351 return FolderListHead
;
2354 Node
= FolderListHead
;
2355 while (Node
!= NULL
)
2357 if (Node
->Index
== Index
)
2365 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2367 * FUNCTION: Returns the absolute offset of a file
2369 * File = Pointer to CFFILE_NODE structure for file
2371 * Status of operation
2376 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2378 (UINT
)File
->File
.FileOffset
,
2379 (UINT
)File
->File
.FileSize
));
2381 Node
= CurrentFolderNode
->DataListHead
;
2382 while (Node
!= NULL
)
2384 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2385 (UINT
)Node
->UncompOffset
,
2386 (UINT
)(Node
->UncompOffset
+ Node
->Data
.UncompSize
),
2387 (UINT
)Node
->Data
.UncompSize
));
2389 /* Node->Data.UncompSize will be 0 if the block is split
2390 (ie. it is the last block in this cabinet) */
2391 if ((Node
->Data
.UncompSize
== 0) ||
2392 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2393 (File
->File
.FileOffset
< Node
->UncompOffset
+
2394 Node
->Data
.UncompSize
)))
2396 File
->DataBlock
= Node
;
2397 return CAB_STATUS_SUCCESS
;
2402 return CAB_STATUS_INVALID_CAB
;
2406 ULONG
CCabinet::LocateFile(char* FileName
,
2409 * FUNCTION: Locates a file in the cabinet
2411 * FileName = Pointer to string with name of file to locate
2412 * File = Address of pointer to CFFILE_NODE structure to fill
2414 * Status of operation
2416 * Current folder is set to the folder of the file
2422 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2424 Node
= FileListHead
;
2425 while (Node
!= NULL
)
2427 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2429 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2430 if (!CurrentFolderNode
)
2432 DPRINT(MID_TRACE
, ("Folder with index number (%u) not found.\n",
2433 Node
->File
.FileControlID
));
2434 return CAB_STATUS_INVALID_CAB
;
2437 if (Node
->DataBlock
== NULL
)
2438 Status
= GetAbsoluteOffset(Node
);
2440 Status
= CAB_STATUS_SUCCESS
;
2447 return CAB_STATUS_NOFILE
;
2451 ULONG
CCabinet::ReadString(char* String
, LONG MaxLength
)
2453 * FUNCTION: Reads a NULL-terminated string from the cabinet
2455 * String = Pointer to buffer to place string
2456 * MaxLength = Maximum length of string
2458 * Status of operation
2468 Status
= ReadBlock(String
, MaxLength
, &BytesRead
);
2469 if (Status
!= CAB_STATUS_SUCCESS
)
2471 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2472 return CAB_STATUS_INVALID_CAB
;
2475 // Find the terminating NULL character
2476 for (Size
= 0; Size
< MaxLength
; Size
++)
2478 if (String
[Size
] == '\0')
2487 DPRINT(MIN_TRACE
, ("Filename in the cabinet file is too long.\n"));
2488 return CAB_STATUS_INVALID_CAB
;
2491 // Compute the offset of the next CFFILE.
2492 // 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.
2493 // + 1 to skip the terminating NULL character as well.
2494 Size
= -(MaxLength
- Size
) + 1;
2497 if( SetFilePointer(FileHandle
,
2500 FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
2502 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2503 return CAB_STATUS_INVALID_CAB
;
2506 if (fseek(FileHandle
, (off_t
)Size
, SEEK_CUR
) != 0)
2508 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2509 return CAB_STATUS_INVALID_CAB
;
2512 return CAB_STATUS_SUCCESS
;
2516 ULONG
CCabinet::ReadFileTable()
2518 * FUNCTION: Reads the file table from the cabinet file
2520 * Status of operation
2528 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%X).\n",
2529 (UINT
)CABHeader
.FileTableOffset
));
2531 /* Seek to file table */
2533 if( SetFilePointer(FileHandle
,
2534 CABHeader
.FileTableOffset
,
2536 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2538 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2539 return CAB_STATUS_INVALID_CAB
;
2542 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2544 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2545 return CAB_STATUS_INVALID_CAB
;
2549 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2551 File
= NewFileNode();
2554 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2555 return CAB_STATUS_NOMEMORY
;
2558 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2559 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2561 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2562 return CAB_STATUS_INVALID_CAB
;
2565 File
->FileName
= (char*)AllocateMemory(PATH_MAX
);
2566 if (!File
->FileName
)
2568 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2569 return CAB_STATUS_NOMEMORY
;
2572 /* Read file name */
2573 Status
= ReadString(File
->FileName
, PATH_MAX
);
2574 if (Status
!= CAB_STATUS_SUCCESS
)
2577 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2579 (UINT
)File
->File
.FileOffset
,
2580 (UINT
)File
->File
.FileSize
,
2581 File
->File
.FileControlID
));
2584 return CAB_STATUS_SUCCESS
;
2588 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2590 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2592 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2594 * Status of operation
2597 ULONG AbsoluteOffset
;
2604 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2605 (UINT
)FolderNode
->Index
, (UINT
)FolderNode
->Folder
.DataOffset
));
2607 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2608 UncompOffset
= FolderNode
->UncompOffset
;
2610 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2612 Node
= NewDataNode(FolderNode
);
2615 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2616 return CAB_STATUS_NOMEMORY
;
2619 /* Seek to data block */
2621 if( SetFilePointer(FileHandle
,
2624 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2626 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2627 return CAB_STATUS_INVALID_CAB
;
2630 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2632 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2633 return CAB_STATUS_INVALID_CAB
;
2637 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2638 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2640 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2641 return CAB_STATUS_INVALID_CAB
;
2644 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2645 (UINT
)AbsoluteOffset
,
2647 (UINT
)Node
->Data
.Checksum
,
2648 Node
->Data
.CompSize
,
2649 Node
->Data
.UncompSize
));
2651 Node
->AbsoluteOffset
= AbsoluteOffset
;
2652 Node
->UncompOffset
= UncompOffset
;
2654 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2655 UncompOffset
+= Node
->Data
.UncompSize
;
2658 FolderUncompSize
= UncompOffset
;
2660 return CAB_STATUS_SUCCESS
;
2664 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2666 * FUNCTION: Creates a new folder node
2668 * Pointer to node if there was enough free memory available, otherwise NULL
2671 PCFFOLDER_NODE Node
;
2673 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2677 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2679 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2681 Node
->Prev
= FolderListTail
;
2683 if (FolderListTail
!= NULL
)
2684 FolderListTail
->Next
= Node
;
2686 FolderListHead
= Node
;
2688 FolderListTail
= Node
;
2694 PCFFILE_NODE
CCabinet::NewFileNode()
2696 * FUNCTION: Creates a new file node
2698 * FolderNode = Pointer to folder node to bind file to
2700 * Pointer to node if there was enough free memory available, otherwise NULL
2705 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2709 memset(Node
, 0, sizeof(CFFILE_NODE
));
2711 Node
->Prev
= FileListTail
;
2713 if (FileListTail
!= NULL
)
2714 FileListTail
->Next
= Node
;
2716 FileListHead
= Node
;
2718 FileListTail
= Node
;
2724 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2726 * FUNCTION: Creates a new data block node
2728 * FolderNode = Pointer to folder node to bind data block to
2730 * Pointer to node if there was enough free memory available, otherwise NULL
2735 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2739 memset(Node
, 0, sizeof(CFDATA_NODE
));
2741 Node
->Prev
= FolderNode
->DataListTail
;
2743 if (FolderNode
->DataListTail
!= NULL
)
2744 FolderNode
->DataListTail
->Next
= Node
;
2746 FolderNode
->DataListHead
= Node
;
2748 FolderNode
->DataListTail
= Node
;
2754 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2756 * FUNCTION: Destroys data block nodes bound to a folder node
2758 * FolderNode = Pointer to folder node
2761 PCFDATA_NODE PrevNode
;
2762 PCFDATA_NODE NextNode
;
2764 NextNode
= FolderNode
->DataListHead
;
2765 while (NextNode
!= NULL
)
2767 PrevNode
= NextNode
->Next
;
2768 FreeMemory(NextNode
);
2769 NextNode
= PrevNode
;
2771 FolderNode
->DataListHead
= NULL
;
2772 FolderNode
->DataListTail
= NULL
;
2776 void CCabinet::DestroyFileNodes()
2778 * FUNCTION: Destroys file nodes
2781 PCFFILE_NODE PrevNode
;
2782 PCFFILE_NODE NextNode
;
2784 NextNode
= FileListHead
;
2785 while (NextNode
!= NULL
)
2787 PrevNode
= NextNode
->Next
;
2788 if (NextNode
->FileName
)
2789 FreeMemory(NextNode
->FileName
);
2790 FreeMemory(NextNode
);
2791 NextNode
= PrevNode
;
2793 FileListHead
= NULL
;
2794 FileListTail
= NULL
;
2798 void CCabinet::DestroyDeletedFileNodes()
2800 * FUNCTION: Destroys file nodes that are marked for deletion
2803 PCFFILE_NODE CurNode
;
2804 PCFFILE_NODE NextNode
;
2806 CurNode
= FileListHead
;
2807 while (CurNode
!= NULL
)
2809 NextNode
= CurNode
->Next
;
2811 if (CurNode
->Delete
)
2813 if (CurNode
->Prev
!= NULL
)
2814 CurNode
->Prev
->Next
= CurNode
->Next
;
2817 FileListHead
= CurNode
->Next
;
2819 FileListHead
->Prev
= NULL
;
2822 if (CurNode
->Next
!= NULL
)
2823 CurNode
->Next
->Prev
= CurNode
->Prev
;
2826 FileListTail
= CurNode
->Prev
;
2828 FileListTail
->Next
= NULL
;
2831 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2833 TotalFileSize
-= (sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(CurNode
->FileName
)) + 1);
2835 if (CurNode
->FileName
)
2836 FreeMemory(CurNode
->FileName
);
2837 FreeMemory(CurNode
);
2844 void CCabinet::DestroyFolderNodes()
2846 * FUNCTION: Destroys folder nodes
2849 PCFFOLDER_NODE PrevNode
;
2850 PCFFOLDER_NODE NextNode
;
2852 NextNode
= FolderListHead
;
2853 while (NextNode
!= NULL
)
2855 PrevNode
= NextNode
->Next
;
2856 DestroyDataNodes(NextNode
);
2857 FreeMemory(NextNode
);
2858 NextNode
= PrevNode
;
2860 FolderListHead
= NULL
;
2861 FolderListTail
= NULL
;
2865 void CCabinet::DestroyDeletedFolderNodes()
2867 * FUNCTION: Destroys folder nodes that are marked for deletion
2870 PCFFOLDER_NODE CurNode
;
2871 PCFFOLDER_NODE NextNode
;
2873 CurNode
= FolderListHead
;
2874 while (CurNode
!= NULL
)
2876 NextNode
= CurNode
->Next
;
2878 if (CurNode
->Delete
)
2880 if (CurNode
->Prev
!= NULL
)
2881 CurNode
->Prev
->Next
= CurNode
->Next
;
2884 FolderListHead
= CurNode
->Next
;
2886 FolderListHead
->Prev
= NULL
;
2889 if (CurNode
->Next
!= NULL
)
2890 CurNode
->Next
->Prev
= CurNode
->Prev
;
2893 FolderListTail
= CurNode
->Prev
;
2895 FolderListTail
->Next
= NULL
;
2898 DestroyDataNodes(CurNode
);
2899 FreeMemory(CurNode
);
2901 TotalFolderSize
-= sizeof(CFFOLDER
);
2908 ULONG
CCabinet::ComputeChecksum(void* Buffer
,
2912 * FUNCTION: Computes checksum for data block
2914 * Buffer = Pointer to data buffer
2915 * Size = Length of data buffer
2916 * Seed = Previously computed checksum
2918 * Checksum of buffer
2921 int UlongCount
; // Number of ULONGs in block
2922 ULONG Checksum
; // Checksum accumulator
2926 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2927 won't accept checksums computed by this routine */
2929 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer
, (UINT
)Size
));
2931 UlongCount
= Size
/ 4; // Number of ULONGs
2932 Checksum
= Seed
; // Init checksum
2933 pb
= (unsigned char*)Buffer
; // Start at front of data block
2935 /* Checksum integral multiple of ULONGs */
2936 while (UlongCount
-- > 0)
2938 /* NOTE: Build ULONG in big/little-endian independent manner */
2939 ul
= *pb
++; // Get low-order byte
2940 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2941 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2942 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2944 Checksum
^= ul
; // Update checksum
2947 /* Checksum remainder bytes */
2952 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2954 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2956 ul
|= *pb
++; // Get low-order byte
2960 Checksum
^= ul
; // Update checksum
2962 /* Return computed checksum */
2967 ULONG
CCabinet::ReadBlock(void* Buffer
,
2971 * FUNCTION: Read a block of data from file
2973 * Buffer = Pointer to data buffer
2974 * Size = Length of data buffer
2975 * BytesRead = Pointer to ULONG that on return will contain
2976 * number of bytes read
2978 * Status of operation
2981 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2982 return CAB_STATUS_INVALID_CAB
;
2983 return CAB_STATUS_SUCCESS
;
2986 bool CCabinet::MatchFileNamePattern(char* FileName
, char* Pattern
)
2988 * FUNCTION: Matches a wildcard character pattern against a file
2990 * FileName = The file name to check
2991 * Pattern = The pattern
2993 * Whether the pattern matches the file
2996 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
2997 * Adapted from code written by Ingo Wilken.
2998 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
3001 char* retryPattern
= NULL
;
3002 char* retryFileName
= NULL
;
3005 while (*FileName
|| *Pattern
)
3012 retryPattern
= Pattern
;
3013 retryFileName
= FileName
;
3017 if (*FileName
++ == '\0')
3023 if (*FileName
== ch
)
3032 Pattern
= retryPattern
;
3033 FileName
= ++retryFileName
;
3047 #ifndef CAB_READ_ONLY
3049 ULONG
CCabinet::InitCabinetHeader()
3051 * FUNCTION: Initializes cabinet header and optional fields
3053 * Status of operation
3059 CABHeader
.FileTableOffset
= 0; // Not known yet
3060 CABHeader
.FolderCount
= 0; // Not known yet
3061 CABHeader
.FileCount
= 0; // Not known yet
3062 CABHeader
.Flags
= 0; // Not known yet
3064 CABHeader
.CabinetNumber
= (USHORT
)CurrentDiskNumber
;
3066 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
3068 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
3069 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
3070 strcpy(CabinetPrev
, "");
3073 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
3075 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3076 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
3077 strcpy(DiskNext
, "");
3082 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3085 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3087 /* Calculate size of name of previous cabinet */
3088 TotalSize
+= (ULONG
)strlen(CabinetPrev
) + 1;
3090 /* Calculate size of label of previous disk */
3091 TotalSize
+= (ULONG
)strlen(DiskPrev
) + 1;
3094 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3097 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3099 /* Calculate size of name of next cabinet */
3100 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3102 NextFieldsSize
= Size
;
3104 /* Calculate size of label of next disk */
3105 Size
= (ULONG
)strlen(DiskNext
) + 1;
3107 NextFieldsSize
+= Size
;
3112 /* Add cabinet reserved area size if present */
3113 if (CabinetReservedFileSize
> 0)
3115 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
3116 TotalSize
+= CabinetReservedFileSize
;
3117 TotalSize
+= sizeof(ULONG
); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3120 DiskSize
+= TotalSize
;
3122 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
3124 return CAB_STATUS_SUCCESS
;
3128 ULONG
CCabinet::WriteCabinetHeader(bool MoreDisks
)
3130 * FUNCTION: Writes the cabinet header and optional fields
3132 * MoreDisks = true if next cabinet name should be included
3134 * Status of operation
3137 PCFFOLDER_NODE FolderNode
;
3138 PCFFILE_NODE FileNode
;
3144 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3145 Size
= TotalHeaderSize
;
3149 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
3150 DiskSize
-= NextFieldsSize
;
3151 Size
= TotalHeaderSize
- NextFieldsSize
;
3154 /* Set absolute folder offsets */
3155 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
3156 CABHeader
.FolderCount
= 0;
3157 FolderNode
= FolderListHead
;
3158 while (FolderNode
!= NULL
)
3160 FolderNode
->Folder
.DataOffset
= BytesWritten
;
3162 BytesWritten
+= FolderNode
->TotalFolderSize
;
3164 CABHeader
.FolderCount
++;
3166 FolderNode
= FolderNode
->Next
;
3169 /* Set absolute offset of file table */
3170 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
3172 /* Count number of files to be committed */
3173 CABHeader
.FileCount
= 0;
3174 FileNode
= FileListHead
;
3175 while (FileNode
!= NULL
)
3177 if (FileNode
->Commit
)
3178 CABHeader
.FileCount
++;
3179 FileNode
= FileNode
->Next
;
3182 CABHeader
.CabinetSize
= DiskSize
;
3186 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), (LPDWORD
)&BytesWritten
, NULL
))
3188 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3189 return CAB_STATUS_CANNOT_WRITE
;
3192 BytesWritten
= sizeof(CFHEADER
);
3193 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
3195 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3196 return CAB_STATUS_CANNOT_WRITE
;
3200 /* Write per-cabinet reserved area if present */
3201 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
3205 ReservedSize
= CabinetReservedFileSize
& 0xffff;
3206 ReservedSize
|= (0 << 16); /* Folder reserved area size */
3207 ReservedSize
|= (0 << 24); /* Folder reserved area size */
3209 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(ULONG
), (LPDWORD
)&BytesWritten
, NULL
))
3211 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3212 return CAB_STATUS_CANNOT_WRITE
;
3215 BytesWritten
= sizeof(ULONG
);
3216 if (fwrite(&ReservedSize
, sizeof(ULONG
), 1, FileHandle
) < 1)
3218 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3219 return CAB_STATUS_CANNOT_WRITE
;
3224 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, (LPDWORD
)&BytesWritten
, NULL
))
3226 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3227 return CAB_STATUS_CANNOT_WRITE
;
3230 BytesWritten
= CabinetReservedFileSize
;
3231 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
3233 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3234 return CAB_STATUS_CANNOT_WRITE
;
3239 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3241 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3243 /* Write name of previous cabinet */
3244 Size
= (ULONG
)strlen(CabinetPrev
) + 1;
3246 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3248 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3249 return CAB_STATUS_CANNOT_WRITE
;
3252 BytesWritten
= Size
;
3253 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
3255 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3256 return CAB_STATUS_CANNOT_WRITE
;
3260 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
3262 /* Write label of previous disk */
3263 Size
= (ULONG
)strlen(DiskPrev
) + 1;
3265 if (!WriteFile(FileHandle
, DiskPrev
, 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(DiskPrev
, Size
, 1, FileHandle
) < 1)
3274 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3275 return CAB_STATUS_CANNOT_WRITE
;
3280 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3282 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3284 /* Write name of next cabinet */
3285 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3287 if (!WriteFile(FileHandle
, CabinetNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3289 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3290 return CAB_STATUS_CANNOT_WRITE
;
3293 BytesWritten
= Size
;
3294 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
3296 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3297 return CAB_STATUS_CANNOT_WRITE
;
3301 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
3303 /* Write label of next disk */
3304 Size
= (ULONG
)strlen(DiskNext
) + 1;
3306 if (!WriteFile(FileHandle
, DiskNext
, 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(DiskNext
, Size
, 1, FileHandle
) < 1)
3315 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3316 return CAB_STATUS_CANNOT_WRITE
;
3321 return CAB_STATUS_SUCCESS
;
3325 ULONG
CCabinet::WriteFolderEntries()
3327 * FUNCTION: Writes folder entries
3329 * Status of operation
3332 PCFFOLDER_NODE FolderNode
;
3335 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
3337 FolderNode
= FolderListHead
;
3338 while (FolderNode
!= NULL
)
3340 if (FolderNode
->Commit
)
3342 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3343 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, (UINT
)FolderNode
->Folder
.DataOffset
));
3346 if (!WriteFile(FileHandle
,
3347 &FolderNode
->Folder
,
3349 (LPDWORD
)&BytesWritten
,
3352 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3353 return CAB_STATUS_CANNOT_WRITE
;
3356 BytesWritten
= sizeof(CFFOLDER
);
3357 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3359 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3360 return CAB_STATUS_CANNOT_WRITE
;
3364 FolderNode
= FolderNode
->Next
;
3367 return CAB_STATUS_SUCCESS
;
3371 ULONG
CCabinet::WriteFileEntries()
3373 * FUNCTION: Writes file entries for all files
3375 * Status of operation
3380 bool SetCont
= false;
3382 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3384 File
= FileListHead
;
3385 while (File
!= NULL
)
3389 /* Remove any continued files that ends in this disk */
3390 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3391 File
->Delete
= true;
3393 /* The file could end in the last (split) block and should therefore
3394 appear in the next disk too */
3396 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3397 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3399 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3400 File
->Delete
= false;
3404 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3405 File
->File
.FileControlID
, (UINT
)File
->File
.FileOffset
, (UINT
)File
->File
.FileSize
, File
->FileName
));
3408 if (!WriteFile(FileHandle
,
3411 (LPDWORD
)&BytesWritten
,
3413 return CAB_STATUS_CANNOT_WRITE
;
3415 BytesWritten
= sizeof(CFFILE
);
3416 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3418 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3419 return CAB_STATUS_CANNOT_WRITE
;
3424 if (!WriteFile(FileHandle
,
3425 GetFileName(File
->FileName
),
3426 (DWORD
)strlen(GetFileName(File
->FileName
)) + 1,
3427 (LPDWORD
)&BytesWritten
,
3429 return CAB_STATUS_CANNOT_WRITE
;
3431 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3432 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3434 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3435 return CAB_STATUS_CANNOT_WRITE
;
3441 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3448 return CAB_STATUS_SUCCESS
;
3452 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3454 * FUNCTION: Writes data blocks to the cabinet
3456 * FolderNode = Pointer to folder node containing the data blocks
3458 * Status of operation
3461 PCFDATA_NODE DataNode
;
3466 DataNode
= FolderNode
->DataListHead
;
3467 if (DataNode
!= NULL
)
3468 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3470 while (DataNode
!= NULL
)
3472 DPRINT(MAX_TRACE
, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3473 (UINT
)DataNode
->ScratchFilePosition
,
3474 DataNode
->Data
.CompSize
,
3475 DataNode
->Data
.UncompSize
));
3477 /* InputBuffer is free for us to use here, so we use it and avoid a
3478 memory allocation. OutputBuffer can't be used here because it may
3479 still contain valid data (if a data block spans two or more disks) */
3480 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3481 if (Status
!= CAB_STATUS_SUCCESS
)
3483 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%u).\n", (UINT
)Status
));
3488 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3489 sizeof(CFDATA
), (LPDWORD
)&BytesWritten
, NULL
))
3491 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3492 return CAB_STATUS_CANNOT_WRITE
;
3495 BytesWritten
= sizeof(CFDATA
);
3496 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3498 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3499 return CAB_STATUS_CANNOT_WRITE
;
3504 if (!WriteFile(FileHandle
, InputBuffer
,
3505 DataNode
->Data
.CompSize
, (LPDWORD
)&BytesWritten
, NULL
))
3507 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3508 return CAB_STATUS_CANNOT_WRITE
;
3511 BytesWritten
= DataNode
->Data
.CompSize
;
3512 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3514 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3515 return CAB_STATUS_CANNOT_WRITE
;
3519 DataNode
= DataNode
->Next
;
3521 return CAB_STATUS_SUCCESS
;
3525 ULONG
CCabinet::WriteDataBlock()
3527 * FUNCTION: Writes the current data block to the scratch file
3529 * Status of operation
3534 PCFDATA_NODE DataNode
;
3538 Status
= Codec
->Compress(OutputBuffer
,
3543 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3544 (UINT
)CurrentIBufferSize
, (UINT
)TotalCompSize
));
3546 CurrentOBuffer
= OutputBuffer
;
3547 CurrentOBufferSize
= TotalCompSize
;
3550 DataNode
= NewDataNode(CurrentFolderNode
);
3553 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3554 return CAB_STATUS_NOMEMORY
;
3557 DiskSize
+= sizeof(CFDATA
);
3559 if (MaxDiskSize
> 0)
3560 /* Disk size is limited */
3561 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3563 BlockIsSplit
= false;
3567 DataNode
->Data
.CompSize
= (USHORT
)(MaxDiskSize
- DiskSize
);
3568 DataNode
->Data
.UncompSize
= 0;
3569 CreateNewDisk
= true;
3573 DataNode
->Data
.CompSize
= (USHORT
)CurrentOBufferSize
;
3574 DataNode
->Data
.UncompSize
= (USHORT
)CurrentIBufferSize
;
3577 DataNode
->Data
.Checksum
= 0;
3578 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3580 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3581 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3583 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3584 (UINT
)DataNode
->Data
.Checksum
,
3585 DataNode
->Data
.CompSize
,
3586 DataNode
->Data
.UncompSize
));
3588 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3589 CurrentOBuffer
, &BytesWritten
);
3590 if (Status
!= CAB_STATUS_SUCCESS
)
3593 DiskSize
+= BytesWritten
;
3595 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3596 CurrentFolderNode
->Folder
.DataBlockCount
++;
3598 CurrentOBuffer
= (unsigned char*)CurrentOBuffer
+ DataNode
->Data
.CompSize
;
3599 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3601 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3605 CurrentIBufferSize
= 0;
3606 CurrentIBuffer
= InputBuffer
;
3609 return CAB_STATUS_SUCCESS
;
3612 #if !defined(_WIN32)
3614 void CCabinet::ConvertDateAndTime(time_t* Time
,
3618 * FUNCTION: Returns file times of a file
3620 * FileHandle = File handle of file to get file times from
3621 * File = Pointer to CFFILE node for file
3623 * Status of operation
3628 timedef
= localtime(Time
);
3630 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3631 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3632 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3634 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3635 | ((timedef
->tm_mon
+ 1) << 5)
3636 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3638 *DosTime
= (timedef
->tm_sec
<< 0)
3639 | (timedef
->tm_min
<< 5)
3640 | (timedef
->tm_hour
<< 11);
3646 ULONG
CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3648 * FUNCTION: Returns file times of a file
3650 * FileHandle = File handle of file to get file times from
3651 * File = Pointer to CFFILE node for file
3653 * Status of operation
3659 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3660 FileTimeToDosDateTime(&FileTime
,
3661 &File
->File
.FileDate
,
3662 &File
->File
.FileTime
);
3667 // Check for an absolute path
3668 if (IsSeparator(File
->FileName
[0]))
3669 strcpy(buf
, File
->FileName
);
3672 if (!getcwd(buf
, sizeof(buf
)))
3673 return CAB_STATUS_CANNOT_READ
;
3674 strcat(buf
, DIR_SEPARATOR_STRING
);
3675 strcat(buf
, File
->FileName
);
3678 if (stat(buf
, &stbuf
) == -1)
3679 return CAB_STATUS_CANNOT_READ
;
3681 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3683 return CAB_STATUS_SUCCESS
;
3687 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3689 * FUNCTION: Returns attributes on a file
3691 * File = Pointer to CFFILE node for file
3693 * Status of operation
3699 Attributes
= GetFileAttributes(File
->FileName
);
3700 if (Attributes
== -1)
3701 return CAB_STATUS_CANNOT_READ
;
3703 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3704 // The IDs for these attributes are the same in the CAB file and under Windows
3705 // If the file has any other attributes, strip them off by the logical AND.
3706 File
->File
.Attributes
= (USHORT
)(Attributes
& 0x37);
3711 // Check for an absolute path
3712 if (IsSeparator(File
->FileName
[0]))
3713 strcpy(buf
, File
->FileName
);
3716 if (!getcwd(buf
, sizeof(buf
)))
3717 return CAB_STATUS_CANNOT_READ
;
3718 strcat(buf
, DIR_SEPARATOR_STRING
);
3719 strcat(buf
, File
->FileName
);
3722 if (stat(buf
, &stbuf
) == -1)
3723 return CAB_STATUS_CANNOT_READ
;
3726 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3727 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3728 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3731 if (stbuf
.st_mode
& S_IFDIR
)
3732 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3734 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3737 return CAB_STATUS_SUCCESS
;
3741 ULONG
CCabinet::SetAttributesOnFile(char* FileName
, USHORT FileAttributes
)
3743 * FUNCTION: Sets attributes on a file
3745 * FileName = File name with path
3746 * FileAttributes = Attributes of that file
3748 * Status of operation
3752 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3753 // The IDs for these attributes are the same in the CAB file and under Windows
3754 // If the file has any other attributes, strip them off by the logical AND.
3755 SetFileAttributes(FileName
, (DWORD
)(FileAttributes
& 0x37));
3757 return CAB_STATUS_SUCCESS
;
3759 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3760 return CAB_STATUS_SUCCESS
;
3764 #endif /* CAB_READ_ONLY */