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
) != 0;
40 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
41 static long _GetSizeOfFile(FILEHANDLE handle
)
44 fseek(handle
, 0, SEEK_END
);
46 fseek(handle
, 0, SEEK_SET
);
49 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
50 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, unsigned long size
, unsigned long* bytesread
)
52 *bytesread
= fread(buffer
, 1, size
, handle
);
53 return *bytesread
== size
;
62 void DumpBuffer(void* Buffer
, unsigned long Size
)
64 FILEHANDLE FileHandle
;
65 unsigned long BytesWritten
;
67 /* Create file, overwrite if it already exists */
68 FileHandle
= CreateFile("dump.bin", // Create this file
69 GENERIC_WRITE
, // Open for writing
72 CREATE_ALWAYS
, // Create or overwrite
73 FILE_ATTRIBUTE_NORMAL
, // Normal file
74 NULL
); // No attribute template
75 if (FileHandle
== INVALID_HANDLE_VALUE
) {
76 DPRINT(MID_TRACE
, ("ERROR OPENING '%d'.\n", (unsigned int)GetLastError()));
80 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
)) {
81 DPRINT(MID_TRACE
, ("ERROR WRITING '%d'.\n", (unsigned int)GetLastError()));
84 CloseFile(FileHandle
);
92 CCFDATAStorage::CCFDATAStorage()
94 * FUNCTION: Default constructor
101 CCFDATAStorage::~CCFDATAStorage()
103 * FUNCTION: Default destructor
106 ASSERT(!FileCreated
);
110 unsigned long CCFDATAStorage::Create(char* FileName
)
112 * FUNCTION: Creates the file
114 * FileName = Pointer to name of file
116 * Status of operation
119 ASSERT(!FileCreated
);
122 if (GetTempPath(MAX_PATH
, FullName
) == 0)
123 return CAB_STATUS_CANNOT_CREATE
;
125 strcat(FullName
, FileName
);
127 /* Create file, overwrite if it already exists */
128 FileHandle
= CreateFile(FullName
, // Create this file
129 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
132 CREATE_ALWAYS
, // Create or overwrite
133 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
134 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
135 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
136 NULL
); // No attribute template
137 if (FileHandle
== INVALID_HANDLE_VALUE
) {
138 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)GetLastError()));
139 return CAB_STATUS_CANNOT_CREATE
;
142 /*if (tmpnam(FullName) == NULL)*/
143 if ((FileHandle
= tmpfile()) == NULL
)
144 return CAB_STATUS_CANNOT_CREATE
;
146 FileHandle = fopen(FullName, "w+b");
147 if (FileHandle == NULL) {
148 DPRINT(MID_TRACE, ("ERROR '%d'.\n", (unsigned int)errno));
149 return CAB_STATUS_CANNOT_CREATE;
156 return CAB_STATUS_SUCCESS
;
160 unsigned long CCFDATAStorage::Destroy()
162 * FUNCTION: Destroys the file
164 * Status of operation
169 CloseFile(FileHandle
);
173 return CAB_STATUS_SUCCESS
;
177 unsigned long CCFDATAStorage::Truncate()
179 * FUNCTION: Truncate the scratch file to zero bytes
181 * Status of operation
185 if (!SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
))
186 return CAB_STATUS_FAILURE
;
187 if (!SetEndOfFile(FileHandle
))
188 return CAB_STATUS_FAILURE
;
191 FileHandle
= tmpfile();
192 if (FileHandle
== NULL
) {
193 DPRINT(MID_TRACE
, ("ERROR '%d'.\n", (unsigned int)errno
));
194 return CAB_STATUS_FAILURE
;
197 return CAB_STATUS_SUCCESS
;
201 unsigned long CCFDATAStorage::Position()
203 * FUNCTION: Returns current position in file
209 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
211 return (unsigned long)ftell(FileHandle
);
216 unsigned long CCFDATAStorage::Seek(long Position
)
218 * FUNCTION: Seeks to an absolute position
220 * Position = Absolute position to seek to
222 * Status of operation
226 if (SetFilePointer(FileHandle
,
229 FILE_BEGIN
) == 0xFFFFFFFF)
230 return CAB_STATUS_FAILURE
;
232 return CAB_STATUS_SUCCESS
;
234 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0) {
235 return CAB_STATUS_FAILURE
;
237 return CAB_STATUS_SUCCESS
;
243 unsigned long CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesRead
)
245 * FUNCTION: Reads a CFDATA block from the file
247 * Data = Pointer to CFDATA block for the buffer
248 * Buffer = Pointer to buffer to store data read
249 * BytesWritten = Pointer to buffer to write number of bytes read
251 * Status of operation
255 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, BytesRead
, NULL
))
256 return CAB_STATUS_CANNOT_READ
;
259 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
260 if (*BytesRead
!= Data
->CompSize
) {
261 return CAB_STATUS_CANNOT_READ
;
264 return CAB_STATUS_SUCCESS
;
268 unsigned long CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, unsigned long* BytesWritten
)
270 * FUNCTION: Writes a CFDATA block to the file
272 * Data = Pointer to CFDATA block for the buffer
273 * Buffer = Pointer to buffer with data to write
274 * BytesWritten = Pointer to buffer to write number of bytes written
276 * Status of operation
280 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, BytesWritten
, NULL
))
281 return CAB_STATUS_CANNOT_WRITE
;
283 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
284 if (*BytesWritten
!= Data
->CompSize
)
285 return CAB_STATUS_CANNOT_WRITE
;
287 return CAB_STATUS_SUCCESS
;
290 #endif /* CAB_READ_ONLY */
297 * FUNCTION: Default constructor
306 *CabinetReservedFile
= '\0';
309 CabinetReservedFileBuffer
= NULL
;
310 CabinetReservedFileSize
= 0;
312 FolderListHead
= NULL
;
313 FolderListTail
= NULL
;
317 Codec
= new CRawCodec();
318 CodecId
= CAB_CODEC_RAW
;
319 CodecSelected
= true;
324 BlockIsSplit
= false;
327 FolderUncompSize
= 0;
328 BytesLeftInBlock
= 0;
330 CurrentDataNode
= NULL
;
334 CCabinet::~CCabinet()
336 * FUNCTION: Default destructor
339 if (CabinetReservedFileBuffer
!= NULL
) {
340 FreeMemory(CabinetReservedFileBuffer
);
341 CabinetReservedFileBuffer
= NULL
;
342 CabinetReservedFileSize
= 0;
349 bool CCabinet::IsSeparator(char Char
)
351 * FUNCTION: Determines if a character is a separator
353 * Char = Character to check
355 * Wether it is a separator
358 if ((Char
== '\\') || (Char
== '/')) {
365 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
367 * FUNCTION: Replaces \ or / with the one used be the host environment
369 * Path = Pointer to string with pathname
370 * Allocate = Specifies wther to allocate memory for the new
371 * string or to change the existing buffer
373 * Pointer to new path
380 newpath
= strdup(Path
);
386 while (Path
[i
] != 0) {
388 if (Path
[i
] == '/') {
392 if (Path
[i
] == '\\') {
396 newpath
[i
] = Path
[i
];
406 char* CCabinet::GetFileName(char* Path
)
408 * FUNCTION: Returns a pointer to file name
410 * Path = Pointer to string with pathname
412 * Pointer to filename
417 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
420 if (IsSeparator(Path
[i
- 1])) j
= i
;
426 void CCabinet::RemoveFileName(char* Path
)
428 * FUNCTION: Removes a file name from a path
430 * Path = Pointer to string with path
436 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
437 FileName
= GetFileName(Path
+ i
);
439 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
441 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
447 bool CCabinet::NormalizePath(char* Path
,
448 unsigned long Length
)
450 * FUNCTION: Normalizes a path
452 * Path = Pointer to string with pathname
453 * Length = Number of bytes in Path
455 * true if there was enough room in Path, or false
461 if ((n
= strlen(Path
)) &&
462 (!IsSeparator(Path
[n
- 1])) &&
463 (OK
= ((n
+ 1) < Length
))) {
464 Path
[n
] = DIR_SEPARATOR_CHAR
;
471 char* CCabinet::GetCabinetName()
473 * FUNCTION: Returns pointer to cabinet file name
475 * Pointer to string with name of cabinet
482 void CCabinet::SetCabinetName(char* FileName
)
484 * FUNCTION: Sets cabinet file name
486 * FileName = Pointer to string with name of cabinet
489 strcpy(CabinetName
, FileName
);
493 void CCabinet::SetDestinationPath(char* DestinationPath
)
495 * FUNCTION: Sets destination path
497 * DestinationPath = Pointer to string with name of destination path
500 strcpy(DestPath
, DestinationPath
);
501 ConvertPath(DestPath
, false);
502 if (strlen(DestPath
) > 0)
503 NormalizePath(DestPath
, MAX_PATH
);
507 char* CCabinet::GetDestinationPath()
509 * FUNCTION: Returns destination path
511 * Pointer to string with name of destination path
518 bool CCabinet::SetCabinetReservedFile(char* FileName
)
520 * FUNCTION: Sets cabinet reserved file
522 * FileName = Pointer to string with name of cabinet reserved file
525 FILEHANDLE FileHandle
;
526 unsigned long BytesRead
;
529 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
530 GENERIC_READ
, // Open for reading
531 FILE_SHARE_READ
, // Share for reading
533 OPEN_EXISTING
, // Existing file only
534 FILE_ATTRIBUTE_NORMAL
, // Normal file
535 NULL
); // No attribute template
536 if (FileHandle
== INVALID_HANDLE_VALUE
) {
537 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
541 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
542 if (FileHandle
== NULL
) {
543 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
548 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
549 if (CabinetReservedFileSize
== (unsigned long)-1) {
550 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
554 if (CabinetReservedFileSize
== 0)
556 CloseFile(FileHandle
);
560 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
561 if (!CabinetReservedFileBuffer
) {
562 CloseFile(FileHandle
);
566 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
)) {
567 CloseFile(FileHandle
);
571 CloseFile(FileHandle
);
573 strcpy(CabinetReservedFile
, FileName
);
579 char* CCabinet::GetCabinetReservedFile()
581 * FUNCTION: Returns cabionet reserved file
583 * Pointer to string with name of cabinet reserved file
586 return CabinetReservedFile
;
590 unsigned long CCabinet::GetCurrentDiskNumber()
592 * FUNCTION: Returns current disk number
594 * Current disk number
597 return CurrentDiskNumber
;
601 unsigned long CCabinet::Open()
603 * FUNCTION: Opens a cabinet file
605 * Status of operation
608 PCFFOLDER_NODE FolderNode
;
609 unsigned long Status
;
613 unsigned long BytesRead
;
616 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
618 return CAB_STATUS_NOMEMORY
;
621 FileHandle
= CreateFile(CabinetName
, // Open this file
622 GENERIC_READ
, // Open for reading
623 FILE_SHARE_READ
, // Share for reading
625 OPEN_EXISTING
, // Existing file only
626 FILE_ATTRIBUTE_NORMAL
, // Normal file
627 NULL
); // No attribute template
629 if (FileHandle
== INVALID_HANDLE_VALUE
) {
630 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
631 return CAB_STATUS_CANNOT_OPEN
;
634 FileHandle
= fopen(CabinetName
, "rb");
635 if (FileHandle
== NULL
) {
636 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
637 return CAB_STATUS_CANNOT_OPEN
;
643 /* Load CAB header */
644 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
645 != CAB_STATUS_SUCCESS
) {
646 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
647 return CAB_STATUS_INVALID_CAB
;
651 if ((BytesRead
!= sizeof(CFHEADER
)) ||
652 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
653 (CABHeader
.Version
!= CAB_VERSION
) ||
654 (CABHeader
.FolderCount
== 0 ) ||
655 (CABHeader
.FileCount
== 0 ) ||
656 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
658 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
659 return CAB_STATUS_INVALID_CAB
;
664 /* Read/skip any reserved bytes */
665 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
666 if ((Status
= ReadBlock(&Size
, sizeof(unsigned long), &BytesRead
))
667 != CAB_STATUS_SUCCESS
) {
668 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
669 return CAB_STATUS_INVALID_CAB
;
671 CabinetReserved
= Size
& 0xFFFF;
672 FolderReserved
= (Size
>> 16) & 0xFF;
673 DataReserved
= (Size
>> 24) & 0xFF;
676 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
677 if (GetLastError() != NO_ERROR
) {
678 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
679 return CAB_STATUS_FAILURE
;
682 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0) {
683 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
684 return CAB_STATUS_FAILURE
;
689 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
690 /* Read name of previous cabinet */
691 Status
= ReadString(CabinetPrev
, 256);
692 if (Status
!= CAB_STATUS_SUCCESS
)
694 /* Read label of previous disk */
695 Status
= ReadString(DiskPrev
, 256);
696 if (Status
!= CAB_STATUS_SUCCESS
)
699 strcpy(CabinetPrev
, "");
700 strcpy(DiskPrev
, "");
703 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
704 /* Read name of next cabinet */
705 Status
= ReadString(CabinetNext
, 256);
706 if (Status
!= CAB_STATUS_SUCCESS
)
708 /* Read label of next disk */
709 Status
= ReadString(DiskNext
, 256);
710 if (Status
!= CAB_STATUS_SUCCESS
)
713 strcpy(CabinetNext
, "");
714 strcpy(DiskNext
, "");
717 /* Read all folders */
718 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++) {
719 FolderNode
= NewFolderNode();
721 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
722 return CAB_STATUS_NOMEMORY
;
726 FolderNode
->UncompOffset
= FolderUncompSize
;
728 FolderNode
->Index
= Index
;
730 if ((Status
= ReadBlock(&FolderNode
->Folder
,
731 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
) {
732 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
733 return CAB_STATUS_INVALID_CAB
;
737 /* Read file entries */
738 Status
= ReadFileTable();
739 if (Status
!= CAB_STATUS_SUCCESS
) {
740 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (unsigned int)Status
));
744 /* Read data blocks for all folders */
745 FolderNode
= FolderListHead
;
746 while (FolderNode
!= NULL
) {
747 Status
= ReadDataBlocks(FolderNode
);
748 if (Status
!= CAB_STATUS_SUCCESS
) {
749 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status
));
752 FolderNode
= FolderNode
->Next
;
755 return CAB_STATUS_SUCCESS
;
759 void CCabinet::Close()
761 * FUNCTION: Closes the cabinet file
765 CloseFile(FileHandle
);
771 unsigned long CCabinet::FindFirst(char* FileName
,
774 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
776 * FileName = Pointer to search criteria
777 * Search = Pointer to search structure
779 * Status of operation
782 RestartSearch
= false;
783 strncpy(Search
->Search
, FileName
, MAX_PATH
);
784 Search
->Next
= FileListHead
;
785 return FindNext(Search
);
789 unsigned long CCabinet::FindNext(PCAB_SEARCH Search
)
791 * FUNCTION: Finds next file in the cabinet that matches a search criteria
793 * Search = Pointer to search structure
795 * Status of operation
798 unsigned long Status
;
801 Search
->Next
= FileListHead
;
803 /* Skip split files already extracted */
804 while ((Search
->Next
) &&
805 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
806 (Search
->Next
->File
.FileOffset
<= LastFileOffset
)) {
807 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
808 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
809 Search
->Next
= Search
->Next
->Next
;
812 RestartSearch
= false;
815 /* FIXME: Check search criteria */
818 if (strlen(DiskNext
) > 0) {
821 SetCabinetName(CabinetNext
);
823 OnDiskChange(CabinetNext
, DiskNext
);
826 if (Status
!= CAB_STATUS_SUCCESS
)
829 Search
->Next
= FileListHead
;
831 return CAB_STATUS_NOFILE
;
833 return CAB_STATUS_NOFILE
;
836 Search
->File
= &Search
->Next
->File
;
837 Search
->FileName
= Search
->Next
->FileName
;
838 Search
->Next
= Search
->Next
->Next
;
839 return CAB_STATUS_SUCCESS
;
843 unsigned long CCabinet::ExtractFile(char* FileName
)
845 * FUNCTION: Extracts a file from the cabinet
847 * FileName = Pointer to buffer with name of file
849 * Status of operation
853 unsigned long Offset
;
854 unsigned long BytesRead
;
855 unsigned long BytesToRead
;
856 unsigned long BytesWritten
;
857 unsigned long BytesSkipped
;
858 unsigned long BytesToWrite
;
859 unsigned long TotalBytesRead
;
860 unsigned long CurrentOffset
;
861 unsigned char* Buffer
;
862 unsigned char* CurrentBuffer
;
866 unsigned long Status
;
871 char DestName
[MAX_PATH
];
872 char TempName
[MAX_PATH
];
874 Status
= LocateFile(FileName
, &File
);
875 if (Status
!= CAB_STATUS_SUCCESS
) {
876 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
880 LastFileOffset
= File
->File
.FileOffset
;
882 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
) {
884 SelectCodec(CAB_CODEC_RAW
);
887 SelectCodec(CAB_CODEC_MSZIP
);
890 return CAB_STATUS_UNSUPPCOMP
;
893 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
894 (unsigned int)File
->File
.FileOffset
,
895 (unsigned int)File
->File
.FileSize
,
896 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
897 (unsigned int)File
->DataBlock
->UncompOffset
));
899 strcpy(DestName
, DestPath
);
900 strcat(DestName
, FileName
);
902 /* Create destination file, fail if it already exists */
904 DestFile
= CreateFile(DestName
, // Create this file
905 GENERIC_WRITE
, // Open for writing
908 CREATE_NEW
, // New file only
909 FILE_ATTRIBUTE_NORMAL
, // Normal file
910 NULL
); // No attribute template
911 if (DestFile
== INVALID_HANDLE_VALUE
) {
912 /* If file exists, ask to overwrite file */
913 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
914 (OnOverwrite(&File
->File
, FileName
))) {
915 /* Create destination file, overwrite if it already exists */
916 DestFile
= CreateFile(DestName
, // Create this file
917 GENERIC_WRITE
, // Open for writing
920 TRUNCATE_EXISTING
, // Truncate the file
921 FILE_ATTRIBUTE_NORMAL
, // Normal file
922 NULL
); // No attribute template
923 if (DestFile
== INVALID_HANDLE_VALUE
)
924 return CAB_STATUS_CANNOT_CREATE
;
926 if (Status
== ERROR_FILE_EXISTS
)
927 return CAB_STATUS_FILE_EXISTS
;
929 return CAB_STATUS_CANNOT_CREATE
;
933 DestFile
= fopen(DestName
, "rb");
934 if (DestFile
!= NULL
) {
936 /* If file exists, ask to overwrite file */
937 if (OnOverwrite(&File
->File
, FileName
)) {
938 DestFile
= fopen(DestName
, "w+b");
939 if (DestFile
== NULL
) {
940 return CAB_STATUS_CANNOT_CREATE
;
943 return CAB_STATUS_FILE_EXISTS
;
946 DestFile
= fopen(DestName
, "w+b");
947 if (DestFile
== NULL
) {
948 return CAB_STATUS_CANNOT_CREATE
;
953 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
)) {
955 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
956 return CAB_STATUS_CANNOT_WRITE
;
959 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
961 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
963 SetAttributesOnFile(File
);
965 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
968 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
969 return CAB_STATUS_NOMEMORY
;
972 /* Call OnExtract event handler */
973 OnExtract(&File
->File
, FileName
);
975 /* Search to start of file */
977 Offset
= SetFilePointer(FileHandle
,
978 File
->DataBlock
->AbsoluteOffset
,
981 if (GetLastError() != NO_ERROR
) {
982 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
983 return CAB_STATUS_INVALID_CAB
;
986 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
987 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
988 return CAB_STATUS_FAILURE
;
990 Offset
= ftell(FileHandle
);
993 Size
= File
->File
.FileSize
;
994 Offset
= File
->File
.FileOffset
;
995 CurrentOffset
= File
->DataBlock
->UncompOffset
;
999 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1002 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%d) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
1003 File
->DataBlock
->UncompOffset
, (unsigned int)ReuseBlock
, Offset
, Size
,
1006 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0)) {
1008 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1010 CurrentBuffer
= Buffer
;
1013 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1015 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1016 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1017 CloseFile(DestFile
);
1019 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1020 return CAB_STATUS_INVALID_CAB
;
1023 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n",
1024 (unsigned int)CFData
.Checksum
,
1025 (unsigned int)CFData
.CompSize
,
1026 (unsigned int)CFData
.UncompSize
));
1028 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1030 BytesToRead
= CFData
.CompSize
;
1032 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1033 (long unsigned int)CurrentBuffer
, (long unsigned int)Buffer
));
1035 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1036 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
1037 CloseFile(DestFile
);
1039 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1040 return CAB_STATUS_INVALID_CAB
;
1043 /* FIXME: Does not work with files generated by makecab.exe */
1045 if (CFData.Checksum != 0) {
1046 unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1047 if (Checksum != CFData.Checksum) {
1048 CloseFile(DestFile);
1050 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1051 Checksum, CFData.Checksum));
1052 return CAB_STATUS_INVALID_CAB;
1056 TotalBytesRead
+= BytesRead
;
1058 CurrentBuffer
+= BytesRead
;
1060 if (CFData
.UncompSize
== 0) {
1061 if (strlen(DiskNext
) == 0)
1062 return CAB_STATUS_NOFILE
;
1064 /* CloseCabinet() will destroy all file entries so in case
1065 FileName refers to the FileName field of a CFFOLDER_NODE
1066 structure, we have to save a copy of the filename */
1067 strcpy(TempName
, FileName
);
1071 SetCabinetName(CabinetNext
);
1073 OnDiskChange(CabinetNext
, DiskNext
);
1076 if (Status
!= CAB_STATUS_SUCCESS
)
1079 /* The first data block of the file will not be
1080 found as it is located in the previous file */
1081 Status
= LocateFile(TempName
, &File
);
1082 if (Status
== CAB_STATUS_NOFILE
) {
1083 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
1087 /* The file is continued in the first data block in the folder */
1088 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1090 /* Search to start of file */
1092 (unsigned int)SetFilePointer(FileHandle
,
1093 File
->DataBlock
->AbsoluteOffset
,
1096 if (GetLastError() != NO_ERROR
) {
1097 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1098 return CAB_STATUS_INVALID_CAB
;
1101 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
1102 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1103 return CAB_STATUS_INVALID_CAB
;
1107 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1108 (unsigned int)File
->File
.FileOffset
,
1109 (unsigned int)File
->File
.FileSize
,
1110 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
1111 (unsigned int)File
->DataBlock
->UncompOffset
));
1113 CurrentDataNode
= File
->DataBlock
;
1116 RestartSearch
= true;
1118 } while (CFData
.UncompSize
== 0);
1120 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1122 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1123 if (Status
!= CS_SUCCESS
) {
1124 CloseFile(DestFile
);
1126 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1127 if (Status
== CS_NOMEMORY
)
1128 return CAB_STATUS_NOMEMORY
;
1129 return CAB_STATUS_INVALID_CAB
;
1132 if (BytesToWrite
!= CFData
.UncompSize
) {
1133 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1134 BytesToWrite
, CFData
.UncompSize
));
1135 return CAB_STATUS_INVALID_CAB
;
1138 BytesLeftInBlock
= BytesToWrite
;
1140 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1142 BytesToWrite
= BytesLeftInBlock
;
1144 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1145 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1146 CurrentDataNode
->Data
.CompSize
));
1148 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1149 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1150 CloseFile(DestFile
);
1152 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1153 return CAB_STATUS_INVALID_CAB
;
1156 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1157 CFData
.CompSize
, CFData
.UncompSize
));
1159 /* Go to next data block */
1161 (unsigned int)SetFilePointer(FileHandle
,
1162 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1163 CurrentDataNode
->Data
.CompSize
,
1166 if (GetLastError() != NO_ERROR
) {
1167 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1168 return CAB_STATUS_INVALID_CAB
;
1171 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1172 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0) {
1173 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1174 return CAB_STATUS_INVALID_CAB
;
1182 BytesSkipped
= (Offset
- CurrentOffset
);
1186 BytesToWrite
-= BytesSkipped
;
1188 if (Size
< BytesToWrite
)
1189 BytesToWrite
= Size
;
1191 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1192 (unsigned int)Offset
,
1193 (unsigned int)CurrentOffset
,
1194 (unsigned int)BytesToWrite
,
1195 (unsigned int)BytesSkipped
, (unsigned int)Skip
,
1196 (unsigned int)Size
));
1199 if (!WriteFile(DestFile
, (void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1200 BytesToWrite
, &BytesWritten
, NULL
) ||
1201 (BytesToWrite
!= BytesWritten
)) {
1202 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1204 BytesWritten
= BytesToWrite
;
1205 if (fwrite((void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1206 BytesToWrite
, 1, DestFile
) < 1) {
1208 CloseFile(DestFile
);
1210 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1211 return CAB_STATUS_CANNOT_WRITE
;
1213 Size
-= BytesToWrite
;
1215 CurrentOffset
+= BytesToWrite
;
1217 /* Don't skip any more bytes */
1222 CloseFile(DestFile
);
1226 return CAB_STATUS_SUCCESS
;
1230 void CCabinet::SelectCodec(unsigned long Id
)
1232 * FUNCTION: Selects codec engine to use
1234 * Id = Codec identifier
1237 if (CodecSelected
) {
1241 CodecSelected
= false;
1248 Codec
= new CRawCodec();
1251 case CAB_CODEC_MSZIP
:
1252 Codec
= new CMSZipCodec();
1259 CodecSelected
= true;
1263 #ifndef CAB_READ_ONLY
1265 /* CAB write methods */
1267 unsigned long CCabinet::NewCabinet()
1269 * FUNCTION: Creates a new cabinet
1271 * Status of operation
1274 unsigned long Status
;
1276 CurrentDiskNumber
= 0;
1278 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1279 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1280 if ((!OutputBuffer
) || (!InputBuffer
)) {
1281 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1282 return CAB_STATUS_NOMEMORY
;
1284 CurrentIBuffer
= InputBuffer
;
1285 CurrentIBufferSize
= 0;
1287 CABHeader
.Signature
= CAB_SIGNATURE
;
1288 CABHeader
.Reserved1
= 0; // Not used
1289 CABHeader
.CabinetSize
= 0; // Not yet known
1290 CABHeader
.Reserved2
= 0; // Not used
1291 CABHeader
.Reserved3
= 0; // Not used
1292 CABHeader
.Version
= CAB_VERSION
;
1293 CABHeader
.FolderCount
= 0; // Not yet known
1294 CABHeader
.FileCount
= 0; // Not yet known
1295 CABHeader
.Flags
= 0; // Not yet known
1296 // FIXME: Should be random
1297 CABHeader
.SetID
= 0x534F;
1298 CABHeader
.CabinetNumber
= 0;
1301 TotalFolderSize
= 0;
1304 DiskSize
= sizeof(CFHEADER
);
1306 InitCabinetHeader();
1308 // NextFolderNumber is 0-based
1309 NextFolderNumber
= 0;
1311 CurrentFolderNode
= NULL
;
1312 Status
= NewFolder();
1313 if (Status
!= CAB_STATUS_SUCCESS
)
1316 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1318 ScratchFile
= new CCFDATAStorage
;
1320 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1321 return CAB_STATUS_NOMEMORY
;
1324 Status
= ScratchFile
->Create("~CAB.tmp");
1326 CreateNewFolder
= false;
1328 CreateNewDisk
= false;
1330 PrevCabinetNumber
= 0;
1336 unsigned long CCabinet::NewDisk()
1338 * FUNCTION: Forces a new disk to be created
1340 * Status of operation
1343 // NextFolderNumber is 0-based
1344 NextFolderNumber
= 1;
1346 CreateNewDisk
= false;
1348 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1350 InitCabinetHeader();
1352 CurrentFolderNode
->TotalFolderSize
= 0;
1354 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1356 return CAB_STATUS_SUCCESS
;
1360 unsigned long CCabinet::NewFolder()
1362 * FUNCTION: Forces a new folder to be created
1364 * Status of operation
1367 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1369 CurrentFolderNode
= NewFolderNode();
1370 if (!CurrentFolderNode
) {
1371 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1372 return CAB_STATUS_NOMEMORY
;
1377 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1379 case CAB_CODEC_MSZIP
:
1380 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1383 return CAB_STATUS_UNSUPPCOMP
;
1386 /* FIXME: This won't work if no files are added to the new folder */
1388 DiskSize
+= sizeof(CFFOLDER
);
1390 TotalFolderSize
+= sizeof(CFFOLDER
);
1394 CABHeader
.FolderCount
++;
1398 return CAB_STATUS_SUCCESS
;
1402 unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1404 * FUNCTION: Writes a file to the scratch file
1406 * FileNode = Pointer to file node
1408 * Status of operation
1411 unsigned long BytesToRead
;
1412 unsigned long BytesRead
;
1413 unsigned long Status
;
1416 if (!ContinueFile
) {
1417 /* Try to open file */
1419 SourceFile
= CreateFile(
1420 FileNode
->FileName
, // Open this file
1421 GENERIC_READ
, // Open for reading
1422 FILE_SHARE_READ
, // Share for reading
1423 NULL
, // No security
1424 OPEN_EXISTING
, // File must exist
1425 FILE_ATTRIBUTE_NORMAL
, // Normal file
1426 NULL
); // No attribute template
1427 if (SourceFile
== INVALID_HANDLE_VALUE
) {
1428 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1429 return CAB_STATUS_NOFILE
;
1432 SourceFile
= fopen(FileNode
->FileName
, "rb");
1433 if (SourceFile
== NULL
) {
1434 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1435 return CAB_STATUS_NOFILE
;
1439 if (CreateNewFolder
) {
1440 /* There is always a new folder after
1441 a split file is completely stored */
1442 Status
= NewFolder();
1443 if (Status
!= CAB_STATUS_SUCCESS
)
1445 CreateNewFolder
= false;
1448 /* Call OnAdd event handler */
1449 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1451 TotalBytesLeft
= FileNode
->File
.FileSize
;
1453 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1454 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1455 FileNode
->File
.FileControlID
= (unsigned short)(NextFolderNumber
- 1);
1456 CurrentFolderNode
->Commit
= true;
1457 PrevCabinetNumber
= CurrentDiskNumber
;
1459 Size
= sizeof(CFFILE
) + strlen(GetFileName(FileNode
->FileName
)) + 1;
1460 CABHeader
.FileTableOffset
+= Size
;
1461 TotalFileSize
+= Size
;
1465 FileNode
->Commit
= true;
1467 if (TotalBytesLeft
> 0) {
1469 if (TotalBytesLeft
> (unsigned long)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1470 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1472 BytesToRead
= TotalBytesLeft
;
1474 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
)) {
1475 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1476 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1477 return CAB_STATUS_INVALID_CAB
;
1480 *(unsigned char**)&CurrentIBuffer
+= BytesRead
;
1482 CurrentIBufferSize
+= (unsigned short)BytesRead
;
1484 if (CurrentIBufferSize
== CAB_BLOCKSIZE
) {
1485 Status
= WriteDataBlock();
1486 if (Status
!= CAB_STATUS_SUCCESS
)
1489 TotalBytesLeft
-= BytesRead
;
1490 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1493 if (TotalBytesLeft
== 0) {
1494 CloseFile(SourceFile
);
1495 FileNode
->Delete
= true;
1497 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) {
1498 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1499 CurrentFolderNode
->Delete
= true;
1501 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1502 Status
= WriteDataBlock();
1503 if (Status
!= CAB_STATUS_SUCCESS
)
1507 CreateNewFolder
= true;
1510 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1511 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1513 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1516 return CAB_STATUS_SUCCESS
;
1520 unsigned long CCabinet::WriteDisk(unsigned long MoreDisks
)
1522 * FUNCTION: Forces the current disk to be written
1524 * MoreDisks = true if there is one or more disks after this disk
1526 * Status of operation
1529 PCFFILE_NODE FileNode
;
1530 unsigned long Status
;
1532 ContinueFile
= false;
1533 FileNode
= FileListHead
;
1534 while (FileNode
!= NULL
) {
1536 Status
= WriteFileToScratchStorage(FileNode
);
1537 if (Status
!= CAB_STATUS_SUCCESS
)
1539 if (CreateNewDisk
) {
1540 /* A data block could span more than two
1541 disks if MaxDiskSize is very small */
1542 while (CreateNewDisk
) {
1543 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1548 ContinueFile
= true;
1549 CreateNewDisk
= false;
1551 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1552 CurrentIBufferSize
, CurrentOBufferSize
));
1554 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1555 Status
= WriteDataBlock();
1556 if (Status
!= CAB_STATUS_SUCCESS
)
1561 ContinueFile
= false;
1562 FileNode
= FileNode
->Next
;
1566 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1567 /* A data block could span more than two
1568 disks if MaxDiskSize is very small */
1570 ASSERT(CreateNewDisk
== false);
1573 if (CreateNewDisk
) {
1574 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1578 CreateNewDisk
= false;
1580 ASSERT(FileNode
== FileListHead
);
1583 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1584 Status
= WriteDataBlock();
1585 if (Status
!= CAB_STATUS_SUCCESS
)
1588 } while (CreateNewDisk
);
1590 CommitDisk(MoreDisks
);
1592 return CAB_STATUS_SUCCESS
;
1596 unsigned long CCabinet::CommitDisk(unsigned long MoreDisks
)
1598 * FUNCTION: Commits the current disk
1600 * MoreDisks = true if there is one or more disks after this disk
1602 * Status of operation
1605 PCFFOLDER_NODE FolderNode
;
1606 unsigned long Status
;
1608 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1610 /* Create file, fail if it already exists */
1612 FileHandle
= CreateFile(CabinetName
, // Create this file
1613 GENERIC_WRITE
, // Open for writing
1615 NULL
, // No security
1616 CREATE_NEW
, // New file only
1617 FILE_ATTRIBUTE_NORMAL
, // Normal file
1618 NULL
); // No attribute template
1619 if (FileHandle
== INVALID_HANDLE_VALUE
) {
1620 unsigned long Status
;
1621 /* If file exists, ask to overwrite file */
1622 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1623 (OnOverwrite(NULL
, CabinetName
))) {
1625 /* Create cabinet file, overwrite if it already exists */
1626 FileHandle
= CreateFile(CabinetName
, // Create this file
1627 GENERIC_WRITE
, // Open for writing
1629 NULL
, // No security
1630 TRUNCATE_EXISTING
, // Truncate the file
1631 FILE_ATTRIBUTE_NORMAL
, // Normal file
1632 NULL
); // No attribute template
1633 if (FileHandle
== INVALID_HANDLE_VALUE
)
1634 return CAB_STATUS_CANNOT_CREATE
;
1636 if (Status
== ERROR_FILE_EXISTS
)
1637 return CAB_STATUS_FILE_EXISTS
;
1639 return CAB_STATUS_CANNOT_CREATE
;
1643 FileHandle
= fopen(CabinetName
, "rb");
1644 if (FileHandle
!= NULL
) {
1646 /* If file exists, ask to overwrite file */
1647 if (OnOverwrite(NULL
, CabinetName
)) {
1648 FileHandle
= fopen(CabinetName
, "w+b");
1649 if (FileHandle
== NULL
) {
1650 return CAB_STATUS_CANNOT_CREATE
;
1653 return CAB_STATUS_FILE_EXISTS
;
1656 FileHandle
= fopen(CabinetName
, "w+b");
1657 if (FileHandle
== NULL
) {
1658 return CAB_STATUS_CANNOT_CREATE
;
1663 WriteCabinetHeader(MoreDisks
!= 0);
1665 Status
= WriteFolderEntries();
1666 if (Status
!= CAB_STATUS_SUCCESS
)
1669 /* Write file entries */
1672 /* Write data blocks */
1673 FolderNode
= FolderListHead
;
1674 while (FolderNode
!= NULL
) {
1675 if (FolderNode
->Commit
) {
1676 Status
= CommitDataBlocks(FolderNode
);
1677 if (Status
!= CAB_STATUS_SUCCESS
)
1679 /* Remove data blocks for folder */
1680 DestroyDataNodes(FolderNode
);
1682 FolderNode
= FolderNode
->Next
;
1685 CloseFile(FileHandle
);
1687 ScratchFile
->Truncate();
1689 return CAB_STATUS_SUCCESS
;
1693 unsigned long CCabinet::CloseDisk()
1695 * FUNCTION: Closes the current disk
1697 * Status of operation
1700 DestroyDeletedFileNodes();
1702 /* Destroy folder nodes that are completely stored */
1703 DestroyDeletedFolderNodes();
1705 CurrentDiskNumber
++;
1707 return CAB_STATUS_SUCCESS
;
1711 unsigned long CCabinet::CloseCabinet()
1713 * FUNCTION: Closes the current cabinet
1715 * Status of operation
1718 unsigned long Status
;
1722 DestroyFolderNodes();
1725 FreeMemory(InputBuffer
);
1730 FreeMemory(OutputBuffer
);
1731 OutputBuffer
= NULL
;
1737 Status
= ScratchFile
->Destroy();
1742 return CAB_STATUS_SUCCESS
;
1746 unsigned long CCabinet::AddFile(char* FileName
)
1748 * FUNCTION: Adds a file to the current disk
1750 * FileName = Pointer to string with file name (full path)
1752 * Status of operation
1756 PCFFILE_NODE FileNode
;
1759 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1761 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1762 return CAB_STATUS_NOMEMORY
;
1764 strcpy(NewFileName
, FileName
);
1765 ConvertPath(NewFileName
, false);
1767 /* Try to open file */
1769 SrcFile
= CreateFile(
1770 NewFileName
, // Open this file
1771 GENERIC_READ
, // Open for reading
1772 FILE_SHARE_READ
, // Share for reading
1773 NULL
, // No security
1774 OPEN_EXISTING
, // File must exist
1775 FILE_ATTRIBUTE_NORMAL
, // Normal file
1776 NULL
); // No attribute template
1777 if (SrcFile
== INVALID_HANDLE_VALUE
) {
1778 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1779 FreeMemory(NewFileName
);
1780 return CAB_STATUS_CANNOT_OPEN
;
1783 SrcFile
= fopen(NewFileName
, "rb");
1784 if (SrcFile
== NULL
) {
1785 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1786 FreeMemory(NewFileName
);
1787 return CAB_STATUS_CANNOT_OPEN
;
1791 FileNode
= NewFileNode();
1793 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1794 FreeMemory(NewFileName
);
1795 return CAB_STATUS_NOMEMORY
;
1798 FileNode
->FolderNode
= CurrentFolderNode
;
1799 FileNode
->FileName
= NewFileName
;
1801 /* FIXME: Check for and handle large files (>= 2GB) */
1802 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1803 if (FileNode
->File
.FileSize
== (unsigned long)-1) {
1804 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1805 FreeMemory(NewFileName
);
1806 return CAB_STATUS_CANNOT_READ
;
1809 GetFileTimes(SrcFile
, FileNode
);
1811 GetAttributesOnFile(FileNode
);
1815 return CAB_STATUS_SUCCESS
;
1819 void CCabinet::SetMaxDiskSize(unsigned long Size
)
1821 * FUNCTION: Sets the maximum size of the current disk
1823 * Size = Maximum size of current disk (0 means no maximum size)
1829 #endif /* CAB_READ_ONLY */
1832 /* Default event handlers */
1834 bool CCabinet::OnOverwrite(PCFFILE File
,
1837 * FUNCTION: Called when extracting a file and it already exists
1839 * File = Pointer to CFFILE for file being extracted
1840 * FileName = Pointer to buffer with name of file (full path)
1842 * true if the file should be overwritten, false if not
1849 void CCabinet::OnExtract(PCFFILE File
,
1852 * FUNCTION: Called just before extracting a file
1854 * File = Pointer to CFFILE for file being extracted
1855 * FileName = Pointer to buffer with name of file (full path)
1861 void CCabinet::OnDiskChange(char* CabinetName
,
1864 * FUNCTION: Called when a new disk is to be processed
1866 * CabinetName = Pointer to buffer with name of cabinet
1867 * DiskLabel = Pointer to buffer with label of disk
1873 #ifndef CAB_READ_ONLY
1875 void CCabinet::OnAdd(PCFFILE File
,
1878 * FUNCTION: Called just before adding a file to a cabinet
1880 * File = Pointer to CFFILE for file being added
1881 * FileName = Pointer to buffer with name of file (full path)
1887 bool CCabinet::OnDiskLabel(unsigned long Number
, char* Label
)
1889 * FUNCTION: Called when a disk needs a label
1891 * Number = Cabinet number that needs a label
1892 * Label = Pointer to buffer to place label of disk
1894 * true if a disk label was returned, false if not
1901 bool CCabinet::OnCabinetName(unsigned long Number
, char* Name
)
1903 * FUNCTION: Called when a cabinet needs a name
1905 * Number = Disk number that needs a name
1906 * Name = Pointer to buffer to place name of cabinet
1908 * true if a cabinet name was returned, false if not
1914 #endif /* CAB_READ_ONLY */
1916 PCFFOLDER_NODE
CCabinet::LocateFolderNode(unsigned long Index
)
1918 * FUNCTION: Locates a folder node
1920 * Index = Folder index
1922 * Pointer to folder node or NULL if the folder node was not found
1925 PCFFOLDER_NODE Node
;
1928 case CAB_FILE_SPLIT
:
1929 return FolderListTail
;
1931 case CAB_FILE_CONTINUED
:
1932 case CAB_FILE_PREV_NEXT
:
1933 return FolderListHead
;
1936 Node
= FolderListHead
;
1937 while (Node
!= NULL
) {
1938 if (Node
->Index
== Index
)
1946 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
1948 * FUNCTION: Returns the absolute offset of a file
1950 * File = Pointer to CFFILE_NODE structure for file
1952 * Status of operation
1957 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1958 (char*)File
->FileName
,
1959 (unsigned int)File
->File
.FileOffset
,
1960 (unsigned int)File
->File
.FileSize
));
1962 Node
= CurrentFolderNode
->DataListHead
;
1963 while (Node
!= NULL
) {
1965 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1966 (unsigned int)Node
->UncompOffset
,
1967 (unsigned int)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
1968 (unsigned int)Node
->Data
.UncompSize
));
1970 /* Node->Data.UncompSize will be 0 if the block is split
1971 (ie. it is the last block in this cabinet) */
1972 if ((Node
->Data
.UncompSize
== 0) ||
1973 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
1974 (File
->File
.FileOffset
< Node
->UncompOffset
+
1975 Node
->Data
.UncompSize
))) {
1976 File
->DataBlock
= Node
;
1977 return CAB_STATUS_SUCCESS
;
1982 return CAB_STATUS_INVALID_CAB
;
1986 unsigned long CCabinet::LocateFile(char* FileName
,
1989 * FUNCTION: Locates a file in the cabinet
1991 * FileName = Pointer to string with name of file to locate
1992 * File = Address of pointer to CFFILE_NODE structure to fill
1994 * Status of operation
1996 * Current folder is set to the folder of the file
2000 unsigned long Status
;
2002 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2004 Node
= FileListHead
;
2005 while (Node
!= NULL
) {
2006 if (strcasecmp(FileName
, Node
->FileName
) == 0) {
2008 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2009 if (!CurrentFolderNode
) {
2010 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
2011 (unsigned int)Node
->File
.FileControlID
));
2012 return CAB_STATUS_INVALID_CAB
;
2015 if (Node
->DataBlock
== NULL
) {
2016 Status
= GetAbsoluteOffset(Node
);
2018 Status
= CAB_STATUS_SUCCESS
;
2024 return CAB_STATUS_NOFILE
;
2028 unsigned long CCabinet::ReadString(char* String
, unsigned long MaxLength
)
2030 * FUNCTION: Reads a NULL-terminated string from the cabinet
2032 * String = Pointer to buffer to place string
2033 * MaxLength = Maximum length of string
2035 * Status of operation
2038 unsigned long BytesRead
;
2039 unsigned long Offset
;
2040 unsigned long Status
;
2047 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2050 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2051 return CAB_STATUS_INVALID_CAB
;
2054 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2055 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
)) {
2056 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2057 return CAB_STATUS_INVALID_CAB
;
2060 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++) {
2061 if (String
[Size
] == '\0') {
2067 Offset
+= BytesRead
;
2071 /* Back up some bytes */
2072 Size
= (BytesRead
- Size
) - 1;
2074 SetLastError(NO_ERROR
);
2075 (unsigned int)SetFilePointer(FileHandle
,
2079 if (GetLastError() != NO_ERROR
) {
2080 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2081 return CAB_STATUS_INVALID_CAB
;
2084 if (fseek(FileHandle
, (off_t
)(-(long)Size
), SEEK_CUR
) != 0) {
2085 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2086 return CAB_STATUS_INVALID_CAB
;
2089 return CAB_STATUS_SUCCESS
;
2093 unsigned long CCabinet::ReadFileTable()
2095 * FUNCTION: Reads the file table from the cabinet file
2097 * Status of operation
2101 unsigned long Status
;
2102 unsigned long BytesRead
;
2105 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2106 CABHeader
.FileTableOffset
));
2108 /* Seek to file table */
2110 SetLastError(NO_ERROR
);
2111 (unsigned int)SetFilePointer(FileHandle
,
2112 CABHeader
.FileTableOffset
,
2115 if (GetLastError() != NO_ERROR
) {
2116 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2117 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2118 return CAB_STATUS_INVALID_CAB
;
2121 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0) {
2122 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2123 return CAB_STATUS_INVALID_CAB
;
2127 for (i
= 0; i
< CABHeader
.FileCount
; i
++) {
2128 File
= NewFileNode();
2130 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2131 return CAB_STATUS_NOMEMORY
;
2134 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2135 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2136 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2137 return CAB_STATUS_INVALID_CAB
;
2140 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2141 if (!File
->FileName
) {
2142 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2143 return CAB_STATUS_NOMEMORY
;
2146 /* Read file name */
2147 Status
= ReadString(File
->FileName
, MAX_PATH
);
2148 if (Status
!= CAB_STATUS_SUCCESS
)
2151 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2152 (char*)File
->FileName
,
2153 (unsigned int)File
->File
.FileOffset
,
2154 (unsigned int)File
->File
.FileSize
,
2155 (unsigned int)File
->File
.FileControlID
));
2158 return CAB_STATUS_SUCCESS
;
2162 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2164 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2166 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2168 * Status of operation
2171 unsigned long AbsoluteOffset
;
2172 unsigned long UncompOffset
;
2174 unsigned long BytesRead
;
2175 unsigned long Status
;
2178 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2179 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2181 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2182 UncompOffset
= FolderNode
->UncompOffset
;
2184 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++) {
2185 Node
= NewDataNode(FolderNode
);
2187 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2188 return CAB_STATUS_NOMEMORY
;
2191 /* Seek to data block */
2193 SetLastError(NO_ERROR
);
2194 (unsigned int)SetFilePointer(FileHandle
,
2198 if (GetLastError() != NO_ERROR
) {
2199 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2200 return CAB_STATUS_INVALID_CAB
;
2203 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0) {
2204 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2205 return CAB_STATUS_INVALID_CAB
;
2209 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2210 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2211 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2212 return CAB_STATUS_INVALID_CAB
;
2215 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2216 (unsigned int)AbsoluteOffset
,
2217 (unsigned int)UncompOffset
,
2218 (unsigned int)Node
->Data
.Checksum
,
2219 (unsigned int)Node
->Data
.CompSize
,
2220 (unsigned int)Node
->Data
.UncompSize
));
2222 Node
->AbsoluteOffset
= AbsoluteOffset
;
2223 Node
->UncompOffset
= UncompOffset
;
2225 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2226 UncompOffset
+= Node
->Data
.UncompSize
;
2229 FolderUncompSize
= UncompOffset
;
2231 return CAB_STATUS_SUCCESS
;
2235 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2237 * FUNCTION: Creates a new folder node
2239 * Pointer to node if there was enough free memory available, otherwise NULL
2242 PCFFOLDER_NODE Node
;
2244 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2248 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2250 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2252 Node
->Prev
= FolderListTail
;
2254 if (FolderListTail
!= NULL
) {
2255 FolderListTail
->Next
= Node
;
2257 FolderListHead
= Node
;
2259 FolderListTail
= Node
;
2265 PCFFILE_NODE
CCabinet::NewFileNode()
2267 * FUNCTION: Creates a new file node
2269 * FolderNode = Pointer to folder node to bind file to
2271 * Pointer to node if there was enough free memory available, otherwise NULL
2276 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2280 memset(Node
, 0, sizeof(CFFILE_NODE
));
2282 Node
->Prev
= FileListTail
;
2284 if (FileListTail
!= NULL
) {
2285 FileListTail
->Next
= Node
;
2287 FileListHead
= Node
;
2289 FileListTail
= Node
;
2295 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2297 * FUNCTION: Creates a new data block node
2299 * FolderNode = Pointer to folder node to bind data block to
2301 * Pointer to node if there was enough free memory available, otherwise NULL
2306 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2310 memset(Node
, 0, sizeof(CFDATA_NODE
));
2312 Node
->Prev
= FolderNode
->DataListTail
;
2314 if (FolderNode
->DataListTail
!= NULL
) {
2315 FolderNode
->DataListTail
->Next
= Node
;
2317 FolderNode
->DataListHead
= Node
;
2319 FolderNode
->DataListTail
= Node
;
2325 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2327 * FUNCTION: Destroys data block nodes bound to a folder node
2329 * FolderNode = Pointer to folder node
2332 PCFDATA_NODE PrevNode
;
2333 PCFDATA_NODE NextNode
;
2335 NextNode
= FolderNode
->DataListHead
;
2336 while (NextNode
!= NULL
) {
2337 PrevNode
= NextNode
->Next
;
2338 FreeMemory(NextNode
);
2339 NextNode
= PrevNode
;
2341 FolderNode
->DataListHead
= NULL
;
2342 FolderNode
->DataListTail
= NULL
;
2346 void CCabinet::DestroyFileNodes()
2348 * FUNCTION: Destroys file nodes
2350 * FolderNode = Pointer to folder node
2353 PCFFILE_NODE PrevNode
;
2354 PCFFILE_NODE NextNode
;
2356 NextNode
= FileListHead
;
2357 while (NextNode
!= NULL
) {
2358 PrevNode
= NextNode
->Next
;
2359 if (NextNode
->FileName
)
2360 FreeMemory(NextNode
->FileName
);
2361 FreeMemory(NextNode
);
2362 NextNode
= PrevNode
;
2364 FileListHead
= NULL
;
2365 FileListTail
= NULL
;
2369 void CCabinet::DestroyDeletedFileNodes()
2371 * FUNCTION: Destroys file nodes that are marked for deletion
2374 PCFFILE_NODE CurNode
;
2375 PCFFILE_NODE NextNode
;
2377 CurNode
= FileListHead
;
2378 while (CurNode
!= NULL
) {
2379 NextNode
= CurNode
->Next
;
2381 if (CurNode
->Delete
) {
2382 if (CurNode
->Prev
!= NULL
) {
2383 CurNode
->Prev
->Next
= CurNode
->Next
;
2385 FileListHead
= CurNode
->Next
;
2387 FileListHead
->Prev
= NULL
;
2390 if (CurNode
->Next
!= NULL
) {
2391 CurNode
->Next
->Prev
= CurNode
->Prev
;
2393 FileListTail
= CurNode
->Prev
;
2395 FileListTail
->Next
= NULL
;
2398 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2400 TotalFileSize
-= (sizeof(CFFILE
) + strlen(GetFileName(CurNode
->FileName
)) + 1);
2402 if (CurNode
->FileName
)
2403 FreeMemory(CurNode
->FileName
);
2404 FreeMemory(CurNode
);
2411 void CCabinet::DestroyFolderNodes()
2413 * FUNCTION: Destroys folder nodes
2416 PCFFOLDER_NODE PrevNode
;
2417 PCFFOLDER_NODE NextNode
;
2419 NextNode
= FolderListHead
;
2420 while (NextNode
!= NULL
) {
2421 PrevNode
= NextNode
->Next
;
2422 DestroyDataNodes(NextNode
);
2423 FreeMemory(NextNode
);
2424 NextNode
= PrevNode
;
2426 FolderListHead
= NULL
;
2427 FolderListTail
= NULL
;
2431 void CCabinet::DestroyDeletedFolderNodes()
2433 * FUNCTION: Destroys folder nodes that are marked for deletion
2436 PCFFOLDER_NODE CurNode
;
2437 PCFFOLDER_NODE NextNode
;
2439 CurNode
= FolderListHead
;
2440 while (CurNode
!= NULL
) {
2441 NextNode
= CurNode
->Next
;
2443 if (CurNode
->Delete
) {
2444 if (CurNode
->Prev
!= NULL
) {
2445 CurNode
->Prev
->Next
= CurNode
->Next
;
2447 FolderListHead
= CurNode
->Next
;
2449 FolderListHead
->Prev
= NULL
;
2452 if (CurNode
->Next
!= NULL
) {
2453 CurNode
->Next
->Prev
= CurNode
->Prev
;
2455 FolderListTail
= CurNode
->Prev
;
2457 FolderListTail
->Next
= NULL
;
2460 DestroyDataNodes(CurNode
);
2461 FreeMemory(CurNode
);
2463 TotalFolderSize
-= sizeof(CFFOLDER
);
2470 unsigned long CCabinet::ComputeChecksum(void* Buffer
,
2474 * FUNCTION: Computes checksum for data block
2476 * Buffer = Pointer to data buffer
2477 * Size = Length of data buffer
2478 * Seed = Previously computed checksum
2480 * Checksum of buffer
2483 int UlongCount
; // Number of ULONGs in block
2484 unsigned long Checksum
; // Checksum accumulator
2488 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2489 won't accept checksums computed by this routine */
2491 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%d)\n", Buffer
, Size
));
2493 UlongCount
= Size
/ 4; // Number of ULONGs
2494 Checksum
= Seed
; // Init checksum
2495 pb
= (unsigned char*)Buffer
; // Start at front of data block
2497 /* Checksum integral multiple of ULONGs */
2498 while (UlongCount
-- > 0) {
2499 /* NOTE: Build unsigned long in big/little-endian independent manner */
2500 ul
= *pb
++; // Get low-order byte
2501 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2502 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3nd byte
2503 ul
|= (((unsigned long)(*pb
++)) << 24); // Add 4th byte
2505 Checksum
^= ul
; // Update checksum
2508 /* Checksum remainder bytes */
2512 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3rd byte
2514 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2516 ul
|= *pb
++; // Get low-order byte
2520 Checksum
^= ul
; // Update checksum
2522 /* Return computed checksum */
2527 unsigned long CCabinet::ReadBlock(void* Buffer
,
2529 unsigned long* BytesRead
)
2531 * FUNCTION: Read a block of data from file
2533 * Buffer = Pointer to data buffer
2534 * Size = Length of data buffer
2535 * BytesRead = Pointer to unsigned long that on return will contain
2536 * number of bytes read
2538 * Status of operation
2541 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2542 return CAB_STATUS_INVALID_CAB
;
2543 return CAB_STATUS_SUCCESS
;
2546 #ifndef CAB_READ_ONLY
2548 unsigned long CCabinet::InitCabinetHeader()
2550 * FUNCTION: Initializes cabinet header and optional fields
2552 * Status of operation
2555 unsigned long TotalSize
;
2558 CABHeader
.FileTableOffset
= 0; // Not known yet
2559 CABHeader
.FolderCount
= 0; // Not known yet
2560 CABHeader
.FileCount
= 0; // Not known yet
2561 CABHeader
.Flags
= 0; // Not known yet
2563 CABHeader
.CabinetNumber
= (unsigned short)CurrentDiskNumber
;
2565 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
))) {
2566 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2567 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2568 strcpy(CabinetPrev
, "");
2571 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
)) {
2572 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2573 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2574 strcpy(DiskNext
, "");
2579 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2581 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2583 /* Calculate size of name of previous cabinet */
2584 TotalSize
+= strlen(CabinetPrev
) + 1;
2586 /* Calculate size of label of previous disk */
2587 TotalSize
+= strlen(DiskPrev
) + 1;
2590 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2592 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2594 /* Calculate size of name of next cabinet */
2595 Size
= strlen(CabinetNext
) + 1;
2597 NextFieldsSize
= Size
;
2599 /* Calculate size of label of next disk */
2600 Size
= strlen(DiskNext
) + 1;
2602 NextFieldsSize
+= Size
;
2606 /* Add cabinet reserved area size if present */
2607 if (CabinetReservedFileSize
> 0)
2609 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2610 TotalSize
+= CabinetReservedFileSize
;
2611 TotalSize
+= sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2614 DiskSize
+= TotalSize
;
2616 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2618 return CAB_STATUS_SUCCESS
;
2622 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks
)
2624 * FUNCTION: Writes the cabinet header and optional fields
2626 * MoreDisks = true if next cabinet name should be included
2628 * Status of operation
2631 PCFFOLDER_NODE FolderNode
;
2632 PCFFILE_NODE FileNode
;
2633 unsigned long BytesWritten
;
2637 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2638 Size
= TotalHeaderSize
;
2640 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2641 DiskSize
-= NextFieldsSize
;
2642 Size
= TotalHeaderSize
- NextFieldsSize
;
2645 /* Set absolute folder offsets */
2646 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2647 CABHeader
.FolderCount
= 0;
2648 FolderNode
= FolderListHead
;
2649 while (FolderNode
!= NULL
) {
2650 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2652 BytesWritten
+= FolderNode
->TotalFolderSize
;
2654 CABHeader
.FolderCount
++;
2656 FolderNode
= FolderNode
->Next
;
2659 /* Set absolute offset of file table */
2660 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2662 /* Count number of files to be committed */
2663 CABHeader
.FileCount
= 0;
2664 FileNode
= FileListHead
;
2665 while (FileNode
!= NULL
) {
2666 if (FileNode
->Commit
)
2667 CABHeader
.FileCount
++;
2668 FileNode
= FileNode
->Next
;
2671 CABHeader
.CabinetSize
= DiskSize
;
2675 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
)) {
2676 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2677 return CAB_STATUS_CANNOT_WRITE
;
2680 BytesWritten
= sizeof(CFHEADER
);
2681 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1) {
2682 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2683 return CAB_STATUS_CANNOT_WRITE
;
2687 /* Write per-cabinet reserved area if present */
2688 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
2689 unsigned long ReservedSize
;
2691 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2692 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2693 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2695 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(unsigned long), &BytesWritten
, NULL
)) {
2696 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2697 return CAB_STATUS_CANNOT_WRITE
;
2700 BytesWritten
= sizeof(unsigned long);
2701 if (fwrite(&ReservedSize
, sizeof(unsigned long), 1, FileHandle
) < 1) {
2702 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2703 return CAB_STATUS_CANNOT_WRITE
;
2708 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesWritten
, NULL
)) {
2709 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2710 return CAB_STATUS_CANNOT_WRITE
;
2713 BytesWritten
= CabinetReservedFileSize
;
2714 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1) {
2715 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2716 return CAB_STATUS_CANNOT_WRITE
;
2721 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2723 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2725 /* Write name of previous cabinet */
2726 Size
= strlen(CabinetPrev
) + 1;
2728 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
)) {
2729 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2730 return CAB_STATUS_CANNOT_WRITE
;
2733 BytesWritten
= Size
;
2734 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1) {
2735 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2736 return CAB_STATUS_CANNOT_WRITE
;
2740 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2742 /* Write label of previous disk */
2743 Size
= strlen(DiskPrev
) + 1;
2745 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
)) {
2746 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2747 return CAB_STATUS_CANNOT_WRITE
;
2750 BytesWritten
= Size
;
2751 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1) {
2752 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2753 return CAB_STATUS_CANNOT_WRITE
;
2758 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2760 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2762 /* Write name of next cabinet */
2763 Size
= strlen(CabinetNext
) + 1;
2765 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
)) {
2766 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2767 return CAB_STATUS_CANNOT_WRITE
;
2770 BytesWritten
= Size
;
2771 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1) {
2772 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2773 return CAB_STATUS_CANNOT_WRITE
;
2777 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2779 /* Write label of next disk */
2780 Size
= strlen(DiskNext
) + 1;
2782 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
)) {
2783 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2784 return CAB_STATUS_CANNOT_WRITE
;
2787 BytesWritten
= Size
;
2788 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1) {
2789 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2790 return CAB_STATUS_CANNOT_WRITE
;
2795 return CAB_STATUS_SUCCESS
;
2799 unsigned long CCabinet::WriteFolderEntries()
2801 * FUNCTION: Writes folder entries
2803 * Status of operation
2806 PCFFOLDER_NODE FolderNode
;
2807 unsigned long BytesWritten
;
2809 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2811 FolderNode
= FolderListHead
;
2812 while (FolderNode
!= NULL
) {
2813 if (FolderNode
->Commit
) {
2815 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2816 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
2819 if (!WriteFile(FileHandle
,
2820 &FolderNode
->Folder
,
2824 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2825 return CAB_STATUS_CANNOT_WRITE
;
2828 BytesWritten
= sizeof(CFFOLDER
);
2829 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1) {
2830 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2831 return CAB_STATUS_CANNOT_WRITE
;
2835 FolderNode
= FolderNode
->Next
;
2838 return CAB_STATUS_SUCCESS
;
2842 unsigned long CCabinet::WriteFileEntries()
2844 * FUNCTION: Writes file entries for all files
2846 * Status of operation
2850 unsigned long BytesWritten
;
2853 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
2855 File
= FileListHead
;
2856 while (File
!= NULL
) {
2858 /* Remove any continued files that ends in this disk */
2859 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
2860 File
->Delete
= true;
2862 /* The file could end in the last (split) block and should therefore
2863 appear in the next disk too */
2865 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
2866 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
)) {
2867 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
2868 File
->Delete
= false;
2872 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
2873 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
2876 if (!WriteFile(FileHandle
,
2881 return CAB_STATUS_CANNOT_WRITE
;
2883 BytesWritten
= sizeof(CFFILE
);
2884 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1) {
2885 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2886 return CAB_STATUS_CANNOT_WRITE
;
2891 if (!WriteFile(FileHandle
,
2892 GetFileName(File
->FileName
),
2893 strlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
2894 return CAB_STATUS_CANNOT_WRITE
;
2896 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
2897 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1) {
2898 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2899 return CAB_STATUS_CANNOT_WRITE
;
2904 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
2911 return CAB_STATUS_SUCCESS
;
2915 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
2917 * FUNCTION: Writes data blocks to the cabinet
2919 * FolderNode = Pointer to folder node containing the data blocks
2921 * Status of operation
2924 PCFDATA_NODE DataNode
;
2925 unsigned long BytesWritten
;
2926 unsigned long BytesRead
;
2927 unsigned long Status
;
2929 DataNode
= FolderNode
->DataListHead
;
2930 if (DataNode
!= NULL
)
2931 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
2933 while (DataNode
!= NULL
) {
2934 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
2935 DataNode
->ScratchFilePosition
,
2936 DataNode
->Data
.CompSize
,
2937 DataNode
->Data
.UncompSize
));
2939 /* InputBuffer is free for us to use here, so we use it and avoid a
2940 memory allocation. OutputBuffer can't be used here because it may
2941 still contain valid data (if a data block spans two or more disks) */
2942 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
2943 if (Status
!= CAB_STATUS_SUCCESS
) {
2944 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (unsigned int)Status
));
2949 if (!WriteFile(FileHandle
, &DataNode
->Data
,
2950 sizeof(CFDATA
), &BytesWritten
, NULL
)) {
2951 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2952 return CAB_STATUS_CANNOT_WRITE
;
2955 BytesWritten
= sizeof(CFDATA
);
2956 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1) {
2957 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2958 return CAB_STATUS_CANNOT_WRITE
;
2963 if (!WriteFile(FileHandle
, InputBuffer
,
2964 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
)) {
2965 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2966 return CAB_STATUS_CANNOT_WRITE
;
2969 BytesWritten
= DataNode
->Data
.CompSize
;
2970 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1) {
2971 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2972 return CAB_STATUS_CANNOT_WRITE
;
2976 DataNode
= DataNode
->Next
;
2978 return CAB_STATUS_SUCCESS
;
2982 unsigned long CCabinet::WriteDataBlock()
2984 * FUNCTION: Writes the current data block to the scratch file
2986 * Status of operation
2989 unsigned long Status
;
2990 unsigned long BytesWritten
;
2991 PCFDATA_NODE DataNode
;
2993 if (!BlockIsSplit
) {
2994 Status
= Codec
->Compress(OutputBuffer
,
2999 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
3000 CurrentIBufferSize
, TotalCompSize
));
3002 CurrentOBuffer
= OutputBuffer
;
3003 CurrentOBufferSize
= TotalCompSize
;
3006 DataNode
= NewDataNode(CurrentFolderNode
);
3008 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3009 return CAB_STATUS_NOMEMORY
;
3012 DiskSize
+= sizeof(CFDATA
);
3014 if (MaxDiskSize
> 0)
3015 /* Disk size is limited */
3016 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3018 BlockIsSplit
= false;
3021 DataNode
->Data
.CompSize
= (unsigned short)(MaxDiskSize
- DiskSize
);
3022 DataNode
->Data
.UncompSize
= 0;
3023 CreateNewDisk
= true;
3025 DataNode
->Data
.CompSize
= (unsigned short)CurrentOBufferSize
;
3026 DataNode
->Data
.UncompSize
= (unsigned short)CurrentIBufferSize
;
3029 DataNode
->Data
.Checksum
= 0;
3030 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3032 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3033 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3035 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3036 (unsigned int)DataNode
->Data
.Checksum
,
3037 (unsigned int)DataNode
->Data
.CompSize
,
3038 (unsigned int)DataNode
->Data
.UncompSize
));
3040 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3041 CurrentOBuffer
, &BytesWritten
);
3042 if (Status
!= CAB_STATUS_SUCCESS
)
3045 DiskSize
+= BytesWritten
;
3047 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3048 CurrentFolderNode
->Folder
.DataBlockCount
++;
3050 *(unsigned char**)&CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3051 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3053 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3055 if (!BlockIsSplit
) {
3056 CurrentIBufferSize
= 0;
3057 CurrentIBuffer
= InputBuffer
;
3060 return CAB_STATUS_SUCCESS
;
3066 void CCabinet::ConvertDateAndTime(time_t* Time
,
3067 unsigned short* DosDate
,
3068 unsigned short* DosTime
)
3070 * FUNCTION: Returns file times of a file
3072 * FileHandle = File handle of file to get file times from
3073 * File = Pointer to CFFILE node for file
3075 * Status of operation
3080 timedef
= localtime(Time
);
3082 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3083 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3084 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3086 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3087 | ((timedef
->tm_mon
+ 1) << 5)
3088 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3090 *DosTime
= (timedef
->tm_sec
<< 0)
3091 | (timedef
->tm_min
<< 5)
3092 | (timedef
->tm_hour
<< 11);
3098 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3100 * FUNCTION: Returns file times of a file
3102 * FileHandle = File handle of file to get file times from
3103 * File = Pointer to CFFILE node for file
3105 * Status of operation
3111 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3112 FileTimeToDosDateTime(&FileTime
,
3113 &File
->File
.FileDate
,
3114 &File
->File
.FileTime
);
3119 // Check for an absolute path
3120 if (IsSeparator(File
->FileName
[0]))
3122 strcpy(buf
, File
->FileName
);
3126 getcwd(buf
, sizeof(buf
));
3127 strcat(buf
, DIR_SEPARATOR_STRING
);
3128 strcat(buf
, File
->FileName
);
3131 if (stat(buf
, &stbuf
) == -1)
3133 return CAB_STATUS_CANNOT_READ
;
3136 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3138 return CAB_STATUS_SUCCESS
;
3142 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3144 * FUNCTION: Returns attributes on a file
3146 * File = Pointer to CFFILE node for file
3148 * Status of operation
3154 Attributes
= GetFileAttributes(File
->FileName
);
3155 if (Attributes
== -1)
3156 return CAB_STATUS_CANNOT_READ
;
3158 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3159 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3161 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3162 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3164 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3165 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3167 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3168 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3170 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3171 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3176 // Check for an absolute path
3177 if (IsSeparator(File
->FileName
[0]))
3179 strcpy(buf
, File
->FileName
);
3183 getcwd(buf
, sizeof(buf
));
3184 strcat(buf
, DIR_SEPARATOR_STRING
);
3185 strcat(buf
, File
->FileName
);
3188 if (stat(buf
, &stbuf
) == -1)
3190 return CAB_STATUS_CANNOT_READ
;
3194 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3195 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3196 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3199 if (stbuf
.st_mode
& S_IFDIR
)
3200 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3202 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3205 return CAB_STATUS_SUCCESS
;
3209 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3211 * FUNCTION: Sets attributes on a file
3213 * File = Pointer to CFFILE node for file
3215 * Status of operation
3219 unsigned long Attributes
= 0;
3221 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3222 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3224 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3225 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3227 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3228 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3230 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3231 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3233 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3234 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3236 SetFileAttributes(File
->FileName
, Attributes
);
3238 return CAB_STATUS_SUCCESS
;
3240 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3241 return CAB_STATUS_SUCCESS
;
3245 #endif /* CAB_READ_ONLY */