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
= fopen(FullName
, "w+b");
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"));
535 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
536 if (FileHandle
== NULL
) {
537 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
542 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
543 if (CabinetReservedFileSize
== (unsigned long)-1) {
544 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
548 if (CabinetReservedFileSize
== 0)
550 CloseFile(FileHandle
);
554 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
555 if (!CabinetReservedFileBuffer
) {
556 CloseFile(FileHandle
);
560 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
)) {
561 CloseFile(FileHandle
);
565 CloseFile(FileHandle
);
567 strcpy(CabinetReservedFile
, FileName
);
573 char* CCabinet::GetCabinetReservedFile()
575 * FUNCTION: Returns cabionet reserved file
577 * Pointer to string with name of cabinet reserved file
580 return CabinetReservedFile
;
584 unsigned long CCabinet::GetCurrentDiskNumber()
586 * FUNCTION: Returns current disk number
588 * Current disk number
591 return CurrentDiskNumber
;
595 unsigned long CCabinet::Open()
597 * FUNCTION: Opens a cabinet file
599 * Status of operation
602 PCFFOLDER_NODE FolderNode
;
603 unsigned long Status
;
607 unsigned long BytesRead
;
610 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
612 return CAB_STATUS_NOMEMORY
;
615 FileHandle
= CreateFile(CabinetName
, // Open this file
616 GENERIC_READ
, // Open for reading
617 FILE_SHARE_READ
, // Share for reading
619 OPEN_EXISTING
, // Existing file only
620 FILE_ATTRIBUTE_NORMAL
, // Normal file
621 NULL
); // No attribute template
623 if (FileHandle
== INVALID_HANDLE_VALUE
) {
624 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
625 return CAB_STATUS_CANNOT_OPEN
;
628 FileHandle
= fopen(CabinetName
, "rb");
629 if (FileHandle
== NULL
) {
630 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
631 return CAB_STATUS_CANNOT_OPEN
;
637 /* Load CAB header */
638 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
639 != CAB_STATUS_SUCCESS
) {
640 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
641 return CAB_STATUS_INVALID_CAB
;
645 if ((BytesRead
!= sizeof(CFHEADER
)) ||
646 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
647 (CABHeader
.Version
!= CAB_VERSION
) ||
648 (CABHeader
.FolderCount
== 0 ) ||
649 (CABHeader
.FileCount
== 0 ) ||
650 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
652 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
653 return CAB_STATUS_INVALID_CAB
;
658 /* Read/skip any reserved bytes */
659 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
660 if ((Status
= ReadBlock(&Size
, sizeof(unsigned long), &BytesRead
))
661 != CAB_STATUS_SUCCESS
) {
662 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
663 return CAB_STATUS_INVALID_CAB
;
665 CabinetReserved
= Size
& 0xFFFF;
666 FolderReserved
= (Size
>> 16) & 0xFF;
667 DataReserved
= (Size
>> 24) & 0xFF;
670 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
671 if (GetLastError() != NO_ERROR
) {
672 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
673 return CAB_STATUS_FAILURE
;
676 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0) {
677 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
678 return CAB_STATUS_FAILURE
;
683 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
684 /* Read name of previous cabinet */
685 Status
= ReadString(CabinetPrev
, 256);
686 if (Status
!= CAB_STATUS_SUCCESS
)
688 /* Read label of previous disk */
689 Status
= ReadString(DiskPrev
, 256);
690 if (Status
!= CAB_STATUS_SUCCESS
)
693 strcpy(CabinetPrev
, "");
694 strcpy(DiskPrev
, "");
697 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
698 /* Read name of next cabinet */
699 Status
= ReadString(CabinetNext
, 256);
700 if (Status
!= CAB_STATUS_SUCCESS
)
702 /* Read label of next disk */
703 Status
= ReadString(DiskNext
, 256);
704 if (Status
!= CAB_STATUS_SUCCESS
)
707 strcpy(CabinetNext
, "");
708 strcpy(DiskNext
, "");
711 /* Read all folders */
712 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++) {
713 FolderNode
= NewFolderNode();
715 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
716 return CAB_STATUS_NOMEMORY
;
720 FolderNode
->UncompOffset
= FolderUncompSize
;
722 FolderNode
->Index
= Index
;
724 if ((Status
= ReadBlock(&FolderNode
->Folder
,
725 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
) {
726 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
727 return CAB_STATUS_INVALID_CAB
;
731 /* Read file entries */
732 Status
= ReadFileTable();
733 if (Status
!= CAB_STATUS_SUCCESS
) {
734 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (unsigned int)Status
));
738 /* Read data blocks for all folders */
739 FolderNode
= FolderListHead
;
740 while (FolderNode
!= NULL
) {
741 Status
= ReadDataBlocks(FolderNode
);
742 if (Status
!= CAB_STATUS_SUCCESS
) {
743 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status
));
746 FolderNode
= FolderNode
->Next
;
749 return CAB_STATUS_SUCCESS
;
753 void CCabinet::Close()
755 * FUNCTION: Closes the cabinet file
759 CloseFile(FileHandle
);
765 unsigned long CCabinet::FindFirst(char* FileName
,
768 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
770 * FileName = Pointer to search criteria
771 * Search = Pointer to search structure
773 * Status of operation
776 RestartSearch
= false;
777 strncpy(Search
->Search
, FileName
, MAX_PATH
);
778 Search
->Next
= FileListHead
;
779 return FindNext(Search
);
783 unsigned long CCabinet::FindNext(PCAB_SEARCH Search
)
785 * FUNCTION: Finds next file in the cabinet that matches a search criteria
787 * Search = Pointer to search structure
789 * Status of operation
792 unsigned long Status
;
795 Search
->Next
= FileListHead
;
797 /* Skip split files already extracted */
798 while ((Search
->Next
) &&
799 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
800 (Search
->Next
->File
.FileOffset
<= LastFileOffset
)) {
801 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
802 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
803 Search
->Next
= Search
->Next
->Next
;
806 RestartSearch
= false;
809 /* FIXME: Check search criteria */
812 if (strlen(DiskNext
) > 0) {
815 SetCabinetName(CabinetNext
);
817 OnDiskChange(CabinetNext
, DiskNext
);
820 if (Status
!= CAB_STATUS_SUCCESS
)
823 Search
->Next
= FileListHead
;
825 return CAB_STATUS_NOFILE
;
827 return CAB_STATUS_NOFILE
;
830 Search
->File
= &Search
->Next
->File
;
831 Search
->FileName
= Search
->Next
->FileName
;
832 Search
->Next
= Search
->Next
->Next
;
833 return CAB_STATUS_SUCCESS
;
837 unsigned long CCabinet::ExtractFile(char* FileName
)
839 * FUNCTION: Extracts a file from the cabinet
841 * FileName = Pointer to buffer with name of file
843 * Status of operation
847 unsigned long Offset
;
848 unsigned long BytesRead
;
849 unsigned long BytesToRead
;
850 unsigned long BytesWritten
;
851 unsigned long BytesSkipped
;
852 unsigned long BytesToWrite
;
853 unsigned long TotalBytesRead
;
854 unsigned long CurrentOffset
;
855 unsigned char* Buffer
;
856 unsigned char* CurrentBuffer
;
860 unsigned long Status
;
865 char DestName
[MAX_PATH
];
866 char TempName
[MAX_PATH
];
868 Status
= LocateFile(FileName
, &File
);
869 if (Status
!= CAB_STATUS_SUCCESS
) {
870 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
874 LastFileOffset
= File
->File
.FileOffset
;
876 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
) {
878 SelectCodec(CAB_CODEC_RAW
);
881 SelectCodec(CAB_CODEC_MSZIP
);
884 return CAB_STATUS_UNSUPPCOMP
;
887 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
888 (unsigned int)File
->File
.FileOffset
,
889 (unsigned int)File
->File
.FileSize
,
890 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
891 (unsigned int)File
->DataBlock
->UncompOffset
));
893 strcpy(DestName
, DestPath
);
894 strcat(DestName
, FileName
);
896 /* Create destination file, fail if it already exists */
898 DestFile
= CreateFile(DestName
, // Create this file
899 GENERIC_WRITE
, // Open for writing
902 CREATE_NEW
, // New file only
903 FILE_ATTRIBUTE_NORMAL
, // Normal file
904 NULL
); // No attribute template
905 if (DestFile
== INVALID_HANDLE_VALUE
) {
906 /* If file exists, ask to overwrite file */
907 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
908 (OnOverwrite(&File
->File
, FileName
))) {
909 /* Create destination file, overwrite if it already exists */
910 DestFile
= CreateFile(DestName
, // Create this file
911 GENERIC_WRITE
, // Open for writing
914 TRUNCATE_EXISTING
, // Truncate the file
915 FILE_ATTRIBUTE_NORMAL
, // Normal file
916 NULL
); // No attribute template
917 if (DestFile
== INVALID_HANDLE_VALUE
)
918 return CAB_STATUS_CANNOT_CREATE
;
920 if (Status
== ERROR_FILE_EXISTS
)
921 return CAB_STATUS_FILE_EXISTS
;
923 return CAB_STATUS_CANNOT_CREATE
;
927 DestFile
= fopen(DestName
, "rb");
928 if (DestFile
!= NULL
) {
930 /* If file exists, ask to overwrite file */
931 if (OnOverwrite(&File
->File
, FileName
)) {
932 DestFile
= fopen(DestName
, "w+b");
933 if (DestFile
== NULL
) {
934 return CAB_STATUS_CANNOT_CREATE
;
937 return CAB_STATUS_FILE_EXISTS
;
940 DestFile
= fopen(DestName
, "w+b");
941 if (DestFile
== NULL
) {
942 return CAB_STATUS_CANNOT_CREATE
;
947 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
)) {
949 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
950 return CAB_STATUS_CANNOT_WRITE
;
953 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
955 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
957 SetAttributesOnFile(File
);
959 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
962 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
963 return CAB_STATUS_NOMEMORY
;
966 /* Call OnExtract event handler */
967 OnExtract(&File
->File
, FileName
);
969 /* Search to start of file */
971 Offset
= SetFilePointer(FileHandle
,
972 File
->DataBlock
->AbsoluteOffset
,
975 if (GetLastError() != NO_ERROR
) {
976 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
977 return CAB_STATUS_INVALID_CAB
;
980 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
981 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
982 return CAB_STATUS_FAILURE
;
984 Offset
= ftell(FileHandle
);
987 Size
= File
->File
.FileSize
;
988 Offset
= File
->File
.FileOffset
;
989 CurrentOffset
= File
->DataBlock
->UncompOffset
;
993 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
996 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%d) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
997 File
->DataBlock
->UncompOffset
, (unsigned int)ReuseBlock
, Offset
, Size
,
1000 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0)) {
1002 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1004 CurrentBuffer
= Buffer
;
1007 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1009 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1010 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1011 CloseFile(DestFile
);
1013 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1014 return CAB_STATUS_INVALID_CAB
;
1017 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n",
1018 (unsigned int)CFData
.Checksum
,
1019 (unsigned int)CFData
.CompSize
,
1020 (unsigned int)CFData
.UncompSize
));
1022 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1024 BytesToRead
= CFData
.CompSize
;
1026 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1027 (long unsigned int)CurrentBuffer
, (long unsigned int)Buffer
));
1029 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1030 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
1031 CloseFile(DestFile
);
1033 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1034 return CAB_STATUS_INVALID_CAB
;
1037 /* FIXME: Does not work with files generated by makecab.exe */
1039 if (CFData.Checksum != 0) {
1040 unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1041 if (Checksum != CFData.Checksum) {
1042 CloseFile(DestFile);
1044 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1045 Checksum, CFData.Checksum));
1046 return CAB_STATUS_INVALID_CAB;
1050 TotalBytesRead
+= BytesRead
;
1052 CurrentBuffer
+= BytesRead
;
1054 if (CFData
.UncompSize
== 0) {
1055 if (strlen(DiskNext
) == 0)
1056 return CAB_STATUS_NOFILE
;
1058 /* CloseCabinet() will destroy all file entries so in case
1059 FileName refers to the FileName field of a CFFOLDER_NODE
1060 structure, we have to save a copy of the filename */
1061 strcpy(TempName
, FileName
);
1065 SetCabinetName(CabinetNext
);
1067 OnDiskChange(CabinetNext
, DiskNext
);
1070 if (Status
!= CAB_STATUS_SUCCESS
)
1073 /* The first data block of the file will not be
1074 found as it is located in the previous file */
1075 Status
= LocateFile(TempName
, &File
);
1076 if (Status
== CAB_STATUS_NOFILE
) {
1077 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
1081 /* The file is continued in the first data block in the folder */
1082 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1084 /* Search to start of file */
1086 (unsigned int)SetFilePointer(FileHandle
,
1087 File
->DataBlock
->AbsoluteOffset
,
1090 if (GetLastError() != NO_ERROR
) {
1091 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1092 return CAB_STATUS_INVALID_CAB
;
1095 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
1096 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1097 return CAB_STATUS_INVALID_CAB
;
1101 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1102 (unsigned int)File
->File
.FileOffset
,
1103 (unsigned int)File
->File
.FileSize
,
1104 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
1105 (unsigned int)File
->DataBlock
->UncompOffset
));
1107 CurrentDataNode
= File
->DataBlock
;
1110 RestartSearch
= true;
1112 } while (CFData
.UncompSize
== 0);
1114 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1116 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1117 if (Status
!= CS_SUCCESS
) {
1118 CloseFile(DestFile
);
1120 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1121 if (Status
== CS_NOMEMORY
)
1122 return CAB_STATUS_NOMEMORY
;
1123 return CAB_STATUS_INVALID_CAB
;
1126 if (BytesToWrite
!= CFData
.UncompSize
) {
1127 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1128 BytesToWrite
, CFData
.UncompSize
));
1129 return CAB_STATUS_INVALID_CAB
;
1132 BytesLeftInBlock
= BytesToWrite
;
1134 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1136 BytesToWrite
= BytesLeftInBlock
;
1138 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1139 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1140 CurrentDataNode
->Data
.CompSize
));
1142 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1143 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1144 CloseFile(DestFile
);
1146 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1147 return CAB_STATUS_INVALID_CAB
;
1150 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1151 CFData
.CompSize
, CFData
.UncompSize
));
1153 /* Go to next data block */
1155 (unsigned int)SetFilePointer(FileHandle
,
1156 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1157 CurrentDataNode
->Data
.CompSize
,
1160 if (GetLastError() != NO_ERROR
) {
1161 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1162 return CAB_STATUS_INVALID_CAB
;
1165 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1166 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0) {
1167 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1168 return CAB_STATUS_INVALID_CAB
;
1176 BytesSkipped
= (Offset
- CurrentOffset
);
1180 BytesToWrite
-= BytesSkipped
;
1182 if (Size
< BytesToWrite
)
1183 BytesToWrite
= Size
;
1185 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1186 (unsigned int)Offset
,
1187 (unsigned int)CurrentOffset
,
1188 (unsigned int)BytesToWrite
,
1189 (unsigned int)BytesSkipped
, (unsigned int)Skip
,
1190 (unsigned int)Size
));
1193 if (!WriteFile(DestFile
, (void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1194 BytesToWrite
, &BytesWritten
, NULL
) ||
1195 (BytesToWrite
!= BytesWritten
)) {
1196 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1198 BytesWritten
= BytesToWrite
;
1199 if (fwrite((void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1200 BytesToWrite
, 1, DestFile
) < 1) {
1202 CloseFile(DestFile
);
1204 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1205 return CAB_STATUS_CANNOT_WRITE
;
1207 Size
-= BytesToWrite
;
1209 CurrentOffset
+= BytesToWrite
;
1211 /* Don't skip any more bytes */
1216 CloseFile(DestFile
);
1220 return CAB_STATUS_SUCCESS
;
1224 void CCabinet::SelectCodec(unsigned long Id
)
1226 * FUNCTION: Selects codec engine to use
1228 * Id = Codec identifier
1231 if (CodecSelected
) {
1235 CodecSelected
= false;
1242 Codec
= new CRawCodec();
1245 case CAB_CODEC_MSZIP
:
1246 Codec
= new CMSZipCodec();
1253 CodecSelected
= true;
1257 #ifndef CAB_READ_ONLY
1259 /* CAB write methods */
1261 unsigned long CCabinet::NewCabinet()
1263 * FUNCTION: Creates a new cabinet
1265 * Status of operation
1268 unsigned long Status
;
1270 CurrentDiskNumber
= 0;
1272 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1273 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1274 if ((!OutputBuffer
) || (!InputBuffer
)) {
1275 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1276 return CAB_STATUS_NOMEMORY
;
1278 CurrentIBuffer
= InputBuffer
;
1279 CurrentIBufferSize
= 0;
1281 CABHeader
.Signature
= CAB_SIGNATURE
;
1282 CABHeader
.Reserved1
= 0; // Not used
1283 CABHeader
.CabinetSize
= 0; // Not yet known
1284 CABHeader
.Reserved2
= 0; // Not used
1285 CABHeader
.Reserved3
= 0; // Not used
1286 CABHeader
.Version
= CAB_VERSION
;
1287 CABHeader
.FolderCount
= 0; // Not yet known
1288 CABHeader
.FileCount
= 0; // Not yet known
1289 CABHeader
.Flags
= 0; // Not yet known
1290 // FIXME: Should be random
1291 CABHeader
.SetID
= 0x534F;
1292 CABHeader
.CabinetNumber
= 0;
1295 TotalFolderSize
= 0;
1298 DiskSize
= sizeof(CFHEADER
);
1300 InitCabinetHeader();
1302 // NextFolderNumber is 0-based
1303 NextFolderNumber
= 0;
1305 CurrentFolderNode
= NULL
;
1306 Status
= NewFolder();
1307 if (Status
!= CAB_STATUS_SUCCESS
)
1310 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1312 ScratchFile
= new CCFDATAStorage
;
1314 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1315 return CAB_STATUS_NOMEMORY
;
1318 Status
= ScratchFile
->Create("~CAB.tmp");
1320 CreateNewFolder
= false;
1322 CreateNewDisk
= false;
1324 PrevCabinetNumber
= 0;
1330 unsigned long CCabinet::NewDisk()
1332 * FUNCTION: Forces a new disk to be created
1334 * Status of operation
1337 // NextFolderNumber is 0-based
1338 NextFolderNumber
= 1;
1340 CreateNewDisk
= false;
1342 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1344 InitCabinetHeader();
1346 CurrentFolderNode
->TotalFolderSize
= 0;
1348 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1350 return CAB_STATUS_SUCCESS
;
1354 unsigned long CCabinet::NewFolder()
1356 * FUNCTION: Forces a new folder to be created
1358 * Status of operation
1361 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1363 CurrentFolderNode
= NewFolderNode();
1364 if (!CurrentFolderNode
) {
1365 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1366 return CAB_STATUS_NOMEMORY
;
1371 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1373 case CAB_CODEC_MSZIP
:
1374 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1377 return CAB_STATUS_UNSUPPCOMP
;
1380 /* FIXME: This won't work if no files are added to the new folder */
1382 DiskSize
+= sizeof(CFFOLDER
);
1384 TotalFolderSize
+= sizeof(CFFOLDER
);
1388 CABHeader
.FolderCount
++;
1392 return CAB_STATUS_SUCCESS
;
1396 unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1398 * FUNCTION: Writes a file to the scratch file
1400 * FileNode = Pointer to file node
1402 * Status of operation
1405 unsigned long BytesToRead
;
1406 unsigned long BytesRead
;
1407 unsigned long Status
;
1410 if (!ContinueFile
) {
1411 /* Try to open file */
1413 SourceFile
= CreateFile(
1414 FileNode
->FileName
, // Open this file
1415 GENERIC_READ
, // Open for reading
1416 FILE_SHARE_READ
, // Share for reading
1417 NULL
, // No security
1418 OPEN_EXISTING
, // File must exist
1419 FILE_ATTRIBUTE_NORMAL
, // Normal file
1420 NULL
); // No attribute template
1421 if (SourceFile
== INVALID_HANDLE_VALUE
) {
1422 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1423 return CAB_STATUS_NOFILE
;
1426 SourceFile
= fopen(FileNode
->FileName
, "rb");
1427 if (SourceFile
== NULL
) {
1428 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1429 return CAB_STATUS_NOFILE
;
1433 if (CreateNewFolder
) {
1434 /* There is always a new folder after
1435 a split file is completely stored */
1436 Status
= NewFolder();
1437 if (Status
!= CAB_STATUS_SUCCESS
)
1439 CreateNewFolder
= false;
1442 /* Call OnAdd event handler */
1443 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1445 TotalBytesLeft
= FileNode
->File
.FileSize
;
1447 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1448 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1449 FileNode
->File
.FileControlID
= NextFolderNumber
- 1;
1450 CurrentFolderNode
->Commit
= true;
1451 PrevCabinetNumber
= CurrentDiskNumber
;
1453 Size
= sizeof(CFFILE
) + strlen(GetFileName(FileNode
->FileName
)) + 1;
1454 CABHeader
.FileTableOffset
+= Size
;
1455 TotalFileSize
+= Size
;
1459 FileNode
->Commit
= true;
1461 if (TotalBytesLeft
> 0) {
1463 if (TotalBytesLeft
> (unsigned long)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1464 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1466 BytesToRead
= TotalBytesLeft
;
1468 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
)) {
1469 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1470 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1471 return CAB_STATUS_INVALID_CAB
;
1474 (unsigned char*)CurrentIBuffer
+= BytesRead
;
1476 CurrentIBufferSize
+= (unsigned short)BytesRead
;
1478 if (CurrentIBufferSize
== CAB_BLOCKSIZE
) {
1479 Status
= WriteDataBlock();
1480 if (Status
!= CAB_STATUS_SUCCESS
)
1483 TotalBytesLeft
-= BytesRead
;
1484 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1487 if (TotalBytesLeft
== 0) {
1488 CloseFile(SourceFile
);
1489 FileNode
->Delete
= true;
1491 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) {
1492 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1493 CurrentFolderNode
->Delete
= true;
1495 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1496 Status
= WriteDataBlock();
1497 if (Status
!= CAB_STATUS_SUCCESS
)
1501 CreateNewFolder
= true;
1504 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1505 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1507 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1510 return CAB_STATUS_SUCCESS
;
1514 unsigned long CCabinet::WriteDisk(unsigned long MoreDisks
)
1516 * FUNCTION: Forces the current disk to be written
1518 * MoreDisks = true if there is one or more disks after this disk
1520 * Status of operation
1523 PCFFILE_NODE FileNode
;
1524 unsigned long Status
;
1526 ContinueFile
= false;
1527 FileNode
= FileListHead
;
1528 while (FileNode
!= NULL
) {
1530 Status
= WriteFileToScratchStorage(FileNode
);
1531 if (Status
!= CAB_STATUS_SUCCESS
)
1533 if (CreateNewDisk
) {
1534 /* A data block could span more than two
1535 disks if MaxDiskSize is very small */
1536 while (CreateNewDisk
) {
1537 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1542 ContinueFile
= true;
1543 CreateNewDisk
= false;
1545 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1546 CurrentIBufferSize
, CurrentOBufferSize
));
1548 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1549 Status
= WriteDataBlock();
1550 if (Status
!= CAB_STATUS_SUCCESS
)
1555 ContinueFile
= false;
1556 FileNode
= FileNode
->Next
;
1560 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1561 /* A data block could span more than two
1562 disks if MaxDiskSize is very small */
1564 ASSERT(CreateNewDisk
== false);
1567 if (CreateNewDisk
) {
1568 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1572 CreateNewDisk
= false;
1574 ASSERT(FileNode
== FileListHead
);
1577 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1578 Status
= WriteDataBlock();
1579 if (Status
!= CAB_STATUS_SUCCESS
)
1582 } while (CreateNewDisk
);
1584 CommitDisk(MoreDisks
);
1586 return CAB_STATUS_SUCCESS
;
1590 unsigned long CCabinet::CommitDisk(unsigned long MoreDisks
)
1592 * FUNCTION: Commits the current disk
1594 * MoreDisks = true if there is one or more disks after this disk
1596 * Status of operation
1599 PCFFOLDER_NODE FolderNode
;
1600 unsigned long Status
;
1602 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1604 /* Create file, fail if it already exists */
1606 FileHandle
= CreateFile(CabinetName
, // Create this file
1607 GENERIC_WRITE
, // Open for writing
1609 NULL
, // No security
1610 CREATE_NEW
, // New file only
1611 FILE_ATTRIBUTE_NORMAL
, // Normal file
1612 NULL
); // No attribute template
1613 if (FileHandle
== INVALID_HANDLE_VALUE
) {
1614 unsigned long Status
;
1615 /* If file exists, ask to overwrite file */
1616 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1617 (OnOverwrite(NULL
, CabinetName
))) {
1619 /* Create cabinet file, overwrite if it already exists */
1620 FileHandle
= CreateFile(CabinetName
, // Create this file
1621 GENERIC_WRITE
, // Open for writing
1623 NULL
, // No security
1624 TRUNCATE_EXISTING
, // Truncate the file
1625 FILE_ATTRIBUTE_NORMAL
, // Normal file
1626 NULL
); // No attribute template
1627 if (FileHandle
== INVALID_HANDLE_VALUE
)
1628 return CAB_STATUS_CANNOT_CREATE
;
1630 if (Status
== ERROR_FILE_EXISTS
)
1631 return CAB_STATUS_FILE_EXISTS
;
1633 return CAB_STATUS_CANNOT_CREATE
;
1637 FileHandle
= fopen(CabinetName
, "rb");
1638 if (FileHandle
!= NULL
) {
1640 /* If file exists, ask to overwrite file */
1641 if (OnOverwrite(NULL
, CabinetName
)) {
1642 FileHandle
= fopen(CabinetName
, "w+b");
1643 if (FileHandle
== NULL
) {
1644 return CAB_STATUS_CANNOT_CREATE
;
1647 return CAB_STATUS_FILE_EXISTS
;
1650 FileHandle
= fopen(CabinetName
, "w+b");
1651 if (FileHandle
== NULL
) {
1652 return CAB_STATUS_CANNOT_CREATE
;
1657 WriteCabinetHeader(MoreDisks
);
1659 Status
= WriteFolderEntries();
1660 if (Status
!= CAB_STATUS_SUCCESS
)
1663 /* Write file entries */
1666 /* Write data blocks */
1667 FolderNode
= FolderListHead
;
1668 while (FolderNode
!= NULL
) {
1669 if (FolderNode
->Commit
) {
1670 Status
= CommitDataBlocks(FolderNode
);
1671 if (Status
!= CAB_STATUS_SUCCESS
)
1673 /* Remove data blocks for folder */
1674 DestroyDataNodes(FolderNode
);
1676 FolderNode
= FolderNode
->Next
;
1679 CloseFile(FileHandle
);
1681 ScratchFile
->Truncate();
1683 return CAB_STATUS_SUCCESS
;
1687 unsigned long CCabinet::CloseDisk()
1689 * FUNCTION: Closes the current disk
1691 * Status of operation
1694 DestroyDeletedFileNodes();
1696 /* Destroy folder nodes that are completely stored */
1697 DestroyDeletedFolderNodes();
1699 CurrentDiskNumber
++;
1701 return CAB_STATUS_SUCCESS
;
1705 unsigned long CCabinet::CloseCabinet()
1707 * FUNCTION: Closes the current cabinet
1709 * Status of operation
1712 unsigned long Status
;
1716 DestroyFolderNodes();
1719 FreeMemory(InputBuffer
);
1724 FreeMemory(OutputBuffer
);
1725 OutputBuffer
= NULL
;
1731 Status
= ScratchFile
->Destroy();
1736 return CAB_STATUS_SUCCESS
;
1740 unsigned long CCabinet::AddFile(char* FileName
)
1742 * FUNCTION: Adds a file to the current disk
1744 * FileName = Pointer to string with file name (full path)
1746 * Status of operation
1750 PCFFILE_NODE FileNode
;
1752 FileNode
= NewFileNode();
1754 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1755 return CAB_STATUS_NOMEMORY
;
1758 FileNode
->FolderNode
= CurrentFolderNode
;
1760 FileNode
->FileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1761 strcpy(FileNode
->FileName
, FileName
);
1762 ConvertPath(FileNode
->FileName
, false);
1764 /* Try to open file */
1766 SrcFile
= CreateFile(
1767 FileNode
->FileName
, // Open this file
1768 GENERIC_READ
, // Open for reading
1769 FILE_SHARE_READ
, // Share for reading
1770 NULL
, // No security
1771 OPEN_EXISTING
, // File must exist
1772 FILE_ATTRIBUTE_NORMAL
, // Normal file
1773 NULL
); // No attribute template
1774 if (SrcFile
== INVALID_HANDLE_VALUE
) {
1775 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1776 return CAB_STATUS_CANNOT_OPEN
;
1779 SrcFile
= fopen(FileNode
->FileName
, "rb");
1780 if (SrcFile
== NULL
) {
1781 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1782 return CAB_STATUS_CANNOT_OPEN
;
1786 /* FIXME: Check for and handle large files (>= 2GB) */
1787 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1788 if (FileNode
->File
.FileSize
== (unsigned long)-1) {
1789 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1790 return CAB_STATUS_CANNOT_READ
;
1793 GetFileTimes(SrcFile
, FileNode
);
1795 GetAttributesOnFile(FileNode
);
1799 return CAB_STATUS_SUCCESS
;
1803 void CCabinet::SetMaxDiskSize(unsigned long Size
)
1805 * FUNCTION: Sets the maximum size of the current disk
1807 * Size = Maximum size of current disk (0 means no maximum size)
1813 #endif /* CAB_READ_ONLY */
1816 /* Default event handlers */
1818 bool CCabinet::OnOverwrite(PCFFILE File
,
1821 * FUNCTION: Called when extracting a file and it already exists
1823 * File = Pointer to CFFILE for file being extracted
1824 * FileName = Pointer to buffer with name of file (full path)
1826 * true if the file should be overwritten, false if not
1833 void CCabinet::OnExtract(PCFFILE File
,
1836 * FUNCTION: Called just before extracting a file
1838 * File = Pointer to CFFILE for file being extracted
1839 * FileName = Pointer to buffer with name of file (full path)
1845 void CCabinet::OnDiskChange(char* CabinetName
,
1848 * FUNCTION: Called when a new disk is to be processed
1850 * CabinetName = Pointer to buffer with name of cabinet
1851 * DiskLabel = Pointer to buffer with label of disk
1857 #ifndef CAB_READ_ONLY
1859 void CCabinet::OnAdd(PCFFILE File
,
1862 * FUNCTION: Called just before adding a file to a cabinet
1864 * File = Pointer to CFFILE for file being added
1865 * FileName = Pointer to buffer with name of file (full path)
1871 bool CCabinet::OnDiskLabel(unsigned long Number
, char* Label
)
1873 * FUNCTION: Called when a disk needs a label
1875 * Number = Cabinet number that needs a label
1876 * Label = Pointer to buffer to place label of disk
1878 * true if a disk label was returned, false if not
1885 bool CCabinet::OnCabinetName(unsigned long Number
, char* Name
)
1887 * FUNCTION: Called when a cabinet needs a name
1889 * Number = Disk number that needs a name
1890 * Name = Pointer to buffer to place name of cabinet
1892 * true if a cabinet name was returned, false if not
1898 #endif /* CAB_READ_ONLY */
1900 PCFFOLDER_NODE
CCabinet::LocateFolderNode(unsigned long Index
)
1902 * FUNCTION: Locates a folder node
1904 * Index = Folder index
1906 * Pointer to folder node or NULL if the folder node was not found
1909 PCFFOLDER_NODE Node
;
1912 case CAB_FILE_SPLIT
:
1913 return FolderListTail
;
1915 case CAB_FILE_CONTINUED
:
1916 case CAB_FILE_PREV_NEXT
:
1917 return FolderListHead
;
1920 Node
= FolderListHead
;
1921 while (Node
!= NULL
) {
1922 if (Node
->Index
== Index
)
1930 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
1932 * FUNCTION: Returns the absolute offset of a file
1934 * File = Pointer to CFFILE_NODE structure for file
1936 * Status of operation
1941 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1942 (char*)File
->FileName
,
1943 (unsigned int)File
->File
.FileOffset
,
1944 (unsigned int)File
->File
.FileSize
));
1946 Node
= CurrentFolderNode
->DataListHead
;
1947 while (Node
!= NULL
) {
1949 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1950 (unsigned int)Node
->UncompOffset
,
1951 (unsigned int)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
1952 (unsigned int)Node
->Data
.UncompSize
));
1954 /* Node->Data.UncompSize will be 0 if the block is split
1955 (ie. it is the last block in this cabinet) */
1956 if ((Node
->Data
.UncompSize
== 0) ||
1957 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
1958 (File
->File
.FileOffset
< Node
->UncompOffset
+
1959 Node
->Data
.UncompSize
))) {
1960 File
->DataBlock
= Node
;
1961 return CAB_STATUS_SUCCESS
;
1966 return CAB_STATUS_INVALID_CAB
;
1970 unsigned long CCabinet::LocateFile(char* FileName
,
1973 * FUNCTION: Locates a file in the cabinet
1975 * FileName = Pointer to string with name of file to locate
1976 * File = Address of pointer to CFFILE_NODE structure to fill
1978 * Status of operation
1980 * Current folder is set to the folder of the file
1984 unsigned long Status
;
1986 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
1988 Node
= FileListHead
;
1989 while (Node
!= NULL
) {
1990 if (strcasecmp(FileName
, Node
->FileName
) == 0) {
1992 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
1993 if (!CurrentFolderNode
) {
1994 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
1995 (unsigned int)Node
->File
.FileControlID
));
1996 return CAB_STATUS_INVALID_CAB
;
1999 if (Node
->DataBlock
== NULL
) {
2000 Status
= GetAbsoluteOffset(Node
);
2002 Status
= CAB_STATUS_SUCCESS
;
2008 return CAB_STATUS_NOFILE
;
2012 unsigned long CCabinet::ReadString(char* String
, unsigned long MaxLength
)
2014 * FUNCTION: Reads a NULL-terminated string from the cabinet
2016 * String = Pointer to buffer to place string
2017 * MaxLength = Maximum length of string
2019 * Status of operation
2022 unsigned long BytesRead
;
2023 unsigned long Offset
;
2024 unsigned long Status
;
2031 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2034 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2035 return CAB_STATUS_INVALID_CAB
;
2038 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2039 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
)) {
2040 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2041 return CAB_STATUS_INVALID_CAB
;
2044 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++) {
2045 if (String
[Size
] == '\0') {
2051 Offset
+= BytesRead
;
2055 /* Back up some bytes */
2056 Size
= (BytesRead
- Size
) - 1;
2058 SetLastError(NO_ERROR
);
2059 (unsigned int)SetFilePointer(FileHandle
,
2063 if (GetLastError() != NO_ERROR
) {
2064 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2065 return CAB_STATUS_INVALID_CAB
;
2068 if (fseek(FileHandle
, (off_t
)(-(long)Size
), SEEK_CUR
) != 0) {
2069 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2070 return CAB_STATUS_INVALID_CAB
;
2073 return CAB_STATUS_SUCCESS
;
2077 unsigned long CCabinet::ReadFileTable()
2079 * FUNCTION: Reads the file table from the cabinet file
2081 * Status of operation
2085 unsigned long Status
;
2086 unsigned long BytesRead
;
2089 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2090 CABHeader
.FileTableOffset
));
2092 /* Seek to file table */
2094 SetLastError(NO_ERROR
);
2095 (unsigned int)SetFilePointer(FileHandle
,
2096 CABHeader
.FileTableOffset
,
2099 if (GetLastError() != NO_ERROR
) {
2100 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2101 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2102 return CAB_STATUS_INVALID_CAB
;
2105 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0) {
2106 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2107 return CAB_STATUS_INVALID_CAB
;
2111 for (i
= 0; i
< CABHeader
.FileCount
; i
++) {
2112 File
= NewFileNode();
2114 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2115 return CAB_STATUS_NOMEMORY
;
2118 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2119 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2120 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2121 return CAB_STATUS_INVALID_CAB
;
2124 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2125 if (!File
->FileName
) {
2126 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2127 return CAB_STATUS_NOMEMORY
;
2130 /* Read file name */
2131 Status
= ReadString(File
->FileName
, MAX_PATH
);
2132 if (Status
!= CAB_STATUS_SUCCESS
)
2135 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2136 (char*)File
->FileName
,
2137 (unsigned int)File
->File
.FileOffset
,
2138 (unsigned int)File
->File
.FileSize
,
2139 (unsigned int)File
->File
.FileControlID
));
2142 return CAB_STATUS_SUCCESS
;
2146 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2148 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2150 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2152 * Status of operation
2155 unsigned long AbsoluteOffset
;
2156 unsigned long UncompOffset
;
2158 unsigned long BytesRead
;
2159 unsigned long Status
;
2162 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2163 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2165 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2166 UncompOffset
= FolderNode
->UncompOffset
;
2168 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++) {
2169 Node
= NewDataNode(FolderNode
);
2171 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2172 return CAB_STATUS_NOMEMORY
;
2175 /* Seek to data block */
2177 SetLastError(NO_ERROR
);
2178 (unsigned int)SetFilePointer(FileHandle
,
2182 if (GetLastError() != NO_ERROR
) {
2183 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2184 return CAB_STATUS_INVALID_CAB
;
2187 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0) {
2188 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2189 return CAB_STATUS_INVALID_CAB
;
2193 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2194 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2195 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2196 return CAB_STATUS_INVALID_CAB
;
2199 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2200 (unsigned int)AbsoluteOffset
,
2201 (unsigned int)UncompOffset
,
2202 (unsigned int)Node
->Data
.Checksum
,
2203 (unsigned int)Node
->Data
.CompSize
,
2204 (unsigned int)Node
->Data
.UncompSize
));
2206 Node
->AbsoluteOffset
= AbsoluteOffset
;
2207 Node
->UncompOffset
= UncompOffset
;
2209 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2210 UncompOffset
+= Node
->Data
.UncompSize
;
2213 FolderUncompSize
= UncompOffset
;
2215 return CAB_STATUS_SUCCESS
;
2219 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2221 * FUNCTION: Creates a new folder node
2223 * Pointer to node if there was enough free memory available, otherwise NULL
2226 PCFFOLDER_NODE Node
;
2228 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2232 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2234 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2236 Node
->Prev
= FolderListTail
;
2238 if (FolderListTail
!= NULL
) {
2239 FolderListTail
->Next
= Node
;
2241 FolderListHead
= Node
;
2243 FolderListTail
= Node
;
2249 PCFFILE_NODE
CCabinet::NewFileNode()
2251 * FUNCTION: Creates a new file node
2253 * FolderNode = Pointer to folder node to bind file to
2255 * Pointer to node if there was enough free memory available, otherwise NULL
2260 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2264 memset(Node
, 0, sizeof(CFFILE_NODE
));
2266 Node
->Prev
= FileListTail
;
2268 if (FileListTail
!= NULL
) {
2269 FileListTail
->Next
= Node
;
2271 FileListHead
= Node
;
2273 FileListTail
= Node
;
2279 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2281 * FUNCTION: Creates a new data block node
2283 * FolderNode = Pointer to folder node to bind data block to
2285 * Pointer to node if there was enough free memory available, otherwise NULL
2290 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2294 memset(Node
, 0, sizeof(CFDATA_NODE
));
2296 Node
->Prev
= FolderNode
->DataListTail
;
2298 if (FolderNode
->DataListTail
!= NULL
) {
2299 FolderNode
->DataListTail
->Next
= Node
;
2301 FolderNode
->DataListHead
= Node
;
2303 FolderNode
->DataListTail
= Node
;
2309 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2311 * FUNCTION: Destroys data block nodes bound to a folder node
2313 * FolderNode = Pointer to folder node
2316 PCFDATA_NODE PrevNode
;
2317 PCFDATA_NODE NextNode
;
2319 NextNode
= FolderNode
->DataListHead
;
2320 while (NextNode
!= NULL
) {
2321 PrevNode
= NextNode
->Next
;
2322 FreeMemory(NextNode
);
2323 NextNode
= PrevNode
;
2325 FolderNode
->DataListHead
= NULL
;
2326 FolderNode
->DataListTail
= NULL
;
2330 void CCabinet::DestroyFileNodes()
2332 * FUNCTION: Destroys file nodes
2334 * FolderNode = Pointer to folder node
2337 PCFFILE_NODE PrevNode
;
2338 PCFFILE_NODE NextNode
;
2340 NextNode
= FileListHead
;
2341 while (NextNode
!= NULL
) {
2342 PrevNode
= NextNode
->Next
;
2343 if (NextNode
->FileName
)
2344 FreeMemory(NextNode
->FileName
);
2345 FreeMemory(NextNode
);
2346 NextNode
= PrevNode
;
2348 FileListHead
= NULL
;
2349 FileListTail
= NULL
;
2353 void CCabinet::DestroyDeletedFileNodes()
2355 * FUNCTION: Destroys file nodes that are marked for deletion
2358 PCFFILE_NODE CurNode
;
2359 PCFFILE_NODE NextNode
;
2361 CurNode
= FileListHead
;
2362 while (CurNode
!= NULL
) {
2363 NextNode
= CurNode
->Next
;
2365 if (CurNode
->Delete
) {
2366 if (CurNode
->Prev
!= NULL
) {
2367 CurNode
->Prev
->Next
= CurNode
->Next
;
2369 FileListHead
= CurNode
->Next
;
2371 FileListHead
->Prev
= NULL
;
2374 if (CurNode
->Next
!= NULL
) {
2375 CurNode
->Next
->Prev
= CurNode
->Prev
;
2377 FileListTail
= CurNode
->Prev
;
2379 FileListTail
->Next
= NULL
;
2382 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2384 TotalFileSize
-= (sizeof(CFFILE
) + strlen(GetFileName(CurNode
->FileName
)) + 1);
2386 if (CurNode
->FileName
)
2387 FreeMemory(CurNode
->FileName
);
2388 FreeMemory(CurNode
);
2395 void CCabinet::DestroyFolderNodes()
2397 * FUNCTION: Destroys folder nodes
2400 PCFFOLDER_NODE PrevNode
;
2401 PCFFOLDER_NODE NextNode
;
2403 NextNode
= FolderListHead
;
2404 while (NextNode
!= NULL
) {
2405 PrevNode
= NextNode
->Next
;
2406 DestroyDataNodes(NextNode
);
2407 FreeMemory(NextNode
);
2408 NextNode
= PrevNode
;
2410 FolderListHead
= NULL
;
2411 FolderListTail
= NULL
;
2415 void CCabinet::DestroyDeletedFolderNodes()
2417 * FUNCTION: Destroys folder nodes that are marked for deletion
2420 PCFFOLDER_NODE CurNode
;
2421 PCFFOLDER_NODE NextNode
;
2423 CurNode
= FolderListHead
;
2424 while (CurNode
!= NULL
) {
2425 NextNode
= CurNode
->Next
;
2427 if (CurNode
->Delete
) {
2428 if (CurNode
->Prev
!= NULL
) {
2429 CurNode
->Prev
->Next
= CurNode
->Next
;
2431 FolderListHead
= CurNode
->Next
;
2433 FolderListHead
->Prev
= NULL
;
2436 if (CurNode
->Next
!= NULL
) {
2437 CurNode
->Next
->Prev
= CurNode
->Prev
;
2439 FolderListTail
= CurNode
->Prev
;
2441 FolderListTail
->Next
= NULL
;
2444 DestroyDataNodes(CurNode
);
2445 FreeMemory(CurNode
);
2447 TotalFolderSize
-= sizeof(CFFOLDER
);
2454 unsigned long CCabinet::ComputeChecksum(void* Buffer
,
2458 * FUNCTION: Computes checksum for data block
2460 * Buffer = Pointer to data buffer
2461 * Size = Length of data buffer
2462 * Seed = Previously computed checksum
2464 * Checksum of buffer
2467 int UlongCount
; // Number of ULONGs in block
2468 unsigned long Checksum
; // Checksum accumulator
2472 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2473 won't accept checksums computed by this routine */
2475 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%X) Size (%d)\n", (unsigned int)Buffer
, Size
));
2477 UlongCount
= Size
/ 4; // Number of ULONGs
2478 Checksum
= Seed
; // Init checksum
2479 pb
= (unsigned char*)Buffer
; // Start at front of data block
2481 /* Checksum integral multiple of ULONGs */
2482 while (UlongCount
-- > 0) {
2483 /* NOTE: Build unsigned long in big/little-endian independent manner */
2484 ul
= *pb
++; // Get low-order byte
2485 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2486 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3nd byte
2487 ul
|= (((unsigned long)(*pb
++)) << 24); // Add 4th byte
2489 Checksum
^= ul
; // Update checksum
2492 /* Checksum remainder bytes */
2496 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3rd byte
2498 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2500 ul
|= *pb
++; // Get low-order byte
2504 Checksum
^= ul
; // Update checksum
2506 /* Return computed checksum */
2511 unsigned long CCabinet::ReadBlock(void* Buffer
,
2513 unsigned long* BytesRead
)
2515 * FUNCTION: Read a block of data from file
2517 * Buffer = Pointer to data buffer
2518 * Size = Length of data buffer
2519 * BytesRead = Pointer to unsigned long that on return will contain
2520 * number of bytes read
2522 * Status of operation
2525 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2526 return CAB_STATUS_INVALID_CAB
;
2527 return CAB_STATUS_SUCCESS
;
2530 #ifndef CAB_READ_ONLY
2532 unsigned long CCabinet::InitCabinetHeader()
2534 * FUNCTION: Initializes cabinet header and optional fields
2536 * Status of operation
2539 unsigned long TotalSize
;
2542 CABHeader
.FileTableOffset
= 0; // Not known yet
2543 CABHeader
.FolderCount
= 0; // Not known yet
2544 CABHeader
.FileCount
= 0; // Not known yet
2545 CABHeader
.Flags
= 0; // Not known yet
2547 CABHeader
.CabinetNumber
= CurrentDiskNumber
;
2549 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
))) {
2550 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2551 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2552 strcpy(CabinetPrev
, "");
2555 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
)) {
2556 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2557 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2558 strcpy(DiskNext
, "");
2563 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2565 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2567 /* Calculate size of name of previous cabinet */
2568 TotalSize
+= strlen(CabinetPrev
) + 1;
2570 /* Calculate size of label of previous disk */
2571 TotalSize
+= strlen(DiskPrev
) + 1;
2574 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2576 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2578 /* Calculate size of name of next cabinet */
2579 Size
= strlen(CabinetNext
) + 1;
2581 NextFieldsSize
= Size
;
2583 /* Calculate size of label of next disk */
2584 Size
= strlen(DiskNext
) + 1;
2586 NextFieldsSize
+= Size
;
2590 /* Add cabinet reserved area size if present */
2591 if (CabinetReservedFileSize
> 0)
2593 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2594 TotalSize
+= CabinetReservedFileSize
;
2595 TotalSize
+= sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2598 DiskSize
+= TotalSize
;
2600 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2602 return CAB_STATUS_SUCCESS
;
2606 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks
)
2608 * FUNCTION: Writes the cabinet header and optional fields
2610 * MoreDisks = true if next cabinet name should be included
2612 * Status of operation
2615 PCFFOLDER_NODE FolderNode
;
2616 PCFFILE_NODE FileNode
;
2617 unsigned long BytesWritten
;
2621 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2622 Size
= TotalHeaderSize
;
2624 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2625 DiskSize
-= NextFieldsSize
;
2626 Size
= TotalHeaderSize
- NextFieldsSize
;
2629 /* Set absolute folder offsets */
2630 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2631 CABHeader
.FolderCount
= 0;
2632 FolderNode
= FolderListHead
;
2633 while (FolderNode
!= NULL
) {
2634 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2636 BytesWritten
+= FolderNode
->TotalFolderSize
;
2638 CABHeader
.FolderCount
++;
2640 FolderNode
= FolderNode
->Next
;
2643 /* Set absolute offset of file table */
2644 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2646 /* Count number of files to be committed */
2647 CABHeader
.FileCount
= 0;
2648 FileNode
= FileListHead
;
2649 while (FileNode
!= NULL
) {
2650 if (FileNode
->Commit
)
2651 CABHeader
.FileCount
++;
2652 FileNode
= FileNode
->Next
;
2655 CABHeader
.CabinetSize
= DiskSize
;
2659 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
)) {
2660 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2661 return CAB_STATUS_CANNOT_WRITE
;
2664 BytesWritten
= sizeof(CFHEADER
);
2665 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1) {
2666 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2667 return CAB_STATUS_CANNOT_WRITE
;
2671 /* Write per-cabinet reserved area if present */
2672 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
2673 unsigned long ReservedSize
;
2675 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2676 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2677 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2679 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(unsigned long), &BytesWritten
, NULL
)) {
2680 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2681 return CAB_STATUS_CANNOT_WRITE
;
2684 BytesWritten
= sizeof(unsigned long);
2685 if (fwrite(&ReservedSize
, sizeof(unsigned long), 1, FileHandle
) < 1) {
2686 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2687 return CAB_STATUS_CANNOT_WRITE
;
2692 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesWritten
, NULL
)) {
2693 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2694 return CAB_STATUS_CANNOT_WRITE
;
2697 BytesWritten
= CabinetReservedFileSize
;
2698 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1) {
2699 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2700 return CAB_STATUS_CANNOT_WRITE
;
2705 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2707 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2709 /* Write name of previous cabinet */
2710 Size
= strlen(CabinetPrev
) + 1;
2712 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
)) {
2713 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2714 return CAB_STATUS_CANNOT_WRITE
;
2717 BytesWritten
= Size
;
2718 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1) {
2719 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2720 return CAB_STATUS_CANNOT_WRITE
;
2724 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2726 /* Write label of previous disk */
2727 Size
= strlen(DiskPrev
) + 1;
2729 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
)) {
2730 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2731 return CAB_STATUS_CANNOT_WRITE
;
2734 BytesWritten
= Size
;
2735 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1) {
2736 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2737 return CAB_STATUS_CANNOT_WRITE
;
2742 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2744 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2746 /* Write name of next cabinet */
2747 Size
= strlen(CabinetNext
) + 1;
2749 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
)) {
2750 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2751 return CAB_STATUS_CANNOT_WRITE
;
2754 BytesWritten
= Size
;
2755 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1) {
2756 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2757 return CAB_STATUS_CANNOT_WRITE
;
2761 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2763 /* Write label of next disk */
2764 Size
= strlen(DiskNext
) + 1;
2766 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
)) {
2767 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2768 return CAB_STATUS_CANNOT_WRITE
;
2771 BytesWritten
= Size
;
2772 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1) {
2773 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2774 return CAB_STATUS_CANNOT_WRITE
;
2779 return CAB_STATUS_SUCCESS
;
2783 unsigned long CCabinet::WriteFolderEntries()
2785 * FUNCTION: Writes folder entries
2787 * Status of operation
2790 PCFFOLDER_NODE FolderNode
;
2791 unsigned long BytesWritten
;
2793 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2795 FolderNode
= FolderListHead
;
2796 while (FolderNode
!= NULL
) {
2797 if (FolderNode
->Commit
) {
2799 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2800 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
2803 if (!WriteFile(FileHandle
,
2804 &FolderNode
->Folder
,
2808 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2809 return CAB_STATUS_CANNOT_WRITE
;
2812 BytesWritten
= sizeof(CFFOLDER
);
2813 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1) {
2814 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2815 return CAB_STATUS_CANNOT_WRITE
;
2819 FolderNode
= FolderNode
->Next
;
2822 return CAB_STATUS_SUCCESS
;
2826 unsigned long CCabinet::WriteFileEntries()
2828 * FUNCTION: Writes file entries for all files
2830 * Status of operation
2834 unsigned long BytesWritten
;
2837 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
2839 File
= FileListHead
;
2840 while (File
!= NULL
) {
2842 /* Remove any continued files that ends in this disk */
2843 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
2844 File
->Delete
= true;
2846 /* The file could end in the last (split) block and should therefore
2847 appear in the next disk too */
2849 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
2850 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
)) {
2851 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
2852 File
->Delete
= false;
2856 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
2857 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
2860 if (!WriteFile(FileHandle
,
2865 return CAB_STATUS_CANNOT_WRITE
;
2867 BytesWritten
= sizeof(CFFILE
);
2868 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1) {
2869 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2870 return CAB_STATUS_CANNOT_WRITE
;
2875 if (!WriteFile(FileHandle
,
2876 GetFileName(File
->FileName
),
2877 strlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
2878 return CAB_STATUS_CANNOT_WRITE
;
2880 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
2881 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1) {
2882 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2883 return CAB_STATUS_CANNOT_WRITE
;
2888 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
2895 return CAB_STATUS_SUCCESS
;
2899 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
2901 * FUNCTION: Writes data blocks to the cabinet
2903 * FolderNode = Pointer to folder node containing the data blocks
2905 * Status of operation
2908 PCFDATA_NODE DataNode
;
2909 unsigned long BytesWritten
;
2910 unsigned long BytesRead
;
2911 unsigned long Status
;
2913 DataNode
= FolderNode
->DataListHead
;
2914 if (DataNode
!= NULL
)
2915 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
2917 while (DataNode
!= NULL
) {
2918 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
2919 DataNode
->ScratchFilePosition
,
2920 DataNode
->Data
.CompSize
,
2921 DataNode
->Data
.UncompSize
));
2923 /* InputBuffer is free for us to use here, so we use it and avoid a
2924 memory allocation. OutputBuffer can't be used here because it may
2925 still contain valid data (if a data block spans two or more disks) */
2926 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
2927 if (Status
!= CAB_STATUS_SUCCESS
) {
2928 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (unsigned int)Status
));
2933 if (!WriteFile(FileHandle
, &DataNode
->Data
,
2934 sizeof(CFDATA
), &BytesWritten
, NULL
)) {
2935 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2936 return CAB_STATUS_CANNOT_WRITE
;
2939 BytesWritten
= sizeof(CFDATA
);
2940 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1) {
2941 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2942 return CAB_STATUS_CANNOT_WRITE
;
2947 if (!WriteFile(FileHandle
, InputBuffer
,
2948 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
)) {
2949 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2950 return CAB_STATUS_CANNOT_WRITE
;
2953 BytesWritten
= DataNode
->Data
.CompSize
;
2954 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1) {
2955 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2956 return CAB_STATUS_CANNOT_WRITE
;
2960 DataNode
= DataNode
->Next
;
2962 return CAB_STATUS_SUCCESS
;
2966 unsigned long CCabinet::WriteDataBlock()
2968 * FUNCTION: Writes the current data block to the scratch file
2970 * Status of operation
2973 unsigned long Status
;
2974 unsigned long BytesWritten
;
2975 PCFDATA_NODE DataNode
;
2977 if (!BlockIsSplit
) {
2978 Status
= Codec
->Compress(OutputBuffer
,
2983 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
2984 CurrentIBufferSize
, TotalCompSize
));
2986 CurrentOBuffer
= OutputBuffer
;
2987 CurrentOBufferSize
= TotalCompSize
;
2990 DataNode
= NewDataNode(CurrentFolderNode
);
2992 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2993 return CAB_STATUS_NOMEMORY
;
2996 DiskSize
+= sizeof(CFDATA
);
2998 if (MaxDiskSize
> 0)
2999 /* Disk size is limited */
3000 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3002 BlockIsSplit
= false;
3005 DataNode
->Data
.CompSize
= (unsigned short)(MaxDiskSize
- DiskSize
);
3006 DataNode
->Data
.UncompSize
= 0;
3007 CreateNewDisk
= true;
3009 DataNode
->Data
.CompSize
= (unsigned short)CurrentOBufferSize
;
3010 DataNode
->Data
.UncompSize
= (unsigned short)CurrentIBufferSize
;
3013 DataNode
->Data
.Checksum
= 0;
3014 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3016 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3017 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3019 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3020 (unsigned int)DataNode
->Data
.Checksum
,
3021 (unsigned int)DataNode
->Data
.CompSize
,
3022 (unsigned int)DataNode
->Data
.UncompSize
));
3024 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3025 CurrentOBuffer
, &BytesWritten
);
3026 if (Status
!= CAB_STATUS_SUCCESS
)
3029 DiskSize
+= BytesWritten
;
3031 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3032 CurrentFolderNode
->Folder
.DataBlockCount
++;
3034 (unsigned char*)CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3035 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3037 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3039 if (!BlockIsSplit
) {
3040 CurrentIBufferSize
= 0;
3041 CurrentIBuffer
= InputBuffer
;
3044 return CAB_STATUS_SUCCESS
;
3050 void CCabinet::ConvertDateAndTime(time_t* Time
,
3051 unsigned short* DosDate
,
3052 unsigned short* DosTime
)
3054 * FUNCTION: Returns file times of a file
3056 * FileHandle = File handle of file to get file times from
3057 * File = Pointer to CFFILE node for file
3059 * Status of operation
3064 timedef
= localtime(Time
);
3066 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3067 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3068 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3070 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3071 | ((timedef
->tm_mon
+ 1) << 5)
3072 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3074 *DosTime
= (timedef
->tm_sec
<< 0)
3075 | (timedef
->tm_min
<< 5)
3076 | (timedef
->tm_hour
<< 11);
3082 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3084 * FUNCTION: Returns file times of a file
3086 * FileHandle = File handle of file to get file times from
3087 * File = Pointer to CFFILE node for file
3089 * Status of operation
3095 if (GetFileTime(FileHandle
, NULL
, &FileTime
, NULL
))
3096 FileTimeToDosDateTime(&FileTime
,
3097 &File
->File
.FileDate
,
3098 &File
->File
.FileTime
);
3103 // Check for an absolute path
3104 if (IsSeparator(File
->FileName
[0]))
3106 strcpy(buf
, File
->FileName
);
3110 getcwd(buf
, sizeof(buf
));
3111 strcat(buf
, DIR_SEPARATOR_STRING
);
3112 strcat(buf
, File
->FileName
);
3115 if (stat(buf
, &stbuf
) == -1)
3117 return CAB_STATUS_CANNOT_READ
;
3120 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3122 return CAB_STATUS_SUCCESS
;
3126 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3128 * FUNCTION: Returns attributes on a file
3130 * File = Pointer to CFFILE node for file
3132 * Status of operation
3138 Attributes
= GetFileAttributes(File
->FileName
);
3139 if (Attributes
== -1)
3140 return CAB_STATUS_CANNOT_READ
;
3142 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3143 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3145 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3146 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3148 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3149 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3151 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3152 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3154 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3155 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3160 // Check for an absolute path
3161 if (IsSeparator(File
->FileName
[0]))
3163 strcpy(buf
, File
->FileName
);
3167 getcwd(buf
, sizeof(buf
));
3168 strcat(buf
, DIR_SEPARATOR_STRING
);
3169 strcat(buf
, File
->FileName
);
3172 if (stat(buf
, &stbuf
) == -1)
3174 return CAB_STATUS_CANNOT_READ
;
3178 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3179 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3180 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3183 if (stbuf
.st_mode
& S_IFDIR
)
3184 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3186 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3189 return CAB_STATUS_SUCCESS
;
3193 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3195 * FUNCTION: Sets attributes on a file
3197 * File = Pointer to CFFILE node for file
3199 * Status of operation
3203 unsigned long Attributes
= 0;
3205 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3206 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3208 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3209 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3211 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3212 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3214 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3215 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3217 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3218 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3220 SetFileAttributes(File
->FileName
, Attributes
);
3222 return CAB_STATUS_SUCCESS
;
3224 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3225 return CAB_STATUS_SUCCESS
;
3229 #endif /* CAB_READ_ONLY */