2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cpp
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Colin Finck <mail@colinfinck.de>
8 * NOTES: Define CAB_READ_ONLY for read only version
10 * CSH 21/03-2001 Created
11 * CSH 15/08-2003 Made it portable
12 * CF 04/05-2007 Reformatted the code to be more consistent and use TABs instead of spaces
13 * CF 04/05-2007 Made it compatible with 64-bit operating systems
14 * CF 18/08-2007 Use typedefs64.h and the Windows types for compatibility with 64-bit operating systems
16 * - Checksum of datablocks should be calculated
17 * - EXTRACT.EXE complains if a disk is created manually
18 * - Folders that are created manually and span disks will result in a damaged cabinet
23 #if defined(__FreeBSD__) || defined(__APPLE__)
24 # include <sys/stat.h>
31 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
32 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
34 ULONG size
= GetFileSize(handle
, NULL
);
35 if (size
== INVALID_FILE_SIZE
)
40 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
41 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
43 return ReadFile(handle
, buffer
, size
, (LPDWORD
)bytesread
, NULL
) != 0;
46 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
47 static LONG
_GetSizeOfFile(FILEHANDLE handle
)
50 fseek(handle
, 0, SEEK_END
);
52 fseek(handle
, 0, SEEK_SET
);
55 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
56 static bool _ReadFileData(FILEHANDLE handle
, void* buffer
, ULONG size
, PULONG bytesread
)
58 *bytesread
= fread(buffer
, 1, size
, handle
);
59 return *bytesread
== size
;
68 void DumpBuffer(void* Buffer
, ULONG Size
)
70 FILEHANDLE FileHandle
;
73 /* Create file, overwrite if it already exists */
74 FileHandle
= CreateFile("dump.bin", // Create this file
75 GENERIC_WRITE
, // Open for writing
78 CREATE_ALWAYS
, // Create or overwrite
79 FILE_ATTRIBUTE_NORMAL
, // Normal file
80 NULL
); // No attribute template
81 if (FileHandle
== INVALID_HANDLE_VALUE
)
83 DPRINT(MID_TRACE
, ("ERROR OPENING '%lu'.\n", (ULONG
)GetLastError()));
87 if (!WriteFile(FileHandle
, Buffer
, Size
, &BytesWritten
, NULL
))
89 DPRINT(MID_TRACE
, ("ERROR WRITING '%lu'.\n", (ULONG
)GetLastError()));
92 CloseFile(FileHandle
);
100 CCFDATAStorage::CCFDATAStorage()
102 * FUNCTION: Default constructor
109 CCFDATAStorage::~CCFDATAStorage()
111 * FUNCTION: Default destructor
114 ASSERT(!FileCreated
);
118 ULONG
CCFDATAStorage::Create(char* FileName
)
120 * FUNCTION: Creates the file
122 * FileName = Pointer to name of file
124 * Status of operation
127 ASSERT(!FileCreated
);
130 if (GetTempPath(MAX_PATH
, FullName
) == 0)
131 return CAB_STATUS_CANNOT_CREATE
;
133 strcat(FullName
, FileName
);
135 /* Create file, overwrite if it already exists */
136 FileHandle
= CreateFile(FullName
, // Create this file
137 GENERIC_READ
| GENERIC_WRITE
, // Open for reading/writing
140 CREATE_ALWAYS
, // Create or overwrite
141 FILE_FLAG_SEQUENTIAL_SCAN
| // Optimize for sequential scans
142 FILE_FLAG_DELETE_ON_CLOSE
| // Delete file when closed
143 FILE_ATTRIBUTE_TEMPORARY
, // Temporary file
144 NULL
); // No attribute template
145 if (FileHandle
== INVALID_HANDLE_VALUE
)
147 DPRINT(MID_TRACE
, ("ERROR '%lu'.\n", (ULONG
)GetLastError()));
148 return CAB_STATUS_CANNOT_CREATE
;
151 /*if (tmpnam(FullName) == NULL)*/
152 if ((FileHandle
= tmpfile()) == NULL
)
153 return CAB_STATUS_CANNOT_CREATE
;
155 FileHandle = fopen(FullName, "w+b");
156 if (FileHandle == NULL) {
157 DPRINT(MID_TRACE, ("ERROR '%lu'.\n", (ULONG)errno));
158 return CAB_STATUS_CANNOT_CREATE;
165 return CAB_STATUS_SUCCESS
;
169 ULONG
CCFDATAStorage::Destroy()
171 * FUNCTION: Destroys the file
173 * Status of operation
178 CloseFile(FileHandle
);
182 return CAB_STATUS_SUCCESS
;
186 ULONG
CCFDATAStorage::Truncate()
188 * FUNCTION: Truncate the scratch file to zero bytes
190 * Status of operation
194 if (!SetFilePointer(FileHandle
, 0, NULL
, FILE_BEGIN
))
195 return CAB_STATUS_FAILURE
;
196 if (!SetEndOfFile(FileHandle
))
197 return CAB_STATUS_FAILURE
;
200 FileHandle
= tmpfile();
201 if (FileHandle
== NULL
)
203 DPRINT(MID_TRACE
, ("ERROR '%lu'.\n", (ULONG
)errno
));
204 return CAB_STATUS_FAILURE
;
207 return CAB_STATUS_SUCCESS
;
211 ULONG
CCFDATAStorage::Position()
213 * FUNCTION: Returns current position in file
219 return SetFilePointer(FileHandle
, 0, NULL
, FILE_CURRENT
);
221 return (ULONG
)ftell(FileHandle
);
226 ULONG
CCFDATAStorage::Seek(LONG Position
)
228 * FUNCTION: Seeks to an absolute position
230 * Position = Absolute position to seek to
232 * Status of operation
236 if (SetFilePointer(FileHandle
,
239 FILE_BEGIN
) == 0xFFFFFFFF)
240 return CAB_STATUS_FAILURE
;
242 return CAB_STATUS_SUCCESS
;
244 if (fseek(FileHandle
, (off_t
)Position
, SEEK_SET
) != 0)
245 return CAB_STATUS_FAILURE
;
247 return CAB_STATUS_SUCCESS
;
252 ULONG
CCFDATAStorage::ReadBlock(PCFDATA Data
, void* Buffer
, PULONG BytesRead
)
254 * FUNCTION: Reads a CFDATA block from the file
256 * Data = Pointer to CFDATA block for the buffer
257 * Buffer = Pointer to buffer to store data read
258 * BytesWritten = Pointer to buffer to write number of bytes read
260 * Status of operation
264 if (!ReadFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesRead
, NULL
))
265 return CAB_STATUS_CANNOT_READ
;
268 *BytesRead
= fread(Buffer
, 1, Data
->CompSize
, FileHandle
);
269 if (*BytesRead
!= Data
->CompSize
)
270 return CAB_STATUS_CANNOT_READ
;
272 return CAB_STATUS_SUCCESS
;
276 ULONG
CCFDATAStorage::WriteBlock(PCFDATA Data
, void* Buffer
, PULONG BytesWritten
)
278 * FUNCTION: Writes a CFDATA block to the file
280 * Data = Pointer to CFDATA block for the buffer
281 * Buffer = Pointer to buffer with data to write
282 * BytesWritten = Pointer to buffer to write number of bytes written
284 * Status of operation
288 if (!WriteFile(FileHandle
, Buffer
, Data
->CompSize
, (LPDWORD
)BytesWritten
, NULL
))
289 return CAB_STATUS_CANNOT_WRITE
;
291 *BytesWritten
= fwrite(Buffer
, 1, Data
->CompSize
, FileHandle
);
292 if (*BytesWritten
!= Data
->CompSize
)
293 return CAB_STATUS_CANNOT_WRITE
;
295 return CAB_STATUS_SUCCESS
;
298 #endif /* CAB_READ_ONLY */
305 * FUNCTION: Default constructor
314 *CabinetReservedFile
= '\0';
317 CabinetReservedFileBuffer
= NULL
;
318 CabinetReservedFileSize
= 0;
320 FolderListHead
= NULL
;
321 FolderListTail
= NULL
;
325 Codec
= new CRawCodec();
326 CodecId
= CAB_CODEC_RAW
;
327 CodecSelected
= true;
332 BlockIsSplit
= false;
335 FolderUncompSize
= 0;
336 BytesLeftInBlock
= 0;
338 CurrentDataNode
= NULL
;
342 CCabinet::~CCabinet()
344 * FUNCTION: Default destructor
347 if (CabinetReservedFileBuffer
!= NULL
)
349 FreeMemory(CabinetReservedFileBuffer
);
350 CabinetReservedFileBuffer
= NULL
;
351 CabinetReservedFileSize
= 0;
358 bool CCabinet::IsSeparator(char Char
)
360 * FUNCTION: Determines if a character is a separator
362 * Char = Character to check
364 * Wether it is a separator
367 if ((Char
== '\\') || (Char
== '/'))
373 char* CCabinet::ConvertPath(char* Path
, bool Allocate
)
375 * FUNCTION: Replaces \ or / with the one used be the host environment
377 * Path = Pointer to string with pathname
378 * Allocate = Specifies wther to allocate memory for the new
379 * string or to change the existing buffer
381 * Pointer to new path
388 newpath
= strdup(Path
);
404 newpath
[i
] = Path
[i
];
414 char* CCabinet::GetFileName(char* Path
)
416 * FUNCTION: Returns a pointer to file name
418 * Path = Pointer to string with pathname
420 * Pointer to filename
425 j
= i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
428 if (IsSeparator(Path
[i
- 1]))
435 void CCabinet::RemoveFileName(char* Path
)
437 * FUNCTION: Removes a file name from a path
439 * Path = Pointer to string with path
445 i
= (Path
[0] ? (Path
[1] == ':' ? 2 : 0) : 0);
446 FileName
= GetFileName(Path
+ i
);
448 if ((FileName
!= (Path
+ i
)) && (IsSeparator(FileName
[-1])))
450 if ((FileName
== (Path
+ i
)) && (IsSeparator(FileName
[0])))
456 bool CCabinet::NormalizePath(char* Path
,
459 * FUNCTION: Normalizes a path
461 * Path = Pointer to string with pathname
462 * Length = Number of bytes in Path
464 * true if there was enough room in Path, or false
470 if ((n
= (ULONG
)strlen(Path
)) &&
471 (!IsSeparator(Path
[n
- 1])) &&
472 (OK
= ((n
+ 1) < Length
)))
474 Path
[n
] = DIR_SEPARATOR_CHAR
;
481 char* CCabinet::GetCabinetName()
483 * FUNCTION: Returns pointer to cabinet file name
485 * Pointer to string with name of cabinet
492 void CCabinet::SetCabinetName(char* FileName
)
494 * FUNCTION: Sets cabinet file name
496 * FileName = Pointer to string with name of cabinet
499 strcpy(CabinetName
, FileName
);
503 void CCabinet::SetDestinationPath(char* DestinationPath
)
505 * FUNCTION: Sets destination path
507 * DestinationPath = Pointer to string with name of destination path
510 strcpy(DestPath
, DestinationPath
);
511 ConvertPath(DestPath
, false);
512 if (strlen(DestPath
) > 0)
513 NormalizePath(DestPath
, MAX_PATH
);
517 char* CCabinet::GetDestinationPath()
519 * FUNCTION: Returns destination path
521 * Pointer to string with name of destination path
528 bool CCabinet::SetCabinetReservedFile(char* FileName
)
530 * FUNCTION: Sets cabinet reserved file
532 * FileName = Pointer to string with name of cabinet reserved file
535 FILEHANDLE FileHandle
;
539 FileHandle
= CreateFile(ConvertPath(FileName
, true), // Open this file
540 GENERIC_READ
, // Open for reading
541 FILE_SHARE_READ
, // Share for reading
543 OPEN_EXISTING
, // Existing file only
544 FILE_ATTRIBUTE_NORMAL
, // Normal file
545 NULL
); // No attribute template
546 if (FileHandle
== INVALID_HANDLE_VALUE
)
548 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
552 FileHandle
= fopen(ConvertPath(FileName
, true), "rb");
553 if (FileHandle
== NULL
)
555 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
560 CabinetReservedFileSize
= GetSizeOfFile(FileHandle
);
561 if (CabinetReservedFileSize
== (ULONG
)-1)
563 DPRINT(MIN_TRACE
, ("Cannot read from cabinet reserved file.\n"));
567 if (CabinetReservedFileSize
== 0)
569 CloseFile(FileHandle
);
573 CabinetReservedFileBuffer
= AllocateMemory(CabinetReservedFileSize
);
574 if (!CabinetReservedFileBuffer
)
576 CloseFile(FileHandle
);
580 if (!ReadFileData(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, &BytesRead
))
582 CloseFile(FileHandle
);
586 CloseFile(FileHandle
);
588 strcpy(CabinetReservedFile
, FileName
);
594 char* CCabinet::GetCabinetReservedFile()
596 * FUNCTION: Returns cabionet reserved file
598 * Pointer to string with name of cabinet reserved file
601 return CabinetReservedFile
;
605 ULONG
CCabinet::GetCurrentDiskNumber()
607 * FUNCTION: Returns current disk number
609 * Current disk number
612 return CurrentDiskNumber
;
616 ULONG
CCabinet::Open()
618 * FUNCTION: Opens a cabinet file
620 * Status of operation
623 PCFFOLDER_NODE FolderNode
;
632 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
634 return CAB_STATUS_NOMEMORY
;
637 FileHandle
= CreateFile(CabinetName
, // Open this file
638 GENERIC_READ
, // Open for reading
639 FILE_SHARE_READ
, // Share for reading
641 OPEN_EXISTING
, // Existing file only
642 FILE_ATTRIBUTE_NORMAL
, // Normal file
643 NULL
); // No attribute template
645 if (FileHandle
== INVALID_HANDLE_VALUE
)
647 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
648 return CAB_STATUS_CANNOT_OPEN
;
651 FileHandle
= fopen(CabinetName
, "rb");
652 if (FileHandle
== NULL
)
654 DPRINT(MID_TRACE
, ("Cannot open file.\n"));
655 return CAB_STATUS_CANNOT_OPEN
;
661 /* Load CAB header */
662 if ((Status
= ReadBlock(&CABHeader
, sizeof(CFHEADER
), &BytesRead
))
663 != CAB_STATUS_SUCCESS
)
665 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
666 return CAB_STATUS_INVALID_CAB
;
670 if ((BytesRead
!= sizeof(CFHEADER
)) ||
671 (CABHeader
.Signature
!= CAB_SIGNATURE
) ||
672 (CABHeader
.Version
!= CAB_VERSION
) ||
673 (CABHeader
.FolderCount
== 0 ) ||
674 (CABHeader
.FileCount
== 0 ) ||
675 (CABHeader
.FileTableOffset
< sizeof(CFHEADER
)))
678 DPRINT(MID_TRACE
, ("File has invalid header.\n"));
679 return CAB_STATUS_INVALID_CAB
;
684 /* Read/skip any reserved bytes */
685 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
687 if ((Status
= ReadBlock(&Size
, sizeof(ULONG
), &BytesRead
))
688 != CAB_STATUS_SUCCESS
)
690 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
691 return CAB_STATUS_INVALID_CAB
;
693 CabinetReserved
= Size
& 0xFFFF;
694 FolderReserved
= (Size
>> 16) & 0xFF;
695 DataReserved
= (Size
>> 24) & 0xFF;
698 SetFilePointer(FileHandle
, CabinetReserved
, NULL
, FILE_CURRENT
);
699 if (GetLastError() != NO_ERROR
)
701 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
702 return CAB_STATUS_FAILURE
;
705 if (fseek(FileHandle
, (off_t
)CabinetReserved
, SEEK_CUR
) != 0)
707 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
708 return CAB_STATUS_FAILURE
;
713 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
715 /* Read name of previous cabinet */
716 Status
= ReadString(CabinetPrev
, 256);
717 if (Status
!= CAB_STATUS_SUCCESS
)
719 /* Read label of previous disk */
720 Status
= ReadString(DiskPrev
, 256);
721 if (Status
!= CAB_STATUS_SUCCESS
)
726 strcpy(CabinetPrev
, "");
727 strcpy(DiskPrev
, "");
730 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
732 /* Read name of next cabinet */
733 Status
= ReadString(CabinetNext
, 256);
734 if (Status
!= CAB_STATUS_SUCCESS
)
736 /* Read label of next disk */
737 Status
= ReadString(DiskNext
, 256);
738 if (Status
!= CAB_STATUS_SUCCESS
)
743 strcpy(CabinetNext
, "");
744 strcpy(DiskNext
, "");
747 /* Read all folders */
748 for (Index
= 0; Index
< CABHeader
.FolderCount
; Index
++)
750 FolderNode
= NewFolderNode();
753 DPRINT(MIN_TRACE
, ("Insufficient resources.\n"));
754 return CAB_STATUS_NOMEMORY
;
758 FolderNode
->UncompOffset
= FolderUncompSize
;
760 FolderNode
->Index
= Index
;
762 if ((Status
= ReadBlock(&FolderNode
->Folder
,
763 sizeof(CFFOLDER
), &BytesRead
)) != CAB_STATUS_SUCCESS
)
765 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
766 return CAB_STATUS_INVALID_CAB
;
770 /* Read file entries */
771 Status
= ReadFileTable();
772 if (Status
!= CAB_STATUS_SUCCESS
)
774 DPRINT(MIN_TRACE
, ("ReadFileTable() failed (%lu).\n", (ULONG
)Status
));
778 /* Read data blocks for all folders */
779 FolderNode
= FolderListHead
;
780 while (FolderNode
!= NULL
)
782 Status
= ReadDataBlocks(FolderNode
);
783 if (Status
!= CAB_STATUS_SUCCESS
)
785 DPRINT(MIN_TRACE
, ("ReadDataBlocks() failed (%lu).\n", (ULONG
)Status
));
788 FolderNode
= FolderNode
->Next
;
791 return CAB_STATUS_SUCCESS
;
795 void CCabinet::Close()
797 * FUNCTION: Closes the cabinet file
802 CloseFile(FileHandle
);
808 ULONG
CCabinet::FindFirst(char* FileName
,
811 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
813 * FileName = Pointer to search criteria
814 * Search = Pointer to search structure
816 * Status of operation
819 RestartSearch
= false;
820 strncpy(Search
->Search
, FileName
, MAX_PATH
);
821 Search
->Next
= FileListHead
;
822 return FindNext(Search
);
826 ULONG
CCabinet::FindNext(PCAB_SEARCH Search
)
828 * FUNCTION: Finds next file in the cabinet that matches a search criteria
830 * Search = Pointer to search structure
832 * Status of operation
839 Search
->Next
= FileListHead
;
841 /* Skip split files already extracted */
842 while ((Search
->Next
) &&
843 (Search
->Next
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
) &&
844 (Search
->Next
->File
.FileOffset
<= LastFileOffset
))
846 DPRINT(MAX_TRACE
, ("Skipping file (%s) FileOffset (0x%lX) LastFileOffset (0x%lX).\n",
847 Search
->Next
->FileName
, Search
->Next
->File
.FileOffset
, LastFileOffset
));
848 Search
->Next
= Search
->Next
->Next
;
851 RestartSearch
= false;
854 /* FIXME: Check search criteria */
858 if (strlen(DiskNext
) > 0)
862 SetCabinetName(CabinetNext
);
864 OnDiskChange(CabinetNext
, DiskNext
);
867 if (Status
!= CAB_STATUS_SUCCESS
)
870 Search
->Next
= FileListHead
;
872 return CAB_STATUS_NOFILE
;
875 return CAB_STATUS_NOFILE
;
878 Search
->File
= &Search
->Next
->File
;
879 Search
->FileName
= Search
->Next
->FileName
;
880 Search
->Next
= Search
->Next
->Next
;
881 return CAB_STATUS_SUCCESS
;
885 ULONG
CCabinet::ExtractFile(char* FileName
)
887 * FUNCTION: Extracts a file from the cabinet
889 * FileName = Pointer to buffer with name of file
891 * Status of operation
901 ULONG TotalBytesRead
;
903 unsigned char* Buffer
;
904 unsigned char* CurrentBuffer
;
913 char DestName
[MAX_PATH
];
914 char TempName
[MAX_PATH
];
916 Status
= LocateFile(FileName
, &File
);
917 if (Status
!= CAB_STATUS_SUCCESS
)
919 DPRINT(MID_TRACE
, ("Cannot locate file (%lu).\n", (ULONG
)Status
));
923 LastFileOffset
= File
->File
.FileOffset
;
925 switch (CurrentFolderNode
->Folder
.CompressionType
& CAB_COMP_MASK
)
928 SelectCodec(CAB_CODEC_RAW
);
932 SelectCodec(CAB_CODEC_MSZIP
);
936 return CAB_STATUS_UNSUPPCOMP
;
939 DPRINT(MAX_TRACE
, ("Extracting file at uncompressed offset (0x%lX) Size (%lu bytes) AO (0x%lX) UO (0x%lX).\n",
940 (ULONG
)File
->File
.FileOffset
,
941 (ULONG
)File
->File
.FileSize
,
942 (ULONG
)File
->DataBlock
->AbsoluteOffset
,
943 (ULONG
)File
->DataBlock
->UncompOffset
));
945 strcpy(DestName
, DestPath
);
946 strcat(DestName
, FileName
);
948 /* Create destination file, fail if it already exists */
950 DestFile
= CreateFile(DestName
, // Create this file
951 GENERIC_WRITE
, // Open for writing
954 CREATE_NEW
, // New file only
955 FILE_ATTRIBUTE_NORMAL
, // Normal file
956 NULL
); // No attribute template
957 if (DestFile
== INVALID_HANDLE_VALUE
)
959 /* If file exists, ask to overwrite file */
960 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
961 (OnOverwrite(&File
->File
, FileName
)))
963 /* Create destination file, overwrite if it already exists */
964 DestFile
= CreateFile(DestName
, // Create this file
965 GENERIC_WRITE
, // Open for writing
968 TRUNCATE_EXISTING
, // Truncate the file
969 FILE_ATTRIBUTE_NORMAL
, // Normal file
970 NULL
); // No attribute template
971 if (DestFile
== INVALID_HANDLE_VALUE
)
972 return CAB_STATUS_CANNOT_CREATE
;
976 if (Status
== ERROR_FILE_EXISTS
)
977 return CAB_STATUS_FILE_EXISTS
;
979 return CAB_STATUS_CANNOT_CREATE
;
983 DestFile
= fopen(DestName
, "rb");
984 if (DestFile
!= NULL
)
987 /* If file exists, ask to overwrite file */
988 if (OnOverwrite(&File
->File
, FileName
))
990 DestFile
= fopen(DestName
, "w+b");
991 if (DestFile
== NULL
)
992 return CAB_STATUS_CANNOT_CREATE
;
995 return CAB_STATUS_FILE_EXISTS
;
999 DestFile
= fopen(DestName
, "w+b");
1000 if (DestFile
== NULL
)
1001 return CAB_STATUS_CANNOT_CREATE
;
1005 if (!DosDateTimeToFileTime(File
->File
.FileDate
, File
->File
.FileTime
, &FileTime
))
1007 CloseFile(DestFile
);
1008 DPRINT(MIN_TRACE
, ("DosDateTimeToFileTime() failed (%lu).\n", GetLastError()));
1009 return CAB_STATUS_CANNOT_WRITE
;
1012 SetFileTime(DestFile
, NULL
, &FileTime
, NULL
);
1014 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1016 SetAttributesOnFile(File
);
1018 Buffer
= (unsigned char*)AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1021 CloseFile(DestFile
);
1022 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1023 return CAB_STATUS_NOMEMORY
;
1026 /* Call OnExtract event handler */
1027 OnExtract(&File
->File
, FileName
);
1029 /* Search to start of file */
1031 Offset
= SetFilePointer(FileHandle
,
1032 File
->DataBlock
->AbsoluteOffset
,
1035 if (GetLastError() != NO_ERROR
)
1037 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1038 return CAB_STATUS_INVALID_CAB
;
1041 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1043 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1044 return CAB_STATUS_FAILURE
;
1046 Offset
= ftell(FileHandle
);
1049 Size
= File
->File
.FileSize
;
1050 Offset
= File
->File
.FileOffset
;
1051 CurrentOffset
= File
->DataBlock
->UncompOffset
;
1055 ReuseBlock
= (CurrentDataNode
== File
->DataBlock
);
1060 DPRINT(MAX_TRACE
, ("CO (0x%lX) ReuseBlock (%lu) Offset (0x%lX) Size (%ld) BytesLeftInBlock (%ld)\n",
1061 File
->DataBlock
->UncompOffset
, (ULONG
)ReuseBlock
, Offset
, Size
,
1064 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock
) || (BytesLeftInBlock
<= 0))
1066 DPRINT(MAX_TRACE
, ("Filling buffer. ReuseBlock (%lu)\n", (ULONG
)ReuseBlock
));
1068 CurrentBuffer
= Buffer
;
1072 DPRINT(MAX_TRACE
, ("Size (%lu bytes).\n", Size
));
1074 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1075 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1077 CloseFile(DestFile
);
1079 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
1080 return CAB_STATUS_INVALID_CAB
;
1083 DPRINT(MAX_TRACE
, ("Data block: Checksum (0x%lX) CompSize (%lu bytes) UncompSize (%lu bytes)\n",
1084 (ULONG
)CFData
.Checksum
,
1085 (ULONG
)CFData
.CompSize
,
1086 (ULONG
)CFData
.UncompSize
));
1088 ASSERT(CFData
.CompSize
<= CAB_BLOCKSIZE
+ 12);
1090 BytesToRead
= CFData
.CompSize
;
1092 DPRINT(MAX_TRACE
, ("Read: (0x%lX,0x%lX).\n",
1093 (_W64
unsigned long)CurrentBuffer
, (_W64
unsigned long)Buffer
));
1095 if (((Status
= ReadBlock(CurrentBuffer
, BytesToRead
, &BytesRead
)) !=
1096 CAB_STATUS_SUCCESS
) || (BytesToRead
!= BytesRead
))
1098 CloseFile(DestFile
);
1100 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
1101 return CAB_STATUS_INVALID_CAB
;
1104 /* FIXME: Does not work with files generated by makecab.exe */
1106 if (CFData.Checksum != 0)
1108 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1109 if (Checksum != CFData.Checksum)
1111 CloseFile(DestFile);
1113 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1114 Checksum, CFData.Checksum));
1115 return CAB_STATUS_INVALID_CAB;
1119 TotalBytesRead
+= BytesRead
;
1121 CurrentBuffer
+= BytesRead
;
1123 if (CFData
.UncompSize
== 0)
1125 if (strlen(DiskNext
) == 0)
1126 return CAB_STATUS_NOFILE
;
1128 /* CloseCabinet() will destroy all file entries so in case
1129 FileName refers to the FileName field of a CFFOLDER_NODE
1130 structure, we have to save a copy of the filename */
1131 strcpy(TempName
, FileName
);
1135 SetCabinetName(CabinetNext
);
1137 OnDiskChange(CabinetNext
, DiskNext
);
1140 if (Status
!= CAB_STATUS_SUCCESS
)
1143 /* The first data block of the file will not be
1144 found as it is located in the previous file */
1145 Status
= LocateFile(TempName
, &File
);
1146 if (Status
== CAB_STATUS_NOFILE
)
1148 DPRINT(MID_TRACE
, ("Cannot locate file (%lu).\n", (ULONG
)Status
));
1152 /* The file is continued in the first data block in the folder */
1153 File
->DataBlock
= CurrentFolderNode
->DataListHead
;
1155 /* Search to start of file */
1157 SetFilePointer(FileHandle
,
1158 File
->DataBlock
->AbsoluteOffset
,
1161 if (GetLastError() != NO_ERROR
)
1163 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1164 return CAB_STATUS_INVALID_CAB
;
1167 if (fseek(FileHandle
, (off_t
)File
->DataBlock
->AbsoluteOffset
, SEEK_SET
) != 0)
1169 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1170 return CAB_STATUS_INVALID_CAB
;
1174 DPRINT(MAX_TRACE
, ("Continuing extraction of file at uncompressed offset (0x%lX) Size (%lu bytes) AO (0x%lX) UO (0x%lX).\n",
1175 (ULONG
)File
->File
.FileOffset
,
1176 (ULONG
)File
->File
.FileSize
,
1177 (ULONG
)File
->DataBlock
->AbsoluteOffset
,
1178 (ULONG
)File
->DataBlock
->UncompOffset
));
1180 CurrentDataNode
= File
->DataBlock
;
1183 RestartSearch
= true;
1185 } while (CFData
.UncompSize
== 0);
1187 DPRINT(MAX_TRACE
, ("TotalBytesRead (%lu).\n", TotalBytesRead
));
1189 Status
= Codec
->Uncompress(OutputBuffer
, Buffer
, TotalBytesRead
, &BytesToWrite
);
1190 if (Status
!= CS_SUCCESS
)
1192 CloseFile(DestFile
);
1194 DPRINT(MID_TRACE
, ("Cannot uncompress block.\n"));
1195 if (Status
== CS_NOMEMORY
)
1196 return CAB_STATUS_NOMEMORY
;
1197 return CAB_STATUS_INVALID_CAB
;
1200 if (BytesToWrite
!= CFData
.UncompSize
)
1202 DPRINT(MID_TRACE
, ("BytesToWrite (%lu) != CFData.UncompSize (%d)\n",
1203 BytesToWrite
, CFData
.UncompSize
));
1204 return CAB_STATUS_INVALID_CAB
;
1207 BytesLeftInBlock
= BytesToWrite
;
1211 DPRINT(MAX_TRACE
, ("Using same buffer. ReuseBlock (%lu)\n", (ULONG
)ReuseBlock
));
1213 BytesToWrite
= BytesLeftInBlock
;
1215 DPRINT(MAX_TRACE
, ("Seeking to absolute offset 0x%lX.\n",
1216 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1217 CurrentDataNode
->Data
.CompSize
));
1219 if (((Status
= ReadBlock(&CFData
, sizeof(CFDATA
), &BytesRead
)) !=
1220 CAB_STATUS_SUCCESS
) || (BytesRead
!= sizeof(CFDATA
)))
1222 CloseFile(DestFile
);
1224 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
1225 return CAB_STATUS_INVALID_CAB
;
1228 DPRINT(MAX_TRACE
, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1229 CFData
.CompSize
, CFData
.UncompSize
));
1231 /* Go to next data block */
1233 SetFilePointer(FileHandle
,
1234 CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1235 CurrentDataNode
->Data
.CompSize
,
1238 if (GetLastError() != NO_ERROR
)
1240 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
1241 return CAB_STATUS_INVALID_CAB
;
1244 if (fseek(FileHandle
, (off_t
)CurrentDataNode
->AbsoluteOffset
+ sizeof(CFDATA
) +
1245 CurrentDataNode
->Data
.CompSize
, SEEK_SET
) != 0)
1247 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
1248 return CAB_STATUS_INVALID_CAB
;
1256 BytesSkipped
= (Offset
- CurrentOffset
);
1260 BytesToWrite
-= BytesSkipped
;
1262 if (Size
< BytesToWrite
)
1263 BytesToWrite
= Size
;
1265 DPRINT(MAX_TRACE
, ("Offset (0x%lX) CurrentOffset (0x%lX) ToWrite (%lu) Skipped (%lu)(%lu) Size (%lu).\n",
1267 (ULONG
)CurrentOffset
,
1268 (ULONG
)BytesToWrite
,
1269 (ULONG
)BytesSkipped
, (ULONG
)Skip
,
1273 if (!WriteFile(DestFile
, (void*)((unsigned long *)OutputBuffer
+ BytesSkipped
),
1274 BytesToWrite
, (LPDWORD
)&BytesWritten
, NULL
) ||
1275 (BytesToWrite
!= BytesWritten
))
1277 DPRINT(MIN_TRACE
, ("Status 0x%lX.\n", GetLastError()));
1279 BytesWritten
= BytesToWrite
;
1280 if (fwrite((void*)((unsigned long *)OutputBuffer
+ BytesSkipped
),
1281 BytesToWrite
, 1, DestFile
) < 1)
1284 CloseFile(DestFile
);
1286 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
1287 return CAB_STATUS_CANNOT_WRITE
;
1289 Size
-= BytesToWrite
;
1291 CurrentOffset
+= BytesToWrite
;
1293 /* Don't skip any more bytes */
1298 CloseFile(DestFile
);
1302 return CAB_STATUS_SUCCESS
;
1306 void CCabinet::SelectCodec(ULONG Id
)
1308 * FUNCTION: Selects codec engine to use
1310 * Id = Codec identifier
1318 CodecSelected
= false;
1326 Codec
= new CRawCodec();
1330 case CAB_CODEC_MSZIP
:
1331 Codec
= new CMSZipCodec();
1339 CodecSelected
= true;
1343 #ifndef CAB_READ_ONLY
1345 /* CAB write methods */
1347 ULONG
CCabinet::NewCabinet()
1349 * FUNCTION: Creates a new cabinet
1351 * Status of operation
1356 CurrentDiskNumber
= 0;
1358 OutputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1359 InputBuffer
= AllocateMemory(CAB_BLOCKSIZE
+ 12); // This should be enough
1360 if ((!OutputBuffer
) || (!InputBuffer
))
1362 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1363 return CAB_STATUS_NOMEMORY
;
1365 CurrentIBuffer
= InputBuffer
;
1366 CurrentIBufferSize
= 0;
1368 CABHeader
.Signature
= CAB_SIGNATURE
;
1369 CABHeader
.Reserved1
= 0; // Not used
1370 CABHeader
.CabinetSize
= 0; // Not yet known
1371 CABHeader
.Reserved2
= 0; // Not used
1372 CABHeader
.Reserved3
= 0; // Not used
1373 CABHeader
.Version
= CAB_VERSION
;
1374 CABHeader
.FolderCount
= 0; // Not yet known
1375 CABHeader
.FileCount
= 0; // Not yet known
1376 CABHeader
.Flags
= 0; // Not yet known
1377 // FIXME: Should be random
1378 CABHeader
.SetID
= 0x534F;
1379 CABHeader
.CabinetNumber
= 0;
1382 TotalFolderSize
= 0;
1385 DiskSize
= sizeof(CFHEADER
);
1387 InitCabinetHeader();
1389 // NextFolderNumber is 0-based
1390 NextFolderNumber
= 0;
1392 CurrentFolderNode
= NULL
;
1393 Status
= NewFolder();
1394 if (Status
!= CAB_STATUS_SUCCESS
)
1397 CurrentFolderNode
->Folder
.DataOffset
= DiskSize
- TotalHeaderSize
;
1399 ScratchFile
= new CCFDATAStorage
;
1402 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1403 return CAB_STATUS_NOMEMORY
;
1406 Status
= ScratchFile
->Create("~CAB.tmp");
1408 CreateNewFolder
= false;
1410 CreateNewDisk
= false;
1412 PrevCabinetNumber
= 0;
1418 ULONG
CCabinet::NewDisk()
1420 * FUNCTION: Forces a new disk to be created
1422 * Status of operation
1425 // NextFolderNumber is 0-based
1426 NextFolderNumber
= 1;
1428 CreateNewDisk
= false;
1430 DiskSize
= sizeof(CFHEADER
) + TotalFolderSize
+ TotalFileSize
;
1432 InitCabinetHeader();
1434 CurrentFolderNode
->TotalFolderSize
= 0;
1436 CurrentFolderNode
->Folder
.DataBlockCount
= 0;
1438 return CAB_STATUS_SUCCESS
;
1442 ULONG
CCabinet::NewFolder()
1444 * FUNCTION: Forces a new folder to be created
1446 * Status of operation
1449 DPRINT(MAX_TRACE
, ("Creating new folder.\n"));
1451 CurrentFolderNode
= NewFolderNode();
1452 if (!CurrentFolderNode
)
1454 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1455 return CAB_STATUS_NOMEMORY
;
1460 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_NONE
;
1463 case CAB_CODEC_MSZIP
:
1464 CurrentFolderNode
->Folder
.CompressionType
= CAB_COMP_MSZIP
;
1468 return CAB_STATUS_UNSUPPCOMP
;
1471 /* FIXME: This won't work if no files are added to the new folder */
1473 DiskSize
+= sizeof(CFFOLDER
);
1475 TotalFolderSize
+= sizeof(CFFOLDER
);
1479 CABHeader
.FolderCount
++;
1483 return CAB_STATUS_SUCCESS
;
1487 ULONG
CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode
)
1489 * FUNCTION: Writes a file to the scratch file
1491 * FileNode = Pointer to file node
1493 * Status of operation
1503 /* Try to open file */
1505 SourceFile
= CreateFile(
1506 FileNode
->FileName
, // Open this file
1507 GENERIC_READ
, // Open for reading
1508 FILE_SHARE_READ
, // Share for reading
1509 NULL
, // No security
1510 OPEN_EXISTING
, // File must exist
1511 FILE_ATTRIBUTE_NORMAL
, // Normal file
1512 NULL
); // No attribute template
1513 if (SourceFile
== INVALID_HANDLE_VALUE
)
1515 DPRINT(MID_TRACE
, ("File not found (%s).\n", FileNode
->FileName
));
1516 return CAB_STATUS_NOFILE
;
1519 SourceFile
= fopen(FileNode
->FileName
, "rb");
1520 if (SourceFile
== NULL
)
1522 DPRINT(MID_TRACE
, ("Cannot open cabinet reserved file.\n"));
1523 return CAB_STATUS_NOFILE
;
1527 if (CreateNewFolder
)
1529 /* There is always a new folder after
1530 a split file is completely stored */
1531 Status
= NewFolder();
1532 if (Status
!= CAB_STATUS_SUCCESS
)
1534 CreateNewFolder
= false;
1537 /* Call OnAdd event handler */
1538 OnAdd(&FileNode
->File
, FileNode
->FileName
);
1540 TotalBytesLeft
= FileNode
->File
.FileSize
;
1542 FileNode
->File
.FileOffset
= CurrentFolderNode
->UncompOffset
;
1543 CurrentFolderNode
->UncompOffset
+= TotalBytesLeft
;
1544 FileNode
->File
.FileControlID
= (USHORT
)(NextFolderNumber
- 1);
1545 CurrentFolderNode
->Commit
= true;
1546 PrevCabinetNumber
= CurrentDiskNumber
;
1548 Size
= sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(FileNode
->FileName
)) + 1;
1549 CABHeader
.FileTableOffset
+= Size
;
1550 TotalFileSize
+= Size
;
1554 FileNode
->Commit
= true;
1556 if (TotalBytesLeft
> 0)
1560 if (TotalBytesLeft
> (ULONG
)CAB_BLOCKSIZE
- CurrentIBufferSize
)
1561 BytesToRead
= CAB_BLOCKSIZE
- CurrentIBufferSize
;
1563 BytesToRead
= TotalBytesLeft
;
1565 if (!ReadFileData(SourceFile
, CurrentIBuffer
, BytesToRead
, &BytesRead
) || (BytesToRead
!= BytesRead
))
1567 DPRINT(MIN_TRACE
, ("Cannot read from file. BytesToRead (%lu) BytesRead (%lu) CurrentIBufferSize (%lu).\n",
1568 BytesToRead
, BytesRead
, CurrentIBufferSize
));
1569 return CAB_STATUS_INVALID_CAB
;
1572 *(unsigned char**)&CurrentIBuffer
+= BytesRead
;
1574 CurrentIBufferSize
+= (USHORT
)BytesRead
;
1576 if (CurrentIBufferSize
== CAB_BLOCKSIZE
)
1578 Status
= WriteDataBlock();
1579 if (Status
!= CAB_STATUS_SUCCESS
)
1582 TotalBytesLeft
-= BytesRead
;
1583 } while ((TotalBytesLeft
> 0) && (!CreateNewDisk
));
1586 if (TotalBytesLeft
== 0)
1588 CloseFile(SourceFile
);
1589 FileNode
->Delete
= true;
1591 if (FileNode
->File
.FileControlID
> CAB_FILE_MAX_FOLDER
)
1593 FileNode
->File
.FileControlID
= CAB_FILE_CONTINUED
;
1594 CurrentFolderNode
->Delete
= true;
1596 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1598 Status
= WriteDataBlock();
1599 if (Status
!= CAB_STATUS_SUCCESS
)
1603 CreateNewFolder
= true;
1608 if (FileNode
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
)
1609 FileNode
->File
.FileControlID
= CAB_FILE_SPLIT
;
1611 FileNode
->File
.FileControlID
= CAB_FILE_PREV_NEXT
;
1614 return CAB_STATUS_SUCCESS
;
1618 ULONG
CCabinet::WriteDisk(ULONG MoreDisks
)
1620 * FUNCTION: Forces the current disk to be written
1622 * MoreDisks = true if there is one or more disks after this disk
1624 * Status of operation
1627 PCFFILE_NODE FileNode
;
1630 ContinueFile
= false;
1631 FileNode
= FileListHead
;
1632 while (FileNode
!= NULL
)
1634 Status
= WriteFileToScratchStorage(FileNode
);
1635 if (Status
!= CAB_STATUS_SUCCESS
)
1640 /* A data block could span more than two
1641 disks if MaxDiskSize is very small */
1642 while (CreateNewDisk
)
1644 DPRINT(MAX_TRACE
, ("Creating new disk.\n"));
1649 ContinueFile
= true;
1650 CreateNewDisk
= false;
1652 DPRINT(MAX_TRACE
, ("First on new disk. CurrentIBufferSize (%lu) CurrentOBufferSize (%lu).\n",
1653 CurrentIBufferSize
, CurrentOBufferSize
));
1655 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1657 Status
= WriteDataBlock();
1658 if (Status
!= CAB_STATUS_SUCCESS
)
1665 ContinueFile
= false;
1666 FileNode
= FileNode
->Next
;
1670 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1672 /* A data block could span more than two
1673 disks if MaxDiskSize is very small */
1675 ASSERT(CreateNewDisk
== false);
1681 DPRINT(MID_TRACE
, ("Creating new disk 2.\n"));
1685 CreateNewDisk
= false;
1687 ASSERT(FileNode
== FileListHead
);
1690 if ((CurrentIBufferSize
> 0) || (CurrentOBufferSize
> 0))
1692 Status
= WriteDataBlock();
1693 if (Status
!= CAB_STATUS_SUCCESS
)
1696 } while (CreateNewDisk
);
1698 CommitDisk(MoreDisks
);
1700 return CAB_STATUS_SUCCESS
;
1704 ULONG
CCabinet::CommitDisk(ULONG MoreDisks
)
1706 * FUNCTION: Commits the current disk
1708 * MoreDisks = true if there is one or more disks after this disk
1710 * Status of operation
1713 PCFFOLDER_NODE FolderNode
;
1716 OnCabinetName(CurrentDiskNumber
, CabinetName
);
1718 /* Create file, fail if it already exists */
1720 FileHandle
= CreateFile(CabinetName
, // Create this file
1721 GENERIC_WRITE
, // Open for writing
1723 NULL
, // No security
1724 CREATE_NEW
, // New file only
1725 FILE_ATTRIBUTE_NORMAL
, // Normal file
1726 NULL
); // No attribute template
1727 if (FileHandle
== INVALID_HANDLE_VALUE
)
1730 /* If file exists, ask to overwrite file */
1731 if (((Status
= GetLastError()) == ERROR_FILE_EXISTS
) &&
1732 (OnOverwrite(NULL
, CabinetName
)))
1735 /* Create cabinet file, overwrite if it already exists */
1736 FileHandle
= CreateFile(CabinetName
, // Create this file
1737 GENERIC_WRITE
, // Open for writing
1739 NULL
, // No security
1740 TRUNCATE_EXISTING
, // Truncate the file
1741 FILE_ATTRIBUTE_NORMAL
, // Normal file
1742 NULL
); // No attribute template
1743 if (FileHandle
== INVALID_HANDLE_VALUE
)
1744 return CAB_STATUS_CANNOT_CREATE
;
1748 if (Status
== ERROR_FILE_EXISTS
)
1749 return CAB_STATUS_FILE_EXISTS
;
1751 return CAB_STATUS_CANNOT_CREATE
;
1755 FileHandle
= fopen(CabinetName
, "rb");
1756 if (FileHandle
!= NULL
)
1759 /* If file exists, ask to overwrite file */
1760 if (OnOverwrite(NULL
, CabinetName
))
1762 FileHandle
= fopen(CabinetName
, "w+b");
1763 if (FileHandle
== NULL
)
1764 return CAB_STATUS_CANNOT_CREATE
;
1767 return CAB_STATUS_FILE_EXISTS
;
1772 FileHandle
= fopen(CabinetName
, "w+b");
1773 if (FileHandle
== NULL
)
1774 return CAB_STATUS_CANNOT_CREATE
;
1778 WriteCabinetHeader(MoreDisks
!= 0);
1780 Status
= WriteFolderEntries();
1781 if (Status
!= CAB_STATUS_SUCCESS
)
1784 /* Write file entries */
1787 /* Write data blocks */
1788 FolderNode
= FolderListHead
;
1789 while (FolderNode
!= NULL
)
1791 if (FolderNode
->Commit
)
1793 Status
= CommitDataBlocks(FolderNode
);
1794 if (Status
!= CAB_STATUS_SUCCESS
)
1796 /* Remove data blocks for folder */
1797 DestroyDataNodes(FolderNode
);
1799 FolderNode
= FolderNode
->Next
;
1802 CloseFile(FileHandle
);
1804 ScratchFile
->Truncate();
1806 return CAB_STATUS_SUCCESS
;
1810 ULONG
CCabinet::CloseDisk()
1812 * FUNCTION: Closes the current disk
1814 * Status of operation
1817 DestroyDeletedFileNodes();
1819 /* Destroy folder nodes that are completely stored */
1820 DestroyDeletedFolderNodes();
1822 CurrentDiskNumber
++;
1824 return CAB_STATUS_SUCCESS
;
1828 ULONG
CCabinet::CloseCabinet()
1830 * FUNCTION: Closes the current cabinet
1832 * Status of operation
1839 DestroyFolderNodes();
1843 FreeMemory(InputBuffer
);
1849 FreeMemory(OutputBuffer
);
1850 OutputBuffer
= NULL
;
1857 Status
= ScratchFile
->Destroy();
1862 return CAB_STATUS_SUCCESS
;
1866 ULONG
CCabinet::AddFile(char* FileName
)
1868 * FUNCTION: Adds a file to the current disk
1870 * FileName = Pointer to string with file name (full path)
1872 * Status of operation
1876 PCFFILE_NODE FileNode
;
1879 NewFileName
= (char*)AllocateMemory(strlen(FileName
) + 1);
1882 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1883 return CAB_STATUS_NOMEMORY
;
1885 strcpy(NewFileName
, FileName
);
1886 ConvertPath(NewFileName
, false);
1888 /* Try to open file */
1890 SrcFile
= CreateFile(
1891 NewFileName
, // Open this file
1892 GENERIC_READ
, // Open for reading
1893 FILE_SHARE_READ
, // Share for reading
1894 NULL
, // No security
1895 OPEN_EXISTING
, // File must exist
1896 FILE_ATTRIBUTE_NORMAL
, // Normal file
1897 NULL
); // No attribute template
1898 if (SrcFile
== INVALID_HANDLE_VALUE
)
1900 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1901 FreeMemory(NewFileName
);
1902 return CAB_STATUS_CANNOT_OPEN
;
1905 SrcFile
= fopen(NewFileName
, "rb");
1906 if (SrcFile
== NULL
)
1908 DPRINT(MID_TRACE
, ("File not found (%s).\n", NewFileName
));
1909 FreeMemory(NewFileName
);
1910 return CAB_STATUS_CANNOT_OPEN
;
1914 FileNode
= NewFileNode();
1917 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
1918 FreeMemory(NewFileName
);
1919 return CAB_STATUS_NOMEMORY
;
1922 FileNode
->FolderNode
= CurrentFolderNode
;
1923 FileNode
->FileName
= NewFileName
;
1925 /* FIXME: Check for and handle large files (>= 2GB) */
1926 FileNode
->File
.FileSize
= GetSizeOfFile(SrcFile
);
1927 if (FileNode
->File
.FileSize
== (ULONG
)-1)
1929 DPRINT(MIN_TRACE
, ("Cannot read from file.\n"));
1930 FreeMemory(NewFileName
);
1931 return CAB_STATUS_CANNOT_READ
;
1934 GetFileTimes(SrcFile
, FileNode
);
1936 GetAttributesOnFile(FileNode
);
1940 return CAB_STATUS_SUCCESS
;
1944 void CCabinet::SetMaxDiskSize(ULONG Size
)
1946 * FUNCTION: Sets the maximum size of the current disk
1948 * Size = Maximum size of current disk (0 means no maximum size)
1954 #endif /* CAB_READ_ONLY */
1957 /* Default event handlers */
1959 bool CCabinet::OnOverwrite(PCFFILE File
,
1962 * FUNCTION: Called when extracting a file and it already exists
1964 * File = Pointer to CFFILE for file being extracted
1965 * FileName = Pointer to buffer with name of file (full path)
1967 * true if the file should be overwritten, false if not
1974 void CCabinet::OnExtract(PCFFILE File
,
1977 * FUNCTION: Called just before extracting a file
1979 * File = Pointer to CFFILE for file being extracted
1980 * FileName = Pointer to buffer with name of file (full path)
1986 void CCabinet::OnDiskChange(char* CabinetName
,
1989 * FUNCTION: Called when a new disk is to be processed
1991 * CabinetName = Pointer to buffer with name of cabinet
1992 * DiskLabel = Pointer to buffer with label of disk
1998 #ifndef CAB_READ_ONLY
2000 void CCabinet::OnAdd(PCFFILE File
,
2003 * FUNCTION: Called just before adding a file to a cabinet
2005 * File = Pointer to CFFILE for file being added
2006 * FileName = Pointer to buffer with name of file (full path)
2012 bool CCabinet::OnDiskLabel(ULONG Number
, char* Label
)
2014 * FUNCTION: Called when a disk needs a label
2016 * Number = Cabinet number that needs a label
2017 * Label = Pointer to buffer to place label of disk
2019 * true if a disk label was returned, false if not
2026 bool CCabinet::OnCabinetName(ULONG Number
, char* Name
)
2028 * FUNCTION: Called when a cabinet needs a name
2030 * Number = Disk number that needs a name
2031 * Name = Pointer to buffer to place name of cabinet
2033 * true if a cabinet name was returned, false if not
2039 #endif /* CAB_READ_ONLY */
2041 PCFFOLDER_NODE
CCabinet::LocateFolderNode(ULONG Index
)
2043 * FUNCTION: Locates a folder node
2045 * Index = Folder index
2047 * Pointer to folder node or NULL if the folder node was not found
2050 PCFFOLDER_NODE Node
;
2054 case CAB_FILE_SPLIT
:
2055 return FolderListTail
;
2057 case CAB_FILE_CONTINUED
:
2058 case CAB_FILE_PREV_NEXT
:
2059 return FolderListHead
;
2062 Node
= FolderListHead
;
2063 while (Node
!= NULL
)
2065 if (Node
->Index
== Index
)
2073 ULONG
CCabinet::GetAbsoluteOffset(PCFFILE_NODE File
)
2075 * FUNCTION: Returns the absolute offset of a file
2077 * File = Pointer to CFFILE_NODE structure for file
2079 * Status of operation
2084 DPRINT(MAX_TRACE
, ("FileName '%s' FileOffset (0x%lX) FileSize (%lu).\n",
2085 (char*)File
->FileName
,
2086 (ULONG
)File
->File
.FileOffset
,
2087 (ULONG
)File
->File
.FileSize
));
2089 Node
= CurrentFolderNode
->DataListHead
;
2090 while (Node
!= NULL
)
2092 DPRINT(MAX_TRACE
, ("GetAbsoluteOffset(): Comparing (0x%lX, 0x%lX) (%lu).\n",
2093 (ULONG
)Node
->UncompOffset
,
2094 (ULONG
)Node
->UncompOffset
+ Node
->Data
.UncompSize
,
2095 (ULONG
)Node
->Data
.UncompSize
));
2097 /* Node->Data.UncompSize will be 0 if the block is split
2098 (ie. it is the last block in this cabinet) */
2099 if ((Node
->Data
.UncompSize
== 0) ||
2100 ((File
->File
.FileOffset
>= Node
->UncompOffset
) &&
2101 (File
->File
.FileOffset
< Node
->UncompOffset
+
2102 Node
->Data
.UncompSize
)))
2104 File
->DataBlock
= Node
;
2105 return CAB_STATUS_SUCCESS
;
2110 return CAB_STATUS_INVALID_CAB
;
2114 ULONG
CCabinet::LocateFile(char* FileName
,
2117 * FUNCTION: Locates a file in the cabinet
2119 * FileName = Pointer to string with name of file to locate
2120 * File = Address of pointer to CFFILE_NODE structure to fill
2122 * Status of operation
2124 * Current folder is set to the folder of the file
2130 DPRINT(MAX_TRACE
, ("FileName '%s'\n", FileName
));
2132 Node
= FileListHead
;
2133 while (Node
!= NULL
)
2135 if (strcasecmp(FileName
, Node
->FileName
) == 0)
2137 CurrentFolderNode
= LocateFolderNode(Node
->File
.FileControlID
);
2138 if (!CurrentFolderNode
)
2140 DPRINT(MID_TRACE
, ("Folder with index number (%lu) not found.\n",
2141 (ULONG
)Node
->File
.FileControlID
));
2142 return CAB_STATUS_INVALID_CAB
;
2145 if (Node
->DataBlock
== NULL
)
2146 Status
= GetAbsoluteOffset(Node
);
2148 Status
= CAB_STATUS_SUCCESS
;
2155 return CAB_STATUS_NOFILE
;
2159 ULONG
CCabinet::ReadString(char* String
, ULONG MaxLength
)
2161 * FUNCTION: Reads a NULL-terminated string from the cabinet
2163 * String = Pointer to buffer to place string
2164 * MaxLength = Maximum length of string
2166 * Status of operation
2179 Size
= ((Offset
+ 32) <= MaxLength
)? 32 : MaxLength
- Offset
;
2183 DPRINT(MIN_TRACE
, ("Too long a filename.\n"));
2184 return CAB_STATUS_INVALID_CAB
;
2187 Status
= ReadBlock(&String
[Offset
], Size
, &BytesRead
);
2188 if ((Status
!= CAB_STATUS_SUCCESS
) || (BytesRead
!= Size
))
2190 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
2191 return CAB_STATUS_INVALID_CAB
;
2194 for (Size
= Offset
; Size
< Offset
+ BytesRead
; Size
++)
2196 if (String
[Size
] == '\0')
2203 Offset
+= BytesRead
;
2207 /* Back up some bytes */
2208 Size
= (BytesRead
- Size
) - 1;
2210 SetLastError(NO_ERROR
);
2211 (ULONG
)SetFilePointer(FileHandle
,
2215 if (GetLastError() != NO_ERROR
)
2217 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2218 return CAB_STATUS_INVALID_CAB
;
2221 if (fseek(FileHandle
, (off_t
)(-(LONG
)Size
), SEEK_CUR
) != 0)
2223 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2224 return CAB_STATUS_INVALID_CAB
;
2227 return CAB_STATUS_SUCCESS
;
2231 ULONG
CCabinet::ReadFileTable()
2233 * FUNCTION: Reads the file table from the cabinet file
2235 * Status of operation
2243 DPRINT(MAX_TRACE
, ("Reading file table at absolute offset (0x%lX).\n",
2244 CABHeader
.FileTableOffset
));
2246 /* Seek to file table */
2248 SetLastError(NO_ERROR
);
2249 SetFilePointer(FileHandle
,
2250 CABHeader
.FileTableOffset
,
2253 if (GetLastError() != NO_ERROR
)
2255 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2256 DPRINT(MIN_TRACE
, ("Error: %lu\n", GetLastError()));
2257 return CAB_STATUS_INVALID_CAB
;
2260 if (fseek(FileHandle
, (off_t
)CABHeader
.FileTableOffset
, SEEK_SET
) != 0)
2262 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2263 return CAB_STATUS_INVALID_CAB
;
2267 for (i
= 0; i
< CABHeader
.FileCount
; i
++)
2269 File
= NewFileNode();
2272 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2273 return CAB_STATUS_NOMEMORY
;
2276 if ((Status
= ReadBlock(&File
->File
, sizeof(CFFILE
),
2277 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2279 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
2280 return CAB_STATUS_INVALID_CAB
;
2283 File
->FileName
= (char*)AllocateMemory(MAX_PATH
);
2284 if (!File
->FileName
)
2286 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2287 return CAB_STATUS_NOMEMORY
;
2290 /* Read file name */
2291 Status
= ReadString(File
->FileName
, MAX_PATH
);
2292 if (Status
!= CAB_STATUS_SUCCESS
)
2295 DPRINT(MAX_TRACE
, ("Found file '%s' at uncompressed offset (0x%lX). Size (%lu bytes) ControlId (0x%lX).\n",
2296 (char*)File
->FileName
,
2297 (ULONG
)File
->File
.FileOffset
,
2298 (ULONG
)File
->File
.FileSize
,
2299 (ULONG
)File
->File
.FileControlID
));
2302 return CAB_STATUS_SUCCESS
;
2306 ULONG
CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode
)
2308 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2310 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2312 * Status of operation
2315 ULONG AbsoluteOffset
;
2322 DPRINT(MAX_TRACE
, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2323 FolderNode
->Index
, FolderNode
->Folder
.DataOffset
));
2325 AbsoluteOffset
= FolderNode
->Folder
.DataOffset
;
2326 UncompOffset
= FolderNode
->UncompOffset
;
2328 for (i
= 0; i
< FolderNode
->Folder
.DataBlockCount
; i
++)
2330 Node
= NewDataNode(FolderNode
);
2333 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
2334 return CAB_STATUS_NOMEMORY
;
2337 /* Seek to data block */
2339 SetLastError(NO_ERROR
);
2340 SetFilePointer(FileHandle
,
2344 if (GetLastError() != NO_ERROR
)
2346 DPRINT(MIN_TRACE
, ("SetFilePointer() failed.\n"));
2347 return CAB_STATUS_INVALID_CAB
;
2350 if (fseek(FileHandle
, (off_t
)AbsoluteOffset
, SEEK_SET
) != 0)
2352 DPRINT(MIN_TRACE
, ("fseek() failed.\n"));
2353 return CAB_STATUS_INVALID_CAB
;
2357 if ((Status
= ReadBlock(&Node
->Data
, sizeof(CFDATA
),
2358 &BytesRead
)) != CAB_STATUS_SUCCESS
)
2360 DPRINT(MIN_TRACE
, ("Cannot read from file (%lu).\n", (ULONG
)Status
));
2361 return CAB_STATUS_INVALID_CAB
;
2364 DPRINT(MAX_TRACE
, ("AbsOffset (0x%lX) UncompOffset (0x%lX) Checksum (0x%lX) CompSize (%lu) UncompSize (%lu).\n",
2365 (ULONG
)AbsoluteOffset
,
2366 (ULONG
)UncompOffset
,
2367 (ULONG
)Node
->Data
.Checksum
,
2368 (ULONG
)Node
->Data
.CompSize
,
2369 (ULONG
)Node
->Data
.UncompSize
));
2371 Node
->AbsoluteOffset
= AbsoluteOffset
;
2372 Node
->UncompOffset
= UncompOffset
;
2374 AbsoluteOffset
+= sizeof(CFDATA
) + Node
->Data
.CompSize
;
2375 UncompOffset
+= Node
->Data
.UncompSize
;
2378 FolderUncompSize
= UncompOffset
;
2380 return CAB_STATUS_SUCCESS
;
2384 PCFFOLDER_NODE
CCabinet::NewFolderNode()
2386 * FUNCTION: Creates a new folder node
2388 * Pointer to node if there was enough free memory available, otherwise NULL
2391 PCFFOLDER_NODE Node
;
2393 Node
= (PCFFOLDER_NODE
)AllocateMemory(sizeof(CFFOLDER_NODE
));
2397 memset(Node
, 0, sizeof(CFFOLDER_NODE
));
2399 Node
->Folder
.CompressionType
= CAB_COMP_NONE
;
2401 Node
->Prev
= FolderListTail
;
2403 if (FolderListTail
!= NULL
)
2404 FolderListTail
->Next
= Node
;
2406 FolderListHead
= Node
;
2408 FolderListTail
= Node
;
2414 PCFFILE_NODE
CCabinet::NewFileNode()
2416 * FUNCTION: Creates a new file node
2418 * FolderNode = Pointer to folder node to bind file to
2420 * Pointer to node if there was enough free memory available, otherwise NULL
2425 Node
= (PCFFILE_NODE
)AllocateMemory(sizeof(CFFILE_NODE
));
2429 memset(Node
, 0, sizeof(CFFILE_NODE
));
2431 Node
->Prev
= FileListTail
;
2433 if (FileListTail
!= NULL
)
2434 FileListTail
->Next
= Node
;
2436 FileListHead
= Node
;
2438 FileListTail
= Node
;
2444 PCFDATA_NODE
CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode
)
2446 * FUNCTION: Creates a new data block node
2448 * FolderNode = Pointer to folder node to bind data block to
2450 * Pointer to node if there was enough free memory available, otherwise NULL
2455 Node
= (PCFDATA_NODE
)AllocateMemory(sizeof(CFDATA_NODE
));
2459 memset(Node
, 0, sizeof(CFDATA_NODE
));
2461 Node
->Prev
= FolderNode
->DataListTail
;
2463 if (FolderNode
->DataListTail
!= NULL
)
2464 FolderNode
->DataListTail
->Next
= Node
;
2466 FolderNode
->DataListHead
= Node
;
2468 FolderNode
->DataListTail
= Node
;
2474 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode
)
2476 * FUNCTION: Destroys data block nodes bound to a folder node
2478 * FolderNode = Pointer to folder node
2481 PCFDATA_NODE PrevNode
;
2482 PCFDATA_NODE NextNode
;
2484 NextNode
= FolderNode
->DataListHead
;
2485 while (NextNode
!= NULL
)
2487 PrevNode
= NextNode
->Next
;
2488 FreeMemory(NextNode
);
2489 NextNode
= PrevNode
;
2491 FolderNode
->DataListHead
= NULL
;
2492 FolderNode
->DataListTail
= NULL
;
2496 void CCabinet::DestroyFileNodes()
2498 * FUNCTION: Destroys file nodes
2500 * FolderNode = Pointer to folder node
2503 PCFFILE_NODE PrevNode
;
2504 PCFFILE_NODE NextNode
;
2506 NextNode
= FileListHead
;
2507 while (NextNode
!= NULL
)
2509 PrevNode
= NextNode
->Next
;
2510 if (NextNode
->FileName
)
2511 FreeMemory(NextNode
->FileName
);
2512 FreeMemory(NextNode
);
2513 NextNode
= PrevNode
;
2515 FileListHead
= NULL
;
2516 FileListTail
= NULL
;
2520 void CCabinet::DestroyDeletedFileNodes()
2522 * FUNCTION: Destroys file nodes that are marked for deletion
2525 PCFFILE_NODE CurNode
;
2526 PCFFILE_NODE NextNode
;
2528 CurNode
= FileListHead
;
2529 while (CurNode
!= NULL
)
2531 NextNode
= CurNode
->Next
;
2533 if (CurNode
->Delete
)
2535 if (CurNode
->Prev
!= NULL
)
2536 CurNode
->Prev
->Next
= CurNode
->Next
;
2539 FileListHead
= CurNode
->Next
;
2541 FileListHead
->Prev
= NULL
;
2544 if (CurNode
->Next
!= NULL
)
2545 CurNode
->Next
->Prev
= CurNode
->Prev
;
2548 FileListTail
= CurNode
->Prev
;
2550 FileListTail
->Next
= NULL
;
2553 DPRINT(MAX_TRACE
, ("Deleting file: '%s'\n", CurNode
->FileName
));
2555 TotalFileSize
-= (sizeof(CFFILE
) + (ULONG
)strlen(GetFileName(CurNode
->FileName
)) + 1);
2557 if (CurNode
->FileName
)
2558 FreeMemory(CurNode
->FileName
);
2559 FreeMemory(CurNode
);
2566 void CCabinet::DestroyFolderNodes()
2568 * FUNCTION: Destroys folder nodes
2571 PCFFOLDER_NODE PrevNode
;
2572 PCFFOLDER_NODE NextNode
;
2574 NextNode
= FolderListHead
;
2575 while (NextNode
!= NULL
)
2577 PrevNode
= NextNode
->Next
;
2578 DestroyDataNodes(NextNode
);
2579 FreeMemory(NextNode
);
2580 NextNode
= PrevNode
;
2582 FolderListHead
= NULL
;
2583 FolderListTail
= NULL
;
2587 void CCabinet::DestroyDeletedFolderNodes()
2589 * FUNCTION: Destroys folder nodes that are marked for deletion
2592 PCFFOLDER_NODE CurNode
;
2593 PCFFOLDER_NODE NextNode
;
2595 CurNode
= FolderListHead
;
2596 while (CurNode
!= NULL
)
2598 NextNode
= CurNode
->Next
;
2600 if (CurNode
->Delete
)
2602 if (CurNode
->Prev
!= NULL
)
2603 CurNode
->Prev
->Next
= CurNode
->Next
;
2606 FolderListHead
= CurNode
->Next
;
2608 FolderListHead
->Prev
= NULL
;
2611 if (CurNode
->Next
!= NULL
)
2612 CurNode
->Next
->Prev
= CurNode
->Prev
;
2615 FolderListTail
= CurNode
->Prev
;
2617 FolderListTail
->Next
= NULL
;
2620 DestroyDataNodes(CurNode
);
2621 FreeMemory(CurNode
);
2623 TotalFolderSize
-= sizeof(CFFOLDER
);
2630 ULONG
CCabinet::ComputeChecksum(void* Buffer
,
2634 * FUNCTION: Computes checksum for data block
2636 * Buffer = Pointer to data buffer
2637 * Size = Length of data buffer
2638 * Seed = Previously computed checksum
2640 * Checksum of buffer
2643 int UlongCount
; // Number of ULONGs in block
2644 ULONG Checksum
; // Checksum accumulator
2648 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2649 won't accept checksums computed by this routine */
2651 DPRINT(MIN_TRACE
, ("Checksumming buffer (0x%p) Size (%lu)\n", Buffer
, Size
));
2653 UlongCount
= Size
/ 4; // Number of ULONGs
2654 Checksum
= Seed
; // Init checksum
2655 pb
= (unsigned char*)Buffer
; // Start at front of data block
2657 /* Checksum integral multiple of ULONGs */
2658 while (UlongCount
-- > 0)
2660 /* NOTE: Build ULONG in big/little-endian independent manner */
2661 ul
= *pb
++; // Get low-order byte
2662 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2663 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3nd byte
2664 ul
|= (((ULONG
)(*pb
++)) << 24); // Add 4th byte
2666 Checksum
^= ul
; // Update checksum
2669 /* Checksum remainder bytes */
2674 ul
|= (((ULONG
)(*pb
++)) << 16); // Add 3rd byte
2676 ul
|= (((ULONG
)(*pb
++)) << 8); // Add 2nd byte
2678 ul
|= *pb
++; // Get low-order byte
2682 Checksum
^= ul
; // Update checksum
2684 /* Return computed checksum */
2689 ULONG
CCabinet::ReadBlock(void* Buffer
,
2693 * FUNCTION: Read a block of data from file
2695 * Buffer = Pointer to data buffer
2696 * Size = Length of data buffer
2697 * BytesRead = Pointer to ULONG that on return will contain
2698 * number of bytes read
2700 * Status of operation
2703 if (!ReadFileData(FileHandle
, Buffer
, Size
, BytesRead
))
2704 return CAB_STATUS_INVALID_CAB
;
2705 return CAB_STATUS_SUCCESS
;
2708 #ifndef CAB_READ_ONLY
2710 ULONG
CCabinet::InitCabinetHeader()
2712 * FUNCTION: Initializes cabinet header and optional fields
2714 * Status of operation
2720 CABHeader
.FileTableOffset
= 0; // Not known yet
2721 CABHeader
.FolderCount
= 0; // Not known yet
2722 CABHeader
.FileCount
= 0; // Not known yet
2723 CABHeader
.Flags
= 0; // Not known yet
2725 CABHeader
.CabinetNumber
= (USHORT
)CurrentDiskNumber
;
2727 if ((CurrentDiskNumber
> 0) && (OnCabinetName(PrevCabinetNumber
, CabinetPrev
)))
2729 CABHeader
.Flags
|= CAB_FLAG_HASPREV
;
2730 if (!OnDiskLabel(PrevCabinetNumber
, DiskPrev
))
2731 strcpy(CabinetPrev
, "");
2734 if (OnCabinetName(CurrentDiskNumber
+ 1, CabinetNext
))
2736 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2737 if (!OnDiskLabel(CurrentDiskNumber
+ 1, DiskNext
))
2738 strcpy(DiskNext
, "");
2743 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
2746 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2748 /* Calculate size of name of previous cabinet */
2749 TotalSize
+= (ULONG
)strlen(CabinetPrev
) + 1;
2751 /* Calculate size of label of previous disk */
2752 TotalSize
+= (ULONG
)strlen(DiskPrev
) + 1;
2755 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
2758 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2760 /* Calculate size of name of next cabinet */
2761 Size
= (ULONG
)strlen(CabinetNext
) + 1;
2763 NextFieldsSize
= Size
;
2765 /* Calculate size of label of next disk */
2766 Size
= (ULONG
)strlen(DiskNext
) + 1;
2768 NextFieldsSize
+= Size
;
2773 /* Add cabinet reserved area size if present */
2774 if (CabinetReservedFileSize
> 0)
2776 CABHeader
.Flags
|= CAB_FLAG_RESERVE
;
2777 TotalSize
+= CabinetReservedFileSize
;
2778 TotalSize
+= sizeof(ULONG
); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2781 DiskSize
+= TotalSize
;
2783 TotalHeaderSize
= sizeof(CFHEADER
) + TotalSize
;
2785 return CAB_STATUS_SUCCESS
;
2789 ULONG
CCabinet::WriteCabinetHeader(bool MoreDisks
)
2791 * FUNCTION: Writes the cabinet header and optional fields
2793 * MoreDisks = true if next cabinet name should be included
2795 * Status of operation
2798 PCFFOLDER_NODE FolderNode
;
2799 PCFFILE_NODE FileNode
;
2805 CABHeader
.Flags
|= CAB_FLAG_HASNEXT
;
2806 Size
= TotalHeaderSize
;
2810 CABHeader
.Flags
&= ~CAB_FLAG_HASNEXT
;
2811 DiskSize
-= NextFieldsSize
;
2812 Size
= TotalHeaderSize
- NextFieldsSize
;
2815 /* Set absolute folder offsets */
2816 BytesWritten
= Size
+ TotalFolderSize
+ TotalFileSize
;
2817 CABHeader
.FolderCount
= 0;
2818 FolderNode
= FolderListHead
;
2819 while (FolderNode
!= NULL
)
2821 FolderNode
->Folder
.DataOffset
= BytesWritten
;
2823 BytesWritten
+= FolderNode
->TotalFolderSize
;
2825 CABHeader
.FolderCount
++;
2827 FolderNode
= FolderNode
->Next
;
2830 /* Set absolute offset of file table */
2831 CABHeader
.FileTableOffset
= Size
+ TotalFolderSize
;
2833 /* Count number of files to be committed */
2834 CABHeader
.FileCount
= 0;
2835 FileNode
= FileListHead
;
2836 while (FileNode
!= NULL
)
2838 if (FileNode
->Commit
)
2839 CABHeader
.FileCount
++;
2840 FileNode
= FileNode
->Next
;
2843 CABHeader
.CabinetSize
= DiskSize
;
2847 if (!WriteFile(FileHandle
, &CABHeader
, sizeof(CFHEADER
), (LPDWORD
)&BytesWritten
, NULL
))
2849 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2850 return CAB_STATUS_CANNOT_WRITE
;
2853 BytesWritten
= sizeof(CFHEADER
);
2854 if (fwrite(&CABHeader
, sizeof(CFHEADER
), 1, FileHandle
) < 1)
2856 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2857 return CAB_STATUS_CANNOT_WRITE
;
2861 /* Write per-cabinet reserved area if present */
2862 if (CABHeader
.Flags
& CAB_FLAG_RESERVE
)
2866 ReservedSize
= CabinetReservedFileSize
& 0xffff;
2867 ReservedSize
|= (0 << 16); /* Folder reserved area size */
2868 ReservedSize
|= (0 << 24); /* Folder reserved area size */
2870 if (!WriteFile(FileHandle
, &ReservedSize
, sizeof(ULONG
), (LPDWORD
)&BytesWritten
, NULL
))
2872 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2873 return CAB_STATUS_CANNOT_WRITE
;
2876 BytesWritten
= sizeof(ULONG
);
2877 if (fwrite(&ReservedSize
, sizeof(ULONG
), 1, FileHandle
) < 1)
2879 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2880 return CAB_STATUS_CANNOT_WRITE
;
2885 if (!WriteFile(FileHandle
, CabinetReservedFileBuffer
, CabinetReservedFileSize
, (LPDWORD
)&BytesWritten
, NULL
))
2887 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2888 return CAB_STATUS_CANNOT_WRITE
;
2891 BytesWritten
= CabinetReservedFileSize
;
2892 if (fwrite(CabinetReservedFileBuffer
, CabinetReservedFileSize
, 1, FileHandle
) < 1)
2894 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2895 return CAB_STATUS_CANNOT_WRITE
;
2900 if ((CABHeader
.Flags
& CAB_FLAG_HASPREV
) > 0)
2902 DPRINT(MAX_TRACE
, ("CabinetPrev '%s'.\n", CabinetPrev
));
2904 /* Write name of previous cabinet */
2905 Size
= (ULONG
)strlen(CabinetPrev
) + 1;
2907 if (!WriteFile(FileHandle
, CabinetPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
2909 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2910 return CAB_STATUS_CANNOT_WRITE
;
2913 BytesWritten
= Size
;
2914 if (fwrite(CabinetPrev
, Size
, 1, FileHandle
) < 1)
2916 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2917 return CAB_STATUS_CANNOT_WRITE
;
2921 DPRINT(MAX_TRACE
, ("DiskPrev '%s'.\n", DiskPrev
));
2923 /* Write label of previous disk */
2924 Size
= (ULONG
)strlen(DiskPrev
) + 1;
2926 if (!WriteFile(FileHandle
, DiskPrev
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
2928 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2929 return CAB_STATUS_CANNOT_WRITE
;
2932 BytesWritten
= Size
;
2933 if (fwrite(DiskPrev
, Size
, 1, FileHandle
) < 1)
2935 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2936 return CAB_STATUS_CANNOT_WRITE
;
2941 if ((CABHeader
.Flags
& CAB_FLAG_HASNEXT
) > 0)
2943 DPRINT(MAX_TRACE
, ("CabinetNext '%s'.\n", CabinetNext
));
2945 /* Write name of next cabinet */
2946 Size
= (ULONG
)strlen(CabinetNext
) + 1;
2948 if (!WriteFile(FileHandle
, CabinetNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
2950 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2951 return CAB_STATUS_CANNOT_WRITE
;
2954 BytesWritten
= Size
;
2955 if (fwrite(CabinetNext
, Size
, 1, FileHandle
) < 1)
2957 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2958 return CAB_STATUS_CANNOT_WRITE
;
2962 DPRINT(MAX_TRACE
, ("DiskNext '%s'.\n", DiskNext
));
2964 /* Write label of next disk */
2965 Size
= (ULONG
)strlen(DiskNext
) + 1;
2967 if (!WriteFile(FileHandle
, DiskNext
, Size
, (LPDWORD
)&BytesWritten
, NULL
))
2969 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2970 return CAB_STATUS_CANNOT_WRITE
;
2973 BytesWritten
= Size
;
2974 if (fwrite(DiskNext
, Size
, 1, FileHandle
) < 1)
2976 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
2977 return CAB_STATUS_CANNOT_WRITE
;
2982 return CAB_STATUS_SUCCESS
;
2986 ULONG
CCabinet::WriteFolderEntries()
2988 * FUNCTION: Writes folder entries
2990 * Status of operation
2993 PCFFOLDER_NODE FolderNode
;
2996 DPRINT(MAX_TRACE
, ("Writing folder table.\n"));
2998 FolderNode
= FolderListHead
;
2999 while (FolderNode
!= NULL
)
3001 if (FolderNode
->Commit
)
3003 DPRINT(MAX_TRACE
, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
3004 FolderNode
->Folder
.CompressionType
, FolderNode
->Folder
.DataBlockCount
, FolderNode
->Folder
.DataOffset
));
3007 if (!WriteFile(FileHandle
,
3008 &FolderNode
->Folder
,
3010 (LPDWORD
)&BytesWritten
,
3013 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3014 return CAB_STATUS_CANNOT_WRITE
;
3017 BytesWritten
= sizeof(CFFOLDER
);
3018 if (fwrite(&FolderNode
->Folder
, sizeof(CFFOLDER
), 1, FileHandle
) < 1)
3020 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3021 return CAB_STATUS_CANNOT_WRITE
;
3025 FolderNode
= FolderNode
->Next
;
3028 return CAB_STATUS_SUCCESS
;
3032 ULONG
CCabinet::WriteFileEntries()
3034 * FUNCTION: Writes file entries for all files
3036 * Status of operation
3041 bool SetCont
= false;
3043 DPRINT(MAX_TRACE
, ("Writing file table.\n"));
3045 File
= FileListHead
;
3046 while (File
!= NULL
)
3050 /* Remove any continued files that ends in this disk */
3051 if (File
->File
.FileControlID
== CAB_FILE_CONTINUED
)
3052 File
->Delete
= true;
3054 /* The file could end in the last (split) block and should therefore
3055 appear in the next disk too */
3057 if ((File
->File
.FileOffset
+ File
->File
.FileSize
>= LastBlockStart
) &&
3058 (File
->File
.FileControlID
<= CAB_FILE_MAX_FOLDER
) && (BlockIsSplit
))
3060 File
->File
.FileControlID
= CAB_FILE_SPLIT
;
3061 File
->Delete
= false;
3065 DPRINT(MAX_TRACE
, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
3066 File
->File
.FileControlID
, File
->File
.FileOffset
, File
->File
.FileSize
, File
->FileName
));
3069 if (!WriteFile(FileHandle
,
3072 (LPDWORD
)&BytesWritten
,
3074 return CAB_STATUS_CANNOT_WRITE
;
3076 BytesWritten
= sizeof(CFFILE
);
3077 if (fwrite(&File
->File
, sizeof(CFFILE
), 1, FileHandle
) < 1)
3079 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3080 return CAB_STATUS_CANNOT_WRITE
;
3085 if (!WriteFile(FileHandle
,
3086 GetFileName(File
->FileName
),
3087 (DWORD
)strlen(GetFileName(File
->FileName
)) + 1,
3088 (LPDWORD
)&BytesWritten
,
3090 return CAB_STATUS_CANNOT_WRITE
;
3092 BytesWritten
= strlen(GetFileName(File
->FileName
)) + 1;
3093 if (fwrite(GetFileName(File
->FileName
), strlen(GetFileName(File
->FileName
)) + 1, 1, FileHandle
) < 1)
3095 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3096 return CAB_STATUS_CANNOT_WRITE
;
3102 File
->File
.FileControlID
= CAB_FILE_CONTINUED
;
3109 return CAB_STATUS_SUCCESS
;
3113 ULONG
CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode
)
3115 * FUNCTION: Writes data blocks to the cabinet
3117 * FolderNode = Pointer to folder node containing the data blocks
3119 * Status of operation
3122 PCFDATA_NODE DataNode
;
3127 DataNode
= FolderNode
->DataListHead
;
3128 if (DataNode
!= NULL
)
3129 Status
= ScratchFile
->Seek(DataNode
->ScratchFilePosition
);
3131 while (DataNode
!= NULL
)
3133 DPRINT(MAX_TRACE
, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
3134 DataNode
->ScratchFilePosition
,
3135 DataNode
->Data
.CompSize
,
3136 DataNode
->Data
.UncompSize
));
3138 /* InputBuffer is free for us to use here, so we use it and avoid a
3139 memory allocation. OutputBuffer can't be used here because it may
3140 still contain valid data (if a data block spans two or more disks) */
3141 Status
= ScratchFile
->ReadBlock(&DataNode
->Data
, InputBuffer
, &BytesRead
);
3142 if (Status
!= CAB_STATUS_SUCCESS
)
3144 DPRINT(MIN_TRACE
, ("Cannot read from scratch file (%lu).\n", (ULONG
)Status
));
3149 if (!WriteFile(FileHandle
, &DataNode
->Data
,
3150 sizeof(CFDATA
), (LPDWORD
)&BytesWritten
, NULL
))
3152 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3153 return CAB_STATUS_CANNOT_WRITE
;
3156 BytesWritten
= sizeof(CFDATA
);
3157 if (fwrite(&DataNode
->Data
, sizeof(CFDATA
), 1, FileHandle
) < 1)
3159 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3160 return CAB_STATUS_CANNOT_WRITE
;
3165 if (!WriteFile(FileHandle
, InputBuffer
,
3166 DataNode
->Data
.CompSize
, (LPDWORD
)&BytesWritten
, NULL
))
3168 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3169 return CAB_STATUS_CANNOT_WRITE
;
3172 BytesWritten
= DataNode
->Data
.CompSize
;
3173 if (fwrite(InputBuffer
, DataNode
->Data
.CompSize
, 1, FileHandle
) < 1)
3175 DPRINT(MIN_TRACE
, ("Cannot write to file.\n"));
3176 return CAB_STATUS_CANNOT_WRITE
;
3180 DataNode
= DataNode
->Next
;
3182 return CAB_STATUS_SUCCESS
;
3186 ULONG
CCabinet::WriteDataBlock()
3188 * FUNCTION: Writes the current data block to the scratch file
3190 * Status of operation
3195 PCFDATA_NODE DataNode
;
3199 Status
= Codec
->Compress(OutputBuffer
,
3204 DPRINT(MAX_TRACE
, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
3205 CurrentIBufferSize
, TotalCompSize
));
3207 CurrentOBuffer
= OutputBuffer
;
3208 CurrentOBufferSize
= TotalCompSize
;
3211 DataNode
= NewDataNode(CurrentFolderNode
);
3214 DPRINT(MIN_TRACE
, ("Insufficient memory.\n"));
3215 return CAB_STATUS_NOMEMORY
;
3218 DiskSize
+= sizeof(CFDATA
);
3220 if (MaxDiskSize
> 0)
3221 /* Disk size is limited */
3222 BlockIsSplit
= (DiskSize
+ CurrentOBufferSize
> MaxDiskSize
);
3224 BlockIsSplit
= false;
3228 DataNode
->Data
.CompSize
= (USHORT
)(MaxDiskSize
- DiskSize
);
3229 DataNode
->Data
.UncompSize
= 0;
3230 CreateNewDisk
= true;
3234 DataNode
->Data
.CompSize
= (USHORT
)CurrentOBufferSize
;
3235 DataNode
->Data
.UncompSize
= (USHORT
)CurrentIBufferSize
;
3238 DataNode
->Data
.Checksum
= 0;
3239 DataNode
->ScratchFilePosition
= ScratchFile
->Position();
3241 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3242 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3244 DPRINT(MAX_TRACE
, ("Writing block. Checksum (0x%lX) CompSize (%lu) UncompSize (%lu).\n",
3245 (ULONG
)DataNode
->Data
.Checksum
,
3246 (ULONG
)DataNode
->Data
.CompSize
,
3247 (ULONG
)DataNode
->Data
.UncompSize
));
3249 Status
= ScratchFile
->WriteBlock(&DataNode
->Data
,
3250 CurrentOBuffer
, &BytesWritten
);
3251 if (Status
!= CAB_STATUS_SUCCESS
)
3254 DiskSize
+= BytesWritten
;
3256 CurrentFolderNode
->TotalFolderSize
+= (BytesWritten
+ sizeof(CFDATA
));
3257 CurrentFolderNode
->Folder
.DataBlockCount
++;
3259 *(unsigned char**)&CurrentOBuffer
+= DataNode
->Data
.CompSize
;
3260 CurrentOBufferSize
-= DataNode
->Data
.CompSize
;
3262 LastBlockStart
+= DataNode
->Data
.UncompSize
;
3266 CurrentIBufferSize
= 0;
3267 CurrentIBuffer
= InputBuffer
;
3270 return CAB_STATUS_SUCCESS
;
3275 void CCabinet::ConvertDateAndTime(time_t* Time
,
3279 * FUNCTION: Returns file times of a file
3281 * FileHandle = File handle of file to get file times from
3282 * File = Pointer to CFFILE node for file
3284 * Status of operation
3289 timedef
= localtime(Time
);
3291 DPRINT(MAX_TRACE
, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3292 timedef
->tm_mday
, timedef
->tm_mon
, timedef
->tm_year
,
3293 timedef
->tm_sec
, timedef
->tm_min
, timedef
->tm_hour
));
3295 *DosDate
= ((timedef
->tm_mday
+ 1) << 0)
3296 | ((timedef
->tm_mon
+ 1) << 5)
3297 | (((timedef
->tm_year
+ 1900) - 1980) << 9);
3299 *DosTime
= (timedef
->tm_sec
<< 0)
3300 | (timedef
->tm_min
<< 5)
3301 | (timedef
->tm_hour
<< 11);
3307 ULONG
CCabinet::GetFileTimes(FILEHANDLE FileHandle
, PCFFILE_NODE File
)
3309 * FUNCTION: Returns file times of a file
3311 * FileHandle = File handle of file to get file times from
3312 * File = Pointer to CFFILE node for file
3314 * Status of operation
3320 if (GetFileTime(FileHandle
, NULL
, NULL
, &FileTime
))
3321 FileTimeToDosDateTime(&FileTime
,
3322 &File
->File
.FileDate
,
3323 &File
->File
.FileTime
);
3328 // Check for an absolute path
3329 if (IsSeparator(File
->FileName
[0]))
3330 strcpy(buf
, File
->FileName
);
3333 getcwd(buf
, sizeof(buf
));
3334 strcat(buf
, DIR_SEPARATOR_STRING
);
3335 strcat(buf
, File
->FileName
);
3338 if (stat(buf
, &stbuf
) == -1)
3339 return CAB_STATUS_CANNOT_READ
;
3341 ConvertDateAndTime(&stbuf
.st_mtime
, &File
->File
.FileDate
, &File
->File
.FileTime
);
3343 return CAB_STATUS_SUCCESS
;
3347 ULONG
CCabinet::GetAttributesOnFile(PCFFILE_NODE File
)
3349 * FUNCTION: Returns attributes on a file
3351 * File = Pointer to CFFILE node for file
3353 * Status of operation
3359 Attributes
= GetFileAttributes(File
->FileName
);
3360 if (Attributes
== -1)
3361 return CAB_STATUS_CANNOT_READ
;
3363 if (Attributes
& FILE_ATTRIBUTE_READONLY
)
3364 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3366 if (Attributes
& FILE_ATTRIBUTE_HIDDEN
)
3367 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3369 if (Attributes
& FILE_ATTRIBUTE_SYSTEM
)
3370 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3372 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
3373 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3375 if (Attributes
& FILE_ATTRIBUTE_ARCHIVE
)
3376 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3381 // Check for an absolute path
3382 if (IsSeparator(File
->FileName
[0]))
3383 strcpy(buf
, File
->FileName
);
3386 getcwd(buf
, sizeof(buf
));
3387 strcat(buf
, DIR_SEPARATOR_STRING
);
3388 strcat(buf
, File
->FileName
);
3391 if (stat(buf
, &stbuf
) == -1)
3392 return CAB_STATUS_CANNOT_READ
;
3395 File
->File
.Attributes
|= CAB_ATTRIB_READONLY
;
3396 File
->File
.Attributes
|= CAB_ATTRIB_HIDDEN
;
3397 File
->File
.Attributes
|= CAB_ATTRIB_SYSTEM
;
3400 if (stbuf
.st_mode
& S_IFDIR
)
3401 File
->File
.Attributes
|= CAB_ATTRIB_DIRECTORY
;
3403 File
->File
.Attributes
|= CAB_ATTRIB_ARCHIVE
;
3406 return CAB_STATUS_SUCCESS
;
3410 ULONG
CCabinet::SetAttributesOnFile(PCFFILE_NODE File
)
3412 * FUNCTION: Sets attributes on a file
3414 * File = Pointer to CFFILE node for file
3416 * Status of operation
3420 ULONG Attributes
= 0;
3422 if (File
->File
.Attributes
& CAB_ATTRIB_READONLY
)
3423 Attributes
|= FILE_ATTRIBUTE_READONLY
;
3425 if (File
->File
.Attributes
& CAB_ATTRIB_HIDDEN
)
3426 Attributes
|= FILE_ATTRIBUTE_HIDDEN
;
3428 if (File
->File
.Attributes
& CAB_ATTRIB_SYSTEM
)
3429 Attributes
|= FILE_ATTRIBUTE_SYSTEM
;
3431 if (File
->File
.Attributes
& CAB_ATTRIB_DIRECTORY
)
3432 Attributes
|= FILE_ATTRIBUTE_DIRECTORY
;
3434 if (File
->File
.Attributes
& CAB_ATTRIB_ARCHIVE
)
3435 Attributes
|= FILE_ATTRIBUTE_ARCHIVE
;
3437 SetFileAttributes(File
->FileName
, Attributes
);
3439 return CAB_STATUS_SUCCESS
;
3441 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3442 return CAB_STATUS_SUCCESS
;
3446 #endif /* CAB_READ_ONLY */