2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cpp
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Colin Finck <mail@colinfinck.de>
8 * NOTES: Define CAB_READ_ONLY for read only version
10 * CSH 21/03-2001 Created
11 * CSH 15/08-2003 Made it portable
12 * CF 04/05-2007 Reformatted the code to be more consistent and use TABs instead of spaces
14 * - Checksum of datablocks should be calculated
15 * - EXTRACT.EXE complains if a disk is created manually
16 * - Folders that are created manually and span disks will result in a damaged cabinet
22 # include <sys/stat.h>
29 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
30 static long _GetSizeOfFile(FILEHANDLE handle
)
32 unsigned long size
= GetFileSize(handle
, NULL
);
33 if (size
== INVALID_FILE_SIZE
)
38 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
39 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, unsigned long size
, unsigned long* bytesread
)
41 return ReadFile(handle
, buffer
, size
, bytesread
, NULL
) != 0;
44 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
45 static long _GetSizeOfFile(FILEHANDLE handle
)
48 fseek(handle
, 0, SEEK_END
);
50 fseek(handle
, 0, SEEK_SET
);
53 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
54 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, unsigned long size
, unsigned long* bytesread
)
56 *bytesread
= fread(buffer
, 1, size
, handle
);
57 return *bytesread
== size
;
66 void DumpBuffer(void* Buffer
, unsigned long Size
)
68 FILEHANDLE FileHandle
;
69 unsigned long BytesWritten
;
71 /* Create file, overwrite if it already exists */
72 FileHandle
= CreateFile("dump.bin", // Create this file
73 GENERIC_WRITE
, // Open for writing
76 CREATE_ALWAYS
, // Create or overwrite
77 FILE_ATTRIBUTE_NORMAL
, // Normal file
78 NULL
); // No attribute template
79 if (FileHandle
== INVALID_HANDLE_VALUE
)
81 DPRINT(MID_TRACE
, ("ERROR OPENING '%d'.\n", (unsigned int)GetLastError()));
85 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
))
87 DPRINT(MID_TRACE
, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError()));
90 CloseFile(FileHandle
);
98 CCFDATAStorage::CCFDATAStorage()
100 * FUNCTION: Default constructor
107 CCFDATAStorage::~CCFDATAStorage()
109 * FUNCTION: Default destructor
112 ASSERT(!FileCreated
);
116 unsigned long CCFDATAStorage::Create(char* FileName
)
118 * FUNCTION: Creates the file
120 * FileName = Pointer to name of file
122 * Status of operation
125 ASSERT(!FileCreated
);
128 if (GetTempPath(MAX_PATH
, FullName
) == 0)
129 return CAB_STATUS_CANNOT_CREATE
;
131 strcat(FullName
, FileName
);
133 /* Create file, overwrite if it already exists */
134 FileHandle
= CreateFile(FullName
, // Create this file
135 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
138 CREATE_ALWAYS
, // Create or overwrite
139 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
140 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
141 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
142 NULL
); // No attribute template
143 if (FileHandle
== INVALID_HANDLE_VALUE
)
145 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)GetLastError()));
146 return CAB_STATUS_CANNOT_CREATE
;
149 /*if (tmpnam(FullName) == NULL)*/
150 if ((FileHandle
= tmpfile()) == NULL
)
151 return CAB_STATUS_CANNOT_CREATE
;
153 FileHandle = fopen(FullName, "w+b");
154 if (FileHandle == NULL) {
155 DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)errno));
156 return CAB_STATUS_CANNOT_CREATE;
163 return CAB_STATUS_SUCCESS
;
167 unsigned long CCFDATAStorage::Destroy()
169 * FUNCTION: Destroys the file
171 * Status of operation
176 CloseFile(FileHandle
);
180 return CAB_STATUS_SUCCESS
;
184 unsigned long CCFDATAStorage::Truncate()
186 * FUNCTION: Truncate the scratch file to zero bytes
188 * Status of operation
192 if (!SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
))
193 return CAB_STATUS_FAILURE
;
194 if (!SetEndOfFile(FileHandle
))
195 return CAB_STATUS_FAILURE
;
198 FileHandle
= tmpfile();
199 if (FileHandle
== NULL
)
201 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)errno
));
202 return CAB_STATUS_FAILURE
;
205 return CAB_STATUS_SUCCESS
;
209 unsigned long CCFDATAStorage::Position()
211 * FUNCTION: Returns current position in file
217 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
219 return (unsigned long)ftell(FileHandle
);
224 unsigned long CCFDATAStorage::Seek(long Position
)
226 * FUNCTION: Seeks to an absolute position
228 * Position = Absolute position to seek to
230 * Status of operation
234 if (SetFilePointer(FileHandle
,
237 FILE_BEGIN
) == 0xFFFFFFFF)
238 return CAB_STATUS_FAILURE
;
240 return CAB_STATUS_SUCCESS
;
242 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0)
243 return CAB_STATUS_FAILURE
;
245 return CAB_STATUS_SUCCESS
;
250 unsigned long CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesRead
)
252 * FUNCTION: Reads a CFDATA block from the file
254 * Data = Pointer to CFDATA block for the buffer
255 * Buffer = Pointer to buffer to store data read
256 * BytesWritten = Pointer to buffer to write number of bytes read
258 * Status of operation
262 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, BytesRead
, NULL
))
263 return CAB_STATUS_CANNOT_READ
;
266 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
267 if (*BytesRead
!= Data
->CompSize
)
268 return CAB_STATUS_CANNOT_READ
;
270 return CAB_STATUS_SUCCESS
;
274 unsigned long CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesWritten
)
276 * FUNCTION: Writes a CFDATA block to the file
278 * Data = Pointer to CFDATA block for the buffer
279 * Buffer = Pointer to buffer with data to write
280 * BytesWritten = Pointer to buffer to write number of bytes written
282 * Status of operation
286 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, BytesWritten
, NULL
))
287 return CAB_STATUS_CANNOT_WRITE
;
289 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
290 if (*BytesWritten
!= Data
->CompSize
)
291 return CAB_STATUS_CANNOT_WRITE
;
293 return CAB_STATUS_SUCCESS
;
296 #endif /* CAB_READ_ONLY */
303 * FUNCTION: Default constructor
312 *CabinetReservedFile
= '\0';
315 CabinetReservedFileBuffer
= NULL
;
316 CabinetReservedFileSize
= 0;
318 FolderListHead
= NULL
;
319 FolderListTail
= NULL
;
323 Codec
= new CRawCodec();
324 CodecId
= CAB_CODEC_RAW
;
325 CodecSelected
= true;
330 BlockIsSplit
= false;
333 FolderUncompSize
= 0;
334 BytesLeftInBlock
= 0;
336 CurrentDataNode
= NULL
;
340 CCabinet::~CCabinet()
342 * FUNCTION: Default destructor
345 if (CabinetReservedFileBuffer
!= NULL
)
347 FreeMemory(CabinetReservedFileBuffer
);
348 CabinetReservedFileBuffer
= NULL
;
349 CabinetReservedFileSize
= 0;
356 bool CCabinet::IsSeparator(char Char
)
358 * FUNCTION: Determines if a character is a separator
360 * Char = Character to check
362 * Wether it is a separator
365 if ((Char
== '\\') || (Char
== '/'))
371 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
373 * FUNCTION: Replaces \ or / with the one used be the host environment
375 * Path = Pointer to string with pathname
376 * Allocate = Specifies wther to allocate memory for the new
377 * string or to change the existing buffer
379 * Pointer to new path
386 newpath
= strdup(Path
);
402 newpath
[i
] = Path
[i
];
412 char* CCabinet::GetFileName(char* Path
)
414 * FUNCTION: Returns a pointer to file name
416 * Path = Pointer to string with pathname
418 * Pointer to filename
423 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
426 if (IsSeparator(Path
[i
- 1]))
433 void CCabinet::RemoveFileName(char* Path
)
435 * FUNCTION: Removes a file name from a path
437 * Path = Pointer to string with path
443 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
444 FileName
= GetFileName(Path
+ i
);
446 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
448 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
454 bool CCabinet::NormalizePath(char* Path
,
455 unsigned long Length
)
457 * FUNCTION: Normalizes a path
459 * Path = Pointer to string with pathname
460 * Length = Number of bytes in Path
462 * true if there was enough room in Path, or false
468 if ((n
= strlen(Path
)) &&
469 (!IsSeparator(Path
[n
- 1])) &&
470 (OK
= ((n
+ 1) < Length
)))
472 Path
[n
] = DIR_SEPARATOR_CHAR
;
479 char* CCabinet::GetCabinetName()
481 * FUNCTION: Returns pointer to cabinet file name
483 * Pointer to string with name of cabinet
490 void CCabinet::SetCabinetName(char* FileName
)
492 * FUNCTION: Sets cabinet file name
494 * FileName = Pointer to string with name of cabinet
497 strcpy(CabinetName
, FileName
);
501 void CCabinet::SetDestinationPath(char* DestinationPath
)
503 * FUNCTION: Sets destination path
505 * DestinationPath = Pointer to string with name of destination path
508 strcpy(DestPath
, DestinationPath
);
509 ConvertPath(DestPath
, false);
510 if (strlen(DestPath
) > 0)
511 NormalizePath(DestPath
, MAX_PATH
);
515 char* CCabinet::GetDestinationPath()
517 * FUNCTION: Returns destination path
519 * Pointer to string with name of destination path
526 bool CCabinet::SetCabinetReservedFile(char* FileName
)
528 * FUNCTION: Sets cabinet reserved file
530 * FileName = Pointer to string with name of cabinet reserved file
533 FILEHANDLE FileHandle
;
534 unsigned long BytesRead
;
537 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
538 GENERIC_READ
, // Open for reading
539 FILE_SHARE_READ
, // Share for reading
541 OPEN_EXISTING
, // Existing file only
542 FILE_ATTRIBUTE_NORMAL
, // Normal file
543 NULL
); // No attribute template
544 if (FileHandle
== INVALID_HANDLE_VALUE
)
546 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
550 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
551 if (FileHandle
== NULL
)
553 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
558 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
559 if (CabinetReservedFileSize
== (unsigned long)-1)
561 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
565 if (CabinetReservedFileSize
== 0)
567 CloseFile(FileHandle
);
571 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
572 if (!CabinetReservedFileBuffer
)
574 CloseFile(FileHandle
);
578 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
))
580 CloseFile(FileHandle
);
584 CloseFile(FileHandle
);
586 strcpy(CabinetReservedFile
, FileName
);
592 char* CCabinet::GetCabinetReservedFile()
594 * FUNCTION: Returns cabionet reserved file
596 * Pointer to string with name of cabinet reserved file
599 return CabinetReservedFile
;
603 unsigned long CCabinet::GetCurrentDiskNumber()
605 * FUNCTION: Returns current disk number
607 * Current disk number
610 return CurrentDiskNumber
;
614 unsigned long CCabinet::Open()
616 * FUNCTION: Opens a cabinet file
618 * Status of operation
621 PCFFOLDER_NODE FolderNode
;
622 unsigned long Status
;
627 unsigned long BytesRead
;
630 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
632 return CAB_STATUS_NOMEMORY
;
635 FileHandle
= CreateFile(CabinetName
, // Open this file
636 GENERIC_READ
, // Open for reading
637 FILE_SHARE_READ
, // Share for reading
639 OPEN_EXISTING
, // Existing file only
640 FILE_ATTRIBUTE_NORMAL
, // Normal file
641 NULL
); // No attribute template
643 if (FileHandle
== INVALID_HANDLE_VALUE
)
645 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
646 return CAB_STATUS_CANNOT_OPEN
;
649 FileHandle
= fopen(CabinetName
, "rb");
650 if (FileHandle
== NULL
)
652 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
653 return CAB_STATUS_CANNOT_OPEN
;
659 /* Load CAB header */
660 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
661 != CAB_STATUS_SUCCESS
)
663 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
664 return CAB_STATUS_INVALID_CAB
;
668 if ((BytesRead
!= sizeof(CFHEADER
)) ||
669 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
670 (CABHeader
.Version
!= CAB_VERSION
) ||
671 (CABHeader
.FolderCount
== 0 ) ||
672 (CABHeader
.FileCount
== 0 ) ||
673 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
)))
676 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
677 return CAB_STATUS_INVALID_CAB
;
682 /* Read/skip any reserved bytes */
683 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
685 if ((Status
= ReadBlock(&Size
, sizeof(unsigned long), &BytesRead
))
686 != CAB_STATUS_SUCCESS
)
688 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
689 return CAB_STATUS_INVALID_CAB
;
691 CabinetReserved
= Size
& 0xFFFF;
692 FolderReserved
= (Size
>> 16) & 0xFF;
693 DataReserved
= (Size
>> 24) & 0xFF;
696 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
697 if (GetLastError() != NO_ERROR
)
699 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
700 return CAB_STATUS_FAILURE
;
703 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0)
705 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
706 return CAB_STATUS_FAILURE
;
711 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
713 /* Read name of previous cabinet */
714 Status
= ReadString(CabinetPrev
, 256);
715 if (Status
!= CAB_STATUS_SUCCESS
)
717 /* Read label of previous disk */
718 Status
= ReadString(DiskPrev
, 256);
719 if (Status
!= CAB_STATUS_SUCCESS
)
724 strcpy(CabinetPrev
, "");
725 strcpy(DiskPrev
, "");
728 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
730 /* Read name of next cabinet */
731 Status
= ReadString(CabinetNext
, 256);
732 if (Status
!= CAB_STATUS_SUCCESS
)
734 /* Read label of next disk */
735 Status
= ReadString(DiskNext
, 256);
736 if (Status
!= CAB_STATUS_SUCCESS
)
741 strcpy(CabinetNext
, "");
742 strcpy(DiskNext
, "");
745 /* Read all folders */
746 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
748 FolderNode
= NewFolderNode();
751 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
752 return CAB_STATUS_NOMEMORY
;
756 FolderNode
->UncompOffset
= FolderUncompSize
;
758 FolderNode
->Index
= Index
;
760 if ((Status
= ReadBlock(&FolderNode
->Folder
,
761 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
763 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
764 return CAB_STATUS_INVALID_CAB
;
768 /* Read file entries */
769 Status
= ReadFileTable();
770 if (Status
!= CAB_STATUS_SUCCESS
)
772 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (unsigned int)Status
));
776 /* Read data blocks for all folders */
777 FolderNode
= FolderListHead
;
778 while (FolderNode
!= NULL
)
780 Status
= ReadDataBlocks(FolderNode
);
781 if (Status
!= CAB_STATUS_SUCCESS
)
783 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status
));
786 FolderNode
= FolderNode
->Next
;
789 return CAB_STATUS_SUCCESS
;
793 void CCabinet::Close()
795 * FUNCTION: Closes the cabinet file
800 CloseFile(FileHandle
);
806 unsigned long CCabinet::FindFirst(char* FileName
,
809 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
811 * FileName = Pointer to search criteria
812 * Search = Pointer to search structure
814 * Status of operation
817 RestartSearch
= false;
818 strncpy(Search
->Search
, FileName
, MAX_PATH
);
819 Search
->Next
= FileListHead
;
820 return FindNext(Search
);
824 unsigned long CCabinet::FindNext(PCAB_SEARCH Search
)
826 * FUNCTION: Finds next file in the cabinet that matches a search criteria
828 * Search = Pointer to search structure
830 * Status of operation
833 unsigned long Status
;
836 Search
->Next
= FileListHead
;
838 /* Skip split files already extracted */
839 while ((Search
->Next
) &&
840 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
841 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
843 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
844 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
845 Search
->Next
= Search
->Next
->Next
;
848 RestartSearch
= false;
851 /* FIXME: Check search criteria */
854 if (strlen(DiskNext
) > 0) {
857 SetCabinetName(CabinetNext
);
859 OnDiskChange(CabinetNext
, DiskNext
);
862 if (Status
!= CAB_STATUS_SUCCESS
)
865 Search
->Next
= FileListHead
;
867 return CAB_STATUS_NOFILE
;
869 return CAB_STATUS_NOFILE
;
872 Search
->File
= &Search
->Next
->File
;
873 Search
->FileName
= Search
->Next
->FileName
;
874 Search
->Next
= Search
->Next
->Next
;
875 return CAB_STATUS_SUCCESS
;
879 unsigned long CCabinet::ExtractFile(char* FileName
)
881 * FUNCTION: Extracts a file from the cabinet
883 * FileName = Pointer to buffer with name of file
885 * Status of operation
889 unsigned long Offset
;
890 unsigned long BytesRead
;
891 unsigned long BytesToRead
;
892 unsigned long BytesWritten
;
893 unsigned long BytesSkipped
;
894 unsigned long BytesToWrite
;
895 unsigned long TotalBytesRead
;
896 unsigned long CurrentOffset
;
897 unsigned char* Buffer
;
898 unsigned char* CurrentBuffer
;
902 unsigned long Status
;
907 char DestName
[MAX_PATH
];
908 char TempName
[MAX_PATH
];
910 Status
= LocateFile(FileName
, &File
);
911 if (Status
!= CAB_STATUS_SUCCESS
)
913 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
917 LastFileOffset
= File
->File
.FileOffset
;
919 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
922 SelectCodec(CAB_CODEC_RAW
);
926 SelectCodec(CAB_CODEC_MSZIP
);
930 return CAB_STATUS_UNSUPPCOMP
;
933 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
934 (unsigned int)File
->File
.FileOffset
,
935 (unsigned int)File
->File
.FileSize
,
936 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
937 (unsigned int)File
->DataBlock
->UncompOffset
));
939 strcpy(DestName
, DestPath
);
940 strcat(DestName
, FileName
);
942 /* Create destination file, fail if it already exists */
944 DestFile
= CreateFile(DestName
, // Create this file
945 GENERIC_WRITE
, // Open for writing
948 CREATE_NEW
, // New file only
949 FILE_ATTRIBUTE_NORMAL
, // Normal file
950 NULL
); // No attribute template
951 if (DestFile
== INVALID_HANDLE_VALUE
)
953 /* If file exists, ask to overwrite file */
954 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
955 (OnOverwrite(&File
->File
, FileName
)))
957 /* Create destination file, overwrite if it already exists */
958 DestFile
= CreateFile(DestName
, // Create this file
959 GENERIC_WRITE
, // Open for writing
962 TRUNCATE_EXISTING
, // Truncate the file
963 FILE_ATTRIBUTE_NORMAL
, // Normal file
964 NULL
); // No attribute template
965 if (DestFile
== INVALID_HANDLE_VALUE
)
966 return CAB_STATUS_CANNOT_CREATE
;
970 if (Status
== ERROR_FILE_EXISTS
)
971 return CAB_STATUS_FILE_EXISTS
;
973 return CAB_STATUS_CANNOT_CREATE
;
977 DestFile
= fopen(DestName
, "rb");
978 if (DestFile
!= NULL
)
981 /* If file exists, ask to overwrite file */
982 if (OnOverwrite(&File
->File
, FileName
))
984 DestFile
= fopen(DestName
, "w+b");
985 if (DestFile
== NULL
)
986 return CAB_STATUS_CANNOT_CREATE
;
989 return CAB_STATUS_FILE_EXISTS
;
993 DestFile
= fopen(DestName
, "w+b");
994 if (DestFile
== NULL
)
995 return CAB_STATUS_CANNOT_CREATE
;
999 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1001 CloseFile(DestFile
);
1002 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
1003 return CAB_STATUS_CANNOT_WRITE
;
1006 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
1008 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1010 SetAttributesOnFile(File
);
1012 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1015 CloseFile(DestFile
);
1016 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1017 return CAB_STATUS_NOMEMORY
;
1020 /* Call OnExtract event handler */
1021 OnExtract(&File
->File
, FileName
);
1023 /* Search to start of file */
1025 Offset
= SetFilePointer(FileHandle
,
1026 File
->DataBlock
->AbsoluteOffset
,
1029 if (GetLastError() != NO_ERROR
)
1031 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1032 return CAB_STATUS_INVALID_CAB
;
1035 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1037 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1038 return CAB_STATUS_FAILURE
;
1040 Offset
= ftell(FileHandle
);
1043 Size
= File
->File
.FileSize
;
1044 Offset
= File
->File
.FileOffset
;
1045 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1049 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1054 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%d) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
1055 File
->DataBlock
->UncompOffset
, (unsigned int)ReuseBlock
, Offset
, Size
,
1058 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1060 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1062 CurrentBuffer
= Buffer
;
1066 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1068 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1069 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1071 CloseFile(DestFile
);
1073 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1074 return CAB_STATUS_INVALID_CAB
;
1077 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n",
1078 (unsigned int)CFData
.Checksum
,
1079 (unsigned int)CFData
.CompSize
,
1080 (unsigned int)CFData
.UncompSize
));
1082 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1084 BytesToRead
= CFData
.CompSize
;
1086 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1087 (long unsigned int)CurrentBuffer
, (long unsigned int)Buffer
));
1089 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1090 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1092 CloseFile(DestFile
);
1094 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1095 return CAB_STATUS_INVALID_CAB
;
1098 /* FIXME: Does not work with files generated by makecab.exe */
1100 if (CFData.Checksum != 0)
1102 unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1103 if (Checksum != CFData.Checksum)
1105 CloseFile(DestFile);
1107 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1108 Checksum, CFData.Checksum));
1109 return CAB_STATUS_INVALID_CAB;
1113 TotalBytesRead
+= BytesRead
;
1115 CurrentBuffer
+= BytesRead
;
1117 if (CFData
.UncompSize
== 0)
1119 if (strlen(DiskNext
) == 0)
1120 return CAB_STATUS_NOFILE
;
1122 /* CloseCabinet() will destroy all file entries so in case
1123 FileName refers to the FileName field of a CFFOLDER_NODE
1124 structure, we have to save a copy of the filename */
1125 strcpy(TempName
, FileName
);
1129 SetCabinetName(CabinetNext
);
1131 OnDiskChange(CabinetNext
, DiskNext
);
1134 if (Status
!= CAB_STATUS_SUCCESS
)
1137 /* The first data block of the file will not be
1138 found as it is located in the previous file */
1139 Status
= LocateFile(TempName
, &File
);
1140 if (Status
== CAB_STATUS_NOFILE
)
1142 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
1146 /* The file is continued in the first data block in the folder */
1147 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1149 /* Search to start of file */
1151 SetFilePointer(FileHandle
,
1152 File
->DataBlock
->AbsoluteOffset
,
1155 if (GetLastError() != NO_ERROR
)
1157 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1158 return CAB_STATUS_INVALID_CAB
;
1161 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1163 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1164 return CAB_STATUS_INVALID_CAB
;
1168 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1169 (unsigned int)File
->File
.FileOffset
,
1170 (unsigned int)File
->File
.FileSize
,
1171 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
1172 (unsigned int)File
->DataBlock
->UncompOffset
));
1174 CurrentDataNode
= File
->DataBlock
;
1177 RestartSearch
= true;
1179 } while (CFData
.UncompSize
== 0);
1181 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1183 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1184 if (Status
!= CS_SUCCESS
)
1186 CloseFile(DestFile
);
1188 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1189 if (Status
== CS_NOMEMORY
)
1190 return CAB_STATUS_NOMEMORY
;
1191 return CAB_STATUS_INVALID_CAB
;
1194 if (BytesToWrite
!= CFData
.UncompSize
)
1196 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1197 BytesToWrite
, CFData
.UncompSize
));
1198 return CAB_STATUS_INVALID_CAB
;
1201 BytesLeftInBlock
= BytesToWrite
;
1205 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1207 BytesToWrite
= BytesLeftInBlock
;
1209 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1210 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1211 CurrentDataNode
->Data
.CompSize
));
1213 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1214 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1216 CloseFile(DestFile
);
1218 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1219 return CAB_STATUS_INVALID_CAB
;
1222 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1223 CFData
.CompSize
, CFData
.UncompSize
));
1225 /* Go to next data block */
1227 SetFilePointer(FileHandle
,
1228 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1229 CurrentDataNode
->Data
.CompSize
,
1232 if (GetLastError() != NO_ERROR
)
1234 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1235 return CAB_STATUS_INVALID_CAB
;
1238 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1239 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1241 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1242 return CAB_STATUS_INVALID_CAB
;
1250 BytesSkipped
= (Offset
- CurrentOffset
);
1254 BytesToWrite
-= BytesSkipped
;
1256 if (Size
< BytesToWrite
)
1257 BytesToWrite
= Size
;
1259 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1260 (unsigned int)Offset
,
1261 (unsigned int)CurrentOffset
,
1262 (unsigned int)BytesToWrite
,
1263 (unsigned int)BytesSkipped
, (unsigned int)Skip
,
1264 (unsigned int)Size
));
1267 if (!WriteFile(DestFile
, (void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1268 BytesToWrite
, &BytesWritten
, NULL
) ||
1269 (BytesToWrite
!= BytesWritten
))
1271 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1273 BytesWritten
= BytesToWrite
;
1274 if (fwrite((void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1275 BytesToWrite
, 1, DestFile
) < 1)
1278 CloseFile(DestFile
);
1280 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1281 return CAB_STATUS_CANNOT_WRITE
;
1283 Size
-= BytesToWrite
;
1285 CurrentOffset
+= BytesToWrite
;
1287 /* Don't skip any more bytes */
1292 CloseFile(DestFile
);
1296 return CAB_STATUS_SUCCESS
;
1300 void CCabinet::SelectCodec(unsigned long Id
)
1302 * FUNCTION: Selects codec engine to use
1304 * Id = Codec identifier
1312 CodecSelected
= false;
1320 Codec
= new CRawCodec();
1324 case CAB_CODEC_MSZIP
:
1325 Codec
= new CMSZipCodec();
1333 CodecSelected
= true;
1337 #ifndef CAB_READ_ONLY
1339 /* CAB write methods */
1341 unsigned long CCabinet::NewCabinet()
1343 * FUNCTION: Creates a new cabinet
1345 * Status of operation
1348 unsigned long Status
;
1350 CurrentDiskNumber
= 0;
1352 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1353 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1354 if ((!OutputBuffer
) || (!InputBuffer
))
1356 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1357 return CAB_STATUS_NOMEMORY
;
1359 CurrentIBuffer
= InputBuffer
;
1360 CurrentIBufferSize
= 0;
1362 CABHeader
.Signature
= CAB_SIGNATURE
;
1363 CABHeader
.Reserved1
= 0; // Not used
1364 CABHeader
.CabinetSize
= 0; // Not yet known
1365 CABHeader
.Reserved2
= 0; // Not used
1366 CABHeader
.Reserved3
= 0; // Not used
1367 CABHeader
.Version
= CAB_VERSION
;
1368 CABHeader
.FolderCount
= 0; // Not yet known
1369 CABHeader
.FileCount
= 0; // Not yet known
1370 CABHeader
.Flags
= 0; // Not yet known
1371 // FIXME: Should be random
1372 CABHeader
.SetID
= 0x534F;
1373 CABHeader
.CabinetNumber
= 0;
1376 TotalFolderSize
= 0;
1379 DiskSize
= sizeof(CFHEADER
);
1381 InitCabinetHeader();
1383 // NextFolderNumber is 0-based
1384 NextFolderNumber
= 0;
1386 CurrentFolderNode
= NULL
;
1387 Status
= NewFolder();
1388 if (Status
!= CAB_STATUS_SUCCESS
)
1391 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1393 ScratchFile
= new CCFDATAStorage
;
1396 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1397 return CAB_STATUS_NOMEMORY
;
1400 Status
= ScratchFile
->Create("~CAB.tmp");
1402 CreateNewFolder
= false;
1404 CreateNewDisk
= false;
1406 PrevCabinetNumber
= 0;
1412 unsigned long CCabinet::NewDisk()
1414 * FUNCTION: Forces a new disk to be created
1416 * Status of operation
1419 // NextFolderNumber is 0-based
1420 NextFolderNumber
= 1;
1422 CreateNewDisk
= false;
1424 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1426 InitCabinetHeader();
1428 CurrentFolderNode
->TotalFolderSize
= 0;
1430 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1432 return CAB_STATUS_SUCCESS
;
1436 unsigned long CCabinet::NewFolder()
1438 * FUNCTION: Forces a new folder to be created
1440 * Status of operation
1443 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1445 CurrentFolderNode
= NewFolderNode();
1446 if (!CurrentFolderNode
)
1448 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1449 return CAB_STATUS_NOMEMORY
;
1454 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1457 case CAB_CODEC_MSZIP
:
1458 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1462 return CAB_STATUS_UNSUPPCOMP
;
1465 /* FIXME: This won't work if no files are added to the new folder */
1467 DiskSize
+= sizeof(CFFOLDER
);
1469 TotalFolderSize
+= sizeof(CFFOLDER
);
1473 CABHeader
.FolderCount
++;
1477 return CAB_STATUS_SUCCESS
;
1481 unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1483 * FUNCTION: Writes a file to the scratch file
1485 * FileNode = Pointer to file node
1487 * Status of operation
1490 unsigned long BytesToRead
;
1491 unsigned long BytesRead
;
1492 unsigned long Status
;
1497 /* Try to open file */
1499 SourceFile
= CreateFile(
1500 FileNode
->FileName
, // Open this file
1501 GENERIC_READ
, // Open for reading
1502 FILE_SHARE_READ
, // Share for reading
1503 NULL
, // No security
1504 OPEN_EXISTING
, // File must exist
1505 FILE_ATTRIBUTE_NORMAL
, // Normal file
1506 NULL
); // No attribute template
1507 if (SourceFile
== INVALID_HANDLE_VALUE
)
1509 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1510 return CAB_STATUS_NOFILE
;
1513 SourceFile
= fopen(FileNode
->FileName
, "rb");
1514 if (SourceFile
== NULL
)
1516 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1517 return CAB_STATUS_NOFILE
;
1521 if (CreateNewFolder
)
1523 /* There is always a new folder after
1524 a split file is completely stored */
1525 Status
= NewFolder();
1526 if (Status
!= CAB_STATUS_SUCCESS
)
1528 CreateNewFolder
= false;
1531 /* Call OnAdd event handler */
1532 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1534 TotalBytesLeft
= FileNode
->File
.FileSize
;
1536 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1537 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1538 FileNode
->File
.FileControlID
= (unsigned short)(NextFolderNumber
- 1);
1539 CurrentFolderNode
->Commit
= true;
1540 PrevCabinetNumber
= CurrentDiskNumber
;
1542 Size
= sizeof(CFFILE
) + strlen(GetFileName(FileNode
->FileName
)) + 1;
1543 CABHeader
.FileTableOffset
+= Size
;
1544 TotalFileSize
+= Size
;
1548 FileNode
->Commit
= true;
1550 if (TotalBytesLeft
> 0)
1554 if (TotalBytesLeft
> (unsigned long)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1555 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1557 BytesToRead
= TotalBytesLeft
;
1559 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1561 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1562 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1563 return CAB_STATUS_INVALID_CAB
;
1566 *(unsigned char**)&CurrentIBuffer
+= BytesRead
;
1568 CurrentIBufferSize
+= (unsigned short)BytesRead
;
1570 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1572 Status
= WriteDataBlock();
1573 if (Status
!= CAB_STATUS_SUCCESS
)
1576 TotalBytesLeft
-= BytesRead
;
1577 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1580 if (TotalBytesLeft
== 0)
1582 CloseFile(SourceFile
);
1583 FileNode
->Delete
= true;
1585 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1587 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1588 CurrentFolderNode
->Delete
= true;
1590 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1592 Status
= WriteDataBlock();
1593 if (Status
!= CAB_STATUS_SUCCESS
)
1597 CreateNewFolder
= true;
1602 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1603 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1605 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1608 return CAB_STATUS_SUCCESS
;
1612 unsigned long CCabinet::WriteDisk(unsigned long MoreDisks
)
1614 * FUNCTION: Forces the current disk to be written
1616 * MoreDisks = true if there is one or more disks after this disk
1618 * Status of operation
1621 PCFFILE_NODE FileNode
;
1622 unsigned long Status
;
1624 ContinueFile
= false;
1625 FileNode
= FileListHead
;
1626 while (FileNode
!= NULL
)
1628 Status
= WriteFileToScratchStorage(FileNode
);
1629 if (Status
!= CAB_STATUS_SUCCESS
)
1634 /* A data block could span more than two
1635 disks if MaxDiskSize is very small */
1636 while (CreateNewDisk
)
1638 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1643 ContinueFile
= true;
1644 CreateNewDisk
= false;
1646 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1647 CurrentIBufferSize
, CurrentOBufferSize
));
1649 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1651 Status
= WriteDataBlock();
1652 if (Status
!= CAB_STATUS_SUCCESS
)
1659 ContinueFile
= false;
1660 FileNode
= FileNode
->Next
;
1664 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1666 /* A data block could span more than two
1667 disks if MaxDiskSize is very small */
1669 ASSERT(CreateNewDisk
== false);
1675 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1679 CreateNewDisk
= false;
1681 ASSERT(FileNode
== FileListHead
);
1684 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1686 Status
= WriteDataBlock();
1687 if (Status
!= CAB_STATUS_SUCCESS
)
1690 } while (CreateNewDisk
);
1692 CommitDisk(MoreDisks
);
1694 return CAB_STATUS_SUCCESS
;
1698 unsigned long CCabinet::CommitDisk(unsigned long MoreDisks
)
1700 * FUNCTION: Commits the current disk
1702 * MoreDisks = true if there is one or more disks after this disk
1704 * Status of operation
1707 PCFFOLDER_NODE FolderNode
;
1708 unsigned long Status
;
1710 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1712 /* Create file, fail if it already exists */
1714 FileHandle
= CreateFile(CabinetName
, // Create this file
1715 GENERIC_WRITE
, // Open for writing
1717 NULL
, // No security
1718 CREATE_NEW
, // New file only
1719 FILE_ATTRIBUTE_NORMAL
, // Normal file
1720 NULL
); // No attribute template
1721 if (FileHandle
== INVALID_HANDLE_VALUE
)
1723 unsigned long Status
;
1724 /* If file exists, ask to overwrite file */
1725 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1726 (OnOverwrite(NULL
, CabinetName
)))
1729 /* Create cabinet file, overwrite if it already exists */
1730 FileHandle
= CreateFile(CabinetName
, // Create this file
1731 GENERIC_WRITE
, // Open for writing
1733 NULL
, // No security
1734 TRUNCATE_EXISTING
, // Truncate the file
1735 FILE_ATTRIBUTE_NORMAL
, // Normal file
1736 NULL
); // No attribute template
1737 if (FileHandle
== INVALID_HANDLE_VALUE
)
1738 return CAB_STATUS_CANNOT_CREATE
;
1742 if (Status
== ERROR_FILE_EXISTS
)
1743 return CAB_STATUS_FILE_EXISTS
;
1745 return CAB_STATUS_CANNOT_CREATE
;
1749 FileHandle
= fopen(CabinetName
, "rb");
1750 if (FileHandle
!= NULL
)
1753 /* If file exists, ask to overwrite file */
1754 if (OnOverwrite(NULL
, CabinetName
))
1756 FileHandle
= fopen(CabinetName
, "w+b");
1757 if (FileHandle
== NULL
)
1758 return CAB_STATUS_CANNOT_CREATE
;
1761 return CAB_STATUS_FILE_EXISTS
;
1766 FileHandle
= fopen(CabinetName
, "w+b");
1767 if (FileHandle
== NULL
)
1768 return CAB_STATUS_CANNOT_CREATE
;
1772 WriteCabinetHeader(MoreDisks
!= 0);
1774 Status
= WriteFolderEntries();
1775 if (Status
!= CAB_STATUS_SUCCESS
)
1778 /* Write file entries */
1781 /* Write data blocks */
1782 FolderNode
= FolderListHead
;
1783 while (FolderNode
!= NULL
)
1785 if (FolderNode
->Commit
)
1787 Status
= CommitDataBlocks(FolderNode
);
1788 if (Status
!= CAB_STATUS_SUCCESS
)
1790 /* Remove data blocks for folder */
1791 DestroyDataNodes(FolderNode
);
1793 FolderNode
= FolderNode
->Next
;
1796 CloseFile(FileHandle
);
1798 ScratchFile
->Truncate();
1800 return CAB_STATUS_SUCCESS
;
1804 unsigned long CCabinet::CloseDisk()
1806 * FUNCTION: Closes the current disk
1808 * Status of operation
1811 DestroyDeletedFileNodes();
1813 /* Destroy folder nodes that are completely stored */
1814 DestroyDeletedFolderNodes();
1816 CurrentDiskNumber
++;
1818 return CAB_STATUS_SUCCESS
;
1822 unsigned long CCabinet::CloseCabinet()
1824 * FUNCTION: Closes the current cabinet
1826 * Status of operation
1829 unsigned long Status
;
1833 DestroyFolderNodes();
1837 FreeMemory(InputBuffer
);
1843 FreeMemory(OutputBuffer
);
1844 OutputBuffer
= NULL
;
1851 Status
= ScratchFile
->Destroy();
1856 return CAB_STATUS_SUCCESS
;
1860 unsigned long CCabinet::AddFile(char* FileName
)
1862 * FUNCTION: Adds a file to the current disk
1864 * FileName = Pointer to string with file name (full path)
1866 * Status of operation
1870 PCFFILE_NODE FileNode
;
1873 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1876 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1877 return CAB_STATUS_NOMEMORY
;
1879 strcpy(NewFileName
, FileName
);
1880 ConvertPath(NewFileName
, false);
1882 /* Try to open file */
1884 SrcFile
= CreateFile(
1885 NewFileName
, // Open this file
1886 GENERIC_READ
, // Open for reading
1887 FILE_SHARE_READ
, // Share for reading
1888 NULL
, // No security
1889 OPEN_EXISTING
, // File must exist
1890 FILE_ATTRIBUTE_NORMAL
, // Normal file
1891 NULL
); // No attribute template
1892 if (SrcFile
== INVALID_HANDLE_VALUE
)
1894 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1895 FreeMemory(NewFileName
);
1896 return CAB_STATUS_CANNOT_OPEN
;
1899 SrcFile
= fopen(NewFileName
, "rb");
1900 if (SrcFile
== NULL
)
1902 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1903 FreeMemory(NewFileName
);
1904 return CAB_STATUS_CANNOT_OPEN
;
1908 FileNode
= NewFileNode();
1911 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1912 FreeMemory(NewFileName
);
1913 return CAB_STATUS_NOMEMORY
;
1916 FileNode
->FolderNode
= CurrentFolderNode
;
1917 FileNode
->FileName
= NewFileName
;
1919 /* FIXME: Check for and handle large files (>= 2GB) */
1920 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1921 if (FileNode
->File
.FileSize
== (unsigned long)-1)
1923 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1924 FreeMemory(NewFileName
);
1925 return CAB_STATUS_CANNOT_READ
;
1928 GetFileTimes(SrcFile
, FileNode
);
1930 GetAttributesOnFile(FileNode
);
1934 return CAB_STATUS_SUCCESS
;
1938 void CCabinet::SetMaxDiskSize(unsigned long Size
)
1940 * FUNCTION: Sets the maximum size of the current disk
1942 * Size = Maximum size of current disk (0 means no maximum size)
1948 #endif /* CAB_READ_ONLY */
1951 /* Default event handlers */
1953 bool CCabinet::OnOverwrite(PCFFILE File
,
1956 * FUNCTION: Called when extracting a file and it already exists
1958 * File = Pointer to CFFILE for file being extracted
1959 * FileName = Pointer to buffer with name of file (full path)
1961 * true if the file should be overwritten, false if not
1968 void CCabinet::OnExtract(PCFFILE File
,
1971 * FUNCTION: Called just before extracting a file
1973 * File = Pointer to CFFILE for file being extracted
1974 * FileName = Pointer to buffer with name of file (full path)
1980 void CCabinet::OnDiskChange(char* CabinetName
,
1983 * FUNCTION: Called when a new disk is to be processed
1985 * CabinetName = Pointer to buffer with name of cabinet
1986 * DiskLabel = Pointer to buffer with label of disk
1992 #ifndef CAB_READ_ONLY
1994 void CCabinet::OnAdd(PCFFILE File
,
1997 * FUNCTION: Called just before adding a file to a cabinet
1999 * File = Pointer to CFFILE for file being added
2000 * FileName = Pointer to buffer with name of file (full path)
2006 bool CCabinet::OnDiskLabel(unsigned long Number
, char* Label
)
2008 * FUNCTION: Called when a disk needs a label
2010 * Number = Cabinet number that needs a label
2011 * Label = Pointer to buffer to place label of disk
2013 * true if a disk label was returned, false if not
2020 bool CCabinet::OnCabinetName(unsigned long Number
, char* Name
)
2022 * FUNCTION: Called when a cabinet needs a name
2024 * Number = Disk number that needs a name
2025 * Name = Pointer to buffer to place name of cabinet
2027 * true if a cabinet name was returned, false if not
2033 #endif /* CAB_READ_ONLY */
2035 PCFFOLDER_NODE
CCabinet::LocateFolderNode(unsigned long Index
)
2037 * FUNCTION: Locates a folder node
2039 * Index = Folder index
2041 * Pointer to folder node or NULL if the folder node was not found
2044 PCFFOLDER_NODE Node
;
2048 case CAB_FILE_SPLIT
:
2049 return FolderListTail
;
2051 case CAB_FILE_CONTINUED
:
2052 case CAB_FILE_PREV_NEXT
:
2053 return FolderListHead
;
2056 Node
= FolderListHead
;
2057 while (Node
!= NULL
)
2059 if (Node
->Index
== Index
)
2067 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2069 * FUNCTION: Returns the absolute offset of a file
2071 * File = Pointer to CFFILE_NODE structure for file
2073 * Status of operation
2078 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
2079 (char*)File
->FileName
,
2080 (unsigned int)File
->File
.FileOffset
,
2081 (unsigned int)File
->File
.FileSize
));
2083 Node
= CurrentFolderNode
->DataListHead
;
2084 while (Node
!= NULL
)
2086 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
2087 (unsigned int)Node
->UncompOffset
,
2088 (unsigned int)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
2089 (unsigned int)Node
->Data
.UncompSize
));
2091 /* Node->Data.UncompSize will be 0 if the block is split
2092 (ie. it is the last block in this cabinet) */
2093 if ((Node
->Data
.UncompSize
== 0) ||
2094 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2095 (File
->File
.FileOffset
< Node
->UncompOffset
+
2096 Node
->Data
.UncompSize
)))
2098 File
->DataBlock
= Node
;
2099 return CAB_STATUS_SUCCESS
;
2104 return CAB_STATUS_INVALID_CAB
;
2108 unsigned long CCabinet::LocateFile(char* FileName
,
2111 * FUNCTION: Locates a file in the cabinet
2113 * FileName = Pointer to string with name of file to locate
2114 * File = Address of pointer to CFFILE_NODE structure to fill
2116 * Status of operation
2118 * Current folder is set to the folder of the file
2122 unsigned long Status
;
2124 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2126 Node
= FileListHead
;
2127 while (Node
!= NULL
)
2129 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2131 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2132 if (!CurrentFolderNode
)
2134 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
2135 (unsigned int)Node
->File
.FileControlID
));
2136 return CAB_STATUS_INVALID_CAB
;
2139 if (Node
->DataBlock
== NULL
)
2140 Status
= GetAbsoluteOffset(Node
);
2142 Status
= CAB_STATUS_SUCCESS
;
2149 return CAB_STATUS_NOFILE
;
2153 unsigned long CCabinet::ReadString(char* String
, unsigned long MaxLength
)
2155 * FUNCTION: Reads a NULL-terminated string from the cabinet
2157 * String = Pointer to buffer to place string
2158 * MaxLength = Maximum length of string
2160 * Status of operation
2163 unsigned long BytesRead
;
2164 unsigned long Offset
;
2165 unsigned long Status
;
2173 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2177 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2178 return CAB_STATUS_INVALID_CAB
;
2181 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2182 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
))
2184 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2185 return CAB_STATUS_INVALID_CAB
;
2188 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++)
2190 if (String
[Size
] == '\0')
2197 Offset
+= BytesRead
;
2201 /* Back up some bytes */
2202 Size
= (BytesRead
- Size
) - 1;
2204 SetLastError(NO_ERROR
);
2205 (unsigned int)SetFilePointer(FileHandle
,
2209 if (GetLastError() != NO_ERROR
)
2211 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2212 return CAB_STATUS_INVALID_CAB
;
2215 if (fseek(FileHandle
, (off_t
)(-(long)Size
), SEEK_CUR
) != 0)
2217 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2218 return CAB_STATUS_INVALID_CAB
;
2221 return CAB_STATUS_SUCCESS
;
2225 unsigned long CCabinet::ReadFileTable()
2227 * FUNCTION: Reads the file table from the cabinet file
2229 * Status of operation
2233 unsigned long Status
;
2234 unsigned long BytesRead
;
2237 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2238 CABHeader
.FileTableOffset
));
2240 /* Seek to file table */
2242 SetLastError(NO_ERROR
);
2243 (unsigned int)SetFilePointer(FileHandle
,
2244 CABHeader
.FileTableOffset
,
2247 if (GetLastError() != NO_ERROR
)
2249 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2250 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2251 return CAB_STATUS_INVALID_CAB
;
2254 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2256 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2257 return CAB_STATUS_INVALID_CAB
;
2261 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2263 File
= NewFileNode();
2266 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2267 return CAB_STATUS_NOMEMORY
;
2270 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2271 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2273 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2274 return CAB_STATUS_INVALID_CAB
;
2277 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2278 if (!File
->FileName
)
2280 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2281 return CAB_STATUS_NOMEMORY
;
2284 /* Read file name */
2285 Status
= ReadString(File
->FileName
, MAX_PATH
);
2286 if (Status
!= CAB_STATUS_SUCCESS
)
2289 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2290 (char*)File
->FileName
,
2291 (unsigned int)File
->File
.FileOffset
,
2292 (unsigned int)File
->File
.FileSize
,
2293 (unsigned int)File
->File
.FileControlID
));
2296 return CAB_STATUS_SUCCESS
;
2300 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2302 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2304 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2306 * Status of operation
2309 unsigned long AbsoluteOffset
;
2310 unsigned long UncompOffset
;
2312 unsigned long BytesRead
;
2313 unsigned long Status
;
2316 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2317 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2319 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2320 UncompOffset
= FolderNode
->UncompOffset
;
2322 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2324 Node
= NewDataNode(FolderNode
);
2327 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2328 return CAB_STATUS_NOMEMORY
;
2331 /* Seek to data block */
2333 SetLastError(NO_ERROR
);
2334 SetFilePointer(FileHandle
,
2338 if (GetLastError() != NO_ERROR
)
2340 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2341 return CAB_STATUS_INVALID_CAB
;
2344 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2346 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2347 return CAB_STATUS_INVALID_CAB
;
2351 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2352 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2354 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2355 return CAB_STATUS_INVALID_CAB
;
2358 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2359 (unsigned int)AbsoluteOffset
,
2360 (unsigned int)UncompOffset
,
2361 (unsigned int)Node
->Data
.Checksum
,
2362 (unsigned int)Node
->Data
.CompSize
,
2363 (unsigned int)Node
->Data
.UncompSize
));
2365 Node
->AbsoluteOffset
= AbsoluteOffset
;
2366 Node
->UncompOffset
= UncompOffset
;
2368 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2369 UncompOffset
+= Node
->Data
.UncompSize
;
2372 FolderUncompSize
= UncompOffset
;
2374 return CAB_STATUS_SUCCESS
;
2378 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2380 * FUNCTION: Creates a new folder node
2382 * Pointer to node if there was enough free memory available, otherwise NULL
2385 PCFFOLDER_NODE Node
;
2387 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2391 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2393 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2395 Node
->Prev
= FolderListTail
;
2397 if (FolderListTail
!= NULL
)
2398 FolderListTail
->Next
= Node
;
2400 FolderListHead
= Node
;
2402 FolderListTail
= Node
;
2408 PCFFILE_NODE
CCabinet::NewFileNode()
2410 * FUNCTION: Creates a new file node
2412 * FolderNode = Pointer to folder node to bind file to
2414 * Pointer to node if there was enough free memory available, otherwise NULL
2419 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2423 memset(Node
, 0, sizeof(CFFILE_NODE
));
2425 Node
->Prev
= FileListTail
;
2427 if (FileListTail
!= NULL
)
2428 FileListTail
->Next
= Node
;
2430 FileListHead
= Node
;
2432 FileListTail
= Node
;
2438 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2440 * FUNCTION: Creates a new data block node
2442 * FolderNode = Pointer to folder node to bind data block to
2444 * Pointer to node if there was enough free memory available, otherwise NULL
2449 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2453 memset(Node
, 0, sizeof(CFDATA_NODE
));
2455 Node
->Prev
= FolderNode
->DataListTail
;
2457 if (FolderNode
->DataListTail
!= NULL
)
2458 FolderNode
->DataListTail
->Next
= Node
;
2460 FolderNode
->DataListHead
= Node
;
2462 FolderNode
->DataListTail
= Node
;
2468 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2470 * FUNCTION: Destroys data block nodes bound to a folder node
2472 * FolderNode = Pointer to folder node
2475 PCFDATA_NODE PrevNode
;
2476 PCFDATA_NODE NextNode
;
2478 NextNode
= FolderNode
->DataListHead
;
2479 while (NextNode
!= NULL
)
2481 PrevNode
= NextNode
->Next
;
2482 FreeMemory(NextNode
);
2483 NextNode
= PrevNode
;
2485 FolderNode
->DataListHead
= NULL
;
2486 FolderNode
->DataListTail
= NULL
;
2490 void CCabinet::DestroyFileNodes()
2492 * FUNCTION: Destroys file nodes
2494 * FolderNode = Pointer to folder node
2497 PCFFILE_NODE PrevNode
;
2498 PCFFILE_NODE NextNode
;
2500 NextNode
= FileListHead
;
2501 while (NextNode
!= NULL
)
2503 PrevNode
= NextNode
->Next
;
2504 if (NextNode
->FileName
)
2505 FreeMemory(NextNode
->FileName
);
2506 FreeMemory(NextNode
);
2507 NextNode
= PrevNode
;
2509 FileListHead
= NULL
;
2510 FileListTail
= NULL
;
2514 void CCabinet::DestroyDeletedFileNodes()
2516 * FUNCTION: Destroys file nodes that are marked for deletion
2519 PCFFILE_NODE CurNode
;
2520 PCFFILE_NODE NextNode
;
2522 CurNode
= FileListHead
;
2523 while (CurNode
!= NULL
)
2525 NextNode
= CurNode
->Next
;
2527 if (CurNode
->Delete
)
2529 if (CurNode
->Prev
!= NULL
)
2530 CurNode
->Prev
->Next
= CurNode
->Next
;
2533 FileListHead
= CurNode
->Next
;
2535 FileListHead
->Prev
= NULL
;
2538 if (CurNode
->Next
!= NULL
)
2539 CurNode
->Next
->Prev
= CurNode
->Prev
;
2542 FileListTail
= CurNode
->Prev
;
2544 FileListTail
->Next
= NULL
;
2547 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2549 TotalFileSize
-= (sizeof(CFFILE
) + strlen(GetFileName(CurNode
->FileName
)) + 1);
2551 if (CurNode
->FileName
)
2552 FreeMemory(CurNode
->FileName
);
2553 FreeMemory(CurNode
);
2560 void CCabinet::DestroyFolderNodes()
2562 * FUNCTION: Destroys folder nodes
2565 PCFFOLDER_NODE PrevNode
;
2566 PCFFOLDER_NODE NextNode
;
2568 NextNode
= FolderListHead
;
2569 while (NextNode
!= NULL
)
2571 PrevNode
= NextNode
->Next
;
2572 DestroyDataNodes(NextNode
);
2573 FreeMemory(NextNode
);
2574 NextNode
= PrevNode
;
2576 FolderListHead
= NULL
;
2577 FolderListTail
= NULL
;
2581 void CCabinet::DestroyDeletedFolderNodes()
2583 * FUNCTION: Destroys folder nodes that are marked for deletion
2586 PCFFOLDER_NODE CurNode
;
2587 PCFFOLDER_NODE NextNode
;
2589 CurNode
= FolderListHead
;
2590 while (CurNode
!= NULL
)
2592 NextNode
= CurNode
->Next
;
2594 if (CurNode
->Delete
)
2596 if (CurNode
->Prev
!= NULL
)
2597 CurNode
->Prev
->Next
= CurNode
->Next
;
2600 FolderListHead
= CurNode
->Next
;
2602 FolderListHead
->Prev
= NULL
;
2605 if (CurNode
->Next
!= NULL
)
2606 CurNode
->Next
->Prev
= CurNode
->Prev
;
2609 FolderListTail
= CurNode
->Prev
;
2611 FolderListTail
->Next
= NULL
;
2614 DestroyDataNodes(CurNode
);
2615 FreeMemory(CurNode
);
2617 TotalFolderSize
-= sizeof(CFFOLDER
);
2624 unsigned long CCabinet::ComputeChecksum(void* Buffer
,
2628 * FUNCTION: Computes checksum for data block
2630 * Buffer = Pointer to data buffer
2631 * Size = Length of data buffer
2632 * Seed = Previously computed checksum
2634 * Checksum of buffer
2637 int UlongCount
; // Number of ULONGs in block
2638 unsigned long Checksum
; // Checksum accumulator
2642 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2643 won't accept checksums computed by this routine */
2645 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%d)\n", Buffer
, Size
));
2647 UlongCount
= Size
/ 4; // Number of ULONGs
2648 Checksum
= Seed
; // Init checksum
2649 pb
= (unsigned char*)Buffer
; // Start at front of data block
2651 /* Checksum integral multiple of ULONGs */
2652 while (UlongCount
-- > 0)
2654 /* NOTE: Build unsigned long in big/little-endian independent manner */
2655 ul
= *pb
++; // Get low-order byte
2656 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2657 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3nd byte
2658 ul
|= (((unsigned long)(*pb
++)) << 24); // Add 4th byte
2660 Checksum
^= ul
; // Update checksum
2663 /* Checksum remainder bytes */
2668 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3rd byte
2670 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2672 ul
|= *pb
++; // Get low-order byte
2676 Checksum
^= ul
; // Update checksum
2678 /* Return computed checksum */
2683 unsigned long CCabinet::ReadBlock(void* Buffer
,
2685 unsigned long* BytesRead
)
2687 * FUNCTION: Read a block of data from file
2689 * Buffer = Pointer to data buffer
2690 * Size = Length of data buffer
2691 * BytesRead = Pointer to unsigned long that on return will contain
2692 * number of bytes read
2694 * Status of operation
2697 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2698 return CAB_STATUS_INVALID_CAB
;
2699 return CAB_STATUS_SUCCESS
;
2702 #ifndef CAB_READ_ONLY
2704 unsigned long CCabinet::InitCabinetHeader()
2706 * FUNCTION: Initializes cabinet header and optional fields
2708 * Status of operation
2711 unsigned long TotalSize
;
2714 CABHeader
.FileTableOffset
= 0; // Not known yet
2715 CABHeader
.FolderCount
= 0; // Not known yet
2716 CABHeader
.FileCount
= 0; // Not known yet
2717 CABHeader
.Flags
= 0; // Not known yet
2719 CABHeader
.CabinetNumber
= (unsigned short)CurrentDiskNumber
;
2721 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
2723 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2724 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2725 strcpy(CabinetPrev
, "");
2728 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
2730 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2731 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2732 strcpy(DiskNext
, "");
2737 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
2740 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2742 /* Calculate size of name of previous cabinet */
2743 TotalSize
+= strlen(CabinetPrev
) + 1;
2745 /* Calculate size of label of previous disk */
2746 TotalSize
+= strlen(DiskPrev
) + 1;
2749 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
2752 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2754 /* Calculate size of name of next cabinet */
2755 Size
= strlen(CabinetNext
) + 1;
2757 NextFieldsSize
= Size
;
2759 /* Calculate size of label of next disk */
2760 Size
= strlen(DiskNext
) + 1;
2762 NextFieldsSize
+= Size
;
2767 /* Add cabinet reserved area size if present */
2768 if (CabinetReservedFileSize
> 0)
2770 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2771 TotalSize
+= CabinetReservedFileSize
;
2772 TotalSize
+= sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2775 DiskSize
+= TotalSize
;
2777 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2779 return CAB_STATUS_SUCCESS
;
2783 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks
)
2785 * FUNCTION: Writes the cabinet header and optional fields
2787 * MoreDisks = true if next cabinet name should be included
2789 * Status of operation
2792 PCFFOLDER_NODE FolderNode
;
2793 PCFFILE_NODE FileNode
;
2794 unsigned long BytesWritten
;
2799 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2800 Size
= TotalHeaderSize
;
2804 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2805 DiskSize
-= NextFieldsSize
;
2806 Size
= TotalHeaderSize
- NextFieldsSize
;
2809 /* Set absolute folder offsets */
2810 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2811 CABHeader
.FolderCount
= 0;
2812 FolderNode
= FolderListHead
;
2813 while (FolderNode
!= NULL
)
2815 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2817 BytesWritten
+= FolderNode
->TotalFolderSize
;
2819 CABHeader
.FolderCount
++;
2821 FolderNode
= FolderNode
->Next
;
2824 /* Set absolute offset of file table */
2825 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2827 /* Count number of files to be committed */
2828 CABHeader
.FileCount
= 0;
2829 FileNode
= FileListHead
;
2830 while (FileNode
!= NULL
)
2832 if (FileNode
->Commit
)
2833 CABHeader
.FileCount
++;
2834 FileNode
= FileNode
->Next
;
2837 CABHeader
.CabinetSize
= DiskSize
;
2841 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
))
2843 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2844 return CAB_STATUS_CANNOT_WRITE
;
2847 BytesWritten
= sizeof(CFHEADER
);
2848 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
2850 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2851 return CAB_STATUS_CANNOT_WRITE
;
2855 /* Write per-cabinet reserved area if present */
2856 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
2858 unsigned long ReservedSize
;
2860 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2861 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2862 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2864 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(unsigned long), &BytesWritten
, NULL
))
2866 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2867 return CAB_STATUS_CANNOT_WRITE
;
2870 BytesWritten
= sizeof(unsigned long);
2871 if (fwrite(&ReservedSize
, sizeof(unsigned long), 1, FileHandle
) < 1)
2873 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2874 return CAB_STATUS_CANNOT_WRITE
;
2879 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesWritten
, NULL
))
2881 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2882 return CAB_STATUS_CANNOT_WRITE
;
2885 BytesWritten
= CabinetReservedFileSize
;
2886 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
2888 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2889 return CAB_STATUS_CANNOT_WRITE
;
2894 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
2896 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2898 /* Write name of previous cabinet */
2899 Size
= strlen(CabinetPrev
) + 1;
2901 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
))
2903 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2904 return CAB_STATUS_CANNOT_WRITE
;
2907 BytesWritten
= Size
;
2908 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
2910 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2911 return CAB_STATUS_CANNOT_WRITE
;
2915 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2917 /* Write label of previous disk */
2918 Size
= strlen(DiskPrev
) + 1;
2920 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
))
2922 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2923 return CAB_STATUS_CANNOT_WRITE
;
2926 BytesWritten
= Size
;
2927 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1)
2929 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2930 return CAB_STATUS_CANNOT_WRITE
;
2935 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
2937 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2939 /* Write name of next cabinet */
2940 Size
= strlen(CabinetNext
) + 1;
2942 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
))
2944 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2945 return CAB_STATUS_CANNOT_WRITE
;
2948 BytesWritten
= Size
;
2949 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
2951 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2952 return CAB_STATUS_CANNOT_WRITE
;
2956 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2958 /* Write label of next disk */
2959 Size
= strlen(DiskNext
) + 1;
2961 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
))
2963 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2964 return CAB_STATUS_CANNOT_WRITE
;
2967 BytesWritten
= Size
;
2968 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1)
2970 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2971 return CAB_STATUS_CANNOT_WRITE
;
2976 return CAB_STATUS_SUCCESS
;
2980 unsigned long CCabinet::WriteFolderEntries()
2982 * FUNCTION: Writes folder entries
2984 * Status of operation
2987 PCFFOLDER_NODE FolderNode
;
2988 unsigned long BytesWritten
;
2990 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2992 FolderNode
= FolderListHead
;
2993 while (FolderNode
!= NULL
)
2995 if (FolderNode
->Commit
)
2997 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2998 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
3001 if (!WriteFile(FileHandle
,
3002 &FolderNode
->Folder
,
3007 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3008 return CAB_STATUS_CANNOT_WRITE
;
3011 BytesWritten
= sizeof(CFFOLDER
);
3012 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3014 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3015 return CAB_STATUS_CANNOT_WRITE
;
3019 FolderNode
= FolderNode
->Next
;
3022 return CAB_STATUS_SUCCESS
;
3026 unsigned long CCabinet::WriteFileEntries()
3028 * FUNCTION: Writes file entries for all files
3030 * Status of operation
3034 unsigned long BytesWritten
;
3035 bool SetCont
= false;
3037 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3039 File
= FileListHead
;
3040 while (File
!= NULL
)
3044 /* Remove any continued files that ends in this disk */
3045 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3046 File
->Delete
= true;
3048 /* The file could end in the last (split) block and should therefore
3049 appear in the next disk too */
3051 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3052 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3054 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3055 File
->Delete
= false;
3059 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
3060 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
3063 if (!WriteFile(FileHandle
,
3068 return CAB_STATUS_CANNOT_WRITE
;
3070 BytesWritten
= sizeof(CFFILE
);
3071 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3073 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3074 return CAB_STATUS_CANNOT_WRITE
;
3079 if (!WriteFile(FileHandle
,
3080 GetFileName(File
->FileName
),
3081 strlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
3082 return CAB_STATUS_CANNOT_WRITE
;
3084 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3085 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3087 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3088 return CAB_STATUS_CANNOT_WRITE
;
3094 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3101 return CAB_STATUS_SUCCESS
;
3105 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3107 * FUNCTION: Writes data blocks to the cabinet
3109 * FolderNode = Pointer to folder node containing the data blocks
3111 * Status of operation
3114 PCFDATA_NODE DataNode
;
3115 unsigned long BytesWritten
;
3116 unsigned long BytesRead
;
3117 unsigned long Status
;
3119 DataNode
= FolderNode
->DataListHead
;
3120 if (DataNode
!= NULL
)
3121 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3123 while (DataNode
!= NULL
)
3125 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
3126 DataNode
->ScratchFilePosition
,
3127 DataNode
->Data
.CompSize
,
3128 DataNode
->Data
.UncompSize
));
3130 /* InputBuffer is free for us to use here, so we use it and avoid a
3131 memory allocation. OutputBuffer can't be used here because it may
3132 still contain valid data (if a data block spans two or more disks) */
3133 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3134 if (Status
!= CAB_STATUS_SUCCESS
)
3136 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (unsigned int)Status
));
3141 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3142 sizeof(CFDATA
), &BytesWritten
, NULL
))
3144 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3145 return CAB_STATUS_CANNOT_WRITE
;
3148 BytesWritten
= sizeof(CFDATA
);
3149 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3151 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3152 return CAB_STATUS_CANNOT_WRITE
;
3157 if (!WriteFile(FileHandle
, InputBuffer
,
3158 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
))
3160 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3161 return CAB_STATUS_CANNOT_WRITE
;
3164 BytesWritten
= DataNode
->Data
.CompSize
;
3165 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3167 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3168 return CAB_STATUS_CANNOT_WRITE
;
3172 DataNode
= DataNode
->Next
;
3174 return CAB_STATUS_SUCCESS
;
3178 unsigned long CCabinet::WriteDataBlock()
3180 * FUNCTION: Writes the current data block to the scratch file
3182 * Status of operation
3185 unsigned long Status
;
3186 unsigned long BytesWritten
;
3187 PCFDATA_NODE DataNode
;
3191 Status
= Codec
->Compress(OutputBuffer
,
3196 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
3197 CurrentIBufferSize
, TotalCompSize
));
3199 CurrentOBuffer
= OutputBuffer
;
3200 CurrentOBufferSize
= TotalCompSize
;
3203 DataNode
= NewDataNode(CurrentFolderNode
);
3206 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3207 return CAB_STATUS_NOMEMORY
;
3210 DiskSize
+= sizeof(CFDATA
);
3212 if (MaxDiskSize
> 0)
3213 /* Disk size is limited */
3214 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3216 BlockIsSplit
= false;
3220 DataNode
->Data
.CompSize
= (unsigned short)(MaxDiskSize
- DiskSize
);
3221 DataNode
->Data
.UncompSize
= 0;
3222 CreateNewDisk
= true;
3226 DataNode
->Data
.CompSize
= (unsigned short)CurrentOBufferSize
;
3227 DataNode
->Data
.UncompSize
= (unsigned short)CurrentIBufferSize
;
3230 DataNode
->Data
.Checksum
= 0;
3231 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3233 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3234 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3236 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3237 (unsigned int)DataNode
->Data
.Checksum
,
3238 (unsigned int)DataNode
->Data
.CompSize
,
3239 (unsigned int)DataNode
->Data
.UncompSize
));
3241 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3242 CurrentOBuffer
, &BytesWritten
);
3243 if (Status
!= CAB_STATUS_SUCCESS
)
3246 DiskSize
+= BytesWritten
;
3248 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3249 CurrentFolderNode
->Folder
.DataBlockCount
++;
3251 *(unsigned char**)&CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3252 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3254 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3258 CurrentIBufferSize
= 0;
3259 CurrentIBuffer
= InputBuffer
;
3262 return CAB_STATUS_SUCCESS
;
3268 void CCabinet::ConvertDateAndTime(time_t* Time
,
3269 unsigned short* DosDate
,
3270 unsigned short* DosTime
)
3272 * FUNCTION: Returns file times of a file
3274 * FileHandle = File handle of file to get file times from
3275 * File = Pointer to CFFILE node for file
3277 * Status of operation
3282 timedef
= localtime(Time
);
3284 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3285 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3286 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3288 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3289 | ((timedef
->tm_mon
+ 1) << 5)
3290 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3292 *DosTime
= (timedef
->tm_sec
<< 0)
3293 | (timedef
->tm_min
<< 5)
3294 | (timedef
->tm_hour
<< 11);
3300 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3302 * FUNCTION: Returns file times of a file
3304 * FileHandle = File handle of file to get file times from
3305 * File = Pointer to CFFILE node for file
3307 * Status of operation
3313 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3314 FileTimeToDosDateTime(&FileTime
,
3315 &File
->File
.FileDate
,
3316 &File
->File
.FileTime
);
3321 // Check for an absolute path
3322 if (IsSeparator(File
->FileName
[0]))
3323 strcpy(buf
, File
->FileName
);
3326 getcwd(buf
, sizeof(buf
));
3327 strcat(buf
, DIR_SEPARATOR_STRING
);
3328 strcat(buf
, File
->FileName
);
3331 if (stat(buf
, &stbuf
) == -1)
3332 return CAB_STATUS_CANNOT_READ
;
3334 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3336 return CAB_STATUS_SUCCESS
;
3340 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3342 * FUNCTION: Returns attributes on a file
3344 * File = Pointer to CFFILE node for file
3346 * Status of operation
3352 Attributes
= GetFileAttributes(File
->FileName
);
3353 if (Attributes
== -1)
3354 return CAB_STATUS_CANNOT_READ
;
3356 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3357 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3359 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3360 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3362 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3363 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3365 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3366 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3368 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3369 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3374 // Check for an absolute path
3375 if (IsSeparator(File
->FileName
[0]))
3376 strcpy(buf
, File
->FileName
);
3379 getcwd(buf
, sizeof(buf
));
3380 strcat(buf
, DIR_SEPARATOR_STRING
);
3381 strcat(buf
, File
->FileName
);
3384 if (stat(buf
, &stbuf
) == -1)
3385 return CAB_STATUS_CANNOT_READ
;
3388 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3389 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3390 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3393 if (stbuf
.st_mode
& S_IFDIR
)
3394 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3396 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3399 return CAB_STATUS_SUCCESS
;
3403 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3405 * FUNCTION: Sets attributes on a file
3407 * File = Pointer to CFFILE node for file
3409 * Status of operation
3413 unsigned long Attributes
= 0;
3415 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3416 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3418 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3419 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3421 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3422 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3424 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3425 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3427 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3428 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3430 SetFileAttributes(File
->FileName
, Attributes
);
3432 return CAB_STATUS_SUCCESS
;
3434 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3435 return CAB_STATUS_SUCCESS
;
3439 #endif /* CAB_READ_ONLY */