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"));
542 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
543 if (FileHandle
== NULL
) {
544 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
549 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
550 if (CabinetReservedFileSize
== (unsigned long)-1) {
551 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
555 if (CabinetReservedFileSize
== 0)
557 CloseFile(FileHandle
);
561 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
562 if (!CabinetReservedFileBuffer
) {
563 CloseFile(FileHandle
);
567 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
)) {
568 CloseFile(FileHandle
);
572 CloseFile(FileHandle
);
574 strcpy(CabinetReservedFile
, FileName
);
580 char* CCabinet::GetCabinetReservedFile()
582 * FUNCTION: Returns cabionet reserved file
584 * Pointer to string with name of cabinet reserved file
587 return CabinetReservedFile
;
591 unsigned long CCabinet::GetCurrentDiskNumber()
593 * FUNCTION: Returns current disk number
595 * Current disk number
598 return CurrentDiskNumber
;
602 unsigned long CCabinet::Open()
604 * FUNCTION: Opens a cabinet file
606 * Status of operation
609 PCFFOLDER_NODE FolderNode
;
610 unsigned long Status
;
614 unsigned long BytesRead
;
617 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
619 return CAB_STATUS_NOMEMORY
;
622 FileHandle
= CreateFile(CabinetName
, // Open this file
623 GENERIC_READ
, // Open for reading
624 FILE_SHARE_READ
, // Share for reading
626 OPEN_EXISTING
, // Existing file only
627 FILE_ATTRIBUTE_NORMAL
, // Normal file
628 NULL
); // No attribute template
630 if (FileHandle
== INVALID_HANDLE_VALUE
) {
631 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
632 return CAB_STATUS_CANNOT_OPEN
;
635 FileHandle
= fopen(CabinetName
, "rb");
636 if (FileHandle
== NULL
) {
637 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
638 return CAB_STATUS_CANNOT_OPEN
;
644 /* Load CAB header */
645 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
646 != CAB_STATUS_SUCCESS
) {
647 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
648 return CAB_STATUS_INVALID_CAB
;
652 if ((BytesRead
!= sizeof(CFHEADER
)) ||
653 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
654 (CABHeader
.Version
!= CAB_VERSION
) ||
655 (CABHeader
.FolderCount
== 0 ) ||
656 (CABHeader
.FileCount
== 0 ) ||
657 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
))) {
659 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
660 return CAB_STATUS_INVALID_CAB
;
665 /* Read/skip any reserved bytes */
666 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
667 if ((Status
= ReadBlock(&Size
, sizeof(unsigned long), &BytesRead
))
668 != CAB_STATUS_SUCCESS
) {
669 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
670 return CAB_STATUS_INVALID_CAB
;
672 CabinetReserved
= Size
& 0xFFFF;
673 FolderReserved
= (Size
>> 16) & 0xFF;
674 DataReserved
= (Size
>> 24) & 0xFF;
677 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
678 if (GetLastError() != NO_ERROR
) {
679 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
680 return CAB_STATUS_FAILURE
;
683 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0) {
684 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
685 return CAB_STATUS_FAILURE
;
690 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
691 /* Read name of previous cabinet */
692 Status
= ReadString(CabinetPrev
, 256);
693 if (Status
!= CAB_STATUS_SUCCESS
)
695 /* Read label of previous disk */
696 Status
= ReadString(DiskPrev
, 256);
697 if (Status
!= CAB_STATUS_SUCCESS
)
700 strcpy(CabinetPrev
, "");
701 strcpy(DiskPrev
, "");
704 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
705 /* Read name of next cabinet */
706 Status
= ReadString(CabinetNext
, 256);
707 if (Status
!= CAB_STATUS_SUCCESS
)
709 /* Read label of next disk */
710 Status
= ReadString(DiskNext
, 256);
711 if (Status
!= CAB_STATUS_SUCCESS
)
714 strcpy(CabinetNext
, "");
715 strcpy(DiskNext
, "");
718 /* Read all folders */
719 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++) {
720 FolderNode
= NewFolderNode();
722 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
723 return CAB_STATUS_NOMEMORY
;
727 FolderNode
->UncompOffset
= FolderUncompSize
;
729 FolderNode
->Index
= Index
;
731 if ((Status
= ReadBlock(&FolderNode
->Folder
,
732 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
) {
733 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
734 return CAB_STATUS_INVALID_CAB
;
738 /* Read file entries */
739 Status
= ReadFileTable();
740 if (Status
!= CAB_STATUS_SUCCESS
) {
741 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%d).\n", (unsigned int)Status
));
745 /* Read data blocks for all folders */
746 FolderNode
= FolderListHead
;
747 while (FolderNode
!= NULL
) {
748 Status
= ReadDataBlocks(FolderNode
);
749 if (Status
!= CAB_STATUS_SUCCESS
) {
750 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%d).\n", (unsigned int)Status
));
753 FolderNode
= FolderNode
->Next
;
756 return CAB_STATUS_SUCCESS
;
760 void CCabinet::Close()
762 * FUNCTION: Closes the cabinet file
766 CloseFile(FileHandle
);
772 unsigned long CCabinet::FindFirst(char* FileName
,
775 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
777 * FileName = Pointer to search criteria
778 * Search = Pointer to search structure
780 * Status of operation
783 RestartSearch
= false;
784 strncpy(Search
->Search
, FileName
, MAX_PATH
);
785 Search
->Next
= FileListHead
;
786 return FindNext(Search
);
790 unsigned long CCabinet::FindNext(PCAB_SEARCH Search
)
792 * FUNCTION: Finds next file in the cabinet that matches a search criteria
794 * Search = Pointer to search structure
796 * Status of operation
799 unsigned long Status
;
802 Search
->Next
= FileListHead
;
804 /* Skip split files already extracted */
805 while ((Search
->Next
) &&
806 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
807 (Search
->Next
->File
.FileOffset
<= LastFileOffset
)) {
808 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
809 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
810 Search
->Next
= Search
->Next
->Next
;
813 RestartSearch
= false;
816 /* FIXME: Check search criteria */
819 if (strlen(DiskNext
) > 0) {
822 SetCabinetName(CabinetNext
);
824 OnDiskChange(CabinetNext
, DiskNext
);
827 if (Status
!= CAB_STATUS_SUCCESS
)
830 Search
->Next
= FileListHead
;
832 return CAB_STATUS_NOFILE
;
834 return CAB_STATUS_NOFILE
;
837 Search
->File
= &Search
->Next
->File
;
838 Search
->FileName
= Search
->Next
->FileName
;
839 Search
->Next
= Search
->Next
->Next
;
840 return CAB_STATUS_SUCCESS
;
844 unsigned long CCabinet::ExtractFile(char* FileName
)
846 * FUNCTION: Extracts a file from the cabinet
848 * FileName = Pointer to buffer with name of file
850 * Status of operation
854 unsigned long Offset
;
855 unsigned long BytesRead
;
856 unsigned long BytesToRead
;
857 unsigned long BytesWritten
;
858 unsigned long BytesSkipped
;
859 unsigned long BytesToWrite
;
860 unsigned long TotalBytesRead
;
861 unsigned long CurrentOffset
;
862 unsigned char* Buffer
;
863 unsigned char* CurrentBuffer
;
867 unsigned long Status
;
872 char DestName
[MAX_PATH
];
873 char TempName
[MAX_PATH
];
875 Status
= LocateFile(FileName
, &File
);
876 if (Status
!= CAB_STATUS_SUCCESS
) {
877 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
881 LastFileOffset
= File
->File
.FileOffset
;
883 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
) {
885 SelectCodec(CAB_CODEC_RAW
);
888 SelectCodec(CAB_CODEC_MSZIP
);
891 return CAB_STATUS_UNSUPPCOMP
;
894 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
895 (unsigned int)File
->File
.FileOffset
,
896 (unsigned int)File
->File
.FileSize
,
897 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
898 (unsigned int)File
->DataBlock
->UncompOffset
));
900 strcpy(DestName
, DestPath
);
901 strcat(DestName
, FileName
);
903 /* Create destination file, fail if it already exists */
905 DestFile
= CreateFile(DestName
, // Create this file
906 GENERIC_WRITE
, // Open for writing
909 CREATE_NEW
, // New file only
910 FILE_ATTRIBUTE_NORMAL
, // Normal file
911 NULL
); // No attribute template
912 if (DestFile
== INVALID_HANDLE_VALUE
) {
913 /* If file exists, ask to overwrite file */
914 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
915 (OnOverwrite(&File
->File
, FileName
))) {
916 /* Create destination file, overwrite if it already exists */
917 DestFile
= CreateFile(DestName
, // Create this file
918 GENERIC_WRITE
, // Open for writing
921 TRUNCATE_EXISTING
, // Truncate the file
922 FILE_ATTRIBUTE_NORMAL
, // Normal file
923 NULL
); // No attribute template
924 if (DestFile
== INVALID_HANDLE_VALUE
)
925 return CAB_STATUS_CANNOT_CREATE
;
927 if (Status
== ERROR_FILE_EXISTS
)
928 return CAB_STATUS_FILE_EXISTS
;
930 return CAB_STATUS_CANNOT_CREATE
;
934 DestFile
= fopen(DestName
, "rb");
935 if (DestFile
!= NULL
) {
937 /* If file exists, ask to overwrite file */
938 if (OnOverwrite(&File
->File
, FileName
)) {
939 DestFile
= fopen(DestName
, "w+b");
940 if (DestFile
== NULL
) {
941 return CAB_STATUS_CANNOT_CREATE
;
944 return CAB_STATUS_FILE_EXISTS
;
947 DestFile
= fopen(DestName
, "w+b");
948 if (DestFile
== NULL
) {
949 return CAB_STATUS_CANNOT_CREATE
;
954 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
)) {
956 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
957 return CAB_STATUS_CANNOT_WRITE
;
960 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
962 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
964 SetAttributesOnFile(File
);
966 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
969 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
970 return CAB_STATUS_NOMEMORY
;
973 /* Call OnExtract event handler */
974 OnExtract(&File
->File
, FileName
);
976 /* Search to start of file */
978 Offset
= SetFilePointer(FileHandle
,
979 File
->DataBlock
->AbsoluteOffset
,
982 if (GetLastError() != NO_ERROR
) {
983 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
984 return CAB_STATUS_INVALID_CAB
;
987 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
988 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
989 return CAB_STATUS_FAILURE
;
991 Offset
= ftell(FileHandle
);
994 Size
= File
->File
.FileSize
;
995 Offset
= File
->File
.FileOffset
;
996 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1000 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1003 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%d) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
1004 File
->DataBlock
->UncompOffset
, (unsigned int)ReuseBlock
, Offset
, Size
,
1007 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0)) {
1009 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1011 CurrentBuffer
= Buffer
;
1014 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1016 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1017 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1018 CloseFile(DestFile
);
1020 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1021 return CAB_STATUS_INVALID_CAB
;
1024 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes)\n",
1025 (unsigned int)CFData
.Checksum
,
1026 (unsigned int)CFData
.CompSize
,
1027 (unsigned int)CFData
.UncompSize
));
1029 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1031 BytesToRead
= CFData
.CompSize
;
1033 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1034 (long unsigned int)CurrentBuffer
, (long unsigned int)Buffer
));
1036 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1037 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
)) {
1038 CloseFile(DestFile
);
1040 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1041 return CAB_STATUS_INVALID_CAB
;
1044 /* FIXME: Does not work with files generated by makecab.exe */
1046 if (CFData.Checksum != 0) {
1047 unsigned long Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1048 if (Checksum != CFData.Checksum) {
1049 CloseFile(DestFile);
1051 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1052 Checksum, CFData.Checksum));
1053 return CAB_STATUS_INVALID_CAB;
1057 TotalBytesRead
+= BytesRead
;
1059 CurrentBuffer
+= BytesRead
;
1061 if (CFData
.UncompSize
== 0) {
1062 if (strlen(DiskNext
) == 0)
1063 return CAB_STATUS_NOFILE
;
1065 /* CloseCabinet() will destroy all file entries so in case
1066 FileName refers to the FileName field of a CFFOLDER_NODE
1067 structure, we have to save a copy of the filename */
1068 strcpy(TempName
, FileName
);
1072 SetCabinetName(CabinetNext
);
1074 OnDiskChange(CabinetNext
, DiskNext
);
1077 if (Status
!= CAB_STATUS_SUCCESS
)
1080 /* The first data block of the file will not be
1081 found as it is located in the previous file */
1082 Status
= LocateFile(TempName
, &File
);
1083 if (Status
== CAB_STATUS_NOFILE
) {
1084 DPRINT(MID_TRACE
, ("Cannot locate file (%d).\n", (unsigned int)Status
));
1088 /* The file is continued in the first data block in the folder */
1089 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1091 /* Search to start of file */
1093 (unsigned int)SetFilePointer(FileHandle
,
1094 File
->DataBlock
->AbsoluteOffset
,
1097 if (GetLastError() != NO_ERROR
) {
1098 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1099 return CAB_STATUS_INVALID_CAB
;
1102 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0) {
1103 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1104 return CAB_STATUS_INVALID_CAB
;
1108 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1109 (unsigned int)File
->File
.FileOffset
,
1110 (unsigned int)File
->File
.FileSize
,
1111 (unsigned int)File
->DataBlock
->AbsoluteOffset
,
1112 (unsigned int)File
->DataBlock
->UncompOffset
));
1114 CurrentDataNode
= File
->DataBlock
;
1117 RestartSearch
= true;
1119 } while (CFData
.UncompSize
== 0);
1121 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1123 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1124 if (Status
!= CS_SUCCESS
) {
1125 CloseFile(DestFile
);
1127 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1128 if (Status
== CS_NOMEMORY
)
1129 return CAB_STATUS_NOMEMORY
;
1130 return CAB_STATUS_INVALID_CAB
;
1133 if (BytesToWrite
!= CFData
.UncompSize
) {
1134 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1135 BytesToWrite
, CFData
.UncompSize
));
1136 return CAB_STATUS_INVALID_CAB
;
1139 BytesLeftInBlock
= BytesToWrite
;
1141 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%d)\n", (unsigned int)ReuseBlock
));
1143 BytesToWrite
= BytesLeftInBlock
;
1145 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1146 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1147 CurrentDataNode
->Data
.CompSize
));
1149 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1150 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
))) {
1151 CloseFile(DestFile
);
1153 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
1154 return CAB_STATUS_INVALID_CAB
;
1157 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1158 CFData
.CompSize
, CFData
.UncompSize
));
1160 /* Go to next data block */
1162 (unsigned int)SetFilePointer(FileHandle
,
1163 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1164 CurrentDataNode
->Data
.CompSize
,
1167 if (GetLastError() != NO_ERROR
) {
1168 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1169 return CAB_STATUS_INVALID_CAB
;
1172 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1173 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0) {
1174 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1175 return CAB_STATUS_INVALID_CAB
;
1183 BytesSkipped
= (Offset
- CurrentOffset
);
1187 BytesToWrite
-= BytesSkipped
;
1189 if (Size
< BytesToWrite
)
1190 BytesToWrite
= Size
;
1192 DPRINT(MAX_TRACE
, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1193 (unsigned int)Offset
,
1194 (unsigned int)CurrentOffset
,
1195 (unsigned int)BytesToWrite
,
1196 (unsigned int)BytesSkipped
, (unsigned int)Skip
,
1197 (unsigned int)Size
));
1200 if (!WriteFile(DestFile
, (void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1201 BytesToWrite
, &BytesWritten
, NULL
) ||
1202 (BytesToWrite
!= BytesWritten
)) {
1203 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1205 BytesWritten
= BytesToWrite
;
1206 if (fwrite((void*)((unsigned long)OutputBuffer
+ BytesSkipped
),
1207 BytesToWrite
, 1, DestFile
) < 1) {
1209 CloseFile(DestFile
);
1211 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1212 return CAB_STATUS_CANNOT_WRITE
;
1214 Size
-= BytesToWrite
;
1216 CurrentOffset
+= BytesToWrite
;
1218 /* Don't skip any more bytes */
1223 CloseFile(DestFile
);
1227 return CAB_STATUS_SUCCESS
;
1231 void CCabinet::SelectCodec(unsigned long Id
)
1233 * FUNCTION: Selects codec engine to use
1235 * Id = Codec identifier
1238 if (CodecSelected
) {
1242 CodecSelected
= false;
1249 Codec
= new CRawCodec();
1252 case CAB_CODEC_MSZIP
:
1253 Codec
= new CMSZipCodec();
1260 CodecSelected
= true;
1264 #ifndef CAB_READ_ONLY
1266 /* CAB write methods */
1268 unsigned long CCabinet::NewCabinet()
1270 * FUNCTION: Creates a new cabinet
1272 * Status of operation
1275 unsigned long Status
;
1277 CurrentDiskNumber
= 0;
1279 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1280 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1281 if ((!OutputBuffer
) || (!InputBuffer
)) {
1282 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1283 return CAB_STATUS_NOMEMORY
;
1285 CurrentIBuffer
= InputBuffer
;
1286 CurrentIBufferSize
= 0;
1288 CABHeader
.Signature
= CAB_SIGNATURE
;
1289 CABHeader
.Reserved1
= 0; // Not used
1290 CABHeader
.CabinetSize
= 0; // Not yet known
1291 CABHeader
.Reserved2
= 0; // Not used
1292 CABHeader
.Reserved3
= 0; // Not used
1293 CABHeader
.Version
= CAB_VERSION
;
1294 CABHeader
.FolderCount
= 0; // Not yet known
1295 CABHeader
.FileCount
= 0; // Not yet known
1296 CABHeader
.Flags
= 0; // Not yet known
1297 // FIXME: Should be random
1298 CABHeader
.SetID
= 0x534F;
1299 CABHeader
.CabinetNumber
= 0;
1302 TotalFolderSize
= 0;
1305 DiskSize
= sizeof(CFHEADER
);
1307 InitCabinetHeader();
1309 // NextFolderNumber is 0-based
1310 NextFolderNumber
= 0;
1312 CurrentFolderNode
= NULL
;
1313 Status
= NewFolder();
1314 if (Status
!= CAB_STATUS_SUCCESS
)
1317 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1319 ScratchFile
= new CCFDATAStorage
;
1321 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1322 return CAB_STATUS_NOMEMORY
;
1325 Status
= ScratchFile
->Create("~CAB.tmp");
1327 CreateNewFolder
= false;
1329 CreateNewDisk
= false;
1331 PrevCabinetNumber
= 0;
1337 unsigned long CCabinet::NewDisk()
1339 * FUNCTION: Forces a new disk to be created
1341 * Status of operation
1344 // NextFolderNumber is 0-based
1345 NextFolderNumber
= 1;
1347 CreateNewDisk
= false;
1349 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1351 InitCabinetHeader();
1353 CurrentFolderNode
->TotalFolderSize
= 0;
1355 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1357 return CAB_STATUS_SUCCESS
;
1361 unsigned long CCabinet::NewFolder()
1363 * FUNCTION: Forces a new folder to be created
1365 * Status of operation
1368 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1370 CurrentFolderNode
= NewFolderNode();
1371 if (!CurrentFolderNode
) {
1372 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1373 return CAB_STATUS_NOMEMORY
;
1378 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1380 case CAB_CODEC_MSZIP
:
1381 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1384 return CAB_STATUS_UNSUPPCOMP
;
1387 /* FIXME: This won't work if no files are added to the new folder */
1389 DiskSize
+= sizeof(CFFOLDER
);
1391 TotalFolderSize
+= sizeof(CFFOLDER
);
1395 CABHeader
.FolderCount
++;
1399 return CAB_STATUS_SUCCESS
;
1403 unsigned long CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1405 * FUNCTION: Writes a file to the scratch file
1407 * FileNode = Pointer to file node
1409 * Status of operation
1412 unsigned long BytesToRead
;
1413 unsigned long BytesRead
;
1414 unsigned long Status
;
1417 if (!ContinueFile
) {
1418 /* Try to open file */
1420 SourceFile
= CreateFile(
1421 FileNode
->FileName
, // Open this file
1422 GENERIC_READ
, // Open for reading
1423 FILE_SHARE_READ
, // Share for reading
1424 NULL
, // No security
1425 OPEN_EXISTING
, // File must exist
1426 FILE_ATTRIBUTE_NORMAL
, // Normal file
1427 NULL
); // No attribute template
1428 if (SourceFile
== INVALID_HANDLE_VALUE
) {
1429 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1430 return CAB_STATUS_NOFILE
;
1433 SourceFile
= fopen(FileNode
->FileName
, "rb");
1434 if (SourceFile
== NULL
) {
1435 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1436 return CAB_STATUS_NOFILE
;
1440 if (CreateNewFolder
) {
1441 /* There is always a new folder after
1442 a split file is completely stored */
1443 Status
= NewFolder();
1444 if (Status
!= CAB_STATUS_SUCCESS
)
1446 CreateNewFolder
= false;
1449 /* Call OnAdd event handler */
1450 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1452 TotalBytesLeft
= FileNode
->File
.FileSize
;
1454 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1455 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1456 FileNode
->File
.FileControlID
= (unsigned short)(NextFolderNumber
- 1);
1457 CurrentFolderNode
->Commit
= true;
1458 PrevCabinetNumber
= CurrentDiskNumber
;
1460 Size
= sizeof(CFFILE
) + strlen(GetFileName(FileNode
->FileName
)) + 1;
1461 CABHeader
.FileTableOffset
+= Size
;
1462 TotalFileSize
+= Size
;
1466 FileNode
->Commit
= true;
1468 if (TotalBytesLeft
> 0) {
1470 if (TotalBytesLeft
> (unsigned long)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1471 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1473 BytesToRead
= TotalBytesLeft
;
1475 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
)) {
1476 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1477 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1478 return CAB_STATUS_INVALID_CAB
;
1481 *(unsigned char**)&CurrentIBuffer
+= BytesRead
;
1483 CurrentIBufferSize
+= (unsigned short)BytesRead
;
1485 if (CurrentIBufferSize
== CAB_BLOCKSIZE
) {
1486 Status
= WriteDataBlock();
1487 if (Status
!= CAB_STATUS_SUCCESS
)
1490 TotalBytesLeft
-= BytesRead
;
1491 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1494 if (TotalBytesLeft
== 0) {
1495 CloseFile(SourceFile
);
1496 FileNode
->Delete
= true;
1498 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) {
1499 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1500 CurrentFolderNode
->Delete
= true;
1502 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1503 Status
= WriteDataBlock();
1504 if (Status
!= CAB_STATUS_SUCCESS
)
1508 CreateNewFolder
= true;
1511 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1512 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1514 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1517 return CAB_STATUS_SUCCESS
;
1521 unsigned long CCabinet::WriteDisk(unsigned long MoreDisks
)
1523 * FUNCTION: Forces the current disk to be written
1525 * MoreDisks = true if there is one or more disks after this disk
1527 * Status of operation
1530 PCFFILE_NODE FileNode
;
1531 unsigned long Status
;
1533 ContinueFile
= false;
1534 FileNode
= FileListHead
;
1535 while (FileNode
!= NULL
) {
1537 Status
= WriteFileToScratchStorage(FileNode
);
1538 if (Status
!= CAB_STATUS_SUCCESS
)
1540 if (CreateNewDisk
) {
1541 /* A data block could span more than two
1542 disks if MaxDiskSize is very small */
1543 while (CreateNewDisk
) {
1544 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1549 ContinueFile
= true;
1550 CreateNewDisk
= false;
1552 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1553 CurrentIBufferSize
, CurrentOBufferSize
));
1555 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1556 Status
= WriteDataBlock();
1557 if (Status
!= CAB_STATUS_SUCCESS
)
1562 ContinueFile
= false;
1563 FileNode
= FileNode
->Next
;
1567 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1568 /* A data block could span more than two
1569 disks if MaxDiskSize is very small */
1571 ASSERT(CreateNewDisk
== false);
1574 if (CreateNewDisk
) {
1575 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1579 CreateNewDisk
= false;
1581 ASSERT(FileNode
== FileListHead
);
1584 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0)) {
1585 Status
= WriteDataBlock();
1586 if (Status
!= CAB_STATUS_SUCCESS
)
1589 } while (CreateNewDisk
);
1591 CommitDisk(MoreDisks
);
1593 return CAB_STATUS_SUCCESS
;
1597 unsigned long CCabinet::CommitDisk(unsigned long MoreDisks
)
1599 * FUNCTION: Commits the current disk
1601 * MoreDisks = true if there is one or more disks after this disk
1603 * Status of operation
1606 PCFFOLDER_NODE FolderNode
;
1607 unsigned long Status
;
1609 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1611 /* Create file, fail if it already exists */
1613 FileHandle
= CreateFile(CabinetName
, // Create this file
1614 GENERIC_WRITE
, // Open for writing
1616 NULL
, // No security
1617 CREATE_NEW
, // New file only
1618 FILE_ATTRIBUTE_NORMAL
, // Normal file
1619 NULL
); // No attribute template
1620 if (FileHandle
== INVALID_HANDLE_VALUE
) {
1621 unsigned long Status
;
1622 /* If file exists, ask to overwrite file */
1623 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1624 (OnOverwrite(NULL
, CabinetName
))) {
1626 /* Create cabinet file, overwrite if it already exists */
1627 FileHandle
= CreateFile(CabinetName
, // Create this file
1628 GENERIC_WRITE
, // Open for writing
1630 NULL
, // No security
1631 TRUNCATE_EXISTING
, // Truncate the file
1632 FILE_ATTRIBUTE_NORMAL
, // Normal file
1633 NULL
); // No attribute template
1634 if (FileHandle
== INVALID_HANDLE_VALUE
)
1635 return CAB_STATUS_CANNOT_CREATE
;
1637 if (Status
== ERROR_FILE_EXISTS
)
1638 return CAB_STATUS_FILE_EXISTS
;
1640 return CAB_STATUS_CANNOT_CREATE
;
1644 FileHandle
= fopen(CabinetName
, "rb");
1645 if (FileHandle
!= NULL
) {
1647 /* If file exists, ask to overwrite file */
1648 if (OnOverwrite(NULL
, CabinetName
)) {
1649 FileHandle
= fopen(CabinetName
, "w+b");
1650 if (FileHandle
== NULL
) {
1651 return CAB_STATUS_CANNOT_CREATE
;
1654 return CAB_STATUS_FILE_EXISTS
;
1657 FileHandle
= fopen(CabinetName
, "w+b");
1658 if (FileHandle
== NULL
) {
1659 return CAB_STATUS_CANNOT_CREATE
;
1664 WriteCabinetHeader(MoreDisks
!= 0);
1666 Status
= WriteFolderEntries();
1667 if (Status
!= CAB_STATUS_SUCCESS
)
1670 /* Write file entries */
1673 /* Write data blocks */
1674 FolderNode
= FolderListHead
;
1675 while (FolderNode
!= NULL
) {
1676 if (FolderNode
->Commit
) {
1677 Status
= CommitDataBlocks(FolderNode
);
1678 if (Status
!= CAB_STATUS_SUCCESS
)
1680 /* Remove data blocks for folder */
1681 DestroyDataNodes(FolderNode
);
1683 FolderNode
= FolderNode
->Next
;
1686 CloseFile(FileHandle
);
1688 ScratchFile
->Truncate();
1690 return CAB_STATUS_SUCCESS
;
1694 unsigned long CCabinet::CloseDisk()
1696 * FUNCTION: Closes the current disk
1698 * Status of operation
1701 DestroyDeletedFileNodes();
1703 /* Destroy folder nodes that are completely stored */
1704 DestroyDeletedFolderNodes();
1706 CurrentDiskNumber
++;
1708 return CAB_STATUS_SUCCESS
;
1712 unsigned long CCabinet::CloseCabinet()
1714 * FUNCTION: Closes the current cabinet
1716 * Status of operation
1719 unsigned long Status
;
1723 DestroyFolderNodes();
1726 FreeMemory(InputBuffer
);
1731 FreeMemory(OutputBuffer
);
1732 OutputBuffer
= NULL
;
1738 Status
= ScratchFile
->Destroy();
1743 return CAB_STATUS_SUCCESS
;
1747 unsigned long CCabinet::AddFile(char* FileName
)
1749 * FUNCTION: Adds a file to the current disk
1751 * FileName = Pointer to string with file name (full path)
1753 * Status of operation
1757 PCFFILE_NODE FileNode
;
1760 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1762 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1763 return CAB_STATUS_NOMEMORY
;
1765 strcpy(NewFileName
, FileName
);
1766 ConvertPath(NewFileName
, false);
1768 /* Try to open file */
1770 SrcFile
= CreateFile(
1771 NewFileName
, // Open this file
1772 GENERIC_READ
, // Open for reading
1773 FILE_SHARE_READ
, // Share for reading
1774 NULL
, // No security
1775 OPEN_EXISTING
, // File must exist
1776 FILE_ATTRIBUTE_NORMAL
, // Normal file
1777 NULL
); // No attribute template
1778 if (SrcFile
== INVALID_HANDLE_VALUE
) {
1779 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1780 FreeMemory(NewFileName
);
1781 return CAB_STATUS_CANNOT_OPEN
;
1784 SrcFile
= fopen(NewFileName
, "rb");
1785 if (SrcFile
== NULL
) {
1786 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1787 FreeMemory(NewFileName
);
1788 return CAB_STATUS_CANNOT_OPEN
;
1792 FileNode
= NewFileNode();
1794 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1795 FreeMemory(NewFileName
);
1796 return CAB_STATUS_NOMEMORY
;
1799 FileNode
->FolderNode
= CurrentFolderNode
;
1800 FileNode
->FileName
= NewFileName
;
1802 /* FIXME: Check for and handle large files (>= 2GB) */
1803 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1804 if (FileNode
->File
.FileSize
== (unsigned long)-1) {
1805 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1806 FreeMemory(NewFileName
);
1807 return CAB_STATUS_CANNOT_READ
;
1810 GetFileTimes(SrcFile
, FileNode
);
1812 GetAttributesOnFile(FileNode
);
1816 return CAB_STATUS_SUCCESS
;
1820 void CCabinet::SetMaxDiskSize(unsigned long Size
)
1822 * FUNCTION: Sets the maximum size of the current disk
1824 * Size = Maximum size of current disk (0 means no maximum size)
1830 #endif /* CAB_READ_ONLY */
1833 /* Default event handlers */
1835 bool CCabinet::OnOverwrite(PCFFILE File
,
1838 * FUNCTION: Called when extracting a file and it already exists
1840 * File = Pointer to CFFILE for file being extracted
1841 * FileName = Pointer to buffer with name of file (full path)
1843 * true if the file should be overwritten, false if not
1850 void CCabinet::OnExtract(PCFFILE File
,
1853 * FUNCTION: Called just before extracting a file
1855 * File = Pointer to CFFILE for file being extracted
1856 * FileName = Pointer to buffer with name of file (full path)
1862 void CCabinet::OnDiskChange(char* CabinetName
,
1865 * FUNCTION: Called when a new disk is to be processed
1867 * CabinetName = Pointer to buffer with name of cabinet
1868 * DiskLabel = Pointer to buffer with label of disk
1874 #ifndef CAB_READ_ONLY
1876 void CCabinet::OnAdd(PCFFILE File
,
1879 * FUNCTION: Called just before adding a file to a cabinet
1881 * File = Pointer to CFFILE for file being added
1882 * FileName = Pointer to buffer with name of file (full path)
1888 bool CCabinet::OnDiskLabel(unsigned long Number
, char* Label
)
1890 * FUNCTION: Called when a disk needs a label
1892 * Number = Cabinet number that needs a label
1893 * Label = Pointer to buffer to place label of disk
1895 * true if a disk label was returned, false if not
1902 bool CCabinet::OnCabinetName(unsigned long Number
, char* Name
)
1904 * FUNCTION: Called when a cabinet needs a name
1906 * Number = Disk number that needs a name
1907 * Name = Pointer to buffer to place name of cabinet
1909 * true if a cabinet name was returned, false if not
1915 #endif /* CAB_READ_ONLY */
1917 PCFFOLDER_NODE
CCabinet::LocateFolderNode(unsigned long Index
)
1919 * FUNCTION: Locates a folder node
1921 * Index = Folder index
1923 * Pointer to folder node or NULL if the folder node was not found
1926 PCFFOLDER_NODE Node
;
1929 case CAB_FILE_SPLIT
:
1930 return FolderListTail
;
1932 case CAB_FILE_CONTINUED
:
1933 case CAB_FILE_PREV_NEXT
:
1934 return FolderListHead
;
1937 Node
= FolderListHead
;
1938 while (Node
!= NULL
) {
1939 if (Node
->Index
== Index
)
1947 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
1949 * FUNCTION: Returns the absolute offset of a file
1951 * File = Pointer to CFFILE_NODE structure for file
1953 * Status of operation
1958 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1959 (char*)File
->FileName
,
1960 (unsigned int)File
->File
.FileOffset
,
1961 (unsigned int)File
->File
.FileSize
));
1963 Node
= CurrentFolderNode
->DataListHead
;
1964 while (Node
!= NULL
) {
1966 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1967 (unsigned int)Node
->UncompOffset
,
1968 (unsigned int)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
1969 (unsigned int)Node
->Data
.UncompSize
));
1971 /* Node->Data.UncompSize will be 0 if the block is split
1972 (ie. it is the last block in this cabinet) */
1973 if ((Node
->Data
.UncompSize
== 0) ||
1974 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
1975 (File
->File
.FileOffset
< Node
->UncompOffset
+
1976 Node
->Data
.UncompSize
))) {
1977 File
->DataBlock
= Node
;
1978 return CAB_STATUS_SUCCESS
;
1983 return CAB_STATUS_INVALID_CAB
;
1987 unsigned long CCabinet::LocateFile(char* FileName
,
1990 * FUNCTION: Locates a file in the cabinet
1992 * FileName = Pointer to string with name of file to locate
1993 * File = Address of pointer to CFFILE_NODE structure to fill
1995 * Status of operation
1997 * Current folder is set to the folder of the file
2001 unsigned long Status
;
2003 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2005 Node
= FileListHead
;
2006 while (Node
!= NULL
) {
2007 if (strcasecmp(FileName
, Node
->FileName
) == 0) {
2009 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2010 if (!CurrentFolderNode
) {
2011 DPRINT(MID_TRACE
, ("Folder with index number (%d) not found.\n",
2012 (unsigned int)Node
->File
.FileControlID
));
2013 return CAB_STATUS_INVALID_CAB
;
2016 if (Node
->DataBlock
== NULL
) {
2017 Status
= GetAbsoluteOffset(Node
);
2019 Status
= CAB_STATUS_SUCCESS
;
2025 return CAB_STATUS_NOFILE
;
2029 unsigned long CCabinet::ReadString(char* String
, unsigned long MaxLength
)
2031 * FUNCTION: Reads a NULL-terminated string from the cabinet
2033 * String = Pointer to buffer to place string
2034 * MaxLength = Maximum length of string
2036 * Status of operation
2039 unsigned long BytesRead
;
2040 unsigned long Offset
;
2041 unsigned long Status
;
2048 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2051 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2052 return CAB_STATUS_INVALID_CAB
;
2055 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2056 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
)) {
2057 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2058 return CAB_STATUS_INVALID_CAB
;
2061 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++) {
2062 if (String
[Size
] == '\0') {
2068 Offset
+= BytesRead
;
2072 /* Back up some bytes */
2073 Size
= (BytesRead
- Size
) - 1;
2075 SetLastError(NO_ERROR
);
2076 (unsigned int)SetFilePointer(FileHandle
,
2080 if (GetLastError() != NO_ERROR
) {
2081 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2082 return CAB_STATUS_INVALID_CAB
;
2085 if (fseek(FileHandle
, (off_t
)(-(long)Size
), SEEK_CUR
) != 0) {
2086 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2087 return CAB_STATUS_INVALID_CAB
;
2090 return CAB_STATUS_SUCCESS
;
2094 unsigned long CCabinet::ReadFileTable()
2096 * FUNCTION: Reads the file table from the cabinet file
2098 * Status of operation
2102 unsigned long Status
;
2103 unsigned long BytesRead
;
2106 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2107 CABHeader
.FileTableOffset
));
2109 /* Seek to file table */
2111 SetLastError(NO_ERROR
);
2112 (unsigned int)SetFilePointer(FileHandle
,
2113 CABHeader
.FileTableOffset
,
2116 if (GetLastError() != NO_ERROR
) {
2117 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2118 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2119 return CAB_STATUS_INVALID_CAB
;
2122 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0) {
2123 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2124 return CAB_STATUS_INVALID_CAB
;
2128 for (i
= 0; i
< CABHeader
.FileCount
; i
++) {
2129 File
= NewFileNode();
2131 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2132 return CAB_STATUS_NOMEMORY
;
2135 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2136 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2137 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2138 return CAB_STATUS_INVALID_CAB
;
2141 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2142 if (!File
->FileName
) {
2143 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2144 return CAB_STATUS_NOMEMORY
;
2147 /* Read file name */
2148 Status
= ReadString(File
->FileName
, MAX_PATH
);
2149 if (Status
!= CAB_STATUS_SUCCESS
)
2152 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2153 (char*)File
->FileName
,
2154 (unsigned int)File
->File
.FileOffset
,
2155 (unsigned int)File
->File
.FileSize
,
2156 (unsigned int)File
->File
.FileControlID
));
2159 return CAB_STATUS_SUCCESS
;
2163 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2165 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2167 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2169 * Status of operation
2172 unsigned long AbsoluteOffset
;
2173 unsigned long UncompOffset
;
2175 unsigned long BytesRead
;
2176 unsigned long Status
;
2179 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2180 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2182 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2183 UncompOffset
= FolderNode
->UncompOffset
;
2185 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++) {
2186 Node
= NewDataNode(FolderNode
);
2188 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2189 return CAB_STATUS_NOMEMORY
;
2192 /* Seek to data block */
2194 SetLastError(NO_ERROR
);
2195 (unsigned int)SetFilePointer(FileHandle
,
2199 if (GetLastError() != NO_ERROR
) {
2200 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2201 return CAB_STATUS_INVALID_CAB
;
2204 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0) {
2205 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2206 return CAB_STATUS_INVALID_CAB
;
2210 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2211 &BytesRead
)) != CAB_STATUS_SUCCESS
) {
2212 DPRINT(MIN_TRACE
, ("Cannot read from file (%d).\n", (unsigned int)Status
));
2213 return CAB_STATUS_INVALID_CAB
;
2216 DPRINT(MAX_TRACE
, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2217 (unsigned int)AbsoluteOffset
,
2218 (unsigned int)UncompOffset
,
2219 (unsigned int)Node
->Data
.Checksum
,
2220 (unsigned int)Node
->Data
.CompSize
,
2221 (unsigned int)Node
->Data
.UncompSize
));
2223 Node
->AbsoluteOffset
= AbsoluteOffset
;
2224 Node
->UncompOffset
= UncompOffset
;
2226 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2227 UncompOffset
+= Node
->Data
.UncompSize
;
2230 FolderUncompSize
= UncompOffset
;
2232 return CAB_STATUS_SUCCESS
;
2236 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2238 * FUNCTION: Creates a new folder node
2240 * Pointer to node if there was enough free memory available, otherwise NULL
2243 PCFFOLDER_NODE Node
;
2245 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2249 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2251 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2253 Node
->Prev
= FolderListTail
;
2255 if (FolderListTail
!= NULL
) {
2256 FolderListTail
->Next
= Node
;
2258 FolderListHead
= Node
;
2260 FolderListTail
= Node
;
2266 PCFFILE_NODE
CCabinet::NewFileNode()
2268 * FUNCTION: Creates a new file node
2270 * FolderNode = Pointer to folder node to bind file to
2272 * Pointer to node if there was enough free memory available, otherwise NULL
2277 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2281 memset(Node
, 0, sizeof(CFFILE_NODE
));
2283 Node
->Prev
= FileListTail
;
2285 if (FileListTail
!= NULL
) {
2286 FileListTail
->Next
= Node
;
2288 FileListHead
= Node
;
2290 FileListTail
= Node
;
2296 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2298 * FUNCTION: Creates a new data block node
2300 * FolderNode = Pointer to folder node to bind data block to
2302 * Pointer to node if there was enough free memory available, otherwise NULL
2307 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2311 memset(Node
, 0, sizeof(CFDATA_NODE
));
2313 Node
->Prev
= FolderNode
->DataListTail
;
2315 if (FolderNode
->DataListTail
!= NULL
) {
2316 FolderNode
->DataListTail
->Next
= Node
;
2318 FolderNode
->DataListHead
= Node
;
2320 FolderNode
->DataListTail
= Node
;
2326 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2328 * FUNCTION: Destroys data block nodes bound to a folder node
2330 * FolderNode = Pointer to folder node
2333 PCFDATA_NODE PrevNode
;
2334 PCFDATA_NODE NextNode
;
2336 NextNode
= FolderNode
->DataListHead
;
2337 while (NextNode
!= NULL
) {
2338 PrevNode
= NextNode
->Next
;
2339 FreeMemory(NextNode
);
2340 NextNode
= PrevNode
;
2342 FolderNode
->DataListHead
= NULL
;
2343 FolderNode
->DataListTail
= NULL
;
2347 void CCabinet::DestroyFileNodes()
2349 * FUNCTION: Destroys file nodes
2351 * FolderNode = Pointer to folder node
2354 PCFFILE_NODE PrevNode
;
2355 PCFFILE_NODE NextNode
;
2357 NextNode
= FileListHead
;
2358 while (NextNode
!= NULL
) {
2359 PrevNode
= NextNode
->Next
;
2360 if (NextNode
->FileName
)
2361 FreeMemory(NextNode
->FileName
);
2362 FreeMemory(NextNode
);
2363 NextNode
= PrevNode
;
2365 FileListHead
= NULL
;
2366 FileListTail
= NULL
;
2370 void CCabinet::DestroyDeletedFileNodes()
2372 * FUNCTION: Destroys file nodes that are marked for deletion
2375 PCFFILE_NODE CurNode
;
2376 PCFFILE_NODE NextNode
;
2378 CurNode
= FileListHead
;
2379 while (CurNode
!= NULL
) {
2380 NextNode
= CurNode
->Next
;
2382 if (CurNode
->Delete
) {
2383 if (CurNode
->Prev
!= NULL
) {
2384 CurNode
->Prev
->Next
= CurNode
->Next
;
2386 FileListHead
= CurNode
->Next
;
2388 FileListHead
->Prev
= NULL
;
2391 if (CurNode
->Next
!= NULL
) {
2392 CurNode
->Next
->Prev
= CurNode
->Prev
;
2394 FileListTail
= CurNode
->Prev
;
2396 FileListTail
->Next
= NULL
;
2399 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2401 TotalFileSize
-= (sizeof(CFFILE
) + strlen(GetFileName(CurNode
->FileName
)) + 1);
2403 if (CurNode
->FileName
)
2404 FreeMemory(CurNode
->FileName
);
2405 FreeMemory(CurNode
);
2412 void CCabinet::DestroyFolderNodes()
2414 * FUNCTION: Destroys folder nodes
2417 PCFFOLDER_NODE PrevNode
;
2418 PCFFOLDER_NODE NextNode
;
2420 NextNode
= FolderListHead
;
2421 while (NextNode
!= NULL
) {
2422 PrevNode
= NextNode
->Next
;
2423 DestroyDataNodes(NextNode
);
2424 FreeMemory(NextNode
);
2425 NextNode
= PrevNode
;
2427 FolderListHead
= NULL
;
2428 FolderListTail
= NULL
;
2432 void CCabinet::DestroyDeletedFolderNodes()
2434 * FUNCTION: Destroys folder nodes that are marked for deletion
2437 PCFFOLDER_NODE CurNode
;
2438 PCFFOLDER_NODE NextNode
;
2440 CurNode
= FolderListHead
;
2441 while (CurNode
!= NULL
) {
2442 NextNode
= CurNode
->Next
;
2444 if (CurNode
->Delete
) {
2445 if (CurNode
->Prev
!= NULL
) {
2446 CurNode
->Prev
->Next
= CurNode
->Next
;
2448 FolderListHead
= CurNode
->Next
;
2450 FolderListHead
->Prev
= NULL
;
2453 if (CurNode
->Next
!= NULL
) {
2454 CurNode
->Next
->Prev
= CurNode
->Prev
;
2456 FolderListTail
= CurNode
->Prev
;
2458 FolderListTail
->Next
= NULL
;
2461 DestroyDataNodes(CurNode
);
2462 FreeMemory(CurNode
);
2464 TotalFolderSize
-= sizeof(CFFOLDER
);
2471 unsigned long CCabinet::ComputeChecksum(void* Buffer
,
2475 * FUNCTION: Computes checksum for data block
2477 * Buffer = Pointer to data buffer
2478 * Size = Length of data buffer
2479 * Seed = Previously computed checksum
2481 * Checksum of buffer
2484 int UlongCount
; // Number of ULONGs in block
2485 unsigned long Checksum
; // Checksum accumulator
2489 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2490 won't accept checksums computed by this routine */
2492 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%X) Size (%d)\n", (unsigned int)Buffer
, Size
));
2494 UlongCount
= Size
/ 4; // Number of ULONGs
2495 Checksum
= Seed
; // Init checksum
2496 pb
= (unsigned char*)Buffer
; // Start at front of data block
2498 /* Checksum integral multiple of ULONGs */
2499 while (UlongCount
-- > 0) {
2500 /* NOTE: Build unsigned long in big/little-endian independent manner */
2501 ul
= *pb
++; // Get low-order byte
2502 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2503 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3nd byte
2504 ul
|= (((unsigned long)(*pb
++)) << 24); // Add 4th byte
2506 Checksum
^= ul
; // Update checksum
2509 /* Checksum remainder bytes */
2513 ul
|= (((unsigned long)(*pb
++)) << 16); // Add 3rd byte
2515 ul
|= (((unsigned long)(*pb
++)) << 8); // Add 2nd byte
2517 ul
|= *pb
++; // Get low-order byte
2521 Checksum
^= ul
; // Update checksum
2523 /* Return computed checksum */
2528 unsigned long CCabinet::ReadBlock(void* Buffer
,
2530 unsigned long* BytesRead
)
2532 * FUNCTION: Read a block of data from file
2534 * Buffer = Pointer to data buffer
2535 * Size = Length of data buffer
2536 * BytesRead = Pointer to unsigned long that on return will contain
2537 * number of bytes read
2539 * Status of operation
2542 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2543 return CAB_STATUS_INVALID_CAB
;
2544 return CAB_STATUS_SUCCESS
;
2547 #ifndef CAB_READ_ONLY
2549 unsigned long CCabinet::InitCabinetHeader()
2551 * FUNCTION: Initializes cabinet header and optional fields
2553 * Status of operation
2556 unsigned long TotalSize
;
2559 CABHeader
.FileTableOffset
= 0; // Not known yet
2560 CABHeader
.FolderCount
= 0; // Not known yet
2561 CABHeader
.FileCount
= 0; // Not known yet
2562 CABHeader
.Flags
= 0; // Not known yet
2564 CABHeader
.CabinetNumber
= (unsigned short)CurrentDiskNumber
;
2566 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
))) {
2567 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2568 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2569 strcpy(CabinetPrev
, "");
2572 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
)) {
2573 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2574 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2575 strcpy(DiskNext
, "");
2580 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2582 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2584 /* Calculate size of name of previous cabinet */
2585 TotalSize
+= strlen(CabinetPrev
) + 1;
2587 /* Calculate size of label of previous disk */
2588 TotalSize
+= strlen(DiskPrev
) + 1;
2591 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2593 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2595 /* Calculate size of name of next cabinet */
2596 Size
= strlen(CabinetNext
) + 1;
2598 NextFieldsSize
= Size
;
2600 /* Calculate size of label of next disk */
2601 Size
= strlen(DiskNext
) + 1;
2603 NextFieldsSize
+= Size
;
2607 /* Add cabinet reserved area size if present */
2608 if (CabinetReservedFileSize
> 0)
2610 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2611 TotalSize
+= CabinetReservedFileSize
;
2612 TotalSize
+= sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2615 DiskSize
+= TotalSize
;
2617 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2619 return CAB_STATUS_SUCCESS
;
2623 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks
)
2625 * FUNCTION: Writes the cabinet header and optional fields
2627 * MoreDisks = true if next cabinet name should be included
2629 * Status of operation
2632 PCFFOLDER_NODE FolderNode
;
2633 PCFFILE_NODE FileNode
;
2634 unsigned long BytesWritten
;
2638 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2639 Size
= TotalHeaderSize
;
2641 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2642 DiskSize
-= NextFieldsSize
;
2643 Size
= TotalHeaderSize
- NextFieldsSize
;
2646 /* Set absolute folder offsets */
2647 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2648 CABHeader
.FolderCount
= 0;
2649 FolderNode
= FolderListHead
;
2650 while (FolderNode
!= NULL
) {
2651 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2653 BytesWritten
+= FolderNode
->TotalFolderSize
;
2655 CABHeader
.FolderCount
++;
2657 FolderNode
= FolderNode
->Next
;
2660 /* Set absolute offset of file table */
2661 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2663 /* Count number of files to be committed */
2664 CABHeader
.FileCount
= 0;
2665 FileNode
= FileListHead
;
2666 while (FileNode
!= NULL
) {
2667 if (FileNode
->Commit
)
2668 CABHeader
.FileCount
++;
2669 FileNode
= FileNode
->Next
;
2672 CABHeader
.CabinetSize
= DiskSize
;
2676 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), &BytesWritten
, NULL
)) {
2677 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2678 return CAB_STATUS_CANNOT_WRITE
;
2681 BytesWritten
= sizeof(CFHEADER
);
2682 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1) {
2683 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2684 return CAB_STATUS_CANNOT_WRITE
;
2688 /* Write per-cabinet reserved area if present */
2689 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
) {
2690 unsigned long ReservedSize
;
2692 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2693 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2694 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2696 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(unsigned long), &BytesWritten
, NULL
)) {
2697 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2698 return CAB_STATUS_CANNOT_WRITE
;
2701 BytesWritten
= sizeof(unsigned long);
2702 if (fwrite(&ReservedSize
, sizeof(unsigned long), 1, FileHandle
) < 1) {
2703 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2704 return CAB_STATUS_CANNOT_WRITE
;
2709 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesWritten
, NULL
)) {
2710 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2711 return CAB_STATUS_CANNOT_WRITE
;
2714 BytesWritten
= CabinetReservedFileSize
;
2715 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1) {
2716 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2717 return CAB_STATUS_CANNOT_WRITE
;
2722 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0) {
2724 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2726 /* Write name of previous cabinet */
2727 Size
= strlen(CabinetPrev
) + 1;
2729 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, &BytesWritten
, NULL
)) {
2730 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2731 return CAB_STATUS_CANNOT_WRITE
;
2734 BytesWritten
= Size
;
2735 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1) {
2736 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2737 return CAB_STATUS_CANNOT_WRITE
;
2741 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2743 /* Write label of previous disk */
2744 Size
= strlen(DiskPrev
) + 1;
2746 if (!WriteFile(FileHandle
, DiskPrev
, Size
, &BytesWritten
, NULL
)) {
2747 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2748 return CAB_STATUS_CANNOT_WRITE
;
2751 BytesWritten
= Size
;
2752 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1) {
2753 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2754 return CAB_STATUS_CANNOT_WRITE
;
2759 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0) {
2761 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2763 /* Write name of next cabinet */
2764 Size
= strlen(CabinetNext
) + 1;
2766 if (!WriteFile(FileHandle
, CabinetNext
, Size
, &BytesWritten
, NULL
)) {
2767 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2768 return CAB_STATUS_CANNOT_WRITE
;
2771 BytesWritten
= Size
;
2772 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1) {
2773 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2774 return CAB_STATUS_CANNOT_WRITE
;
2778 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2780 /* Write label of next disk */
2781 Size
= strlen(DiskNext
) + 1;
2783 if (!WriteFile(FileHandle
, DiskNext
, Size
, &BytesWritten
, NULL
)) {
2784 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2785 return CAB_STATUS_CANNOT_WRITE
;
2788 BytesWritten
= Size
;
2789 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1) {
2790 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2791 return CAB_STATUS_CANNOT_WRITE
;
2796 return CAB_STATUS_SUCCESS
;
2800 unsigned long CCabinet::WriteFolderEntries()
2802 * FUNCTION: Writes folder entries
2804 * Status of operation
2807 PCFFOLDER_NODE FolderNode
;
2808 unsigned long BytesWritten
;
2810 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2812 FolderNode
= FolderListHead
;
2813 while (FolderNode
!= NULL
) {
2814 if (FolderNode
->Commit
) {
2816 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2817 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
2820 if (!WriteFile(FileHandle
,
2821 &FolderNode
->Folder
,
2825 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2826 return CAB_STATUS_CANNOT_WRITE
;
2829 BytesWritten
= sizeof(CFFOLDER
);
2830 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1) {
2831 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2832 return CAB_STATUS_CANNOT_WRITE
;
2836 FolderNode
= FolderNode
->Next
;
2839 return CAB_STATUS_SUCCESS
;
2843 unsigned long CCabinet::WriteFileEntries()
2845 * FUNCTION: Writes file entries for all files
2847 * Status of operation
2851 unsigned long BytesWritten
;
2854 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
2856 File
= FileListHead
;
2857 while (File
!= NULL
) {
2859 /* Remove any continued files that ends in this disk */
2860 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
2861 File
->Delete
= true;
2863 /* The file could end in the last (split) block and should therefore
2864 appear in the next disk too */
2866 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
2867 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
)) {
2868 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
2869 File
->Delete
= false;
2873 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
2874 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
2877 if (!WriteFile(FileHandle
,
2882 return CAB_STATUS_CANNOT_WRITE
;
2884 BytesWritten
= sizeof(CFFILE
);
2885 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1) {
2886 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2887 return CAB_STATUS_CANNOT_WRITE
;
2892 if (!WriteFile(FileHandle
,
2893 GetFileName(File
->FileName
),
2894 strlen(GetFileName(File
->FileName
)) + 1, &BytesWritten
, NULL
))
2895 return CAB_STATUS_CANNOT_WRITE
;
2897 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
2898 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1) {
2899 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2900 return CAB_STATUS_CANNOT_WRITE
;
2905 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
2912 return CAB_STATUS_SUCCESS
;
2916 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
2918 * FUNCTION: Writes data blocks to the cabinet
2920 * FolderNode = Pointer to folder node containing the data blocks
2922 * Status of operation
2925 PCFDATA_NODE DataNode
;
2926 unsigned long BytesWritten
;
2927 unsigned long BytesRead
;
2928 unsigned long Status
;
2930 DataNode
= FolderNode
->DataListHead
;
2931 if (DataNode
!= NULL
)
2932 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
2934 while (DataNode
!= NULL
) {
2935 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
2936 DataNode
->ScratchFilePosition
,
2937 DataNode
->Data
.CompSize
,
2938 DataNode
->Data
.UncompSize
));
2940 /* InputBuffer is free for us to use here, so we use it and avoid a
2941 memory allocation. OutputBuffer can't be used here because it may
2942 still contain valid data (if a data block spans two or more disks) */
2943 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
2944 if (Status
!= CAB_STATUS_SUCCESS
) {
2945 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%d).\n", (unsigned int)Status
));
2950 if (!WriteFile(FileHandle
, &DataNode
->Data
,
2951 sizeof(CFDATA
), &BytesWritten
, NULL
)) {
2952 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2953 return CAB_STATUS_CANNOT_WRITE
;
2956 BytesWritten
= sizeof(CFDATA
);
2957 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1) {
2958 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2959 return CAB_STATUS_CANNOT_WRITE
;
2964 if (!WriteFile(FileHandle
, InputBuffer
,
2965 DataNode
->Data
.CompSize
, &BytesWritten
, NULL
)) {
2966 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2967 return CAB_STATUS_CANNOT_WRITE
;
2970 BytesWritten
= DataNode
->Data
.CompSize
;
2971 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1) {
2972 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2973 return CAB_STATUS_CANNOT_WRITE
;
2977 DataNode
= DataNode
->Next
;
2979 return CAB_STATUS_SUCCESS
;
2983 unsigned long CCabinet::WriteDataBlock()
2985 * FUNCTION: Writes the current data block to the scratch file
2987 * Status of operation
2990 unsigned long Status
;
2991 unsigned long BytesWritten
;
2992 PCFDATA_NODE DataNode
;
2994 if (!BlockIsSplit
) {
2995 Status
= Codec
->Compress(OutputBuffer
,
3000 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
3001 CurrentIBufferSize
, TotalCompSize
));
3003 CurrentOBuffer
= OutputBuffer
;
3004 CurrentOBufferSize
= TotalCompSize
;
3007 DataNode
= NewDataNode(CurrentFolderNode
);
3009 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3010 return CAB_STATUS_NOMEMORY
;
3013 DiskSize
+= sizeof(CFDATA
);
3015 if (MaxDiskSize
> 0)
3016 /* Disk size is limited */
3017 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3019 BlockIsSplit
= false;
3022 DataNode
->Data
.CompSize
= (unsigned short)(MaxDiskSize
- DiskSize
);
3023 DataNode
->Data
.UncompSize
= 0;
3024 CreateNewDisk
= true;
3026 DataNode
->Data
.CompSize
= (unsigned short)CurrentOBufferSize
;
3027 DataNode
->Data
.UncompSize
= (unsigned short)CurrentIBufferSize
;
3030 DataNode
->Data
.Checksum
= 0;
3031 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3033 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3034 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3036 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3037 (unsigned int)DataNode
->Data
.Checksum
,
3038 (unsigned int)DataNode
->Data
.CompSize
,
3039 (unsigned int)DataNode
->Data
.UncompSize
));
3041 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3042 CurrentOBuffer
, &BytesWritten
);
3043 if (Status
!= CAB_STATUS_SUCCESS
)
3046 DiskSize
+= BytesWritten
;
3048 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3049 CurrentFolderNode
->Folder
.DataBlockCount
++;
3051 *(unsigned char**)&CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3052 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3054 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3056 if (!BlockIsSplit
) {
3057 CurrentIBufferSize
= 0;
3058 CurrentIBuffer
= InputBuffer
;
3061 return CAB_STATUS_SUCCESS
;
3067 void CCabinet::ConvertDateAndTime(time_t* Time
,
3068 unsigned short* DosDate
,
3069 unsigned short* DosTime
)
3071 * FUNCTION: Returns file times of a file
3073 * FileHandle = File handle of file to get file times from
3074 * File = Pointer to CFFILE node for file
3076 * Status of operation
3081 timedef
= localtime(Time
);
3083 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3084 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3085 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3087 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3088 | ((timedef
->tm_mon
+ 1) << 5)
3089 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3091 *DosTime
= (timedef
->tm_sec
<< 0)
3092 | (timedef
->tm_min
<< 5)
3093 | (timedef
->tm_hour
<< 11);
3099 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3101 * FUNCTION: Returns file times of a file
3103 * FileHandle = File handle of file to get file times from
3104 * File = Pointer to CFFILE node for file
3106 * Status of operation
3112 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3113 FileTimeToDosDateTime(&FileTime
,
3114 &File
->File
.FileDate
,
3115 &File
->File
.FileTime
);
3120 // Check for an absolute path
3121 if (IsSeparator(File
->FileName
[0]))
3123 strcpy(buf
, File
->FileName
);
3127 getcwd(buf
, sizeof(buf
));
3128 strcat(buf
, DIR_SEPARATOR_STRING
);
3129 strcat(buf
, File
->FileName
);
3132 if (stat(buf
, &stbuf
) == -1)
3134 return CAB_STATUS_CANNOT_READ
;
3137 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3139 return CAB_STATUS_SUCCESS
;
3143 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3145 * FUNCTION: Returns attributes on a file
3147 * File = Pointer to CFFILE node for file
3149 * Status of operation
3155 Attributes
= GetFileAttributes(File
->FileName
);
3156 if (Attributes
== -1)
3157 return CAB_STATUS_CANNOT_READ
;
3159 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3160 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3162 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3163 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3165 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3166 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3168 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3169 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3171 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3172 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3177 // Check for an absolute path
3178 if (IsSeparator(File
->FileName
[0]))
3180 strcpy(buf
, File
->FileName
);
3184 getcwd(buf
, sizeof(buf
));
3185 strcat(buf
, DIR_SEPARATOR_STRING
);
3186 strcat(buf
, File
->FileName
);
3189 if (stat(buf
, &stbuf
) == -1)
3191 return CAB_STATUS_CANNOT_READ
;
3195 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3196 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3197 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3200 if (stbuf
.st_mode
& S_IFDIR
)
3201 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3203 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3206 return CAB_STATUS_SUCCESS
;
3210 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3212 * FUNCTION: Sets attributes on a file
3214 * File = Pointer to CFFILE node for file
3216 * Status of operation
3220 unsigned long Attributes
= 0;
3222 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3223 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3225 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3226 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3228 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3229 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3231 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3232 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3234 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3235 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3237 SetFileAttributes(File
->FileName
, Attributes
);
3239 return CAB_STATUS_SUCCESS
;
3241 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3242 return CAB_STATUS_SUCCESS
;
3246 #endif /* CAB_READ_ONLY */