2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cpp
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Colin Finck <mail@colinfinck.de>
8 * NOTES: Define CAB_READ_ONLY for read only version
10 * CSH 21/03-2001 Created
11 * CSH 15/08-2003 Made it portable
12 * CF 04/05-2007 Made it compatible with 64-bit operating systems
14 * - Checksum of datablocks should be calculated
15 * - EXTRACT.EXE complains if a disk is created manually
16 * - Folders that are created manually and span disks will result in a damaged cabinet
24 #if defined(__FreeBSD__) || defined(__APPLE__)
25 # include <sys/stat.h>
32 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
33 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
35 ULONG size
= GetFileSize(handle
, NULL
);
36 if (size
== INVALID_FILE_SIZE
)
41 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
42 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
44 return ReadFile(handle
, buffer
, size
, (LPDWORD
)bytesread
, NULL
) != 0;
47 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
48 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
51 fseek(handle
, 0, SEEK_END
);
53 fseek(handle
, 0, SEEK_SET
);
56 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
57 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
59 *bytesread
= fread(buffer
, 1, size
, handle
);
60 return *bytesread
== size
;
69 void DumpBuffer(void* Buffer
, ULONG Size
)
71 FILEHANDLE FileHandle
;
74 /* Create file, overwrite if it already exists */
75 FileHandle
= CreateFile("dump.bin", // Create this file
76 GENERIC_WRITE
, // Open for writing
79 CREATE_ALWAYS
, // Create or overwrite
80 FILE_ATTRIBUTE_NORMAL
, // Normal file
81 NULL
); // No attribute template
82 if (FileHandle
== INVALID_HANDLE_VALUE
)
84 DPRINT(MID_TRACE
, ("ERROR OPENING '%u'.\n", (UINT
)GetLastError()));
88 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
))
90 DPRINT(MID_TRACE
, ("ERROR WRITING '%u'.\n", (UINT
)GetLastError()));
93 CloseFile(FileHandle
);
101 CCFDATAStorage::CCFDATAStorage()
103 * FUNCTION: Default constructor
110 CCFDATAStorage::~CCFDATAStorage()
112 * FUNCTION: Default destructor
115 ASSERT(!FileCreated
);
119 ULONG
CCFDATAStorage::Create(const char* FileName
)
121 * FUNCTION: Creates the file
123 * FileName = Pointer to name of file
125 * Status of operation
128 ASSERT(!FileCreated
);
131 if (GetTempPath(MAX_PATH
, FullName
) == 0)
132 return CAB_STATUS_CANNOT_CREATE
;
134 strcat(FullName
, FileName
);
136 /* Create file, overwrite if it already exists */
137 FileHandle
= CreateFile(FullName
, // Create this file
138 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
141 CREATE_ALWAYS
, // Create or overwrite
142 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
143 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
144 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
145 NULL
); // No attribute template
146 if (FileHandle
== INVALID_HANDLE_VALUE
)
148 DPRINT(MID_TRACE
, ("ERROR '%u'.\n", (UINT
)GetLastError()));
149 return CAB_STATUS_CANNOT_CREATE
;
152 /*if (tmpnam(FullName) == NULL)*/
153 if ((FileHandle
= tmpfile()) == NULL
)
154 return CAB_STATUS_CANNOT_CREATE
;
156 FileHandle = fopen(FullName, "w+b");
157 if (FileHandle == NULL) {
158 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno));
159 return CAB_STATUS_CANNOT_CREATE;
166 return CAB_STATUS_SUCCESS
;
170 ULONG
CCFDATAStorage::Destroy()
172 * FUNCTION: Destroys the file
174 * Status of operation
179 CloseFile(FileHandle
);
183 return CAB_STATUS_SUCCESS
;
187 ULONG
CCFDATAStorage::Truncate()
189 * FUNCTION: Truncate the scratch file to zero bytes
191 * Status of operation
195 if( SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
196 return CAB_STATUS_FAILURE
;
197 if (!SetEndOfFile(FileHandle
))
198 return CAB_STATUS_FAILURE
;
201 FileHandle
= tmpfile();
202 if (FileHandle
== NULL
)
204 DPRINT(MID_TRACE
, ("ERROR '%i'.\n", errno
));
205 return CAB_STATUS_FAILURE
;
208 return CAB_STATUS_SUCCESS
;
212 ULONG
CCFDATAStorage::Position()
214 * FUNCTION: Returns current position in file
220 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
222 return (ULONG
)ftell(FileHandle
);
227 ULONG
CCFDATAStorage::Seek(LONG Position
)
229 * FUNCTION: Seeks to an absolute position
231 * Position = Absolute position to seek to
233 * Status of operation
237 if( SetFilePointer(FileHandle
,
240 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
241 return CAB_STATUS_FAILURE
;
243 return CAB_STATUS_SUCCESS
;
245 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0)
246 return CAB_STATUS_FAILURE
;
248 return CAB_STATUS_SUCCESS
;
253 ULONG
CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, PULONG BytesRead
)
255 * FUNCTION: Reads a CFDATA block from the file
257 * Data = Pointer to CFDATA block for the buffer
258 * Buffer = Pointer to buffer to store data read
259 * BytesWritten = Pointer to buffer to write number of bytes read
261 * Status of operation
265 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesRead
, NULL
))
266 return CAB_STATUS_CANNOT_READ
;
269 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
270 if (*BytesRead
!= Data
->CompSize
)
271 return CAB_STATUS_CANNOT_READ
;
273 return CAB_STATUS_SUCCESS
;
277 ULONG
CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, PULONG BytesWritten
)
279 * FUNCTION: Writes a CFDATA block to the file
281 * Data = Pointer to CFDATA block for the buffer
282 * Buffer = Pointer to buffer with data to write
283 * BytesWritten = Pointer to buffer to write number of bytes written
285 * Status of operation
289 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesWritten
, NULL
))
290 return CAB_STATUS_CANNOT_WRITE
;
292 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
293 if (*BytesWritten
!= Data
->CompSize
)
294 return CAB_STATUS_CANNOT_WRITE
;
296 return CAB_STATUS_SUCCESS
;
299 #endif /* CAB_READ_ONLY */
306 * FUNCTION: Default constructor
315 *CabinetReservedFile
= '\0';
318 CabinetReservedFileBuffer
= NULL
;
319 CabinetReservedFileSize
= 0;
321 FolderListHead
= NULL
;
322 FolderListTail
= NULL
;
325 CriteriaListHead
= NULL
;
326 CriteriaListTail
= NULL
;
330 CodecSelected
= false;
335 BlockIsSplit
= false;
338 FolderUncompSize
= 0;
339 BytesLeftInBlock
= 0;
341 CurrentDataNode
= NULL
;
345 CCabinet::~CCabinet()
347 * FUNCTION: Default destructor
350 if (CabinetReservedFileBuffer
!= NULL
)
352 FreeMemory(CabinetReservedFileBuffer
);
353 CabinetReservedFileBuffer
= NULL
;
354 CabinetReservedFileSize
= 0;
361 bool CCabinet::IsSeparator(char Char
)
363 * FUNCTION: Determines if a character is a separator
365 * Char = Character to check
367 * Whether it is a separator
370 if ((Char
== '\\') || (Char
== '/'))
376 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
378 * FUNCTION: Replaces \ or / with the one used by the host environment
380 * Path = Pointer to string with pathname
381 * Allocate = Specifies whether to allocate memory for the new
382 * string or to change the existing buffer
384 * Pointer to new path
391 newpath
= strdup(Path
);
407 newpath
[i
] = Path
[i
];
417 char* CCabinet::GetFileName(char* Path
)
419 * FUNCTION: Returns a pointer to file name
421 * Path = Pointer to string with pathname
423 * Pointer to filename
428 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
431 if (IsSeparator(Path
[i
- 1]))
438 void CCabinet::RemoveFileName(char* Path
)
440 * FUNCTION: Removes a file name from a path
442 * Path = Pointer to string with path
448 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
449 FileName
= GetFileName(Path
+ i
);
451 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
453 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
459 bool CCabinet::NormalizePath(char* Path
,
462 * FUNCTION: Normalizes a path
464 * Path = Pointer to string with pathname
465 * Length = Number of bytes in Path
467 * true if there was enough room in Path, or false
473 if ((n
= (ULONG
)strlen(Path
)) &&
474 (!IsSeparator(Path
[n
- 1])) &&
475 (OK
= ((n
+ 1) < Length
)))
477 Path
[n
] = DIR_SEPARATOR_CHAR
;
484 char* CCabinet::GetCabinetName()
486 * FUNCTION: Returns pointer to cabinet file name
488 * Pointer to string with name of cabinet
495 void CCabinet::SetCabinetName(char* FileName
)
497 * FUNCTION: Sets cabinet file name
499 * FileName = Pointer to string with name of cabinet
502 strcpy(CabinetName
, FileName
);
506 void CCabinet::SetDestinationPath(char* DestinationPath
)
508 * FUNCTION: Sets destination path
510 * DestinationPath = Pointer to string with name of destination path
513 strcpy(DestPath
, DestinationPath
);
514 ConvertPath(DestPath
, false);
515 if (strlen(DestPath
) > 0)
516 NormalizePath(DestPath
, MAX_PATH
);
519 ULONG
CCabinet::AddSearchCriteria(char* SearchCriteria
)
521 * FUNCTION: Adds a criteria to the search criteria list
523 * SearchCriteria = String with the search criteria to add
525 * Status of operation
528 PSEARCH_CRITERIA Criteria
;
530 // Add the criteria to the list of search criteria
531 Criteria
= (PSEARCH_CRITERIA
)AllocateMemory(sizeof(SEARCH_CRITERIA
));
534 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
535 return CAB_STATUS_NOMEMORY
;
538 Criteria
->Prev
= CriteriaListTail
;
539 Criteria
->Next
= NULL
;
542 CriteriaListTail
->Next
= Criteria
;
544 CriteriaListHead
= Criteria
;
546 CriteriaListTail
= Criteria
;
548 // Set the actual criteria string
549 Criteria
->Search
= (char*)AllocateMemory(strlen(SearchCriteria
) + 1);
550 if (!Criteria
->Search
)
552 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
553 return CAB_STATUS_NOMEMORY
;
556 strcpy(Criteria
->Search
, SearchCriteria
);
558 return CAB_STATUS_SUCCESS
;
561 void CCabinet::DestroySearchCriteria()
563 * FUNCTION: Destroys the list with the search criteria
566 PSEARCH_CRITERIA Criteria
;
567 PSEARCH_CRITERIA NextCriteria
;
569 Criteria
= CriteriaListHead
;
573 NextCriteria
= Criteria
->Next
;
575 FreeMemory(Criteria
->Search
);
576 FreeMemory(Criteria
);
578 Criteria
= NextCriteria
;
581 CriteriaListHead
= NULL
;
582 CriteriaListTail
= NULL
;
585 bool CCabinet::HasSearchCriteria()
587 * FUNCTION: Returns whether we have search criteria
589 * Whether we have search criteria or not.
592 return (CriteriaListHead
!= NULL
);
595 bool CCabinet::SetCompressionCodec(char* CodecName
)
597 * FUNCTION: Selects the codec to use for compression
599 * CodecName = Pointer to a string with the name of the codec
602 if( !strcasecmp(CodecName
, "raw") )
603 SelectCodec(CAB_CODEC_RAW
);
604 else if( !strcasecmp(CodecName
, "mszip") )
605 SelectCodec(CAB_CODEC_MSZIP
);
608 printf("Invalid codec specified!\n");
615 char* CCabinet::GetDestinationPath()
617 * FUNCTION: Returns destination path
619 * Pointer to string with name of destination path
626 bool CCabinet::SetCabinetReservedFile(char* FileName
)
628 * FUNCTION: Sets cabinet reserved file
630 * FileName = Pointer to string with name of cabinet reserved file
633 FILEHANDLE FileHandle
;
637 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
638 GENERIC_READ
, // Open for reading
639 FILE_SHARE_READ
, // Share for reading
641 OPEN_EXISTING
, // Existing file only
642 FILE_ATTRIBUTE_NORMAL
, // Normal file
643 NULL
); // No attribute template
644 if (FileHandle
== INVALID_HANDLE_VALUE
)
646 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
650 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
651 if (FileHandle
== NULL
)
653 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
658 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
659 if (CabinetReservedFileSize
== (ULONG
)-1)
661 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
665 if (CabinetReservedFileSize
== 0)
667 CloseFile(FileHandle
);
671 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
672 if (!CabinetReservedFileBuffer
)
674 CloseFile(FileHandle
);
678 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
))
680 CloseFile(FileHandle
);
684 CloseFile(FileHandle
);
686 strcpy(CabinetReservedFile
, FileName
);
692 char* CCabinet::GetCabinetReservedFile()
694 * FUNCTION: Returns cabionet reserved file
696 * Pointer to string with name of cabinet reserved file
699 return CabinetReservedFile
;
703 ULONG
CCabinet::GetCurrentDiskNumber()
705 * FUNCTION: Returns current disk number
707 * Current disk number
710 return CurrentDiskNumber
;
714 ULONG
CCabinet::Open()
716 * FUNCTION: Opens a cabinet file
718 * Status of operation
721 PCFFOLDER_NODE FolderNode
;
730 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
732 return CAB_STATUS_NOMEMORY
;
735 FileHandle
= CreateFile(CabinetName
, // Open this file
736 GENERIC_READ
, // Open for reading
737 FILE_SHARE_READ
, // Share for reading
739 OPEN_EXISTING
, // Existing file only
740 FILE_ATTRIBUTE_NORMAL
, // Normal file
741 NULL
); // No attribute template
743 if (FileHandle
== INVALID_HANDLE_VALUE
)
745 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
746 return CAB_STATUS_CANNOT_OPEN
;
749 FileHandle
= fopen(CabinetName
, "rb");
750 if (FileHandle
== NULL
)
752 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
753 return CAB_STATUS_CANNOT_OPEN
;
759 /* Load CAB header */
760 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
761 != CAB_STATUS_SUCCESS
)
763 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
764 return CAB_STATUS_INVALID_CAB
;
768 if ((BytesRead
!= sizeof(CFHEADER
)) ||
769 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
770 (CABHeader
.Version
!= CAB_VERSION
) ||
771 (CABHeader
.FolderCount
== 0 ) ||
772 (CABHeader
.FileCount
== 0 ) ||
773 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
)))
776 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
777 return CAB_STATUS_INVALID_CAB
;
782 /* Read/skip any reserved bytes */
783 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
785 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
))
786 != CAB_STATUS_SUCCESS
)
788 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
789 return CAB_STATUS_INVALID_CAB
;
791 CabinetReserved
= Size
& 0xFFFF;
792 FolderReserved
= (Size
>> 16) & 0xFF;
793 DataReserved
= (Size
>> 24) & 0xFF;
796 if (SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
798 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
799 return CAB_STATUS_FAILURE
;
802 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0)
804 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
805 return CAB_STATUS_FAILURE
;
810 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
812 /* Read name of previous cabinet */
813 Status
= ReadString(CabinetPrev
, 256);
814 if (Status
!= CAB_STATUS_SUCCESS
)
816 /* Read label of previous disk */
817 Status
= ReadString(DiskPrev
, 256);
818 if (Status
!= CAB_STATUS_SUCCESS
)
823 strcpy(CabinetPrev
, "");
824 strcpy(DiskPrev
, "");
827 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
829 /* Read name of next cabinet */
830 Status
= ReadString(CabinetNext
, 256);
831 if (Status
!= CAB_STATUS_SUCCESS
)
833 /* Read label of next disk */
834 Status
= ReadString(DiskNext
, 256);
835 if (Status
!= CAB_STATUS_SUCCESS
)
840 strcpy(CabinetNext
, "");
841 strcpy(DiskNext
, "");
844 /* Read all folders */
845 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
847 FolderNode
= NewFolderNode();
850 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
851 return CAB_STATUS_NOMEMORY
;
855 FolderNode
->UncompOffset
= FolderUncompSize
;
857 FolderNode
->Index
= Index
;
859 if ((Status
= ReadBlock(&FolderNode
->Folder
,
860 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
862 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
863 return CAB_STATUS_INVALID_CAB
;
867 /* Read file entries */
868 Status
= ReadFileTable();
869 if (Status
!= CAB_STATUS_SUCCESS
)
871 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%u).\n", (UINT
)Status
));
875 /* Read data blocks for all folders */
876 FolderNode
= FolderListHead
;
877 while (FolderNode
!= NULL
)
879 Status
= ReadDataBlocks(FolderNode
);
880 if (Status
!= CAB_STATUS_SUCCESS
)
882 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%u).\n", (UINT
)Status
));
885 FolderNode
= FolderNode
->Next
;
888 return CAB_STATUS_SUCCESS
;
892 void CCabinet::Close()
894 * FUNCTION: Closes the cabinet file
899 CloseFile(FileHandle
);
905 ULONG
CCabinet::FindFirst(PCAB_SEARCH Search
)
907 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
909 * Search = Pointer to search structure
911 * Status of operation
914 RestartSearch
= false;
915 Search
->Next
= FileListHead
;
916 return FindNext(Search
);
920 ULONG
CCabinet::FindNext(PCAB_SEARCH Search
)
922 * FUNCTION: Finds next file in the cabinet that matches a search criteria
924 * Search = Pointer to search structure
926 * Status of operation
930 PSEARCH_CRITERIA Criteria
;
935 Search
->Next
= FileListHead
;
937 /* Skip split files already extracted */
938 while ((Search
->Next
) &&
939 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
940 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
942 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
943 Search
->Next
->FileName
, (UINT
)Search
->Next
->File
.FileOffset
, (UINT
)LastFileOffset
));
944 Search
->Next
= Search
->Next
->Next
;
947 RestartSearch
= false;
950 /* Check each search criteria against each file */
953 // Some features (like displaying cabinets) don't require search criteria, so we can just break here.
954 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx".
955 if(!CriteriaListHead
)
958 Criteria
= CriteriaListHead
;
962 if(MatchFileNamePattern(Search
->Next
->FileName
, Criteria
->Search
))
968 Criteria
= Criteria
->Next
;
974 Search
->Next
= Search
->Next
->Next
;
979 if (strlen(DiskNext
) > 0)
983 SetCabinetName(CabinetNext
);
985 OnDiskChange(CabinetNext
, DiskNext
);
988 if (Status
!= CAB_STATUS_SUCCESS
)
991 Search
->Next
= FileListHead
;
993 return CAB_STATUS_NOFILE
;
996 return CAB_STATUS_NOFILE
;
999 Search
->File
= &Search
->Next
->File
;
1000 Search
->FileName
= Search
->Next
->FileName
;
1001 Search
->Next
= Search
->Next
->Next
;
1002 return CAB_STATUS_SUCCESS
;
1006 ULONG
CCabinet::ExtractFile(char* FileName
)
1008 * FUNCTION: Extracts a file from the cabinet
1010 * FileName = Pointer to buffer with name of file
1012 * Status of operation
1022 ULONG TotalBytesRead
;
1023 ULONG CurrentOffset
;
1025 PUCHAR CurrentBuffer
;
1026 FILEHANDLE DestFile
;
1034 CHAR DestName
[MAX_PATH
];
1035 CHAR TempName
[MAX_PATH
];
1037 Status
= LocateFile(FileName
, &File
);
1038 if (Status
!= CAB_STATUS_SUCCESS
)
1040 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1044 LastFileOffset
= File
->File
.FileOffset
;
1046 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
1049 SelectCodec(CAB_CODEC_RAW
);
1052 case CAB_COMP_MSZIP
:
1053 SelectCodec(CAB_CODEC_MSZIP
);
1057 return CAB_STATUS_UNSUPPCOMP
;
1060 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1061 (UINT
)File
->File
.FileOffset
,
1062 (UINT
)File
->File
.FileSize
,
1063 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1064 (UINT
)File
->DataBlock
->UncompOffset
));
1066 strcpy(DestName
, DestPath
);
1067 strcat(DestName
, FileName
);
1069 /* Create destination file, fail if it already exists */
1071 DestFile
= CreateFile(DestName
, // Create this file
1072 GENERIC_WRITE
, // Open for writing
1074 NULL
, // No security
1075 CREATE_NEW
, // New file only
1076 FILE_ATTRIBUTE_NORMAL
, // Normal file
1077 NULL
); // No attribute template
1078 if (DestFile
== INVALID_HANDLE_VALUE
)
1080 /* If file exists, ask to overwrite file */
1081 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1082 (OnOverwrite(&File
->File
, FileName
)))
1084 /* Create destination file, overwrite if it already exists */
1085 DestFile
= CreateFile(DestName
, // Create this file
1086 GENERIC_WRITE
, // Open for writing
1088 NULL
, // No security
1089 TRUNCATE_EXISTING
, // Truncate the file
1090 FILE_ATTRIBUTE_NORMAL
, // Normal file
1091 NULL
); // No attribute template
1092 if (DestFile
== INVALID_HANDLE_VALUE
)
1093 return CAB_STATUS_CANNOT_CREATE
;
1097 if (Status
== ERROR_FILE_EXISTS
)
1098 return CAB_STATUS_FILE_EXISTS
;
1100 return CAB_STATUS_CANNOT_CREATE
;
1104 DestFile
= fopen(DestName
, "rb");
1105 if (DestFile
!= NULL
)
1108 /* If file exists, ask to overwrite file */
1109 if (OnOverwrite(&File
->File
, FileName
))
1111 DestFile
= fopen(DestName
, "w+b");
1112 if (DestFile
== NULL
)
1113 return CAB_STATUS_CANNOT_CREATE
;
1116 return CAB_STATUS_FILE_EXISTS
;
1120 DestFile
= fopen(DestName
, "w+b");
1121 if (DestFile
== NULL
)
1122 return CAB_STATUS_CANNOT_CREATE
;
1126 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1128 CloseFile(DestFile
);
1129 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%u).\n", (UINT
)GetLastError()));
1130 return CAB_STATUS_CANNOT_WRITE
;
1133 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
1135 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1137 SetAttributesOnFile(DestName
, File
->File
.Attributes
);
1139 Buffer
= (PUCHAR
)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1142 CloseFile(DestFile
);
1143 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1144 return CAB_STATUS_NOMEMORY
;
1147 /* Call OnExtract event handler */
1148 OnExtract(&File
->File
, FileName
);
1150 /* Search to start of file */
1152 Offset
= SetFilePointer(FileHandle
,
1153 File
->DataBlock
->AbsoluteOffset
,
1156 if (Offset
== INVALID_SET_FILE_POINTER
)
1158 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1159 return CAB_STATUS_INVALID_CAB
;
1162 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1164 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1165 return CAB_STATUS_FAILURE
;
1167 Offset
= ftell(FileHandle
);
1170 Size
= File
->File
.FileSize
;
1171 Offset
= File
->File
.FileOffset
;
1172 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1176 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1181 DPRINT(MAX_TRACE
, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1182 (UINT
)File
->DataBlock
->UncompOffset
, (UINT
)ReuseBlock
, (UINT
)Offset
, (UINT
)Size
,
1183 (UINT
)BytesLeftInBlock
));
1185 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1187 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1189 CurrentBuffer
= Buffer
;
1193 DPRINT(MAX_TRACE
, ("Size (%u bytes).\n", (UINT
)Size
));
1195 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1196 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1198 CloseFile(DestFile
);
1200 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1201 return CAB_STATUS_INVALID_CAB
;
1204 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n",
1205 (UINT
)CFData
.Checksum
,
1207 CFData
.UncompSize
));
1209 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1211 BytesToRead
= CFData
.CompSize
;
1213 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1214 (unsigned long)CurrentBuffer
, (unsigned long)Buffer
));
1216 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1217 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1219 CloseFile(DestFile
);
1221 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1222 return CAB_STATUS_INVALID_CAB
;
1225 /* FIXME: Does not work with files generated by makecab.exe */
1227 if (CFData.Checksum != 0)
1229 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1230 if (Checksum != CFData.Checksum)
1232 CloseFile(DestFile);
1234 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1235 Checksum, CFData.Checksum));
1236 return CAB_STATUS_INVALID_CAB;
1240 TotalBytesRead
+= BytesRead
;
1242 CurrentBuffer
+= BytesRead
;
1244 if (CFData
.UncompSize
== 0)
1246 if (strlen(DiskNext
) == 0)
1247 return CAB_STATUS_NOFILE
;
1249 /* CloseCabinet() will destroy all file entries so in case
1250 FileName refers to the FileName field of a CFFOLDER_NODE
1251 structure, we have to save a copy of the filename */
1252 strcpy(TempName
, FileName
);
1256 SetCabinetName(CabinetNext
);
1258 OnDiskChange(CabinetNext
, DiskNext
);
1261 if (Status
!= CAB_STATUS_SUCCESS
)
1264 /* The first data block of the file will not be
1265 found as it is located in the previous file */
1266 Status
= LocateFile(TempName
, &File
);
1267 if (Status
== CAB_STATUS_NOFILE
)
1269 DPRINT(MID_TRACE
, ("Cannot locate file (%u).\n", (UINT
)Status
));
1273 /* The file is continued in the first data block in the folder */
1274 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1276 /* Search to start of file */
1278 if( SetFilePointer(FileHandle
,
1279 File
->DataBlock
->AbsoluteOffset
,
1281 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1283 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1284 return CAB_STATUS_INVALID_CAB
;
1287 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1289 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1290 return CAB_STATUS_INVALID_CAB
;
1294 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1295 (UINT
)File
->File
.FileOffset
,
1296 (UINT
)File
->File
.FileSize
,
1297 (UINT
)File
->DataBlock
->AbsoluteOffset
,
1298 (UINT
)File
->DataBlock
->UncompOffset
));
1300 CurrentDataNode
= File
->DataBlock
;
1303 RestartSearch
= true;
1305 } while (CFData
.UncompSize
== 0);
1307 DPRINT(MAX_TRACE
, ("TotalBytesRead (%u).\n", (UINT
)TotalBytesRead
));
1309 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1310 if (Status
!= CS_SUCCESS
)
1312 CloseFile(DestFile
);
1314 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1315 if (Status
== CS_NOMEMORY
)
1316 return CAB_STATUS_NOMEMORY
;
1317 return CAB_STATUS_INVALID_CAB
;
1320 if (BytesToWrite
!= CFData
.UncompSize
)
1322 DPRINT(MID_TRACE
, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n",
1323 (UINT
)BytesToWrite
, CFData
.UncompSize
));
1324 return CAB_STATUS_INVALID_CAB
;
1327 BytesLeftInBlock
= BytesToWrite
;
1331 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%u)\n", (UINT
)ReuseBlock
));
1333 BytesToWrite
= BytesLeftInBlock
;
1335 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%X.\n",
1336 (UINT
)(CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) + CurrentDataNode
->Data
.CompSize
)));
1338 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1339 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1341 CloseFile(DestFile
);
1343 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
1344 return CAB_STATUS_INVALID_CAB
;
1347 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1348 CFData
.CompSize
, CFData
.UncompSize
));
1350 /* Go to next data block */
1352 if( SetFilePointer(FileHandle
,
1353 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1354 CurrentDataNode
->Data
.CompSize
,
1356 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
1358 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
1359 return CAB_STATUS_INVALID_CAB
;
1362 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1363 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1365 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1366 return CAB_STATUS_INVALID_CAB
;
1374 BytesSkipped
= (Offset
- CurrentOffset
);
1378 BytesToWrite
-= BytesSkipped
;
1380 if (Size
< BytesToWrite
)
1381 BytesToWrite
= Size
;
1383 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n",
1385 (UINT
)CurrentOffset
,
1387 (UINT
)BytesSkipped
, (UINT
)Skip
,
1391 if (!WriteFile(DestFile
, (void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1392 BytesToWrite
, (LPDWORD
)&BytesWritten
, NULL
) ||
1393 (BytesToWrite
!= BytesWritten
))
1395 DPRINT(MIN_TRACE
, ("Status 0x%X.\n", (UINT
)GetLastError()));
1397 BytesWritten
= BytesToWrite
;
1398 if (fwrite((void*)((PUCHAR
)OutputBuffer
+ BytesSkipped
),
1399 BytesToWrite
, 1, DestFile
) < 1)
1402 CloseFile(DestFile
);
1404 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1405 return CAB_STATUS_CANNOT_WRITE
;
1407 Size
-= BytesToWrite
;
1409 CurrentOffset
+= BytesToWrite
;
1411 /* Don't skip any more bytes */
1416 CloseFile(DestFile
);
1420 return CAB_STATUS_SUCCESS
;
1423 bool CCabinet::IsCodecSelected()
1425 * FUNCTION: Returns the value of CodecSelected
1427 * Whether a codec is selected
1430 return CodecSelected
;
1433 void CCabinet::SelectCodec(LONG Id
)
1435 * FUNCTION: Selects codec engine to use
1437 * Id = Codec identifier
1445 CodecSelected
= false;
1452 Codec
= new CRawCodec();
1455 case CAB_CODEC_MSZIP
:
1456 Codec
= new CMSZipCodec();
1464 CodecSelected
= true;
1468 #ifndef CAB_READ_ONLY
1470 /* CAB write methods */
1472 ULONG
CCabinet::NewCabinet()
1474 * FUNCTION: Creates a new cabinet
1476 * Status of operation
1481 CurrentDiskNumber
= 0;
1483 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1484 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1485 if ((!OutputBuffer
) || (!InputBuffer
))
1487 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1488 return CAB_STATUS_NOMEMORY
;
1490 CurrentIBuffer
= InputBuffer
;
1491 CurrentIBufferSize
= 0;
1493 CABHeader
.Signature
= CAB_SIGNATURE
;
1494 CABHeader
.Reserved1
= 0; // Not used
1495 CABHeader
.CabinetSize
= 0; // Not yet known
1496 CABHeader
.Reserved2
= 0; // Not used
1497 CABHeader
.Reserved3
= 0; // Not used
1498 CABHeader
.Version
= CAB_VERSION
;
1499 CABHeader
.FolderCount
= 0; // Not yet known
1500 CABHeader
.FileCount
= 0; // Not yet known
1501 CABHeader
.Flags
= 0; // Not yet known
1502 // FIXME: Should be random
1503 CABHeader
.SetID
= 0x534F;
1504 CABHeader
.CabinetNumber
= 0;
1507 TotalFolderSize
= 0;
1510 DiskSize
= sizeof(CFHEADER
);
1512 InitCabinetHeader();
1514 // NextFolderNumber is 0-based
1515 NextFolderNumber
= 0;
1517 CurrentFolderNode
= NULL
;
1518 Status
= NewFolder();
1519 if (Status
!= CAB_STATUS_SUCCESS
)
1522 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1524 ScratchFile
= new CCFDATAStorage
;
1527 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1528 return CAB_STATUS_NOMEMORY
;
1531 Status
= ScratchFile
->Create("~CAB.tmp");
1533 CreateNewFolder
= false;
1535 CreateNewDisk
= false;
1537 PrevCabinetNumber
= 0;
1543 ULONG
CCabinet::NewDisk()
1545 * FUNCTION: Forces a new disk to be created
1547 * Status of operation
1550 // NextFolderNumber is 0-based
1551 NextFolderNumber
= 1;
1553 CreateNewDisk
= false;
1555 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1557 InitCabinetHeader();
1559 CurrentFolderNode
->TotalFolderSize
= 0;
1561 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1563 return CAB_STATUS_SUCCESS
;
1567 ULONG
CCabinet::NewFolder()
1569 * FUNCTION: Forces a new folder to be created
1571 * Status of operation
1574 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1576 CurrentFolderNode
= NewFolderNode();
1577 if (!CurrentFolderNode
)
1579 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1580 return CAB_STATUS_NOMEMORY
;
1585 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1588 case CAB_CODEC_MSZIP
:
1589 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1593 return CAB_STATUS_UNSUPPCOMP
;
1596 /* FIXME: This won't work if no files are added to the new folder */
1598 DiskSize
+= sizeof(CFFOLDER
);
1600 TotalFolderSize
+= sizeof(CFFOLDER
);
1604 CABHeader
.FolderCount
++;
1608 return CAB_STATUS_SUCCESS
;
1612 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1614 * FUNCTION: Writes a file to the scratch file
1616 * FileNode = Pointer to file node
1618 * Status of operation
1628 /* Try to open file */
1630 SourceFile
= CreateFile(
1631 FileNode
->FileName
, // Open this file
1632 GENERIC_READ
, // Open for reading
1633 FILE_SHARE_READ
, // Share for reading
1634 NULL
, // No security
1635 OPEN_EXISTING
, // File must exist
1636 FILE_ATTRIBUTE_NORMAL
, // Normal file
1637 NULL
); // No attribute template
1638 if (SourceFile
== INVALID_HANDLE_VALUE
)
1640 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1641 return CAB_STATUS_NOFILE
;
1644 SourceFile
= fopen(FileNode
->FileName
, "rb");
1645 if (SourceFile
== NULL
)
1647 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1648 return CAB_STATUS_NOFILE
;
1652 if (CreateNewFolder
)
1654 /* There is always a new folder after
1655 a split file is completely stored */
1656 Status
= NewFolder();
1657 if (Status
!= CAB_STATUS_SUCCESS
)
1659 CreateNewFolder
= false;
1662 /* Call OnAdd event handler */
1663 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1665 TotalBytesLeft
= FileNode
->File
.FileSize
;
1667 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1668 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1669 FileNode
->File
.FileControlID
= (USHORT
)(NextFolderNumber
- 1);
1670 CurrentFolderNode
->Commit
= true;
1671 PrevCabinetNumber
= CurrentDiskNumber
;
1673 Size
= sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(FileNode
->FileName
)) + 1;
1674 CABHeader
.FileTableOffset
+= Size
;
1675 TotalFileSize
+= Size
;
1679 FileNode
->Commit
= true;
1681 if (TotalBytesLeft
> 0)
1685 if (TotalBytesLeft
> (ULONG
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1686 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1688 BytesToRead
= TotalBytesLeft
;
1690 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1692 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n",
1693 (UINT
)BytesToRead
, (UINT
)BytesRead
, (UINT
)CurrentIBufferSize
));
1694 return CAB_STATUS_INVALID_CAB
;
1697 CurrentIBuffer
= (unsigned char*)CurrentIBuffer
+ BytesRead
;
1698 CurrentIBufferSize
+= (USHORT
)BytesRead
;
1700 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1702 Status
= WriteDataBlock();
1703 if (Status
!= CAB_STATUS_SUCCESS
)
1706 TotalBytesLeft
-= BytesRead
;
1707 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1710 if (TotalBytesLeft
== 0)
1712 CloseFile(SourceFile
);
1713 FileNode
->Delete
= true;
1715 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1717 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1718 CurrentFolderNode
->Delete
= true;
1720 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1722 Status
= WriteDataBlock();
1723 if (Status
!= CAB_STATUS_SUCCESS
)
1727 CreateNewFolder
= true;
1732 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1733 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1735 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1738 return CAB_STATUS_SUCCESS
;
1742 ULONG
CCabinet::WriteDisk(ULONG MoreDisks
)
1744 * FUNCTION: Forces the current disk to be written
1746 * MoreDisks = true if there is one or more disks after this disk
1748 * Status of operation
1751 PCFFILE_NODE FileNode
;
1754 ContinueFile
= false;
1755 FileNode
= FileListHead
;
1756 while (FileNode
!= NULL
)
1758 Status
= WriteFileToScratchStorage(FileNode
);
1759 if (Status
!= CAB_STATUS_SUCCESS
)
1764 /* A data block could span more than two
1765 disks if MaxDiskSize is very small */
1766 while (CreateNewDisk
)
1768 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1773 ContinueFile
= true;
1774 CreateNewDisk
= false;
1776 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1777 (UINT
)CurrentIBufferSize
, (UINT
)CurrentOBufferSize
));
1779 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1781 Status
= WriteDataBlock();
1782 if (Status
!= CAB_STATUS_SUCCESS
)
1789 ContinueFile
= false;
1790 FileNode
= FileNode
->Next
;
1794 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1796 /* A data block could span more than two
1797 disks if MaxDiskSize is very small */
1799 ASSERT(CreateNewDisk
== false);
1805 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1809 CreateNewDisk
= false;
1811 ASSERT(FileNode
== FileListHead
);
1814 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1816 Status
= WriteDataBlock();
1817 if (Status
!= CAB_STATUS_SUCCESS
)
1820 } while (CreateNewDisk
);
1822 CommitDisk(MoreDisks
);
1824 return CAB_STATUS_SUCCESS
;
1828 ULONG
CCabinet::CommitDisk(ULONG MoreDisks
)
1830 * FUNCTION: Commits the current disk
1832 * MoreDisks = true if there is one or more disks after this disk
1834 * Status of operation
1837 PCFFOLDER_NODE FolderNode
;
1840 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1842 /* Create file, fail if it already exists */
1844 FileHandle
= CreateFile(CabinetName
, // Create this file
1845 GENERIC_WRITE
, // Open for writing
1847 NULL
, // No security
1848 CREATE_NEW
, // New file only
1849 FILE_ATTRIBUTE_NORMAL
, // Normal file
1850 NULL
); // No attribute template
1851 if (FileHandle
== INVALID_HANDLE_VALUE
)
1854 /* If file exists, ask to overwrite file */
1855 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1856 (OnOverwrite(NULL
, CabinetName
)))
1859 /* Create cabinet file, overwrite if it already exists */
1860 FileHandle
= CreateFile(CabinetName
, // Create this file
1861 GENERIC_WRITE
, // Open for writing
1863 NULL
, // No security
1864 TRUNCATE_EXISTING
, // Truncate the file
1865 FILE_ATTRIBUTE_NORMAL
, // Normal file
1866 NULL
); // No attribute template
1867 if (FileHandle
== INVALID_HANDLE_VALUE
)
1868 return CAB_STATUS_CANNOT_CREATE
;
1872 if (Status
== ERROR_FILE_EXISTS
)
1873 return CAB_STATUS_FILE_EXISTS
;
1875 return CAB_STATUS_CANNOT_CREATE
;
1879 FileHandle
= fopen(CabinetName
, "rb");
1880 if (FileHandle
!= NULL
)
1883 /* If file exists, ask to overwrite file */
1884 if (OnOverwrite(NULL
, CabinetName
))
1886 FileHandle
= fopen(CabinetName
, "w+b");
1887 if (FileHandle
== NULL
)
1888 return CAB_STATUS_CANNOT_CREATE
;
1891 return CAB_STATUS_FILE_EXISTS
;
1896 FileHandle
= fopen(CabinetName
, "w+b");
1897 if (FileHandle
== NULL
)
1898 return CAB_STATUS_CANNOT_CREATE
;
1902 WriteCabinetHeader(MoreDisks
!= 0);
1904 Status
= WriteFolderEntries();
1905 if (Status
!= CAB_STATUS_SUCCESS
)
1908 /* Write file entries */
1911 /* Write data blocks */
1912 FolderNode
= FolderListHead
;
1913 while (FolderNode
!= NULL
)
1915 if (FolderNode
->Commit
)
1917 Status
= CommitDataBlocks(FolderNode
);
1918 if (Status
!= CAB_STATUS_SUCCESS
)
1920 /* Remove data blocks for folder */
1921 DestroyDataNodes(FolderNode
);
1923 FolderNode
= FolderNode
->Next
;
1926 CloseFile(FileHandle
);
1928 ScratchFile
->Truncate();
1930 return CAB_STATUS_SUCCESS
;
1934 ULONG
CCabinet::CloseDisk()
1936 * FUNCTION: Closes the current disk
1938 * Status of operation
1941 DestroyDeletedFileNodes();
1943 /* Destroy folder nodes that are completely stored */
1944 DestroyDeletedFolderNodes();
1946 CurrentDiskNumber
++;
1948 return CAB_STATUS_SUCCESS
;
1952 ULONG
CCabinet::CloseCabinet()
1954 * FUNCTION: Closes the current cabinet
1956 * Status of operation
1963 DestroyFolderNodes();
1967 FreeMemory(InputBuffer
);
1973 FreeMemory(OutputBuffer
);
1974 OutputBuffer
= NULL
;
1981 Status
= ScratchFile
->Destroy();
1986 return CAB_STATUS_SUCCESS
;
1990 ULONG
CCabinet::AddFile(char* FileName
)
1992 * FUNCTION: Adds a file to the current disk
1994 * FileName = Pointer to string with file name (full path)
1996 * Status of operation
2000 PCFFILE_NODE FileNode
;
2003 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
2006 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2007 return CAB_STATUS_NOMEMORY
;
2009 strcpy(NewFileName
, FileName
);
2010 ConvertPath(NewFileName
, false);
2012 /* Try to open file */
2014 SrcFile
= CreateFile(
2015 NewFileName
, // Open this file
2016 GENERIC_READ
, // Open for reading
2017 FILE_SHARE_READ
, // Share for reading
2018 NULL
, // No security
2019 OPEN_EXISTING
, // File must exist
2020 FILE_ATTRIBUTE_NORMAL
, // Normal file
2021 NULL
); // No attribute template
2022 if (SrcFile
== INVALID_HANDLE_VALUE
)
2024 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2025 FreeMemory(NewFileName
);
2026 return CAB_STATUS_CANNOT_OPEN
;
2029 SrcFile
= fopen(NewFileName
, "rb");
2030 if (SrcFile
== NULL
)
2032 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
2033 FreeMemory(NewFileName
);
2034 return CAB_STATUS_CANNOT_OPEN
;
2038 FileNode
= NewFileNode();
2041 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2042 FreeMemory(NewFileName
);
2043 return CAB_STATUS_NOMEMORY
;
2046 FileNode
->FolderNode
= CurrentFolderNode
;
2047 FileNode
->FileName
= NewFileName
;
2049 /* FIXME: Check for and handle large files (>= 2GB) */
2050 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
2051 if (FileNode
->File
.FileSize
== (ULONG
)-1)
2053 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
2054 FreeMemory(NewFileName
);
2055 return CAB_STATUS_CANNOT_READ
;
2058 if (GetFileTimes(SrcFile
, FileNode
) != CAB_STATUS_SUCCESS
)
2060 DPRINT(MIN_TRACE
, ("Cannot read file times.\n"));
2061 FreeMemory(NewFileName
);
2062 return CAB_STATUS_CANNOT_READ
;
2065 if (GetAttributesOnFile(FileNode
) != CAB_STATUS_SUCCESS
)
2067 DPRINT(MIN_TRACE
, ("Cannot read file attributes.\n"));
2068 FreeMemory(NewFileName
);
2069 return CAB_STATUS_CANNOT_READ
;
2074 return CAB_STATUS_SUCCESS
;
2077 bool CCabinet::CreateSimpleCabinet()
2079 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2084 char szFilePath
[MAX_PATH
];
2085 char szFile
[MAX_PATH
];
2086 PSEARCH_CRITERIA Criteria
;
2091 WIN32_FIND_DATA FindFileData
;
2098 // Initialize a new cabinet
2099 Status
= NewCabinet();
2100 if (Status
!= CAB_STATUS_SUCCESS
)
2102 DPRINT(MIN_TRACE
, ("Cannot create cabinet (%u).\n", (UINT
)Status
));
2106 // Add each file in the criteria list
2107 Criteria
= CriteriaListHead
;
2111 // Store the file path with a trailing slash in szFilePath
2112 ConvertPath(Criteria
->Search
, false);
2113 pszFile
= strrchr(Criteria
->Search
, DIR_SEPARATOR_CHAR
);
2117 // Set the pointer to the start of the file name, not the slash
2120 strncpy(szFilePath
, Criteria
->Search
, pszFile
- Criteria
->Search
);
2121 szFilePath
[pszFile
- Criteria
->Search
] = 0;
2125 pszFile
= Criteria
->Search
;
2130 // needed for opendir()
2131 strcpy(szFilePath
, "./");
2136 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2137 hFind
= FindFirstFile(Criteria
->Search
, &FindFileData
);
2139 // Don't stop if a search criteria is not found
2140 if(hFind
== INVALID_HANDLE_VALUE
&& GetLastError() != ERROR_FILE_NOT_FOUND
)
2142 DPRINT(MIN_TRACE
, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria
->Search
, (UINT
)GetLastError()));
2148 if(!(FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
2150 strcpy(szFile
, szFilePath
);
2151 strcat(szFile
, FindFileData
.cFileName
);
2153 Status
= AddFile(szFile
);
2155 if(Status
!= CAB_STATUS_SUCCESS
)
2157 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2162 while(FindNextFile(hFind
, &FindFileData
));
2166 // 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
2167 dirp
= opendir(szFilePath
);
2171 while( (dp
= readdir(dirp
)) )
2173 strcpy(szFile
, szFilePath
);
2174 strcat(szFile
, dp
->d_name
);
2176 if(stat(szFile
, &stbuf
) == 0)
2178 if(stbuf
.st_mode
!= S_IFDIR
)
2180 if(MatchFileNamePattern(dp
->d_name
, pszFile
))
2182 Status
= AddFile(szFile
);
2184 if(Status
!= CAB_STATUS_SUCCESS
)
2186 DPRINT(MIN_TRACE
, ("Cannot add file to cabinet (%u).\n", (UINT
)Status
));
2194 DPRINT(MIN_TRACE
, ("stat failed, error code is %i\n", errno
));
2203 Criteria
= Criteria
->Next
;
2206 Status
= WriteDisk(false);
2207 if (Status
== CAB_STATUS_SUCCESS
)
2208 Status
= CloseDisk();
2209 if (Status
!= CAB_STATUS_SUCCESS
)
2211 DPRINT(MIN_TRACE
, ("Cannot write disk (%u).\n", (UINT
)Status
));
2219 DestroySearchCriteria();
2223 void CCabinet::SetMaxDiskSize(ULONG Size
)
2225 * FUNCTION: Sets the maximum size of the current disk
2227 * Size = Maximum size of current disk (0 means no maximum size)
2233 #endif /* CAB_READ_ONLY */
2236 /* Default event handlers */
2238 bool CCabinet::OnOverwrite(PCFFILE File
,
2241 * FUNCTION: Called when extracting a file and it already exists
2243 * File = Pointer to CFFILE for file being extracted
2244 * FileName = Pointer to buffer with name of file (full path)
2246 * true if the file should be overwritten, false if not
2253 void CCabinet::OnExtract(PCFFILE File
,
2256 * FUNCTION: Called just before extracting a file
2258 * File = Pointer to CFFILE for file being extracted
2259 * FileName = Pointer to buffer with name of file (full path)
2265 void CCabinet::OnDiskChange(char* CabinetName
,
2268 * FUNCTION: Called when a new disk is to be processed
2270 * CabinetName = Pointer to buffer with name of cabinet
2271 * DiskLabel = Pointer to buffer with label of disk
2277 #ifndef CAB_READ_ONLY
2279 void CCabinet::OnAdd(PCFFILE File
,
2282 * FUNCTION: Called just before adding a file to a cabinet
2284 * File = Pointer to CFFILE for file being added
2285 * FileName = Pointer to buffer with name of file (full path)
2291 bool CCabinet::OnDiskLabel(ULONG Number
, char* Label
)
2293 * FUNCTION: Called when a disk needs a label
2295 * Number = Cabinet number that needs a label
2296 * Label = Pointer to buffer to place label of disk
2298 * true if a disk label was returned, false if not
2305 bool CCabinet::OnCabinetName(ULONG Number
, char* Name
)
2307 * FUNCTION: Called when a cabinet needs a name
2309 * Number = Disk number that needs a name
2310 * Name = Pointer to buffer to place name of cabinet
2312 * true if a cabinet name was returned, false if not
2318 #endif /* CAB_READ_ONLY */
2320 PCFFOLDER_NODE
CCabinet::LocateFolderNode(ULONG Index
)
2322 * FUNCTION: Locates a folder node
2324 * Index = Folder index
2326 * Pointer to folder node or NULL if the folder node was not found
2329 PCFFOLDER_NODE Node
;
2333 case CAB_FILE_SPLIT
:
2334 return FolderListTail
;
2336 case CAB_FILE_CONTINUED
:
2337 case CAB_FILE_PREV_NEXT
:
2338 return FolderListHead
;
2341 Node
= FolderListHead
;
2342 while (Node
!= NULL
)
2344 if (Node
->Index
== Index
)
2352 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2354 * FUNCTION: Returns the absolute offset of a file
2356 * File = Pointer to CFFILE_NODE structure for file
2358 * Status of operation
2363 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2365 (UINT
)File
->File
.FileOffset
,
2366 (UINT
)File
->File
.FileSize
));
2368 Node
= CurrentFolderNode
->DataListHead
;
2369 while (Node
!= NULL
)
2371 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2372 (UINT
)Node
->UncompOffset
,
2373 (UINT
)(Node
->UncompOffset
+ Node
->Data
.UncompSize
),
2374 (UINT
)Node
->Data
.UncompSize
));
2376 /* Node->Data.UncompSize will be 0 if the block is split
2377 (ie. it is the last block in this cabinet) */
2378 if ((Node
->Data
.UncompSize
== 0) ||
2379 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2380 (File
->File
.FileOffset
< Node
->UncompOffset
+
2381 Node
->Data
.UncompSize
)))
2383 File
->DataBlock
= Node
;
2384 return CAB_STATUS_SUCCESS
;
2389 return CAB_STATUS_INVALID_CAB
;
2393 ULONG
CCabinet::LocateFile(char* FileName
,
2396 * FUNCTION: Locates a file in the cabinet
2398 * FileName = Pointer to string with name of file to locate
2399 * File = Address of pointer to CFFILE_NODE structure to fill
2401 * Status of operation
2403 * Current folder is set to the folder of the file
2409 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2411 Node
= FileListHead
;
2412 while (Node
!= NULL
)
2414 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2416 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2417 if (!CurrentFolderNode
)
2419 DPRINT(MID_TRACE
, ("Folder with index number (%u) not found.\n",
2420 Node
->File
.FileControlID
));
2421 return CAB_STATUS_INVALID_CAB
;
2424 if (Node
->DataBlock
== NULL
)
2425 Status
= GetAbsoluteOffset(Node
);
2427 Status
= CAB_STATUS_SUCCESS
;
2434 return CAB_STATUS_NOFILE
;
2438 ULONG
CCabinet::ReadString(char* String
, LONG MaxLength
)
2440 * FUNCTION: Reads a NULL-terminated string from the cabinet
2442 * String = Pointer to buffer to place string
2443 * MaxLength = Maximum length of string
2445 * Status of operation
2455 Status
= ReadBlock(String
, MaxLength
, &BytesRead
);
2456 if (Status
!= CAB_STATUS_SUCCESS
)
2458 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2459 return CAB_STATUS_INVALID_CAB
;
2462 // Find the terminating NULL character
2463 for (Size
= 0; Size
< MaxLength
; Size
++)
2465 if (String
[Size
] == '\0')
2474 DPRINT(MIN_TRACE
, ("Filename in the cabinet file is too long.\n"));
2475 return CAB_STATUS_INVALID_CAB
;
2478 // Compute the offset of the next CFFILE.
2479 // 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.
2480 // + 1 to skip the terminating NULL character as well.
2481 Size
= -(MaxLength
- Size
) + 1;
2484 if( SetFilePointer(FileHandle
,
2487 FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
2489 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2490 return CAB_STATUS_INVALID_CAB
;
2493 if (fseek(FileHandle
, (off_t
)Size
, SEEK_CUR
) != 0)
2495 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2496 return CAB_STATUS_INVALID_CAB
;
2499 return CAB_STATUS_SUCCESS
;
2503 ULONG
CCabinet::ReadFileTable()
2505 * FUNCTION: Reads the file table from the cabinet file
2507 * Status of operation
2515 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%X).\n",
2516 (UINT
)CABHeader
.FileTableOffset
));
2518 /* Seek to file table */
2520 if( SetFilePointer(FileHandle
,
2521 CABHeader
.FileTableOffset
,
2523 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2525 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2526 return CAB_STATUS_INVALID_CAB
;
2529 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2531 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2532 return CAB_STATUS_INVALID_CAB
;
2536 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2538 File
= NewFileNode();
2541 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2542 return CAB_STATUS_NOMEMORY
;
2545 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2546 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2548 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2549 return CAB_STATUS_INVALID_CAB
;
2552 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2553 if (!File
->FileName
)
2555 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2556 return CAB_STATUS_NOMEMORY
;
2559 /* Read file name */
2560 Status
= ReadString(File
->FileName
, MAX_PATH
);
2561 if (Status
!= CAB_STATUS_SUCCESS
)
2564 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2566 (UINT
)File
->File
.FileOffset
,
2567 (UINT
)File
->File
.FileSize
,
2568 File
->File
.FileControlID
));
2571 return CAB_STATUS_SUCCESS
;
2575 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2577 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2579 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2581 * Status of operation
2584 ULONG AbsoluteOffset
;
2591 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2592 (UINT
)FolderNode
->Index
, (UINT
)FolderNode
->Folder
.DataOffset
));
2594 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2595 UncompOffset
= FolderNode
->UncompOffset
;
2597 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2599 Node
= NewDataNode(FolderNode
);
2602 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2603 return CAB_STATUS_NOMEMORY
;
2606 /* Seek to data block */
2608 if( SetFilePointer(FileHandle
,
2611 FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
2613 DPRINT(MIN_TRACE
, ("SetFilePointer() failed, error code is %u.\n", (UINT
)GetLastError()));
2614 return CAB_STATUS_INVALID_CAB
;
2617 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2619 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2620 return CAB_STATUS_INVALID_CAB
;
2624 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2625 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2627 DPRINT(MIN_TRACE
, ("Cannot read from file (%u).\n", (UINT
)Status
));
2628 return CAB_STATUS_INVALID_CAB
;
2631 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2632 (UINT
)AbsoluteOffset
,
2634 (UINT
)Node
->Data
.Checksum
,
2635 Node
->Data
.CompSize
,
2636 Node
->Data
.UncompSize
));
2638 Node
->AbsoluteOffset
= AbsoluteOffset
;
2639 Node
->UncompOffset
= UncompOffset
;
2641 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2642 UncompOffset
+= Node
->Data
.UncompSize
;
2645 FolderUncompSize
= UncompOffset
;
2647 return CAB_STATUS_SUCCESS
;
2651 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2653 * FUNCTION: Creates a new folder node
2655 * Pointer to node if there was enough free memory available, otherwise NULL
2658 PCFFOLDER_NODE Node
;
2660 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2664 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2666 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2668 Node
->Prev
= FolderListTail
;
2670 if (FolderListTail
!= NULL
)
2671 FolderListTail
->Next
= Node
;
2673 FolderListHead
= Node
;
2675 FolderListTail
= Node
;
2681 PCFFILE_NODE
CCabinet::NewFileNode()
2683 * FUNCTION: Creates a new file node
2685 * FolderNode = Pointer to folder node to bind file to
2687 * Pointer to node if there was enough free memory available, otherwise NULL
2692 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2696 memset(Node
, 0, sizeof(CFFILE_NODE
));
2698 Node
->Prev
= FileListTail
;
2700 if (FileListTail
!= NULL
)
2701 FileListTail
->Next
= Node
;
2703 FileListHead
= Node
;
2705 FileListTail
= Node
;
2711 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2713 * FUNCTION: Creates a new data block node
2715 * FolderNode = Pointer to folder node to bind data block to
2717 * Pointer to node if there was enough free memory available, otherwise NULL
2722 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2726 memset(Node
, 0, sizeof(CFDATA_NODE
));
2728 Node
->Prev
= FolderNode
->DataListTail
;
2730 if (FolderNode
->DataListTail
!= NULL
)
2731 FolderNode
->DataListTail
->Next
= Node
;
2733 FolderNode
->DataListHead
= Node
;
2735 FolderNode
->DataListTail
= Node
;
2741 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2743 * FUNCTION: Destroys data block nodes bound to a folder node
2745 * FolderNode = Pointer to folder node
2748 PCFDATA_NODE PrevNode
;
2749 PCFDATA_NODE NextNode
;
2751 NextNode
= FolderNode
->DataListHead
;
2752 while (NextNode
!= NULL
)
2754 PrevNode
= NextNode
->Next
;
2755 FreeMemory(NextNode
);
2756 NextNode
= PrevNode
;
2758 FolderNode
->DataListHead
= NULL
;
2759 FolderNode
->DataListTail
= NULL
;
2763 void CCabinet::DestroyFileNodes()
2765 * FUNCTION: Destroys file nodes
2768 PCFFILE_NODE PrevNode
;
2769 PCFFILE_NODE NextNode
;
2771 NextNode
= FileListHead
;
2772 while (NextNode
!= NULL
)
2774 PrevNode
= NextNode
->Next
;
2775 if (NextNode
->FileName
)
2776 FreeMemory(NextNode
->FileName
);
2777 FreeMemory(NextNode
);
2778 NextNode
= PrevNode
;
2780 FileListHead
= NULL
;
2781 FileListTail
= NULL
;
2785 void CCabinet::DestroyDeletedFileNodes()
2787 * FUNCTION: Destroys file nodes that are marked for deletion
2790 PCFFILE_NODE CurNode
;
2791 PCFFILE_NODE NextNode
;
2793 CurNode
= FileListHead
;
2794 while (CurNode
!= NULL
)
2796 NextNode
= CurNode
->Next
;
2798 if (CurNode
->Delete
)
2800 if (CurNode
->Prev
!= NULL
)
2801 CurNode
->Prev
->Next
= CurNode
->Next
;
2804 FileListHead
= CurNode
->Next
;
2806 FileListHead
->Prev
= NULL
;
2809 if (CurNode
->Next
!= NULL
)
2810 CurNode
->Next
->Prev
= CurNode
->Prev
;
2813 FileListTail
= CurNode
->Prev
;
2815 FileListTail
->Next
= NULL
;
2818 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2820 TotalFileSize
-= (sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(CurNode
->FileName
)) + 1);
2822 if (CurNode
->FileName
)
2823 FreeMemory(CurNode
->FileName
);
2824 FreeMemory(CurNode
);
2831 void CCabinet::DestroyFolderNodes()
2833 * FUNCTION: Destroys folder nodes
2836 PCFFOLDER_NODE PrevNode
;
2837 PCFFOLDER_NODE NextNode
;
2839 NextNode
= FolderListHead
;
2840 while (NextNode
!= NULL
)
2842 PrevNode
= NextNode
->Next
;
2843 DestroyDataNodes(NextNode
);
2844 FreeMemory(NextNode
);
2845 NextNode
= PrevNode
;
2847 FolderListHead
= NULL
;
2848 FolderListTail
= NULL
;
2852 void CCabinet::DestroyDeletedFolderNodes()
2854 * FUNCTION: Destroys folder nodes that are marked for deletion
2857 PCFFOLDER_NODE CurNode
;
2858 PCFFOLDER_NODE NextNode
;
2860 CurNode
= FolderListHead
;
2861 while (CurNode
!= NULL
)
2863 NextNode
= CurNode
->Next
;
2865 if (CurNode
->Delete
)
2867 if (CurNode
->Prev
!= NULL
)
2868 CurNode
->Prev
->Next
= CurNode
->Next
;
2871 FolderListHead
= CurNode
->Next
;
2873 FolderListHead
->Prev
= NULL
;
2876 if (CurNode
->Next
!= NULL
)
2877 CurNode
->Next
->Prev
= CurNode
->Prev
;
2880 FolderListTail
= CurNode
->Prev
;
2882 FolderListTail
->Next
= NULL
;
2885 DestroyDataNodes(CurNode
);
2886 FreeMemory(CurNode
);
2888 TotalFolderSize
-= sizeof(CFFOLDER
);
2895 ULONG
CCabinet::ComputeChecksum(void* Buffer
,
2899 * FUNCTION: Computes checksum for data block
2901 * Buffer = Pointer to data buffer
2902 * Size = Length of data buffer
2903 * Seed = Previously computed checksum
2905 * Checksum of buffer
2908 int UlongCount
; // Number of ULONGs in block
2909 ULONG Checksum
; // Checksum accumulator
2913 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2914 won't accept checksums computed by this routine */
2916 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer
, (UINT
)Size
));
2918 UlongCount
= Size
/ 4; // Number of ULONGs
2919 Checksum
= Seed
; // Init checksum
2920 pb
= (unsigned char*)Buffer
; // Start at front of data block
2922 /* Checksum integral multiple of ULONGs */
2923 while (UlongCount
-- > 0)
2925 /* NOTE: Build ULONG in big/little-endian independent manner */
2926 ul
= *pb
++; // Get low-order byte
2927 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2928 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2929 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2931 Checksum
^= ul
; // Update checksum
2934 /* Checksum remainder bytes */
2939 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2941 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2943 ul
|= *pb
++; // Get low-order byte
2947 Checksum
^= ul
; // Update checksum
2949 /* Return computed checksum */
2954 ULONG
CCabinet::ReadBlock(void* Buffer
,
2958 * FUNCTION: Read a block of data from file
2960 * Buffer = Pointer to data buffer
2961 * Size = Length of data buffer
2962 * BytesRead = Pointer to ULONG that on return will contain
2963 * number of bytes read
2965 * Status of operation
2968 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2969 return CAB_STATUS_INVALID_CAB
;
2970 return CAB_STATUS_SUCCESS
;
2973 bool CCabinet::MatchFileNamePattern(char* FileName
, char* Pattern
)
2975 * FUNCTION: Matches a wildcard character pattern against a file
2977 * FileName = The file name to check
2978 * Pattern = The pattern
2980 * Whether the pattern matches the file
2983 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
2984 * Adapted from code written by Ingo Wilken.
2985 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
2988 char* retryPattern
= NULL
;
2989 char* retryFileName
= NULL
;
2992 while (*FileName
|| *Pattern
)
2999 retryPattern
= Pattern
;
3000 retryFileName
= FileName
;
3004 if (*FileName
++ == '\0')
3010 if (*FileName
== ch
)
3019 Pattern
= retryPattern
;
3020 FileName
= ++retryFileName
;
3034 #ifndef CAB_READ_ONLY
3036 ULONG
CCabinet::InitCabinetHeader()
3038 * FUNCTION: Initializes cabinet header and optional fields
3040 * Status of operation
3046 CABHeader
.FileTableOffset
= 0; // Not known yet
3047 CABHeader
.FolderCount
= 0; // Not known yet
3048 CABHeader
.FileCount
= 0; // Not known yet
3049 CABHeader
.Flags
= 0; // Not known yet
3051 CABHeader
.CabinetNumber
= (USHORT
)CurrentDiskNumber
;
3053 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
3055 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
3056 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
3057 strcpy(CabinetPrev
, "");
3060 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
3062 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3063 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
3064 strcpy(DiskNext
, "");
3069 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3072 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3074 /* Calculate size of name of previous cabinet */
3075 TotalSize
+= (ULONG
)strlen(CabinetPrev
) + 1;
3077 /* Calculate size of label of previous disk */
3078 TotalSize
+= (ULONG
)strlen(DiskPrev
) + 1;
3081 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3084 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3086 /* Calculate size of name of next cabinet */
3087 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3089 NextFieldsSize
= Size
;
3091 /* Calculate size of label of next disk */
3092 Size
= (ULONG
)strlen(DiskNext
) + 1;
3094 NextFieldsSize
+= Size
;
3099 /* Add cabinet reserved area size if present */
3100 if (CabinetReservedFileSize
> 0)
3102 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
3103 TotalSize
+= CabinetReservedFileSize
;
3104 TotalSize
+= sizeof(ULONG
); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3107 DiskSize
+= TotalSize
;
3109 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
3111 return CAB_STATUS_SUCCESS
;
3115 ULONG
CCabinet::WriteCabinetHeader(bool MoreDisks
)
3117 * FUNCTION: Writes the cabinet header and optional fields
3119 * MoreDisks = true if next cabinet name should be included
3121 * Status of operation
3124 PCFFOLDER_NODE FolderNode
;
3125 PCFFILE_NODE FileNode
;
3131 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
3132 Size
= TotalHeaderSize
;
3136 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
3137 DiskSize
-= NextFieldsSize
;
3138 Size
= TotalHeaderSize
- NextFieldsSize
;
3141 /* Set absolute folder offsets */
3142 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
3143 CABHeader
.FolderCount
= 0;
3144 FolderNode
= FolderListHead
;
3145 while (FolderNode
!= NULL
)
3147 FolderNode
->Folder
.DataOffset
= BytesWritten
;
3149 BytesWritten
+= FolderNode
->TotalFolderSize
;
3151 CABHeader
.FolderCount
++;
3153 FolderNode
= FolderNode
->Next
;
3156 /* Set absolute offset of file table */
3157 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
3159 /* Count number of files to be committed */
3160 CABHeader
.FileCount
= 0;
3161 FileNode
= FileListHead
;
3162 while (FileNode
!= NULL
)
3164 if (FileNode
->Commit
)
3165 CABHeader
.FileCount
++;
3166 FileNode
= FileNode
->Next
;
3169 CABHeader
.CabinetSize
= DiskSize
;
3173 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), (LPDWORD
)&BytesWritten
, NULL
))
3175 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3176 return CAB_STATUS_CANNOT_WRITE
;
3179 BytesWritten
= sizeof(CFHEADER
);
3180 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
3182 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3183 return CAB_STATUS_CANNOT_WRITE
;
3187 /* Write per-cabinet reserved area if present */
3188 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
3192 ReservedSize
= CabinetReservedFileSize
& 0xffff;
3193 ReservedSize
|= (0 << 16); /* Folder reserved area size */
3194 ReservedSize
|= (0 << 24); /* Folder reserved area size */
3196 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(ULONG
), (LPDWORD
)&BytesWritten
, NULL
))
3198 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3199 return CAB_STATUS_CANNOT_WRITE
;
3202 BytesWritten
= sizeof(ULONG
);
3203 if (fwrite(&ReservedSize
, sizeof(ULONG
), 1, FileHandle
) < 1)
3205 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3206 return CAB_STATUS_CANNOT_WRITE
;
3211 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, (LPDWORD
)&BytesWritten
, NULL
))
3213 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3214 return CAB_STATUS_CANNOT_WRITE
;
3217 BytesWritten
= CabinetReservedFileSize
;
3218 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
3220 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3221 return CAB_STATUS_CANNOT_WRITE
;
3226 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
3228 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
3230 /* Write name of previous cabinet */
3231 Size
= (ULONG
)strlen(CabinetPrev
) + 1;
3233 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3235 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3236 return CAB_STATUS_CANNOT_WRITE
;
3239 BytesWritten
= Size
;
3240 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
3242 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3243 return CAB_STATUS_CANNOT_WRITE
;
3247 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
3249 /* Write label of previous disk */
3250 Size
= (ULONG
)strlen(DiskPrev
) + 1;
3252 if (!WriteFile(FileHandle
, DiskPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3254 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3255 return CAB_STATUS_CANNOT_WRITE
;
3258 BytesWritten
= Size
;
3259 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1)
3261 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3262 return CAB_STATUS_CANNOT_WRITE
;
3267 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
3269 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
3271 /* Write name of next cabinet */
3272 Size
= (ULONG
)strlen(CabinetNext
) + 1;
3274 if (!WriteFile(FileHandle
, CabinetNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3276 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3277 return CAB_STATUS_CANNOT_WRITE
;
3280 BytesWritten
= Size
;
3281 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
3283 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3284 return CAB_STATUS_CANNOT_WRITE
;
3288 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
3290 /* Write label of next disk */
3291 Size
= (ULONG
)strlen(DiskNext
) + 1;
3293 if (!WriteFile(FileHandle
, DiskNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
3295 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3296 return CAB_STATUS_CANNOT_WRITE
;
3299 BytesWritten
= Size
;
3300 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1)
3302 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3303 return CAB_STATUS_CANNOT_WRITE
;
3308 return CAB_STATUS_SUCCESS
;
3312 ULONG
CCabinet::WriteFolderEntries()
3314 * FUNCTION: Writes folder entries
3316 * Status of operation
3319 PCFFOLDER_NODE FolderNode
;
3322 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
3324 FolderNode
= FolderListHead
;
3325 while (FolderNode
!= NULL
)
3327 if (FolderNode
->Commit
)
3329 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3330 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, (UINT
)FolderNode
->Folder
.DataOffset
));
3333 if (!WriteFile(FileHandle
,
3334 &FolderNode
->Folder
,
3336 (LPDWORD
)&BytesWritten
,
3339 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3340 return CAB_STATUS_CANNOT_WRITE
;
3343 BytesWritten
= sizeof(CFFOLDER
);
3344 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3346 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3347 return CAB_STATUS_CANNOT_WRITE
;
3351 FolderNode
= FolderNode
->Next
;
3354 return CAB_STATUS_SUCCESS
;
3358 ULONG
CCabinet::WriteFileEntries()
3360 * FUNCTION: Writes file entries for all files
3362 * Status of operation
3367 bool SetCont
= false;
3369 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3371 File
= FileListHead
;
3372 while (File
!= NULL
)
3376 /* Remove any continued files that ends in this disk */
3377 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3378 File
->Delete
= true;
3380 /* The file could end in the last (split) block and should therefore
3381 appear in the next disk too */
3383 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3384 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3386 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3387 File
->Delete
= false;
3391 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3392 File
->File
.FileControlID
, (UINT
)File
->File
.FileOffset
, (UINT
)File
->File
.FileSize
, File
->FileName
));
3395 if (!WriteFile(FileHandle
,
3398 (LPDWORD
)&BytesWritten
,
3400 return CAB_STATUS_CANNOT_WRITE
;
3402 BytesWritten
= sizeof(CFFILE
);
3403 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3405 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3406 return CAB_STATUS_CANNOT_WRITE
;
3411 if (!WriteFile(FileHandle
,
3412 GetFileName(File
->FileName
),
3413 (DWORD
)strlen(GetFileName(File
->FileName
)) + 1,
3414 (LPDWORD
)&BytesWritten
,
3416 return CAB_STATUS_CANNOT_WRITE
;
3418 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3419 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3421 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3422 return CAB_STATUS_CANNOT_WRITE
;
3428 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3435 return CAB_STATUS_SUCCESS
;
3439 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3441 * FUNCTION: Writes data blocks to the cabinet
3443 * FolderNode = Pointer to folder node containing the data blocks
3445 * Status of operation
3448 PCFDATA_NODE DataNode
;
3453 DataNode
= FolderNode
->DataListHead
;
3454 if (DataNode
!= NULL
)
3455 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3457 while (DataNode
!= NULL
)
3459 DPRINT(MAX_TRACE
, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3460 (UINT
)DataNode
->ScratchFilePosition
,
3461 DataNode
->Data
.CompSize
,
3462 DataNode
->Data
.UncompSize
));
3464 /* InputBuffer is free for us to use here, so we use it and avoid a
3465 memory allocation. OutputBuffer can't be used here because it may
3466 still contain valid data (if a data block spans two or more disks) */
3467 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3468 if (Status
!= CAB_STATUS_SUCCESS
)
3470 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%u).\n", (UINT
)Status
));
3475 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3476 sizeof(CFDATA
), (LPDWORD
)&BytesWritten
, NULL
))
3478 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3479 return CAB_STATUS_CANNOT_WRITE
;
3482 BytesWritten
= sizeof(CFDATA
);
3483 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3485 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3486 return CAB_STATUS_CANNOT_WRITE
;
3491 if (!WriteFile(FileHandle
, InputBuffer
,
3492 DataNode
->Data
.CompSize
, (LPDWORD
)&BytesWritten
, NULL
))
3494 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3495 return CAB_STATUS_CANNOT_WRITE
;
3498 BytesWritten
= DataNode
->Data
.CompSize
;
3499 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3501 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3502 return CAB_STATUS_CANNOT_WRITE
;
3506 DataNode
= DataNode
->Next
;
3508 return CAB_STATUS_SUCCESS
;
3512 ULONG
CCabinet::WriteDataBlock()
3514 * FUNCTION: Writes the current data block to the scratch file
3516 * Status of operation
3521 PCFDATA_NODE DataNode
;
3525 Status
= Codec
->Compress(OutputBuffer
,
3530 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3531 (UINT
)CurrentIBufferSize
, (UINT
)TotalCompSize
));
3533 CurrentOBuffer
= OutputBuffer
;
3534 CurrentOBufferSize
= TotalCompSize
;
3537 DataNode
= NewDataNode(CurrentFolderNode
);
3540 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3541 return CAB_STATUS_NOMEMORY
;
3544 DiskSize
+= sizeof(CFDATA
);
3546 if (MaxDiskSize
> 0)
3547 /* Disk size is limited */
3548 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3550 BlockIsSplit
= false;
3554 DataNode
->Data
.CompSize
= (USHORT
)(MaxDiskSize
- DiskSize
);
3555 DataNode
->Data
.UncompSize
= 0;
3556 CreateNewDisk
= true;
3560 DataNode
->Data
.CompSize
= (USHORT
)CurrentOBufferSize
;
3561 DataNode
->Data
.UncompSize
= (USHORT
)CurrentIBufferSize
;
3564 DataNode
->Data
.Checksum
= 0;
3565 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3567 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3568 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3570 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3571 (UINT
)DataNode
->Data
.Checksum
,
3572 DataNode
->Data
.CompSize
,
3573 DataNode
->Data
.UncompSize
));
3575 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3576 CurrentOBuffer
, &BytesWritten
);
3577 if (Status
!= CAB_STATUS_SUCCESS
)
3580 DiskSize
+= BytesWritten
;
3582 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3583 CurrentFolderNode
->Folder
.DataBlockCount
++;
3585 CurrentOBuffer
= (unsigned char*)CurrentOBuffer
+ DataNode
->Data
.CompSize
;
3586 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3588 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3592 CurrentIBufferSize
= 0;
3593 CurrentIBuffer
= InputBuffer
;
3596 return CAB_STATUS_SUCCESS
;
3601 void CCabinet::ConvertDateAndTime(time_t* Time
,
3605 * FUNCTION: Returns file times of a file
3607 * FileHandle = File handle of file to get file times from
3608 * File = Pointer to CFFILE node for file
3610 * Status of operation
3615 timedef
= localtime(Time
);
3617 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3618 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3619 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3621 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3622 | ((timedef
->tm_mon
+ 1) << 5)
3623 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3625 *DosTime
= (timedef
->tm_sec
<< 0)
3626 | (timedef
->tm_min
<< 5)
3627 | (timedef
->tm_hour
<< 11);
3633 ULONG
CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3635 * FUNCTION: Returns file times of a file
3637 * FileHandle = File handle of file to get file times from
3638 * File = Pointer to CFFILE node for file
3640 * Status of operation
3646 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3647 FileTimeToDosDateTime(&FileTime
,
3648 &File
->File
.FileDate
,
3649 &File
->File
.FileTime
);
3654 // Check for an absolute path
3655 if (IsSeparator(File
->FileName
[0]))
3656 strcpy(buf
, File
->FileName
);
3659 if (!getcwd(buf
, sizeof(buf
)))
3660 return CAB_STATUS_CANNOT_READ
;
3661 strcat(buf
, DIR_SEPARATOR_STRING
);
3662 strcat(buf
, File
->FileName
);
3665 if (stat(buf
, &stbuf
) == -1)
3666 return CAB_STATUS_CANNOT_READ
;
3668 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3670 return CAB_STATUS_SUCCESS
;
3674 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3676 * FUNCTION: Returns attributes on a file
3678 * File = Pointer to CFFILE node for file
3680 * Status of operation
3686 Attributes
= GetFileAttributes(File
->FileName
);
3687 if (Attributes
== -1)
3688 return CAB_STATUS_CANNOT_READ
;
3690 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3691 // The IDs for these attributes are the same in the CAB file and under Windows
3692 // If the file has any other attributes, strip them off by the logical AND.
3693 File
->File
.Attributes
= (USHORT
)(Attributes
& 0x37);
3698 // Check for an absolute path
3699 if (IsSeparator(File
->FileName
[0]))
3700 strcpy(buf
, File
->FileName
);
3703 if (!getcwd(buf
, sizeof(buf
)))
3704 return CAB_STATUS_CANNOT_READ
;
3705 strcat(buf
, DIR_SEPARATOR_STRING
);
3706 strcat(buf
, File
->FileName
);
3709 if (stat(buf
, &stbuf
) == -1)
3710 return CAB_STATUS_CANNOT_READ
;
3713 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3714 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3715 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3718 if (stbuf
.st_mode
& S_IFDIR
)
3719 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3721 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3724 return CAB_STATUS_SUCCESS
;
3728 ULONG
CCabinet::SetAttributesOnFile(char* FileName
, USHORT FileAttributes
)
3730 * FUNCTION: Sets attributes on a file
3732 * FileName = File name with path
3733 * FileAttributes = Attributes of that file
3735 * Status of operation
3739 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3740 // The IDs for these attributes are the same in the CAB file and under Windows
3741 // If the file has any other attributes, strip them off by the logical AND.
3742 SetFileAttributes(FileName
, (DWORD
)(FileAttributes
& 0x37));
3744 return CAB_STATUS_SUCCESS
;
3746 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3747 return CAB_STATUS_SUCCESS
;
3751 #endif /* CAB_READ_ONLY */