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 * NOTES: Define CAB_READ_ONLY for read only version
9 * CSH 21/03-2001 Created
10 * CSH 15/08-2003 Made it portable
12 * - Checksum of datablocks should be calculated
13 * - EXTRACT.EXE complains if a disk is created manually
14 * - Folders that are created manually and span disks will result in a damaged cabinet
24 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
25 static long _GetSizeOfFile(FILEHANDLE handle
)
27 unsigned long size
= GetFileSize(handle
, NULL
);
28 if (size
== INVALID_FILE_SIZE
)
34 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
35 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, unsigned long size
, unsigned long* bytesread
)
37 return ReadFile(handle
, buffer
, size
, bytesread
, NULL
);
40 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
41 static long _GetSizeOfFile(FILEHANDLE handle
)
44 fseek(handle
, 0, SEEK_END
);
46 fseek(handle
, 0, SEEK_SET
);
49 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
50 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, unsigned long size
, unsigned long* bytesread
)
52 *bytesread
= fread(buffer
, 1, size
, handle
);
53 return *bytesread
== size
;
62 void DumpBuffer(void* Buffer
, unsigned long Size
)
64 FILEHANDLE FileHandle
;
65 unsigned long BytesWritten
;
67 /* Create file, overwrite if it already exists */
68 FileHandle
= CreateFile("dump.bin", // Create this file
69 GENERIC_WRITE
, // Open for writing
72 CREATE_ALWAYS
, // Create or overwrite
73 FILE_ATTRIBUTE_NORMAL
, // Normal file
74 NULL
); // No attribute template
75 if (FileHandle
== INVALID_HANDLE_VALUE
) {
76 DPRINT(MID_TRACE
, ("ERROR OPENING '%d'.\n", (unsigned int)GetLastError()));
80 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
)) {
81 DPRINT(MID_TRACE
, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError()));
84 CloseFile(FileHandle
);
92 CCFDATAStorage::CCFDATAStorage()
94 * FUNCTION: Default constructor
101 CCFDATAStorage::~CCFDATAStorage()
103 * FUNCTION: Default destructor
106 ASSERT(!FileCreated
);
110 unsigned long CCFDATAStorage::Create(char* FileName
)
112 * FUNCTION: Creates the file
114 * FileName = Pointer to name of file
116 * Status of operation
119 ASSERT(!FileCreated
);
122 if (GetTempPath(MAX_PATH
, FullName
) == 0)
123 return CAB_STATUS_CANNOT_CREATE
;
125 strcat(FullName
, FileName
);
127 /* Create file, overwrite if it already exists */
128 FileHandle
= CreateFile(FullName
, // Create this file
129 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
132 CREATE_ALWAYS
, // Create or overwrite
133 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
134 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
135 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
136 NULL
); // No attribute template
137 if (FileHandle
== INVALID_HANDLE_VALUE
) {
138 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)GetLastError()));
139 return CAB_STATUS_CANNOT_CREATE
;
142 /*if (tmpnam(FullName) == NULL)*/
143 if ((FileHandle
= tmpfile()) == NULL
)
144 return CAB_STATUS_CANNOT_CREATE
;
146 FileHandle = fopen(FullName, "w+b");
147 if (FileHandle == NULL) {
148 DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)errno));
149 return CAB_STATUS_CANNOT_CREATE;
156 return CAB_STATUS_SUCCESS
;
160 unsigned long CCFDATAStorage::Destroy()
162 * FUNCTION: Destroys the file
164 * Status of operation
169 CloseFile(FileHandle
);
173 return CAB_STATUS_SUCCESS
;
177 unsigned long CCFDATAStorage::Truncate()
179 * FUNCTION: Truncate the scratch file to zero bytes
181 * Status of operation
185 if (!SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
))
186 return CAB_STATUS_FAILURE
;
187 if (!SetEndOfFile(FileHandle
))
188 return CAB_STATUS_FAILURE
;
191 FileHandle
= tmpfile();
192 if (FileHandle
== NULL
) {
193 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)errno
));
194 return CAB_STATUS_FAILURE
;
197 return CAB_STATUS_SUCCESS
;
201 unsigned long CCFDATAStorage::Position()
203 * FUNCTION: Returns current position in file
209 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
211 return (unsigned long)ftell(FileHandle
);
216 unsigned long CCFDATAStorage::Seek(long Position
)
218 * FUNCTION: Seeks to an absolute position
220 * Position = Absolute position to seek to
222 * Status of operation
226 if (SetFilePointer(FileHandle
,
229 FILE_BEGIN
) == 0xFFFFFFFF)
230 return CAB_STATUS_FAILURE
;
232 return CAB_STATUS_SUCCESS
;
234 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0) {
235 return CAB_STATUS_FAILURE
;
237 return CAB_STATUS_SUCCESS
;
243 unsigned long CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesRead
)
245 * FUNCTION: Reads a CFDATA block from the file
247 * Data = Pointer to CFDATA block for the buffer
248 * Buffer = Pointer to buffer to store data read
249 * BytesWritten = Pointer to buffer to write number of bytes read
251 * Status of operation
255 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, BytesRead
, NULL
))
256 return CAB_STATUS_CANNOT_READ
;
259 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
260 if (*BytesRead
!= Data
->CompSize
) {
261 return CAB_STATUS_CANNOT_READ
;
264 return CAB_STATUS_SUCCESS
;
268 unsigned long CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesWritten
)
270 * FUNCTION: Writes a CFDATA block to the file
272 * Data = Pointer to CFDATA block for the buffer
273 * Buffer = Pointer to buffer with data to write
274 * BytesWritten = Pointer to buffer to write number of bytes written
276 * Status of operation
280 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, BytesWritten
, NULL
))
281 return CAB_STATUS_CANNOT_WRITE
;
283 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
284 if (*BytesWritten
!= Data
->CompSize
)
285 return CAB_STATUS_CANNOT_WRITE
;
287 return CAB_STATUS_SUCCESS
;
290 #endif /* CAB_READ_ONLY */
297 * FUNCTION: Default constructor
301 strcpy(DestPath
, "");
302 strcpy(CabinetReservedFile
, "");
303 CabinetReservedFileBuffer
= NULL
;
304 CabinetReservedFileSize
= 0;
306 FolderListHead
= NULL
;
307 FolderListTail
= NULL
;
311 Codec
= new CRawCodec();
312 CodecId
= CAB_CODEC_RAW
;
313 CodecSelected
= true;
318 BlockIsSplit
= false;
321 FolderUncompSize
= 0;
322 BytesLeftInBlock
= 0;
324 CurrentDataNode
= NULL
;
328 CCabinet::~CCabinet()
330 * FUNCTION: Default destructor
333 if (CabinetReservedFileBuffer
!= NULL
) {
334 FreeMemory(CabinetReservedFileBuffer
);
335 CabinetReservedFileBuffer
= NULL
;
336 CabinetReservedFileSize
= 0;
343 bool CCabinet::IsSeparator(char Char
)
345 * FUNCTION: Determines if a character is a separator
347 * Char = Character to check
349 * Wether it is a separator
352 if ((Char
== '\\') || (Char
== '/')) {
359 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
361 * FUNCTION: Replaces \ or / with the one used be the host environment
363 * Path = Pointer to string with pathname
364 * Allocate = Specifies wther to allocate memory for the new
365 * string or to change the existing buffer
367 * Pointer to new path
374 newpath
= strdup(Path
);
380 while (Path
[i
] != 0) {
382 if (Path
[i
] == '/') {
386 if (Path
[i
] == '\\') {
390 newpath
[i
] = Path
[i
];
400 char* CCabinet::GetFileName(char* Path
)
402 * FUNCTION: Returns a pointer to file name
404 * Path = Pointer to string with pathname
406 * Pointer to filename
411 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
414 if (IsSeparator(Path
[i
- 1])) j
= i
;
420 void CCabinet::RemoveFileName(char* Path
)
422 * FUNCTION: Removes a file name from a path
424 * Path = Pointer to string with path
430 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
431 FileName
= GetFileName(Path
+ i
);
433 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
435 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
441 bool CCabinet::NormalizePath(char* Path
,
442 unsigned long Length
)
444 * FUNCTION: Normalizes a path
446 * Path = Pointer to string with pathname
447 * Length = Number of bytes in Path
449 * true if there was enough room in Path, or false
455 if ((n
= strlen(Path
)) &&
456 (!IsSeparator(Path
[n
- 1])) &&
457 (OK
= ((n
+ 1) < Length
))) {
458 Path
[n
] = DIR_SEPARATOR_CHAR
;
465 char* CCabinet::GetCabinetName()
467 * FUNCTION: Returns pointer to cabinet file name
469 * Pointer to string with name of cabinet
476 void CCabinet::SetCabinetName(char* FileName
)
478 * FUNCTION: Sets cabinet file name
480 * FileName = Pointer to string with name of cabinet
483 strcpy(CabinetName
, FileName
);
487 void CCabinet::SetDestinationPath(char* DestinationPath
)
489 * FUNCTION: Sets destination path
491 * DestinationPath = Pointer to string with name of destination path
494 strcpy(DestPath
, DestinationPath
);
495 ConvertPath(DestPath
, false);
496 if (strlen(DestPath
) > 0)
497 NormalizePath(DestPath
, MAX_PATH
);
501 char* CCabinet::GetDestinationPath()
503 * FUNCTION: Returns destination path
505 * Pointer to string with name of destination path
512 bool CCabinet::SetCabinetReservedFile(char* FileName
)
514 * FUNCTION: Sets cabinet reserved file
516 * FileName = Pointer to string with name of cabinet reserved file
519 FILEHANDLE FileHandle
;
520 unsigned long BytesRead
;
523 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
524 GENERIC_READ
, // Open for reading
525 FILE_SHARE_READ
, // Share for reading
527 OPEN_EXISTING
, // Existing file only
528 FILE_ATTRIBUTE_NORMAL
, // Normal file
529 NULL
); // No attribute template
530 if (FileHandle
== INVALID_HANDLE_VALUE
) {
531 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
536 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
537 if (FileHandle
== NULL
) {
538 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
543 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
544 if (CabinetReservedFileSize
== (unsigned long)-1) {
545 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
549 if (CabinetReservedFileSize
== 0)
551 CloseFile(FileHandle
);
555 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
556 if (!CabinetReservedFileBuffer
) {
557 CloseFile(FileHandle
);
561 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
)) {
562 CloseFile(FileHandle
);
566 CloseFile(FileHandle
);
568 strcpy(CabinetReservedFile
, FileName
);
574 char* CCabinet::GetCabinetReservedFile()
576 * FUNCTION: Returns cabionet reserved file
578 * Pointer to string with name of cabinet reserved file
581 return CabinetReservedFile
;
585 unsigned long CCabinet::GetCurrentDiskNumber()
587 * FUNCTION: Returns current disk number
589 * Current disk number
592 return CurrentDiskNumber
;
596 unsigned long CCabinet::Open()
598 * FUNCTION: Opens a cabinet file
600 * Status of operation
603 PCFFOLDER_NODE FolderNode
;
604 unsigned long Status
;
608 unsigned long BytesRead
;
611 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
613 return CAB_STATUS_NOMEMORY
;
616 FileHandle
= CreateFile(CabinetName
, // Open this file
617 GENERIC_READ
, // Open for reading
618 FILE_SHARE_READ
, // Share for reading
620 OPEN_EXISTING
, // Existing file only
621 FILE_ATTRIBUTE_NORMAL
, // Normal file
622 NULL
); // No attribute template
624 if (FileHandle
== INVALID_HANDLE_VALUE
) {
625 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
626 return CAB_STATUS_CANNOT_OPEN
;
629 FileHandle
= fopen(CabinetName
, "rb");
630 if (FileHandle
== NULL
) {
631 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
632 return CAB_STATUS_CANNOT_OPEN
;
638 /* Load CAB header */
639 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
640 != CAB_STATUS_SUCCESS
) {
641 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
642 return CAB_STATUS_INVALID_CAB
;
646 if ((BytesRead
!= sizeof(CFHEADER
)) ||
647 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
648 (CABHeader
.Version
!= CAB_VERSION
) ||
649 (CABHeader
.FolderCount
== 0 ) ||
650 (CABHeader
.FileCount
== 0 ) ||
651 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
653 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
654 return CAB_STATUS_INVALID_CAB
;
659 /* Read/skip any reserved bytes */
660 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
661 if ((Status
= ReadBlock(&Size
, sizeof(unsigned long), &BytesRead
))
662 != CAB_STATUS_SUCCESS
) {
663 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
664 return CAB_STATUS_INVALID_CAB
;
666 CabinetReserved
= Size
& 0xFFFF;
667 FolderReserved
= (Size
>> 16) & 0xFF;
668 DataReserved
= (Size
>> 24) & 0xFF;
671 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
672 if (GetLastError() != NO_ERROR
) {
673 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
674 return CAB_STATUS_FAILURE
;
677 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0) {
678 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
679 return CAB_STATUS_FAILURE
;
684 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
685 /* Read name of previous cabinet */
686 Status
= ReadString(CabinetPrev
, 256);
687 if (Status
!= CAB_STATUS_SUCCESS
)
689 /* Read label of previous disk */
690 Status
= ReadString(DiskPrev
, 256);
691 if (Status
!= CAB_STATUS_SUCCESS
)
694 strcpy(CabinetPrev
, "");
695 strcpy(DiskPrev
, "");
698 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
699 /* Read name of next cabinet */
700 Status
= ReadString(CabinetNext
, 256);
701 if (Status
!= CAB_STATUS_SUCCESS
)
703 /* Read label of next disk */
704 Status
= ReadString(DiskNext
, 256);
705 if (Status
!= CAB_STATUS_SUCCESS
)
708 strcpy(CabinetNext
, "");
709 strcpy(DiskNext
, "");
712 /* Read all folders */
713 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++) {
714 FolderNode
= NewFolderNode();
716 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
717 return CAB_STATUS_NOMEMORY
;
721 FolderNode
->UncompOffset
= FolderUncompSize
;
723 FolderNode
->Index
= Index
;
725 if ((Status
= ReadBlock(&FolderNode
->Folder
,
726 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
) {
727 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
728 return CAB_STATUS_INVALID_CAB
;
732 /* Read file entries */
733 Status
= ReadFileTable();
734 if (Status
!= CAB_STATUS_SUCCESS
) {
735 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (unsigned int)Status
));
739 /* Read data blocks for all folders */
740 FolderNode
= FolderListHead
;
741 while (FolderNode
!= NULL
) {
742 Status
= ReadDataBlocks(FolderNode
);
743 if (Status
!= CAB_STATUS_SUCCESS
) {
744 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status
));
747 FolderNode
= FolderNode
->Next
;
750 return CAB_STATUS_SUCCESS
;
754 void CCabinet::Close()
756 * FUNCTION: Closes the cabinet file
760 CloseFile(FileHandle
);
766 unsigned long CCabinet::FindFirst(char* FileName
,
769 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
771 * FileName = Pointer to search criteria
772 * Search = Pointer to search structure
774 * Status of operation
777 RestartSearch
= false;
778 strncpy(Search
->Search
, FileName
, MAX_PATH
);
779 Search
->Next
= FileListHead
;
780 return FindNext(Search
);
784 unsigned long CCabinet::FindNext(PCAB_SEARCH Search
)
786 * FUNCTION: Finds next file in the cabinet that matches a search criteria
788 * Search = Pointer to search structure
790 * Status of operation
793 unsigned long Status
;
796 Search
->Next
= FileListHead
;
798 /* Skip split files already extracted */
799 while ((Search
->Next
) &&
800 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
801 (Search
->Next
->File
.FileOffset
<= LastFileOffset
)) {
802 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
803 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
804 Search
->Next
= Search
->Next
->Next
;
807 RestartSearch
= false;
810 /* FIXME: Check search criteria */
813 if (strlen(DiskNext
) > 0) {
816 SetCabinetName(CabinetNext
);
818 OnDiskChange(CabinetNext
, DiskNext
);
821 if (Status
!= CAB_STATUS_SUCCESS
)
824 Search
->Next
= FileListHead
;
826 return CAB_STATUS_NOFILE
;
828 return CAB_STATUS_NOFILE
;
831 Search
->File
= &Search
->Next
->File
;
832 Search
->FileName
= Search
->Next
->FileName
;
833 Search
->Next
= Search
->Next
->Next
;
834 return CAB_STATUS_SUCCESS
;
838 unsigned long CCabinet::ExtractFile(char* FileName
)
840 * FUNCTION: Extracts a file from the cabinet
842 * FileName = Pointer to buffer with name of file
844 * Status of operation
848 unsigned long Offset
;
849 unsigned long BytesRead
;
850 unsigned long BytesToRead
;
851 unsigned long BytesWritten
;
852 unsigned long BytesSkipped
;
853 unsigned long BytesToWrite
;
854 unsigned long TotalBytesRead
;
855 unsigned long CurrentOffset
;
856 unsigned char* Buffer
;
857 unsigned char* CurrentBuffer
;
861 unsigned long Status
;
866 char DestName
[MAX_PATH
];
867 char TempName
[MAX_PATH
];
869 Status
= LocateFile(FileName
, &File
);
870 if (Status
!= CAB_STATUS_SUCCESS
) {
871 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
875 LastFileOffset
= File
->File
.FileOffset
;
877 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
) {
879 SelectCodec(CAB_CODEC_RAW
);
882 SelectCodec(CAB_CODEC_MSZIP
);
885 return CAB_STATUS_UNSUPPCOMP
;
888 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
889 (unsigned int)File
->File
.FileOffset
,
890 (unsigned int)File
->File
.FileSize
,
891 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
892 (unsigned int)File
->DataBlock
->UncompOffset
));
894 strcpy(DestName
, DestPath
);
895 strcat(DestName
, FileName
);
897 /* Create destination file, fail if it already exists */
899 DestFile
= CreateFile(DestName
, // Create this file
900 GENERIC_WRITE
, // Open for writing
903 CREATE_NEW
, // New file only
904 FILE_ATTRIBUTE_NORMAL
, // Normal file
905 NULL
); // No attribute template
906 if (DestFile
== INVALID_HANDLE_VALUE
) {
907 /* If file exists, ask to overwrite file */
908 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
909 (OnOverwrite(&File
->File
, FileName
))) {
910 /* Create destination file, overwrite if it already exists */
911 DestFile
= CreateFile(DestName
, // Create this file
912 GENERIC_WRITE
, // Open for writing
915 TRUNCATE_EXISTING
, // Truncate the file
916 FILE_ATTRIBUTE_NORMAL
, // Normal file
917 NULL
); // No attribute template
918 if (DestFile
== INVALID_HANDLE_VALUE
)
919 return CAB_STATUS_CANNOT_CREATE
;
921 if (Status
== ERROR_FILE_EXISTS
)
922 return CAB_STATUS_FILE_EXISTS
;
924 return CAB_STATUS_CANNOT_CREATE
;
928 DestFile
= fopen(DestName
, "rb");
929 if (DestFile
!= NULL
) {
931 /* If file exists, ask to overwrite file */
932 if (OnOverwrite(&File
->File
, FileName
)) {
933 DestFile
= fopen(DestName
, "w+b");
934 if (DestFile
== NULL
) {
935 return CAB_STATUS_CANNOT_CREATE
;
938 return CAB_STATUS_FILE_EXISTS
;
941 DestFile
= fopen(DestName
, "w+b");
942 if (DestFile
== NULL
) {
943 return CAB_STATUS_CANNOT_CREATE
;
948 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
)) {
950 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
951 return CAB_STATUS_CANNOT_WRITE
;
954 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
956 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
958 SetAttributesOnFile(File
);
960 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
963 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
964 return CAB_STATUS_NOMEMORY
;
967 /* Call OnExtract event handler */
968 OnExtract(&File
->File
, FileName
);
970 /* Search to start of file */
972 Offset
= SetFilePointer(FileHandle
,
973 File
->DataBlock
->AbsoluteOffset
,
976 if (GetLastError() != NO_ERROR
) {
977 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
978 return CAB_STATUS_INVALID_CAB
;
981 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
982 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
983 return CAB_STATUS_FAILURE
;
985 Offset
= ftell(FileHandle
);
988 Size
= File
->File
.FileSize
;
989 Offset
= File
->File
.FileOffset
;
990 CurrentOffset
= File
->DataBlock
->UncompOffset
;
994 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
997 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%d) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
998 File
->DataBlock
->UncompOffset
, (unsigned int)ReuseBlock
, Offset
, Size
,
1001 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0)) {
1003 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1005 CurrentBuffer
= Buffer
;
1008 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1010 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1011 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1012 CloseFile(DestFile
);
1014 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1015 return CAB_STATUS_INVALID_CAB
;
1018 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n",
1019 (unsigned int)CFData
.Checksum
,
1020 (unsigned int)CFData
.CompSize
,
1021 (unsigned int)CFData
.UncompSize
));
1023 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1025 BytesToRead
= CFData
.CompSize
;
1027 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1028 (long unsigned int)CurrentBuffer
, (long unsigned int)Buffer
));
1030 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1031 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
1032 CloseFile(DestFile
);
1034 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1035 return CAB_STATUS_INVALID_CAB
;
1038 /* FIXME: Does not work with files generated by makecab.exe */
1040 if (CFData.Checksum != 0) {
1041 unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1042 if (Checksum != CFData.Checksum) {
1043 CloseFile(DestFile);
1045 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1046 Checksum, CFData.Checksum));
1047 return CAB_STATUS_INVALID_CAB;
1051 TotalBytesRead
+= BytesRead
;
1053 CurrentBuffer
+= BytesRead
;
1055 if (CFData
.UncompSize
== 0) {
1056 if (strlen(DiskNext
) == 0)
1057 return CAB_STATUS_NOFILE
;
1059 /* CloseCabinet() will destroy all file entries so in case
1060 FileName refers to the FileName field of a CFFOLDER_NODE
1061 structure, we have to save a copy of the filename */
1062 strcpy(TempName
, FileName
);
1066 SetCabinetName(CabinetNext
);
1068 OnDiskChange(CabinetNext
, DiskNext
);
1071 if (Status
!= CAB_STATUS_SUCCESS
)
1074 /* The first data block of the file will not be
1075 found as it is located in the previous file */
1076 Status
= LocateFile(TempName
, &File
);
1077 if (Status
== CAB_STATUS_NOFILE
) {
1078 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
1082 /* The file is continued in the first data block in the folder */
1083 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1085 /* Search to start of file */
1087 (unsigned int)SetFilePointer(FileHandle
,
1088 File
->DataBlock
->AbsoluteOffset
,
1091 if (GetLastError() != NO_ERROR
) {
1092 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1093 return CAB_STATUS_INVALID_CAB
;
1096 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
1097 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1098 return CAB_STATUS_INVALID_CAB
;
1102 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1103 (unsigned int)File
->File
.FileOffset
,
1104 (unsigned int)File
->File
.FileSize
,
1105 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
1106 (unsigned int)File
->DataBlock
->UncompOffset
));
1108 CurrentDataNode
= File
->DataBlock
;
1111 RestartSearch
= true;
1113 } while (CFData
.UncompSize
== 0);
1115 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1117 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1118 if (Status
!= CS_SUCCESS
) {
1119 CloseFile(DestFile
);
1121 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1122 if (Status
== CS_NOMEMORY
)
1123 return CAB_STATUS_NOMEMORY
;
1124 return CAB_STATUS_INVALID_CAB
;
1127 if (BytesToWrite
!= CFData
.UncompSize
) {
1128 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1129 BytesToWrite
, CFData
.UncompSize
));
1130 return CAB_STATUS_INVALID_CAB
;
1133 BytesLeftInBlock
= BytesToWrite
;
1135 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1137 BytesToWrite
= BytesLeftInBlock
;
1139 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1140 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1141 CurrentDataNode
->Data
.CompSize
));
1143 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1144 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1145 CloseFile(DestFile
);
1147 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1148 return CAB_STATUS_INVALID_CAB
;
1151 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1152 CFData
.CompSize
, CFData
.UncompSize
));
1154 /* Go to next data block */
1156 (unsigned int)SetFilePointer(FileHandle
,
1157 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1158 CurrentDataNode
->Data
.CompSize
,
1161 if (GetLastError() != NO_ERROR
) {
1162 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1163 return CAB_STATUS_INVALID_CAB
;
1166 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1167 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0) {
1168 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1169 return CAB_STATUS_INVALID_CAB
;
1177 BytesSkipped
= (Offset
- CurrentOffset
);
1181 BytesToWrite
-= BytesSkipped
;
1183 if (Size
< BytesToWrite
)
1184 BytesToWrite
= Size
;
1186 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1187 (unsigned int)Offset
,
1188 (unsigned int)CurrentOffset
,
1189 (unsigned int)BytesToWrite
,
1190 (unsigned int)BytesSkipped
, (unsigned int)Skip
,
1191 (unsigned int)Size
));
1194 if (!WriteFile(DestFile
, (void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1195 BytesToWrite
, &BytesWritten
, NULL
) ||
1196 (BytesToWrite
!= BytesWritten
)) {
1197 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1199 BytesWritten
= BytesToWrite
;
1200 if (fwrite((void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1201 BytesToWrite
, 1, DestFile
) < 1) {
1203 CloseFile(DestFile
);
1205 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1206 return CAB_STATUS_CANNOT_WRITE
;
1208 Size
-= BytesToWrite
;
1210 CurrentOffset
+= BytesToWrite
;
1212 /* Don't skip any more bytes */
1217 CloseFile(DestFile
);
1221 return CAB_STATUS_SUCCESS
;
1225 void CCabinet::SelectCodec(unsigned long Id
)
1227 * FUNCTION: Selects codec engine to use
1229 * Id = Codec identifier
1232 if (CodecSelected
) {
1236 CodecSelected
= false;
1243 Codec
= new CRawCodec();
1246 case CAB_CODEC_MSZIP
:
1247 Codec
= new CMSZipCodec();
1254 CodecSelected
= true;
1258 #ifndef CAB_READ_ONLY
1260 /* CAB write methods */
1262 unsigned long CCabinet::NewCabinet()
1264 * FUNCTION: Creates a new cabinet
1266 * Status of operation
1269 unsigned long Status
;
1271 CurrentDiskNumber
= 0;
1273 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1274 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1275 if ((!OutputBuffer
) || (!InputBuffer
)) {
1276 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1277 return CAB_STATUS_NOMEMORY
;
1279 CurrentIBuffer
= InputBuffer
;
1280 CurrentIBufferSize
= 0;
1282 CABHeader
.Signature
= CAB_SIGNATURE
;
1283 CABHeader
.Reserved1
= 0; // Not used
1284 CABHeader
.CabinetSize
= 0; // Not yet known
1285 CABHeader
.Reserved2
= 0; // Not used
1286 CABHeader
.Reserved3
= 0; // Not used
1287 CABHeader
.Version
= CAB_VERSION
;
1288 CABHeader
.FolderCount
= 0; // Not yet known
1289 CABHeader
.FileCount
= 0; // Not yet known
1290 CABHeader
.Flags
= 0; // Not yet known
1291 // FIXME: Should be random
1292 CABHeader
.SetID
= 0x534F;
1293 CABHeader
.CabinetNumber
= 0;
1296 TotalFolderSize
= 0;
1299 DiskSize
= sizeof(CFHEADER
);
1301 InitCabinetHeader();
1303 // NextFolderNumber is 0-based
1304 NextFolderNumber
= 0;
1306 CurrentFolderNode
= NULL
;
1307 Status
= NewFolder();
1308 if (Status
!= CAB_STATUS_SUCCESS
)
1311 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1313 ScratchFile
= new CCFDATAStorage
;
1315 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1316 return CAB_STATUS_NOMEMORY
;
1319 Status
= ScratchFile
->Create("~CAB.tmp");
1321 CreateNewFolder
= false;
1323 CreateNewDisk
= false;
1325 PrevCabinetNumber
= 0;
1331 unsigned long CCabinet::NewDisk()
1333 * FUNCTION: Forces a new disk to be created
1335 * Status of operation
1338 // NextFolderNumber is 0-based
1339 NextFolderNumber
= 1;
1341 CreateNewDisk
= false;
1343 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1345 InitCabinetHeader();
1347 CurrentFolderNode
->TotalFolderSize
= 0;
1349 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1351 return CAB_STATUS_SUCCESS
;
1355 unsigned long CCabinet::NewFolder()
1357 * FUNCTION: Forces a new folder to be created
1359 * Status of operation
1362 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1364 CurrentFolderNode
= NewFolderNode();
1365 if (!CurrentFolderNode
) {
1366 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1367 return CAB_STATUS_NOMEMORY
;
1372 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1374 case CAB_CODEC_MSZIP
:
1375 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1378 return CAB_STATUS_UNSUPPCOMP
;
1381 /* FIXME: This won't work if no files are added to the new folder */
1383 DiskSize
+= sizeof(CFFOLDER
);
1385 TotalFolderSize
+= sizeof(CFFOLDER
);
1389 CABHeader
.FolderCount
++;
1393 return CAB_STATUS_SUCCESS
;
1397 unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1399 * FUNCTION: Writes a file to the scratch file
1401 * FileNode = Pointer to file node
1403 * Status of operation
1406 unsigned long BytesToRead
;
1407 unsigned long BytesRead
;
1408 unsigned long Status
;
1411 if (!ContinueFile
) {
1412 /* Try to open file */
1414 SourceFile
= CreateFile(
1415 FileNode
->FileName
, // Open this file
1416 GENERIC_READ
, // Open for reading
1417 FILE_SHARE_READ
, // Share for reading
1418 NULL
, // No security
1419 OPEN_EXISTING
, // File must exist
1420 FILE_ATTRIBUTE_NORMAL
, // Normal file
1421 NULL
); // No attribute template
1422 if (SourceFile
== INVALID_HANDLE_VALUE
) {
1423 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1424 return CAB_STATUS_NOFILE
;
1427 SourceFile
= fopen(FileNode
->FileName
, "rb");
1428 if (SourceFile
== NULL
) {
1429 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1430 return CAB_STATUS_NOFILE
;
1434 if (CreateNewFolder
) {
1435 /* There is always a new folder after
1436 a split file is completely stored */
1437 Status
= NewFolder();
1438 if (Status
!= CAB_STATUS_SUCCESS
)
1440 CreateNewFolder
= false;
1443 /* Call OnAdd event handler */
1444 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1446 TotalBytesLeft
= FileNode
->File
.FileSize
;
1448 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1449 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1450 FileNode
->File
.FileControlID
= NextFolderNumber
- 1;
1451 CurrentFolderNode
->Commit
= true;
1452 PrevCabinetNumber
= CurrentDiskNumber
;
1454 Size
= sizeof(CFFILE
) + strlen(GetFileName(FileNode
->FileName
)) + 1;
1455 CABHeader
.FileTableOffset
+= Size
;
1456 TotalFileSize
+= Size
;
1460 FileNode
->Commit
= true;
1462 if (TotalBytesLeft
> 0) {
1464 if (TotalBytesLeft
> (unsigned long)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1465 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1467 BytesToRead
= TotalBytesLeft
;
1469 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
)) {
1470 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1471 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1472 return CAB_STATUS_INVALID_CAB
;
1475 *(unsigned char**)&CurrentIBuffer
+= BytesRead
;
1477 CurrentIBufferSize
+= (unsigned short)BytesRead
;
1479 if (CurrentIBufferSize
== CAB_BLOCKSIZE
) {
1480 Status
= WriteDataBlock();
1481 if (Status
!= CAB_STATUS_SUCCESS
)
1484 TotalBytesLeft
-= BytesRead
;
1485 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1488 if (TotalBytesLeft
== 0) {
1489 CloseFile(SourceFile
);
1490 FileNode
->Delete
= true;
1492 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) {
1493 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1494 CurrentFolderNode
->Delete
= true;
1496 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1497 Status
= WriteDataBlock();
1498 if (Status
!= CAB_STATUS_SUCCESS
)
1502 CreateNewFolder
= true;
1505 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1506 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1508 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1511 return CAB_STATUS_SUCCESS
;
1515 unsigned long CCabinet::WriteDisk(unsigned long MoreDisks
)
1517 * FUNCTION: Forces the current disk to be written
1519 * MoreDisks = true if there is one or more disks after this disk
1521 * Status of operation
1524 PCFFILE_NODE FileNode
;
1525 unsigned long Status
;
1527 ContinueFile
= false;
1528 FileNode
= FileListHead
;
1529 while (FileNode
!= NULL
) {
1531 Status
= WriteFileToScratchStorage(FileNode
);
1532 if (Status
!= CAB_STATUS_SUCCESS
)
1534 if (CreateNewDisk
) {
1535 /* A data block could span more than two
1536 disks if MaxDiskSize is very small */
1537 while (CreateNewDisk
) {
1538 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1543 ContinueFile
= true;
1544 CreateNewDisk
= false;
1546 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1547 CurrentIBufferSize
, CurrentOBufferSize
));
1549 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1550 Status
= WriteDataBlock();
1551 if (Status
!= CAB_STATUS_SUCCESS
)
1556 ContinueFile
= false;
1557 FileNode
= FileNode
->Next
;
1561 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1562 /* A data block could span more than two
1563 disks if MaxDiskSize is very small */
1565 ASSERT(CreateNewDisk
== false);
1568 if (CreateNewDisk
) {
1569 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1573 CreateNewDisk
= false;
1575 ASSERT(FileNode
== FileListHead
);
1578 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1579 Status
= WriteDataBlock();
1580 if (Status
!= CAB_STATUS_SUCCESS
)
1583 } while (CreateNewDisk
);
1585 CommitDisk(MoreDisks
);
1587 return CAB_STATUS_SUCCESS
;
1591 unsigned long CCabinet::CommitDisk(unsigned long MoreDisks
)
1593 * FUNCTION: Commits the current disk
1595 * MoreDisks = true if there is one or more disks after this disk
1597 * Status of operation
1600 PCFFOLDER_NODE FolderNode
;
1601 unsigned long Status
;
1603 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1605 /* Create file, fail if it already exists */
1607 FileHandle
= CreateFile(CabinetName
, // Create this file
1608 GENERIC_WRITE
, // Open for writing
1610 NULL
, // No security
1611 CREATE_NEW
, // New file only
1612 FILE_ATTRIBUTE_NORMAL
, // Normal file
1613 NULL
); // No attribute template
1614 if (FileHandle
== INVALID_HANDLE_VALUE
) {
1615 unsigned long Status
;
1616 /* If file exists, ask to overwrite file */
1617 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1618 (OnOverwrite(NULL
, CabinetName
))) {
1620 /* Create cabinet file, overwrite if it already exists */
1621 FileHandle
= CreateFile(CabinetName
, // Create this file
1622 GENERIC_WRITE
, // Open for writing
1624 NULL
, // No security
1625 TRUNCATE_EXISTING
, // Truncate the file
1626 FILE_ATTRIBUTE_NORMAL
, // Normal file
1627 NULL
); // No attribute template
1628 if (FileHandle
== INVALID_HANDLE_VALUE
)
1629 return CAB_STATUS_CANNOT_CREATE
;
1631 if (Status
== ERROR_FILE_EXISTS
)
1632 return CAB_STATUS_FILE_EXISTS
;
1634 return CAB_STATUS_CANNOT_CREATE
;
1638 FileHandle
= fopen(CabinetName
, "rb");
1639 if (FileHandle
!= NULL
) {
1641 /* If file exists, ask to overwrite file */
1642 if (OnOverwrite(NULL
, CabinetName
)) {
1643 FileHandle
= fopen(CabinetName
, "w+b");
1644 if (FileHandle
== NULL
) {
1645 return CAB_STATUS_CANNOT_CREATE
;
1648 return CAB_STATUS_FILE_EXISTS
;
1651 FileHandle
= fopen(CabinetName
, "w+b");
1652 if (FileHandle
== NULL
) {
1653 return CAB_STATUS_CANNOT_CREATE
;
1658 WriteCabinetHeader(MoreDisks
);
1660 Status
= WriteFolderEntries();
1661 if (Status
!= CAB_STATUS_SUCCESS
)
1664 /* Write file entries */
1667 /* Write data blocks */
1668 FolderNode
= FolderListHead
;
1669 while (FolderNode
!= NULL
) {
1670 if (FolderNode
->Commit
) {
1671 Status
= CommitDataBlocks(FolderNode
);
1672 if (Status
!= CAB_STATUS_SUCCESS
)
1674 /* Remove data blocks for folder */
1675 DestroyDataNodes(FolderNode
);
1677 FolderNode
= FolderNode
->Next
;
1680 CloseFile(FileHandle
);
1682 ScratchFile
->Truncate();
1684 return CAB_STATUS_SUCCESS
;
1688 unsigned long CCabinet::CloseDisk()
1690 * FUNCTION: Closes the current disk
1692 * Status of operation
1695 DestroyDeletedFileNodes();
1697 /* Destroy folder nodes that are completely stored */
1698 DestroyDeletedFolderNodes();
1700 CurrentDiskNumber
++;
1702 return CAB_STATUS_SUCCESS
;
1706 unsigned long CCabinet::CloseCabinet()
1708 * FUNCTION: Closes the current cabinet
1710 * Status of operation
1713 unsigned long Status
;
1717 DestroyFolderNodes();
1720 FreeMemory(InputBuffer
);
1725 FreeMemory(OutputBuffer
);
1726 OutputBuffer
= NULL
;
1732 Status
= ScratchFile
->Destroy();
1737 return CAB_STATUS_SUCCESS
;
1741 unsigned long CCabinet::AddFile(char* FileName
)
1743 * FUNCTION: Adds a file to the current disk
1745 * FileName = Pointer to string with file name (full path)
1747 * Status of operation
1751 PCFFILE_NODE FileNode
;
1753 FileNode
= NewFileNode();
1755 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1756 return CAB_STATUS_NOMEMORY
;
1759 FileNode
->FolderNode
= CurrentFolderNode
;
1761 FileNode
->FileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1762 strcpy(FileNode
->FileName
, FileName
);
1763 ConvertPath(FileNode
->FileName
, false);
1765 /* Try to open file */
1767 SrcFile
= CreateFile(
1768 FileNode
->FileName
, // Open this file
1769 GENERIC_READ
, // Open for reading
1770 FILE_SHARE_READ
, // Share for reading
1771 NULL
, // No security
1772 OPEN_EXISTING
, // File must exist
1773 FILE_ATTRIBUTE_NORMAL
, // Normal file
1774 NULL
); // No attribute template
1775 if (SrcFile
== INVALID_HANDLE_VALUE
) {
1776 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1777 return CAB_STATUS_CANNOT_OPEN
;
1780 SrcFile
= fopen(FileNode
->FileName
, "rb");
1781 if (SrcFile
== NULL
) {
1782 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1783 return CAB_STATUS_CANNOT_OPEN
;
1787 /* FIXME: Check for and handle large files (>= 2GB) */
1788 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1789 if (FileNode
->File
.FileSize
== (unsigned long)-1) {
1790 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1791 return CAB_STATUS_CANNOT_READ
;
1794 GetFileTimes(SrcFile
, FileNode
);
1796 GetAttributesOnFile(FileNode
);
1800 return CAB_STATUS_SUCCESS
;
1804 void CCabinet::SetMaxDiskSize(unsigned long Size
)
1806 * FUNCTION: Sets the maximum size of the current disk
1808 * Size = Maximum size of current disk (0 means no maximum size)
1814 #endif /* CAB_READ_ONLY */
1817 /* Default event handlers */
1819 bool CCabinet::OnOverwrite(PCFFILE File
,
1822 * FUNCTION: Called when extracting a file and it already exists
1824 * File = Pointer to CFFILE for file being extracted
1825 * FileName = Pointer to buffer with name of file (full path)
1827 * true if the file should be overwritten, false if not
1834 void CCabinet::OnExtract(PCFFILE File
,
1837 * FUNCTION: Called just before extracting a file
1839 * File = Pointer to CFFILE for file being extracted
1840 * FileName = Pointer to buffer with name of file (full path)
1846 void CCabinet::OnDiskChange(char* CabinetName
,
1849 * FUNCTION: Called when a new disk is to be processed
1851 * CabinetName = Pointer to buffer with name of cabinet
1852 * DiskLabel = Pointer to buffer with label of disk
1858 #ifndef CAB_READ_ONLY
1860 void CCabinet::OnAdd(PCFFILE File
,
1863 * FUNCTION: Called just before adding a file to a cabinet
1865 * File = Pointer to CFFILE for file being added
1866 * FileName = Pointer to buffer with name of file (full path)
1872 bool CCabinet::OnDiskLabel(unsigned long Number
, char* Label
)
1874 * FUNCTION: Called when a disk needs a label
1876 * Number = Cabinet number that needs a label
1877 * Label = Pointer to buffer to place label of disk
1879 * true if a disk label was returned, false if not
1886 bool CCabinet::OnCabinetName(unsigned long Number
, char* Name
)
1888 * FUNCTION: Called when a cabinet needs a name
1890 * Number = Disk number that needs a name
1891 * Name = Pointer to buffer to place name of cabinet
1893 * true if a cabinet name was returned, false if not
1899 #endif /* CAB_READ_ONLY */
1901 PCFFOLDER_NODE
CCabinet::LocateFolderNode(unsigned long Index
)
1903 * FUNCTION: Locates a folder node
1905 * Index = Folder index
1907 * Pointer to folder node or NULL if the folder node was not found
1910 PCFFOLDER_NODE Node
;
1913 case CAB_FILE_SPLIT
:
1914 return FolderListTail
;
1916 case CAB_FILE_CONTINUED
:
1917 case CAB_FILE_PREV_NEXT
:
1918 return FolderListHead
;
1921 Node
= FolderListHead
;
1922 while (Node
!= NULL
) {
1923 if (Node
->Index
== Index
)
1931 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
1933 * FUNCTION: Returns the absolute offset of a file
1935 * File = Pointer to CFFILE_NODE structure for file
1937 * Status of operation
1942 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1943 (char*)File
->FileName
,
1944 (unsigned int)File
->File
.FileOffset
,
1945 (unsigned int)File
->File
.FileSize
));
1947 Node
= CurrentFolderNode
->DataListHead
;
1948 while (Node
!= NULL
) {
1950 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1951 (unsigned int)Node
->UncompOffset
,
1952 (unsigned int)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
1953 (unsigned int)Node
->Data
.UncompSize
));
1955 /* Node->Data.UncompSize will be 0 if the block is split
1956 (ie. it is the last block in this cabinet) */
1957 if ((Node
->Data
.UncompSize
== 0) ||
1958 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
1959 (File
->File
.FileOffset
< Node
->UncompOffset
+
1960 Node
->Data
.UncompSize
))) {
1961 File
->DataBlock
= Node
;
1962 return CAB_STATUS_SUCCESS
;
1967 return CAB_STATUS_INVALID_CAB
;
1971 unsigned long CCabinet::LocateFile(char* FileName
,
1974 * FUNCTION: Locates a file in the cabinet
1976 * FileName = Pointer to string with name of file to locate
1977 * File = Address of pointer to CFFILE_NODE structure to fill
1979 * Status of operation
1981 * Current folder is set to the folder of the file
1985 unsigned long Status
;
1987 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
1989 Node
= FileListHead
;
1990 while (Node
!= NULL
) {
1991 if (strcasecmp(FileName
, Node
->FileName
) == 0) {
1993 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
1994 if (!CurrentFolderNode
) {
1995 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
1996 (unsigned int)Node
->File
.FileControlID
));
1997 return CAB_STATUS_INVALID_CAB
;
2000 if (Node
->DataBlock
== NULL
) {
2001 Status
= GetAbsoluteOffset(Node
);
2003 Status
= CAB_STATUS_SUCCESS
;
2009 return CAB_STATUS_NOFILE
;
2013 unsigned long CCabinet::ReadString(char* String
, unsigned long MaxLength
)
2015 * FUNCTION: Reads a NULL-terminated string from the cabinet
2017 * String = Pointer to buffer to place string
2018 * MaxLength = Maximum length of string
2020 * Status of operation
2023 unsigned long BytesRead
;
2024 unsigned long Offset
;
2025 unsigned long Status
;
2032 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2035 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2036 return CAB_STATUS_INVALID_CAB
;
2039 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2040 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
)) {
2041 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2042 return CAB_STATUS_INVALID_CAB
;
2045 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++) {
2046 if (String
[Size
] == '\0') {
2052 Offset
+= BytesRead
;
2056 /* Back up some bytes */
2057 Size
= (BytesRead
- Size
) - 1;
2059 SetLastError(NO_ERROR
);
2060 (unsigned int)SetFilePointer(FileHandle
,
2064 if (GetLastError() != NO_ERROR
) {
2065 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2066 return CAB_STATUS_INVALID_CAB
;
2069 if (fseek(FileHandle
, (off_t
)(-(long)Size
), SEEK_CUR
) != 0) {
2070 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2071 return CAB_STATUS_INVALID_CAB
;
2074 return CAB_STATUS_SUCCESS
;
2078 unsigned long CCabinet::ReadFileTable()
2080 * FUNCTION: Reads the file table from the cabinet file
2082 * Status of operation
2086 unsigned long Status
;
2087 unsigned long BytesRead
;
2090 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2091 CABHeader
.FileTableOffset
));
2093 /* Seek to file table */
2095 SetLastError(NO_ERROR
);
2096 (unsigned int)SetFilePointer(FileHandle
,
2097 CABHeader
.FileTableOffset
,
2100 if (GetLastError() != NO_ERROR
) {
2101 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2102 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2103 return CAB_STATUS_INVALID_CAB
;
2106 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0) {
2107 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2108 return CAB_STATUS_INVALID_CAB
;
2112 for (i
= 0; i
< CABHeader
.FileCount
; i
++) {
2113 File
= NewFileNode();
2115 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2116 return CAB_STATUS_NOMEMORY
;
2119 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2120 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2121 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2122 return CAB_STATUS_INVALID_CAB
;
2125 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2126 if (!File
->FileName
) {
2127 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2128 return CAB_STATUS_NOMEMORY
;
2131 /* Read file name */
2132 Status
= ReadString(File
->FileName
, MAX_PATH
);
2133 if (Status
!= CAB_STATUS_SUCCESS
)
2136 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2137 (char*)File
->FileName
,
2138 (unsigned int)File
->File
.FileOffset
,
2139 (unsigned int)File
->File
.FileSize
,
2140 (unsigned int)File
->File
.FileControlID
));
2143 return CAB_STATUS_SUCCESS
;
2147 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2149 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2151 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2153 * Status of operation
2156 unsigned long AbsoluteOffset
;
2157 unsigned long UncompOffset
;
2159 unsigned long BytesRead
;
2160 unsigned long Status
;
2163 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2164 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2166 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2167 UncompOffset
= FolderNode
->UncompOffset
;
2169 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++) {
2170 Node
= NewDataNode(FolderNode
);
2172 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2173 return CAB_STATUS_NOMEMORY
;
2176 /* Seek to data block */
2178 SetLastError(NO_ERROR
);
2179 (unsigned int)SetFilePointer(FileHandle
,
2183 if (GetLastError() != NO_ERROR
) {
2184 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2185 return CAB_STATUS_INVALID_CAB
;
2188 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0) {
2189 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2190 return CAB_STATUS_INVALID_CAB
;
2194 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2195 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2196 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2197 return CAB_STATUS_INVALID_CAB
;
2200 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2201 (unsigned int)AbsoluteOffset
,
2202 (unsigned int)UncompOffset
,
2203 (unsigned int)Node
->Data
.Checksum
,
2204 (unsigned int)Node
->Data
.CompSize
,
2205 (unsigned int)Node
->Data
.UncompSize
));
2207 Node
->AbsoluteOffset
= AbsoluteOffset
;
2208 Node
->UncompOffset
= UncompOffset
;
2210 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2211 UncompOffset
+= Node
->Data
.UncompSize
;
2214 FolderUncompSize
= UncompOffset
;
2216 return CAB_STATUS_SUCCESS
;
2220 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2222 * FUNCTION: Creates a new folder node
2224 * Pointer to node if there was enough free memory available, otherwise NULL
2227 PCFFOLDER_NODE Node
;
2229 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2233 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2235 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2237 Node
->Prev
= FolderListTail
;
2239 if (FolderListTail
!= NULL
) {
2240 FolderListTail
->Next
= Node
;
2242 FolderListHead
= Node
;
2244 FolderListTail
= Node
;
2250 PCFFILE_NODE
CCabinet::NewFileNode()
2252 * FUNCTION: Creates a new file node
2254 * FolderNode = Pointer to folder node to bind file to
2256 * Pointer to node if there was enough free memory available, otherwise NULL
2261 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2265 memset(Node
, 0, sizeof(CFFILE_NODE
));
2267 Node
->Prev
= FileListTail
;
2269 if (FileListTail
!= NULL
) {
2270 FileListTail
->Next
= Node
;
2272 FileListHead
= Node
;
2274 FileListTail
= Node
;
2280 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2282 * FUNCTION: Creates a new data block node
2284 * FolderNode = Pointer to folder node to bind data block to
2286 * Pointer to node if there was enough free memory available, otherwise NULL
2291 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2295 memset(Node
, 0, sizeof(CFDATA_NODE
));
2297 Node
->Prev
= FolderNode
->DataListTail
;
2299 if (FolderNode
->DataListTail
!= NULL
) {
2300 FolderNode
->DataListTail
->Next
= Node
;
2302 FolderNode
->DataListHead
= Node
;
2304 FolderNode
->DataListTail
= Node
;
2310 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2312 * FUNCTION: Destroys data block nodes bound to a folder node
2314 * FolderNode = Pointer to folder node
2317 PCFDATA_NODE PrevNode
;
2318 PCFDATA_NODE NextNode
;
2320 NextNode
= FolderNode
->DataListHead
;
2321 while (NextNode
!= NULL
) {
2322 PrevNode
= NextNode
->Next
;
2323 FreeMemory(NextNode
);
2324 NextNode
= PrevNode
;
2326 FolderNode
->DataListHead
= NULL
;
2327 FolderNode
->DataListTail
= NULL
;
2331 void CCabinet::DestroyFileNodes()
2333 * FUNCTION: Destroys file nodes
2335 * FolderNode = Pointer to folder node
2338 PCFFILE_NODE PrevNode
;
2339 PCFFILE_NODE NextNode
;
2341 NextNode
= FileListHead
;
2342 while (NextNode
!= NULL
) {
2343 PrevNode
= NextNode
->Next
;
2344 if (NextNode
->FileName
)
2345 FreeMemory(NextNode
->FileName
);
2346 FreeMemory(NextNode
);
2347 NextNode
= PrevNode
;
2349 FileListHead
= NULL
;
2350 FileListTail
= NULL
;
2354 void CCabinet::DestroyDeletedFileNodes()
2356 * FUNCTION: Destroys file nodes that are marked for deletion
2359 PCFFILE_NODE CurNode
;
2360 PCFFILE_NODE NextNode
;
2362 CurNode
= FileListHead
;
2363 while (CurNode
!= NULL
) {
2364 NextNode
= CurNode
->Next
;
2366 if (CurNode
->Delete
) {
2367 if (CurNode
->Prev
!= NULL
) {
2368 CurNode
->Prev
->Next
= CurNode
->Next
;
2370 FileListHead
= CurNode
->Next
;
2372 FileListHead
->Prev
= NULL
;
2375 if (CurNode
->Next
!= NULL
) {
2376 CurNode
->Next
->Prev
= CurNode
->Prev
;
2378 FileListTail
= CurNode
->Prev
;
2380 FileListTail
->Next
= NULL
;
2383 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2385 TotalFileSize
-= (sizeof(CFFILE
) + strlen(GetFileName(CurNode
->FileName
)) + 1);
2387 if (CurNode
->FileName
)
2388 FreeMemory(CurNode
->FileName
);
2389 FreeMemory(CurNode
);
2396 void CCabinet::DestroyFolderNodes()
2398 * FUNCTION: Destroys folder nodes
2401 PCFFOLDER_NODE PrevNode
;
2402 PCFFOLDER_NODE NextNode
;
2404 NextNode
= FolderListHead
;
2405 while (NextNode
!= NULL
) {
2406 PrevNode
= NextNode
->Next
;
2407 DestroyDataNodes(NextNode
);
2408 FreeMemory(NextNode
);
2409 NextNode
= PrevNode
;
2411 FolderListHead
= NULL
;
2412 FolderListTail
= NULL
;
2416 void CCabinet::DestroyDeletedFolderNodes()
2418 * FUNCTION: Destroys folder nodes that are marked for deletion
2421 PCFFOLDER_NODE CurNode
;
2422 PCFFOLDER_NODE NextNode
;
2424 CurNode
= FolderListHead
;
2425 while (CurNode
!= NULL
) {
2426 NextNode
= CurNode
->Next
;
2428 if (CurNode
->Delete
) {
2429 if (CurNode
->Prev
!= NULL
) {
2430 CurNode
->Prev
->Next
= CurNode
->Next
;
2432 FolderListHead
= CurNode
->Next
;
2434 FolderListHead
->Prev
= NULL
;
2437 if (CurNode
->Next
!= NULL
) {
2438 CurNode
->Next
->Prev
= CurNode
->Prev
;
2440 FolderListTail
= CurNode
->Prev
;
2442 FolderListTail
->Next
= NULL
;
2445 DestroyDataNodes(CurNode
);
2446 FreeMemory(CurNode
);
2448 TotalFolderSize
-= sizeof(CFFOLDER
);
2455 unsigned long CCabinet::ComputeChecksum(void* Buffer
,
2459 * FUNCTION: Computes checksum for data block
2461 * Buffer = Pointer to data buffer
2462 * Size = Length of data buffer
2463 * Seed = Previously computed checksum
2465 * Checksum of buffer
2468 int UlongCount
; // Number of ULONGs in block
2469 unsigned long Checksum
; // Checksum accumulator
2473 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2474 won't accept checksums computed by this routine */
2476 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%X) Size (%d)\n", (unsigned int)Buffer
, Size
));
2478 UlongCount
= Size
/ 4; // Number of ULONGs
2479 Checksum
= Seed
; // Init checksum
2480 pb
= (unsigned char*)Buffer
; // Start at front of data block
2482 /* Checksum integral multiple of ULONGs */
2483 while (UlongCount
-- > 0) {
2484 /* NOTE: Build unsigned long in big/little-endian independent manner */
2485 ul
= *pb
++; // Get low-order byte
2486 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2487 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3nd byte
2488 ul
|= (((unsigned long)(*pb
++)) << 24); // Add 4th byte
2490 Checksum
^= ul
; // Update checksum
2493 /* Checksum remainder bytes */
2497 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3rd byte
2499 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2501 ul
|= *pb
++; // Get low-order byte
2505 Checksum
^= ul
; // Update checksum
2507 /* Return computed checksum */
2512 unsigned long CCabinet::ReadBlock(void* Buffer
,
2514 unsigned long* BytesRead
)
2516 * FUNCTION: Read a block of data from file
2518 * Buffer = Pointer to data buffer
2519 * Size = Length of data buffer
2520 * BytesRead = Pointer to unsigned long that on return will contain
2521 * number of bytes read
2523 * Status of operation
2526 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2527 return CAB_STATUS_INVALID_CAB
;
2528 return CAB_STATUS_SUCCESS
;
2531 #ifndef CAB_READ_ONLY
2533 unsigned long CCabinet::InitCabinetHeader()
2535 * FUNCTION: Initializes cabinet header and optional fields
2537 * Status of operation
2540 unsigned long TotalSize
;
2543 CABHeader
.FileTableOffset
= 0; // Not known yet
2544 CABHeader
.FolderCount
= 0; // Not known yet
2545 CABHeader
.FileCount
= 0; // Not known yet
2546 CABHeader
.Flags
= 0; // Not known yet
2548 CABHeader
.CabinetNumber
= CurrentDiskNumber
;
2550 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
))) {
2551 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2552 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2553 strcpy(CabinetPrev
, "");
2556 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
)) {
2557 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2558 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2559 strcpy(DiskNext
, "");
2564 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2566 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2568 /* Calculate size of name of previous cabinet */
2569 TotalSize
+= strlen(CabinetPrev
) + 1;
2571 /* Calculate size of label of previous disk */
2572 TotalSize
+= strlen(DiskPrev
) + 1;
2575 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2577 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2579 /* Calculate size of name of next cabinet */
2580 Size
= strlen(CabinetNext
) + 1;
2582 NextFieldsSize
= Size
;
2584 /* Calculate size of label of next disk */
2585 Size
= strlen(DiskNext
) + 1;
2587 NextFieldsSize
+= Size
;
2591 /* Add cabinet reserved area size if present */
2592 if (CabinetReservedFileSize
> 0)
2594 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2595 TotalSize
+= CabinetReservedFileSize
;
2596 TotalSize
+= sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2599 DiskSize
+= TotalSize
;
2601 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2603 return CAB_STATUS_SUCCESS
;
2607 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks
)
2609 * FUNCTION: Writes the cabinet header and optional fields
2611 * MoreDisks = true if next cabinet name should be included
2613 * Status of operation
2616 PCFFOLDER_NODE FolderNode
;
2617 PCFFILE_NODE FileNode
;
2618 unsigned long BytesWritten
;
2622 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2623 Size
= TotalHeaderSize
;
2625 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2626 DiskSize
-= NextFieldsSize
;
2627 Size
= TotalHeaderSize
- NextFieldsSize
;
2630 /* Set absolute folder offsets */
2631 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2632 CABHeader
.FolderCount
= 0;
2633 FolderNode
= FolderListHead
;
2634 while (FolderNode
!= NULL
) {
2635 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2637 BytesWritten
+= FolderNode
->TotalFolderSize
;
2639 CABHeader
.FolderCount
++;
2641 FolderNode
= FolderNode
->Next
;
2644 /* Set absolute offset of file table */
2645 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2647 /* Count number of files to be committed */
2648 CABHeader
.FileCount
= 0;
2649 FileNode
= FileListHead
;
2650 while (FileNode
!= NULL
) {
2651 if (FileNode
->Commit
)
2652 CABHeader
.FileCount
++;
2653 FileNode
= FileNode
->Next
;
2656 CABHeader
.CabinetSize
= DiskSize
;
2660 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
)) {
2661 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2662 return CAB_STATUS_CANNOT_WRITE
;
2665 BytesWritten
= sizeof(CFHEADER
);
2666 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1) {
2667 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2668 return CAB_STATUS_CANNOT_WRITE
;
2672 /* Write per-cabinet reserved area if present */
2673 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
2674 unsigned long ReservedSize
;
2676 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2677 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2678 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2680 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(unsigned long), &BytesWritten
, NULL
)) {
2681 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2682 return CAB_STATUS_CANNOT_WRITE
;
2685 BytesWritten
= sizeof(unsigned long);
2686 if (fwrite(&ReservedSize
, sizeof(unsigned long), 1, FileHandle
) < 1) {
2687 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2688 return CAB_STATUS_CANNOT_WRITE
;
2693 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesWritten
, NULL
)) {
2694 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2695 return CAB_STATUS_CANNOT_WRITE
;
2698 BytesWritten
= CabinetReservedFileSize
;
2699 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1) {
2700 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2701 return CAB_STATUS_CANNOT_WRITE
;
2706 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2708 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2710 /* Write name of previous cabinet */
2711 Size
= strlen(CabinetPrev
) + 1;
2713 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
)) {
2714 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2715 return CAB_STATUS_CANNOT_WRITE
;
2718 BytesWritten
= Size
;
2719 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1) {
2720 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2721 return CAB_STATUS_CANNOT_WRITE
;
2725 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2727 /* Write label of previous disk */
2728 Size
= strlen(DiskPrev
) + 1;
2730 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
)) {
2731 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2732 return CAB_STATUS_CANNOT_WRITE
;
2735 BytesWritten
= Size
;
2736 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1) {
2737 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2738 return CAB_STATUS_CANNOT_WRITE
;
2743 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2745 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2747 /* Write name of next cabinet */
2748 Size
= strlen(CabinetNext
) + 1;
2750 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
)) {
2751 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2752 return CAB_STATUS_CANNOT_WRITE
;
2755 BytesWritten
= Size
;
2756 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1) {
2757 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2758 return CAB_STATUS_CANNOT_WRITE
;
2762 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2764 /* Write label of next disk */
2765 Size
= strlen(DiskNext
) + 1;
2767 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
)) {
2768 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2769 return CAB_STATUS_CANNOT_WRITE
;
2772 BytesWritten
= Size
;
2773 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1) {
2774 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2775 return CAB_STATUS_CANNOT_WRITE
;
2780 return CAB_STATUS_SUCCESS
;
2784 unsigned long CCabinet::WriteFolderEntries()
2786 * FUNCTION: Writes folder entries
2788 * Status of operation
2791 PCFFOLDER_NODE FolderNode
;
2792 unsigned long BytesWritten
;
2794 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2796 FolderNode
= FolderListHead
;
2797 while (FolderNode
!= NULL
) {
2798 if (FolderNode
->Commit
) {
2800 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2801 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
2804 if (!WriteFile(FileHandle
,
2805 &FolderNode
->Folder
,
2809 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2810 return CAB_STATUS_CANNOT_WRITE
;
2813 BytesWritten
= sizeof(CFFOLDER
);
2814 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1) {
2815 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2816 return CAB_STATUS_CANNOT_WRITE
;
2820 FolderNode
= FolderNode
->Next
;
2823 return CAB_STATUS_SUCCESS
;
2827 unsigned long CCabinet::WriteFileEntries()
2829 * FUNCTION: Writes file entries for all files
2831 * Status of operation
2835 unsigned long BytesWritten
;
2838 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
2840 File
= FileListHead
;
2841 while (File
!= NULL
) {
2843 /* Remove any continued files that ends in this disk */
2844 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
2845 File
->Delete
= true;
2847 /* The file could end in the last (split) block and should therefore
2848 appear in the next disk too */
2850 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
2851 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
)) {
2852 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
2853 File
->Delete
= false;
2857 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
2858 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
2861 if (!WriteFile(FileHandle
,
2866 return CAB_STATUS_CANNOT_WRITE
;
2868 BytesWritten
= sizeof(CFFILE
);
2869 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1) {
2870 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2871 return CAB_STATUS_CANNOT_WRITE
;
2876 if (!WriteFile(FileHandle
,
2877 GetFileName(File
->FileName
),
2878 strlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
2879 return CAB_STATUS_CANNOT_WRITE
;
2881 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
2882 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1) {
2883 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2884 return CAB_STATUS_CANNOT_WRITE
;
2889 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
2896 return CAB_STATUS_SUCCESS
;
2900 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
2902 * FUNCTION: Writes data blocks to the cabinet
2904 * FolderNode = Pointer to folder node containing the data blocks
2906 * Status of operation
2909 PCFDATA_NODE DataNode
;
2910 unsigned long BytesWritten
;
2911 unsigned long BytesRead
;
2912 unsigned long Status
;
2914 DataNode
= FolderNode
->DataListHead
;
2915 if (DataNode
!= NULL
)
2916 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
2918 while (DataNode
!= NULL
) {
2919 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
2920 DataNode
->ScratchFilePosition
,
2921 DataNode
->Data
.CompSize
,
2922 DataNode
->Data
.UncompSize
));
2924 /* InputBuffer is free for us to use here, so we use it and avoid a
2925 memory allocation. OutputBuffer can't be used here because it may
2926 still contain valid data (if a data block spans two or more disks) */
2927 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
2928 if (Status
!= CAB_STATUS_SUCCESS
) {
2929 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (unsigned int)Status
));
2934 if (!WriteFile(FileHandle
, &DataNode
->Data
,
2935 sizeof(CFDATA
), &BytesWritten
, NULL
)) {
2936 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2937 return CAB_STATUS_CANNOT_WRITE
;
2940 BytesWritten
= sizeof(CFDATA
);
2941 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1) {
2942 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2943 return CAB_STATUS_CANNOT_WRITE
;
2948 if (!WriteFile(FileHandle
, InputBuffer
,
2949 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
)) {
2950 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2951 return CAB_STATUS_CANNOT_WRITE
;
2954 BytesWritten
= DataNode
->Data
.CompSize
;
2955 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1) {
2956 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2957 return CAB_STATUS_CANNOT_WRITE
;
2961 DataNode
= DataNode
->Next
;
2963 return CAB_STATUS_SUCCESS
;
2967 unsigned long CCabinet::WriteDataBlock()
2969 * FUNCTION: Writes the current data block to the scratch file
2971 * Status of operation
2974 unsigned long Status
;
2975 unsigned long BytesWritten
;
2976 PCFDATA_NODE DataNode
;
2978 if (!BlockIsSplit
) {
2979 Status
= Codec
->Compress(OutputBuffer
,
2984 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
2985 CurrentIBufferSize
, TotalCompSize
));
2987 CurrentOBuffer
= OutputBuffer
;
2988 CurrentOBufferSize
= TotalCompSize
;
2991 DataNode
= NewDataNode(CurrentFolderNode
);
2993 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2994 return CAB_STATUS_NOMEMORY
;
2997 DiskSize
+= sizeof(CFDATA
);
2999 if (MaxDiskSize
> 0)
3000 /* Disk size is limited */
3001 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3003 BlockIsSplit
= false;
3006 DataNode
->Data
.CompSize
= (unsigned short)(MaxDiskSize
- DiskSize
);
3007 DataNode
->Data
.UncompSize
= 0;
3008 CreateNewDisk
= true;
3010 DataNode
->Data
.CompSize
= (unsigned short)CurrentOBufferSize
;
3011 DataNode
->Data
.UncompSize
= (unsigned short)CurrentIBufferSize
;
3014 DataNode
->Data
.Checksum
= 0;
3015 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3017 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3018 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3020 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3021 (unsigned int)DataNode
->Data
.Checksum
,
3022 (unsigned int)DataNode
->Data
.CompSize
,
3023 (unsigned int)DataNode
->Data
.UncompSize
));
3025 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3026 CurrentOBuffer
, &BytesWritten
);
3027 if (Status
!= CAB_STATUS_SUCCESS
)
3030 DiskSize
+= BytesWritten
;
3032 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3033 CurrentFolderNode
->Folder
.DataBlockCount
++;
3035 *(unsigned char**)&CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3036 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3038 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3040 if (!BlockIsSplit
) {
3041 CurrentIBufferSize
= 0;
3042 CurrentIBuffer
= InputBuffer
;
3045 return CAB_STATUS_SUCCESS
;
3051 void CCabinet::ConvertDateAndTime(time_t* Time
,
3052 unsigned short* DosDate
,
3053 unsigned short* DosTime
)
3055 * FUNCTION: Returns file times of a file
3057 * FileHandle = File handle of file to get file times from
3058 * File = Pointer to CFFILE node for file
3060 * Status of operation
3065 timedef
= localtime(Time
);
3067 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3068 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3069 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3071 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3072 | ((timedef
->tm_mon
+ 1) << 5)
3073 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3075 *DosTime
= (timedef
->tm_sec
<< 0)
3076 | (timedef
->tm_min
<< 5)
3077 | (timedef
->tm_hour
<< 11);
3083 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3085 * FUNCTION: Returns file times of a file
3087 * FileHandle = File handle of file to get file times from
3088 * File = Pointer to CFFILE node for file
3090 * Status of operation
3096 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3097 FileTimeToDosDateTime(&FileTime
,
3098 &File
->File
.FileDate
,
3099 &File
->File
.FileTime
);
3104 // Check for an absolute path
3105 if (IsSeparator(File
->FileName
[0]))
3107 strcpy(buf
, File
->FileName
);
3111 getcwd(buf
, sizeof(buf
));
3112 strcat(buf
, DIR_SEPARATOR_STRING
);
3113 strcat(buf
, File
->FileName
);
3116 if (stat(buf
, &stbuf
) == -1)
3118 return CAB_STATUS_CANNOT_READ
;
3121 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3123 return CAB_STATUS_SUCCESS
;
3127 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3129 * FUNCTION: Returns attributes on a file
3131 * File = Pointer to CFFILE node for file
3133 * Status of operation
3139 Attributes
= GetFileAttributes(File
->FileName
);
3140 if (Attributes
== -1)
3141 return CAB_STATUS_CANNOT_READ
;
3143 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3144 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3146 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3147 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3149 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3150 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3152 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3153 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3155 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3156 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3161 // Check for an absolute path
3162 if (IsSeparator(File
->FileName
[0]))
3164 strcpy(buf
, File
->FileName
);
3168 getcwd(buf
, sizeof(buf
));
3169 strcat(buf
, DIR_SEPARATOR_STRING
);
3170 strcat(buf
, File
->FileName
);
3173 if (stat(buf
, &stbuf
) == -1)
3175 return CAB_STATUS_CANNOT_READ
;
3179 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3180 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3181 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3184 if (stbuf
.st_mode
& S_IFDIR
)
3185 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3187 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3190 return CAB_STATUS_SUCCESS
;
3194 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3196 * FUNCTION: Sets attributes on a file
3198 * File = Pointer to CFFILE node for file
3200 * Status of operation
3204 unsigned long Attributes
= 0;
3206 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3207 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3209 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3210 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3212 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3213 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3215 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3216 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3218 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3219 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3221 SetFileAttributes(File
->FileName
, Attributes
);
3223 return CAB_STATUS_SUCCESS
;
3225 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3226 return CAB_STATUS_SUCCESS
;
3230 #endif /* CAB_READ_ONLY */