2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: apps/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
11 * - Checksum of datablocks should be calculated
12 * - EXTRACT.EXE complains if a disk is created manually
13 * - Folders that are created manually and span disks will result in a damaged cabinet
27 VOID DumpBuffer(PVOID Buffer, DWORD Size)
32 /* Create file, overwrite if it already exists */
33 FileHandle = CreateFile("dump.bin", // Create this file
34 GENERIC_WRITE, // Open for writing
37 CREATE_ALWAYS, // Create or overwrite
38 FILE_ATTRIBUTE_NORMAL, // Normal file
39 NULL); // No attribute template
40 if (FileHandle == INVALID_HANDLE_VALUE) {
41 DPRINT(MID_TRACE, ("ERROR OPENING '%d'.\n", (UINT)GetLastError()));
45 if (!WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL)) {
46 DPRINT(MID_TRACE, ("ERROR WRITING '%d'.\n", (UINT)GetLastError()));
49 CloseHandle(FileHandle);
57 CCFDATAStorage::CCFDATAStorage()
59 * FUNCTION: Default constructor
66 CCFDATAStorage::~CCFDATAStorage()
68 * FUNCTION: Default destructor
75 ULONG CCFDATAStorage::Create(LPTSTR FileName)
77 * FUNCTION: Creates the file
79 * FileName = Pointer to name of file
84 TCHAR FullName[MAX_PATH];
88 if (GetTempPath(MAX_PATH, FullName) == 0)
89 return CAB_STATUS_CANNOT_CREATE;
91 lstrcat(FullName, FileName);
93 /* Create file, overwrite if it already exists */
94 FileHandle = CreateFile(FullName, // Create this file
95 GENERIC_READ | GENERIC_WRITE, // Open for reading/writing
98 CREATE_ALWAYS, // Create or overwrite
99 FILE_FLAG_SEQUENTIAL_SCAN | // Optimize for sequential scans
100 FILE_FLAG_DELETE_ON_CLOSE | // Delete file when closed
101 FILE_ATTRIBUTE_TEMPORARY, // Temporary file
102 NULL); // No attribute template
103 if (FileHandle == INVALID_HANDLE_VALUE) {
104 DPRINT(MID_TRACE, ("ERROR '%d'.\n", (UINT)GetLastError()));
105 return CAB_STATUS_CANNOT_CREATE;
110 return CAB_STATUS_SUCCESS;
114 ULONG CCFDATAStorage::Destroy()
116 * FUNCTION: Destroys the file
118 * Status of operation
123 CloseHandle(FileHandle);
127 return CAB_STATUS_SUCCESS;
131 ULONG CCFDATAStorage::Truncate()
133 * FUNCTION: Truncate the scratch file to zero bytes
135 * Status of operation
138 if (!SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN))
139 return CAB_STATUS_FAILURE;
140 if (!SetEndOfFile(FileHandle))
141 return CAB_STATUS_FAILURE;
142 return CAB_STATUS_SUCCESS;
146 ULONG CCFDATAStorage::Position()
148 * FUNCTION: Returns current position in file
153 return SetFilePointer(FileHandle, 0, NULL, FILE_CURRENT);
157 ULONG CCFDATAStorage::Seek(LONG Position)
159 * FUNCTION: Seeks to an absolute position
161 * Position = Absolute position to seek to
163 * Status of operation
166 if (SetFilePointer(FileHandle,
169 FILE_BEGIN) == 0xFFFFFFFF)
170 return CAB_STATUS_FAILURE;
172 return CAB_STATUS_SUCCESS;
176 ULONG CCFDATAStorage::ReadBlock(PCFDATA Data, PVOID Buffer, PDWORD BytesRead)
178 * FUNCTION: Reads a CFDATA block from the file
180 * Data = Pointer to CFDATA block for the buffer
181 * Buffer = Pointer to buffer to store data read
182 * BytesWritten = Pointer to buffer to write number of bytes read
184 * Status of operation
187 if (!ReadFile(FileHandle, Buffer, Data->CompSize, BytesRead, NULL))
188 return CAB_STATUS_CANNOT_READ;
190 return CAB_STATUS_SUCCESS;
194 ULONG CCFDATAStorage::WriteBlock(PCFDATA Data, PVOID Buffer, PDWORD BytesWritten)
196 * FUNCTION: Writes a CFDATA block to the file
198 * Data = Pointer to CFDATA block for the buffer
199 * Buffer = Pointer to buffer with data to write
200 * BytesWritten = Pointer to buffer to write number of bytes written
202 * Status of operation
205 if (!WriteFile(FileHandle, Buffer, Data->CompSize, BytesWritten, NULL))
206 return CAB_STATUS_CANNOT_WRITE;
208 return CAB_STATUS_SUCCESS;
211 #endif /* CAB_READ_ONLY */
218 * FUNCTION: Default constructor
222 lstrcpy(DestPath, "");
224 FolderListHead = NULL;
225 FolderListTail = NULL;
229 Codec = new CRawCodec();
230 CodecId = CAB_CODEC_RAW;
231 CodecSelected = TRUE;
236 BlockIsSplit = FALSE;
239 FolderUncompSize = 0;
240 BytesLeftInBlock = 0;
242 CurrentDataNode = NULL;
246 CCabinet::~CCabinet()
248 * FUNCTION: Default destructor
256 LPTSTR CCabinet::GetFileName(LPTSTR Path)
258 * FUNCTION: Returns a pointer to file name
260 * Path = Pointer to string with pathname
262 * Pointer to filename
267 j = i = (Path[0] ? (Path[1] == ':' ? 2 : 0) : 0);
270 if (Path [i - 1] == '\\') j = i;
276 VOID CCabinet::RemoveFileName(LPTSTR Path)
278 * FUNCTION: Removes a file name from a path
280 * Path = Pointer to string with path
286 i = (Path [0] ? (Path[1] == ':' ? 2 : 0) : 0);
287 FileName = GetFileName(Path + i);
289 if ((FileName != (Path + i)) && (FileName [-1] == '\\'))
291 if ((FileName == (Path + i)) && (FileName [0] == '\\'))
297 BOOL CCabinet::NormalizePath(LPTSTR Path,
300 * FUNCTION: Normalizes a path
302 * Path = Pointer to string with pathname
303 * Length = Number of bytes in Path
305 * TRUE if there was enough room in Path, or FALSE
311 if ((n = lstrlen(Path)) &&
312 (Path[n - 1] != '\\') &&
313 (OK = ((n + 1) < Length))) {
321 LPTSTR CCabinet::GetCabinetName()
323 * FUNCTION: Returns pointer to cabinet file name
325 * Pointer to string with name of cabinet
332 VOID CCabinet::SetCabinetName(LPTSTR FileName)
334 * FUNCTION: Sets cabinet file name
336 * FileName = Pointer to string with name of cabinet
339 lstrcpy(CabinetName, FileName);
343 VOID CCabinet::SetDestinationPath(LPTSTR DestinationPath)
345 * FUNCTION: Sets destination path
347 * DestinationPath = Pointer to string with name of destination path
350 lstrcpy(DestPath, DestinationPath);
351 if (lstrlen(DestPath) > 0)
352 NormalizePath(DestPath, MAX_PATH);
356 LPTSTR CCabinet::GetDestinationPath()
358 * FUNCTION: Returns destination path
360 * Pointer to string with name of destination path
367 DWORD CCabinet::GetCurrentDiskNumber()
369 * FUNCTION: Returns current disk number
371 * Current disk number
374 return CurrentDiskNumber;
378 ULONG CCabinet::Open()
380 * FUNCTION: Opens a cabinet file
382 * Status of operation
385 PCFFOLDER_NODE FolderNode;
393 OutputBuffer = HeapAlloc(GetProcessHeap(),
394 0, CAB_BLOCKSIZE + 12); // This should be enough
396 return CAB_STATUS_NOMEMORY;
398 FileHandle = CreateFile(CabinetName, // Open this file
399 GENERIC_READ, // Open for reading
400 FILE_SHARE_READ, // Share for reading
402 OPEN_EXISTING, // Existing file only
403 FILE_ATTRIBUTE_NORMAL, // Normal file
404 NULL); // No attribute template
406 if (FileHandle == INVALID_HANDLE_VALUE) {
407 DPRINT(MID_TRACE, ("Cannot open file.\n"));
408 return CAB_STATUS_CANNOT_OPEN;
413 /* Load CAB header */
414 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead))
415 != CAB_STATUS_SUCCESS) {
416 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
417 return CAB_STATUS_INVALID_CAB;
421 if ((BytesRead != sizeof(CFHEADER)) ||
422 (CABHeader.Signature != CAB_SIGNATURE ) ||
423 (CABHeader.Version != CAB_VERSION ) ||
424 (CABHeader.FolderCount == 0 ) ||
425 (CABHeader.FileCount == 0 ) ||
426 (CABHeader.FileTableOffset < sizeof(CFHEADER))) {
428 DPRINT(MID_TRACE, ("File has invalid header.\n"));
429 return CAB_STATUS_INVALID_CAB;
434 /* Read/skip any reserved bytes */
435 if (CABHeader.Flags & CAB_FLAG_RESERVE) {
436 if ((Status = ReadBlock(&Size, sizeof(DWORD), &BytesRead))
437 != CAB_STATUS_SUCCESS) {
438 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
439 return CAB_STATUS_INVALID_CAB;
441 CabinetReserved = Size & 0xFFFF;
442 FolderReserved = (Size >> 16) & 0xFF;
443 DataReserved = (Size >> 24) & 0xFF;
445 SetFilePointer(FileHandle, CabinetReserved, NULL, FILE_CURRENT);
446 if (GetLastError() != NO_ERROR) {
447 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
448 return CAB_STATUS_NOMEMORY;
452 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) {
453 /* Read name of previous cabinet */
454 Status = ReadString(CabinetPrev, 256);
455 if (Status != CAB_STATUS_SUCCESS)
457 /* Read label of previous disk */
458 Status = ReadString(DiskPrev, 256);
459 if (Status != CAB_STATUS_SUCCESS)
462 lstrcpy(CabinetPrev, "");
463 lstrcpy(DiskPrev, "");
466 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) {
467 /* Read name of next cabinet */
468 Status = ReadString(CabinetNext, 256);
469 if (Status != CAB_STATUS_SUCCESS)
471 /* Read label of next disk */
472 Status = ReadString(DiskNext, 256);
473 if (Status != CAB_STATUS_SUCCESS)
476 lstrcpy(CabinetNext, "");
477 lstrcpy(DiskNext, "");
480 /* Read all folders */
481 for (Index = 0; Index < CABHeader.FolderCount; Index++) {
482 FolderNode = NewFolderNode();
484 DPRINT(MIN_TRACE, ("Insufficient resources.\n"));
485 return CAB_STATUS_NOMEMORY;
489 FolderNode->UncompOffset = FolderUncompSize;
491 FolderNode->Index = Index;
493 if ((Status = ReadBlock(&FolderNode->Folder,
494 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) {
495 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
496 return CAB_STATUS_INVALID_CAB;
500 /* Read file entries */
501 Status = ReadFileTable();
502 if (Status != CAB_STATUS_SUCCESS) {
503 DPRINT(MIN_TRACE, ("ReadFileTable() failed (%d).\n", (UINT)Status));
507 /* Read data blocks for all folders */
508 FolderNode = FolderListHead;
509 while (FolderNode != NULL) {
510 Status = ReadDataBlocks(FolderNode);
511 if (Status != CAB_STATUS_SUCCESS) {
512 DPRINT(MIN_TRACE, ("ReadDataBlocks() failed (%d).\n", (UINT)Status));
515 FolderNode = FolderNode->Next;
518 return CAB_STATUS_SUCCESS;
522 VOID CCabinet::Close()
524 * FUNCTION: Closes the cabinet file
528 CloseHandle(FileHandle);
534 ULONG CCabinet::FindFirst(LPTSTR FileName,
537 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
539 * FileName = Pointer to search criteria
540 * Search = Pointer to search structure
542 * Status of operation
545 RestartSearch = FALSE;
546 strncpy(Search->Search, FileName, MAX_PATH);
547 Search->Next = FileListHead;
548 return FindNext(Search);
552 ULONG CCabinet::FindNext(PCAB_SEARCH Search)
554 * FUNCTION: Finds next file in the cabinet that matches a search criteria
556 * Search = Pointer to search structure
558 * Status of operation
564 Search->Next = FileListHead;
566 /* Skip split files already extracted */
567 while ((Search->Next) &&
568 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) &&
569 (Search->Next->File.FileOffset <= LastFileOffset)) {
570 DPRINT(MAX_TRACE, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
571 Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset));
572 Search->Next = Search->Next->Next;
575 RestartSearch = FALSE;
578 /* FIXME: Check search criteria */
581 if (lstrlen(DiskNext) > 0) {
584 SetCabinetName(CabinetNext);
586 OnDiskChange(CabinetNext, DiskNext);
589 if (Status != CAB_STATUS_SUCCESS)
592 Search->Next = FileListHead;
594 return CAB_STATUS_NOFILE;
596 return CAB_STATUS_NOFILE;
599 Search->File = &Search->Next->File;
600 Search->FileName = Search->Next->FileName;
601 Search->Next = Search->Next->Next;
602 return CAB_STATUS_SUCCESS;
606 ULONG CCabinet::ExtractFile(LPTSTR FileName)
608 * FUNCTION: Extracts a file from the cabinet
610 * FileName = Pointer to buffer with name of file
612 * Status of operation
622 DWORD TotalBytesRead;
625 PUCHAR CurrentBuffer;
632 TCHAR DestName[MAX_PATH];
633 TCHAR TempName[MAX_PATH];
635 Status = LocateFile(FileName, &File);
636 if (Status != CAB_STATUS_SUCCESS) {
637 DPRINT(MID_TRACE, ("Cannot locate file (%d).\n", (UINT)Status));
641 LastFileOffset = File->File.FileOffset;
643 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) {
645 SelectCodec(CAB_CODEC_RAW);
648 SelectCodec(CAB_CODEC_MSZIP);
651 return CAB_STATUS_UNSUPPCOMP;
654 DPRINT(MAX_TRACE, ("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
655 (UINT)File->File.FileOffset,
656 (UINT)File->File.FileSize,
657 (UINT)File->DataBlock->AbsoluteOffset,
658 (UINT)File->DataBlock->UncompOffset));
660 lstrcpy(DestName, DestPath);
661 lstrcat(DestName, FileName);
663 /* Create destination file, fail if it already exists */
664 DestFile = CreateFile(DestName, // Create this file
665 GENERIC_WRITE, // Open for writing
668 CREATE_NEW, // New file only
669 FILE_ATTRIBUTE_NORMAL, // Normal file
670 NULL); // No attribute template
671 if (DestFile == INVALID_HANDLE_VALUE) {
672 /* If file exists, ask to overwrite file */
673 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
674 (OnOverwrite(&File->File, FileName))) {
675 /* Create destination file, overwrite if it already exists */
676 DestFile = CreateFile(DestName, // Create this file
677 GENERIC_WRITE, // Open for writing
680 TRUNCATE_EXISTING, // Truncate the file
681 FILE_ATTRIBUTE_NORMAL, // Normal file
682 NULL); // No attribute template
683 if (DestFile == INVALID_HANDLE_VALUE)
684 return CAB_STATUS_CANNOT_CREATE;
686 if (Status == ERROR_FILE_EXISTS)
687 return CAB_STATUS_FILE_EXISTS;
689 return CAB_STATUS_CANNOT_CREATE;
693 if (!DosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) {
694 CloseHandle(DestFile);
695 DPRINT(MIN_TRACE, ("DosDateTimeToFileTime() failed (%d).\n", GetLastError()));
696 return CAB_STATUS_CANNOT_WRITE;
699 SetFileTime(DestFile, NULL, &FileTime, NULL);
701 SetAttributesOnFile(File);
703 Buffer = (PUCHAR)HeapAlloc(GetProcessHeap(),
704 0, CAB_BLOCKSIZE + 12); // This should be enough
706 CloseHandle(DestFile);
707 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
708 return CAB_STATUS_NOMEMORY;
711 /* Call OnExtract event handler */
712 OnExtract(&File->File, FileName);
714 /* Search to start of file */
715 Offset = SetFilePointer(FileHandle,
716 File->DataBlock->AbsoluteOffset,
719 if (GetLastError() != NO_ERROR) {
720 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
721 return CAB_STATUS_INVALID_CAB;
724 Size = File->File.FileSize;
725 Offset = File->File.FileOffset;
726 CurrentOffset = File->DataBlock->UncompOffset;
730 ReuseBlock = (CurrentDataNode == File->DataBlock);
733 DPRINT(MAX_TRACE, ("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
734 File->DataBlock->UncompOffset, (UINT)ReuseBlock, Offset, Size,
737 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) {
739 DPRINT(MAX_TRACE, ("Filling buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock));
741 CurrentBuffer = Buffer;
744 DPRINT(MAX_TRACE, ("Size (%d bytes).\n", Size));
746 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
747 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) {
748 CloseHandle(DestFile);
749 HeapFree(GetProcessHeap(), 0, Buffer);
750 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
751 return CAB_STATUS_INVALID_CAB;
754 DPRINT(MAX_TRACE, ("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
755 (UINT)CFData.Checksum,
756 (UINT)CFData.CompSize,
757 (UINT)CFData.UncompSize,
758 (UINT)SetFilePointer(FileHandle, 0, NULL, FILE_CURRENT)));
760 ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
762 BytesToRead = CFData.CompSize;
764 DPRINT(MAX_TRACE, ("Read: (0x%X,0x%X).\n",
765 CurrentBuffer, Buffer));
767 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) !=
768 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) {
769 CloseHandle(DestFile);
770 HeapFree(GetProcessHeap(), 0, Buffer);
771 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
772 return CAB_STATUS_INVALID_CAB;
775 /* FIXME: Does not work with files generated by makecab.exe */
777 if (CFData.Checksum != 0) {
778 DWORD Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
779 if (Checksum != CFData.Checksum) {
780 CloseHandle(DestFile);
781 HeapFree(GetProcessHeap(), 0, Buffer);
782 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
783 Checksum, CFData.Checksum));
784 return CAB_STATUS_INVALID_CAB;
788 TotalBytesRead += BytesRead;
790 CurrentBuffer += BytesRead;
792 if (CFData.UncompSize == 0) {
793 if (lstrlen(DiskNext) == 0)
794 return CAB_STATUS_NOFILE;
796 /* CloseCabinet() will destroy all file entries so in case
797 FileName refers to the FileName field of a CFFOLDER_NODE
798 structure, we have to save a copy of the filename */
799 lstrcpy(TempName, FileName);
803 SetCabinetName(CabinetNext);
805 OnDiskChange(CabinetNext, DiskNext);
808 if (Status != CAB_STATUS_SUCCESS)
811 /* The first data block of the file will not be
812 found as it is located in the previous file */
813 Status = LocateFile(TempName, &File);
814 if (Status == CAB_STATUS_NOFILE) {
815 DPRINT(MID_TRACE, ("Cannot locate file (%d).\n", (UINT)Status));
819 /* The file is continued in the first data block in the folder */
820 File->DataBlock = CurrentFolderNode->DataListHead;
822 /* Search to start of file */
823 SetFilePointer(FileHandle,
824 File->DataBlock->AbsoluteOffset,
827 if (GetLastError() != NO_ERROR) {
828 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
829 return CAB_STATUS_INVALID_CAB;
832 DPRINT(MAX_TRACE, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
833 (UINT)File->File.FileOffset,
834 (UINT)File->File.FileSize,
835 (UINT)File->DataBlock->AbsoluteOffset,
836 (UINT)File->DataBlock->UncompOffset));
838 CurrentDataNode = File->DataBlock;
841 RestartSearch = TRUE;
843 } while (CFData.UncompSize == 0);
845 DPRINT(MAX_TRACE, ("TotalBytesRead (%d).\n", TotalBytesRead));
847 Status = Codec->Uncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite);
848 if (Status != CS_SUCCESS) {
849 CloseHandle(DestFile);
850 HeapFree(GetProcessHeap(), 0, Buffer);
851 DPRINT(MID_TRACE, ("Cannot uncompress block.\n"));
852 if (Status == CS_NOMEMORY)
853 return CAB_STATUS_NOMEMORY;
854 return CAB_STATUS_INVALID_CAB;
857 if (BytesToWrite != CFData.UncompSize) {
858 DPRINT(MID_TRACE, ("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
859 BytesToWrite, CFData.UncompSize));
860 return CAB_STATUS_INVALID_CAB;
863 BytesLeftInBlock = BytesToWrite;
865 DPRINT(MAX_TRACE, ("Using same buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock));
867 BytesToWrite = BytesLeftInBlock;
869 DPRINT(MAX_TRACE, ("Seeking to absolute offset 0x%X.\n",
870 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
871 CurrentDataNode->Data.CompSize));
873 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
874 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) {
875 CloseHandle(DestFile);
876 HeapFree(GetProcessHeap(), 0, Buffer);
877 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
878 return CAB_STATUS_INVALID_CAB;
881 DPRINT(MAX_TRACE, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
882 CFData.CompSize, CFData.UncompSize));
884 /* Go to next data block */
885 SetFilePointer(FileHandle,
886 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
887 CurrentDataNode->Data.CompSize,
890 if (GetLastError() != NO_ERROR) {
891 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
892 return CAB_STATUS_INVALID_CAB;
899 BytesSkipped = (Offset - CurrentOffset);
903 BytesToWrite -= BytesSkipped;
905 if (Size < BytesToWrite)
908 DPRINT(MAX_TRACE, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
912 (UINT)BytesSkipped, (UINT)Skip,
915 if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
916 BytesToWrite, &BytesWritten, NULL) ||
917 (BytesToWrite != BytesWritten)) {
919 DPRINT(MIN_TRACE, ("Status 0x%X.\n", GetLastError()));
921 CloseHandle(DestFile);
922 HeapFree(GetProcessHeap(), 0, Buffer);
923 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
924 return CAB_STATUS_CANNOT_WRITE;
926 Size -= BytesToWrite;
928 CurrentOffset += BytesToWrite;
930 /* Don't skip any more bytes */
935 CloseHandle(DestFile);
937 HeapFree(GetProcessHeap(), 0, Buffer);
939 return CAB_STATUS_SUCCESS;
943 VOID CCabinet::SelectCodec(ULONG Id)
945 * FUNCTION: Selects codec engine to use
947 * Id = Codec identifier
954 CodecSelected = FALSE;
960 Codec = new CRawCodec();
962 case CAB_CODEC_MSZIP:
963 Codec = new CMSZipCodec();
970 CodecSelected = TRUE;
974 #ifndef CAB_READ_ONLY
976 /* CAB write methods */
978 ULONG CCabinet::NewCabinet()
980 * FUNCTION: Creates a new cabinet
982 * Status of operation
987 CurrentDiskNumber = 0;
989 OutputBuffer = HeapAlloc(GetProcessHeap(), 0, CAB_BLOCKSIZE + 12); // This should be enough
990 InputBuffer = HeapAlloc(GetProcessHeap(), 0, CAB_BLOCKSIZE + 12); // This should be enough
991 if ((!OutputBuffer) || (!InputBuffer)) {
992 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
993 return CAB_STATUS_NOMEMORY;
995 CurrentIBuffer = InputBuffer;
996 CurrentIBufferSize = 0;
998 CABHeader.Signature = CAB_SIGNATURE;
999 CABHeader.Reserved1 = 0; // Not used
1000 CABHeader.CabinetSize = 0; // Not yet known
1001 CABHeader.Reserved2 = 0; // Not used
1002 CABHeader.Reserved3 = 0; // Not used
1003 CABHeader.Version = CAB_VERSION;
1004 CABHeader.FolderCount = 0; // Not yet known
1005 CABHeader.FileCount = 0; // Not yet known
1006 CABHeader.Flags = 0; // Not yet known
1007 // FIXME: Should be random
1008 CABHeader.SetID = 0x534F;
1009 CABHeader.CabinetNumber = 0;
1012 TotalFolderSize = 0;
1015 DiskSize = sizeof(CFHEADER);
1017 InitCabinetHeader();
1019 // NextFolderNumber is 0-based
1020 NextFolderNumber = 0;
1022 CurrentFolderNode = NULL;
1023 Status = NewFolder();
1024 if (Status != CAB_STATUS_SUCCESS)
1027 CurrentFolderNode->Folder.DataOffset = DiskSize - TotalHeaderSize;
1029 ScratchFile = new CCFDATAStorage;
1031 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1032 return CAB_STATUS_NOMEMORY;
1035 Status = ScratchFile->Create("~CAB.tmp");
1037 CreateNewFolder = FALSE;
1039 CreateNewDisk = FALSE;
1041 PrevCabinetNumber = 0;
1047 ULONG CCabinet::NewDisk()
1049 * FUNCTION: Forces a new disk to be created
1051 * Status of operation
1054 // NextFolderNumber is 0-based
1055 NextFolderNumber = 1;
1057 CreateNewDisk = FALSE;
1059 DiskSize = sizeof(CFHEADER) + TotalFolderSize + TotalFileSize;
1061 InitCabinetHeader();
1063 CurrentFolderNode->TotalFolderSize = 0;
1065 CurrentFolderNode->Folder.DataBlockCount = 0;
1067 return CAB_STATUS_SUCCESS;
1071 ULONG CCabinet::NewFolder()
1073 * FUNCTION: Forces a new folder to be created
1075 * Status of operation
1078 DPRINT(MAX_TRACE, ("Creating new folder.\n"));
1080 CurrentFolderNode = NewFolderNode();
1081 if (!CurrentFolderNode) {
1082 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1083 return CAB_STATUS_NOMEMORY;
1088 CurrentFolderNode->Folder.CompressionType = CAB_COMP_NONE;
1090 case CAB_CODEC_MSZIP:
1091 CurrentFolderNode->Folder.CompressionType = CAB_COMP_MSZIP;
1094 return CAB_STATUS_UNSUPPCOMP;
1097 /* FIXME: This won't work if no files are added to the new folder */
1099 DiskSize += sizeof(CFFOLDER);
1101 TotalFolderSize += sizeof(CFFOLDER);
1105 CABHeader.FolderCount++;
1109 return CAB_STATUS_SUCCESS;
1113 ULONG CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode)
1115 * FUNCTION: Writes a file to the scratch file
1117 * FileNode = Pointer to file node
1119 * Status of operation
1127 if (!ContinueFile) {
1128 /* Try to open file */
1129 SourceFile = CreateFile(
1130 FileNode->FileName, // Open this file
1131 GENERIC_READ, // Open for reading
1132 FILE_SHARE_READ, // Share for reading
1133 NULL, // No security
1134 OPEN_EXISTING, // File must exist
1135 FILE_ATTRIBUTE_NORMAL, // Normal file
1136 NULL); // No attribute template
1137 if (SourceFile == INVALID_HANDLE_VALUE) {
1138 DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName));
1139 return CAB_STATUS_NOFILE;
1142 if (CreateNewFolder) {
1143 /* There is always a new folder after
1144 a split file is completely stored */
1145 Status = NewFolder();
1146 if (Status != CAB_STATUS_SUCCESS)
1148 CreateNewFolder = FALSE;
1151 /* Call OnAdd event handler */
1152 OnAdd(&FileNode->File, FileNode->FileName);
1154 TotalBytesLeft = FileNode->File.FileSize;
1156 FileNode->File.FileOffset = CurrentFolderNode->UncompOffset;
1157 CurrentFolderNode->UncompOffset += TotalBytesLeft;
1158 FileNode->File.FileControlID = NextFolderNumber - 1;
1159 CurrentFolderNode->Commit = TRUE;
1160 PrevCabinetNumber = CurrentDiskNumber;
1162 Size = sizeof(CFFILE) + lstrlen(GetFileName(FileNode->FileName)) + 1;
1163 CABHeader.FileTableOffset += Size;
1164 TotalFileSize += Size;
1168 FileNode->Commit = TRUE;
1170 if (TotalBytesLeft > 0) {
1172 if (TotalBytesLeft > (DWORD)CAB_BLOCKSIZE - CurrentIBufferSize)
1173 BytesToRead = CAB_BLOCKSIZE - CurrentIBufferSize;
1175 BytesToRead = TotalBytesLeft;
1177 if ((!ReadFile(SourceFile, CurrentIBuffer, BytesToRead, &BytesRead, NULL)
1178 != CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) {
1179 DPRINT(MIN_TRACE, ("Cannot read from file. BytesToRead (%d) BytesRead (%d) CurrentIBufferSize (%d).\n",
1180 BytesToRead, BytesRead, CurrentIBufferSize));
1181 return CAB_STATUS_INVALID_CAB;
1184 (PUCHAR)CurrentIBuffer += BytesRead;
1186 CurrentIBufferSize += (WORD)BytesRead;
1188 if (CurrentIBufferSize == CAB_BLOCKSIZE) {
1189 Status = WriteDataBlock();
1190 if (Status != CAB_STATUS_SUCCESS)
1193 TotalBytesLeft -= BytesRead;
1194 } while ((TotalBytesLeft > 0) && (!CreateNewDisk));
1197 if (TotalBytesLeft == 0) {
1198 CloseHandle(SourceFile);
1199 FileNode->Delete = TRUE;
1201 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER) {
1202 FileNode->File.FileControlID = CAB_FILE_CONTINUED;
1203 CurrentFolderNode->Delete = TRUE;
1205 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) {
1206 Status = WriteDataBlock();
1207 if (Status != CAB_STATUS_SUCCESS)
1211 CreateNewFolder = TRUE;
1214 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER)
1215 FileNode->File.FileControlID = CAB_FILE_SPLIT;
1217 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT;
1220 return CAB_STATUS_SUCCESS;
1224 ULONG CCabinet::WriteDisk(DWORD MoreDisks)
1226 * FUNCTION: Forces the current disk to be written
1228 * MoreDisks = TRUE if there is one or more disks after this disk
1230 * Status of operation
1233 PCFFILE_NODE FileNode;
1236 ContinueFile = FALSE;
1237 FileNode = FileListHead;
1238 while (FileNode != NULL) {
1240 Status = WriteFileToScratchStorage(FileNode);
1241 if (Status != CAB_STATUS_SUCCESS)
1243 if (CreateNewDisk) {
1244 /* A data block could span more than two
1245 disks if MaxDiskSize is very small */
1246 while (CreateNewDisk) {
1247 DPRINT(MAX_TRACE, ("Creating new disk.\n"));
1252 ContinueFile = TRUE;
1253 CreateNewDisk = FALSE;
1255 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%d) CurrentOBufferSize (%d).\n",
1256 CurrentIBufferSize, CurrentOBufferSize));
1258 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) {
1259 Status = WriteDataBlock();
1260 if (Status != CAB_STATUS_SUCCESS)
1265 ContinueFile = FALSE;
1266 FileNode = FileNode->Next;
1270 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) {
1271 /* A data block could span more than two
1272 disks if MaxDiskSize is very small */
1274 ASSERT(CreateNewDisk == FALSE);
1277 if (CreateNewDisk) {
1278 DPRINT(MID_TRACE, ("Creating new disk 2.\n"));
1282 CreateNewDisk = FALSE;
1284 ASSERT(FileNode == FileListHead);
1287 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) {
1288 Status = WriteDataBlock();
1289 if (Status != CAB_STATUS_SUCCESS)
1292 } while (CreateNewDisk);
1294 CommitDisk(MoreDisks);
1296 return CAB_STATUS_SUCCESS;
1300 ULONG CCabinet::CommitDisk(DWORD MoreDisks)
1302 * FUNCTION: Commits the current disk
1304 * MoreDisks = TRUE if there is one or more disks after this disk
1306 * Status of operation
1309 PCFFOLDER_NODE FolderNode;
1312 OnCabinetName(CurrentDiskNumber, CabinetName);
1314 /* Create file, fail if it already exists */
1315 FileHandle = CreateFile(CabinetName, // Create this file
1316 GENERIC_WRITE, // Open for writing
1318 NULL, // No security
1319 CREATE_NEW, // New file only
1320 FILE_ATTRIBUTE_NORMAL, // Normal file
1321 NULL); // No attribute template
1322 if (FileHandle == INVALID_HANDLE_VALUE) {
1324 /* If file exists, ask to overwrite file */
1325 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
1326 (OnOverwrite(NULL, CabinetName))) {
1328 /* Create cabinet file, overwrite if it already exists */
1329 FileHandle = CreateFile(CabinetName, // Create this file
1330 GENERIC_WRITE, // Open for writing
1332 NULL, // No security
1333 TRUNCATE_EXISTING, // Truncate the file
1334 FILE_ATTRIBUTE_NORMAL, // Normal file
1335 NULL); // No attribute template
1336 if (FileHandle == INVALID_HANDLE_VALUE)
1337 return CAB_STATUS_CANNOT_CREATE;
1339 if (Status == ERROR_FILE_EXISTS)
1340 return CAB_STATUS_FILE_EXISTS;
1342 return CAB_STATUS_CANNOT_CREATE;
1346 WriteCabinetHeader(MoreDisks);
1348 Status = WriteFolderEntries();
1349 if (Status != CAB_STATUS_SUCCESS)
1352 /* Write file entries */
1355 /* Write data blocks */
1356 FolderNode = FolderListHead;
1357 while (FolderNode != NULL) {
1358 if (FolderNode->Commit) {
1359 Status = CommitDataBlocks(FolderNode);
1360 if (Status != CAB_STATUS_SUCCESS)
1362 /* Remove data blocks for folder */
1363 DestroyDataNodes(FolderNode);
1365 FolderNode = FolderNode->Next;
1368 CloseHandle(FileHandle);
1370 ScratchFile->Truncate();
1372 return CAB_STATUS_SUCCESS;
1376 ULONG CCabinet::CloseDisk()
1378 * FUNCTION: Closes the current disk
1380 * Status of operation
1383 DestroyDeletedFileNodes();
1385 /* Destroy folder nodes that are completely stored */
1386 DestroyDeletedFolderNodes();
1388 CurrentDiskNumber++;
1390 return CAB_STATUS_SUCCESS;
1394 ULONG CCabinet::CloseCabinet()
1396 * FUNCTION: Closes the current cabinet
1398 * Status of operation
1401 PCFFOLDER_NODE PrevNode;
1402 PCFFOLDER_NODE NextNode;
1407 DestroyFolderNodes();
1410 HeapFree(GetProcessHeap(), 0, InputBuffer);
1415 HeapFree(GetProcessHeap(), 0, OutputBuffer);
1416 OutputBuffer = NULL;
1422 Status = ScratchFile->Destroy();
1427 return CAB_STATUS_SUCCESS;
1431 ULONG CCabinet::AddFile(LPTSTR FileName)
1433 * FUNCTION: Adds a file to the current disk
1435 * FileName = Pointer to string with file name (full path)
1437 * Status of operation
1442 PCFFILE_NODE FileNode;
1444 FileNode = NewFileNode();
1446 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1447 return CAB_STATUS_NOMEMORY;
1450 FileNode->FolderNode = CurrentFolderNode;
1452 FileNode->FileName = (LPTSTR)HeapAlloc(GetProcessHeap(),
1453 0, lstrlen(FileName) + 1);
1454 lstrcpy(FileNode->FileName, FileName);
1456 /* Try to open file */
1457 SrcFile = CreateFile(
1458 FileNode->FileName, // Open this file
1459 GENERIC_READ, // Open for reading
1460 FILE_SHARE_READ, // Share for reading
1461 NULL, // No security
1462 OPEN_EXISTING, // File must exist
1463 FILE_ATTRIBUTE_NORMAL, // Normal file
1464 NULL); // No attribute template
1465 if (SrcFile == INVALID_HANDLE_VALUE) {
1466 DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName));
1467 return CAB_STATUS_CANNOT_OPEN;
1470 /* FIXME: Check for and handle large files (>= 2GB) */
1471 FileNode->File.FileSize = GetFileSize(SrcFile, NULL);
1472 if (GetLastError() != NO_ERROR) {
1473 DPRINT(MIN_TRACE, ("Cannot read from file.\n"));
1474 return CAB_STATUS_CANNOT_READ;
1477 if (GetFileTime(SrcFile, NULL, &FileTime, NULL))
1478 FileTimeToDosDateTime(&FileTime,
1479 &FileNode->File.FileDate,
1480 &FileNode->File.FileTime);
1482 GetAttributesOnFile(FileNode);
1484 CloseHandle(SrcFile);
1486 return CAB_STATUS_SUCCESS;
1490 VOID CCabinet::SetMaxDiskSize(DWORD Size)
1492 * FUNCTION: Sets the maximum size of the current disk
1494 * Size = Maximum size of current disk (0 means no maximum size)
1500 #endif /* CAB_READ_ONLY */
1503 /* Default event handlers */
1505 BOOL CCabinet::OnOverwrite(PCFFILE File,
1508 * FUNCTION: Called when extracting a file and it already exists
1510 * File = Pointer to CFFILE for file being extracted
1511 * FileName = Pointer to buffer with name of file (full path)
1513 * TRUE if the file should be overwritten, FALSE if not
1520 VOID CCabinet::OnExtract(PCFFILE File,
1523 * FUNCTION: Called just before extracting a file
1525 * File = Pointer to CFFILE for file being extracted
1526 * FileName = Pointer to buffer with name of file (full path)
1532 VOID CCabinet::OnDiskChange(LPTSTR CabinetName,
1535 * FUNCTION: Called when a new disk is to be processed
1537 * CabinetName = Pointer to buffer with name of cabinet
1538 * DiskLabel = Pointer to buffer with label of disk
1544 #ifndef CAB_READ_ONLY
1546 VOID CCabinet::OnAdd(PCFFILE File,
1549 * FUNCTION: Called just before adding a file to a cabinet
1551 * File = Pointer to CFFILE for file being added
1552 * FileName = Pointer to buffer with name of file (full path)
1558 BOOL CCabinet::OnDiskLabel(ULONG Number, LPTSTR Label)
1560 * FUNCTION: Called when a disk needs a label
1562 * Number = Cabinet number that needs a label
1563 * Label = Pointer to buffer to place label of disk
1565 * TRUE if a disk label was returned, FALSE if not
1572 BOOL CCabinet::OnCabinetName(ULONG Number, LPTSTR Name)
1574 * FUNCTION: Called when a cabinet needs a name
1576 * Number = Disk number that needs a name
1577 * Name = Pointer to buffer to place name of cabinet
1579 * TRUE if a cabinet name was returned, FALSE if not
1585 #endif /* CAB_READ_ONLY */
1587 PCFFOLDER_NODE CCabinet::LocateFolderNode(DWORD Index)
1589 * FUNCTION: Locates a folder node
1591 * Index = Folder index
1593 * Pointer to folder node or NULL if the folder node was not found
1596 PCFFOLDER_NODE Node;
1599 case CAB_FILE_SPLIT:
1600 return FolderListTail;
1602 case CAB_FILE_CONTINUED:
1603 case CAB_FILE_PREV_NEXT:
1604 return FolderListHead;
1607 Node = FolderListHead;
1608 while (Node != NULL) {
1609 if (Node->Index == Index)
1617 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File)
1619 * FUNCTION: Returns the absolute offset of a file
1621 * File = Pointer to CFFILE_NODE structure for file
1623 * Status of operation
1628 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1629 (LPTSTR)File->FileName,
1630 (UINT)File->File.FileOffset,
1631 (UINT)File->File.FileSize));
1633 Node = CurrentFolderNode->DataListHead;
1634 while (Node != NULL) {
1636 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1637 (UINT)Node->UncompOffset,
1638 (UINT)Node->UncompOffset + Node->Data.UncompSize,
1639 (UINT)Node->Data.UncompSize));
1641 /* Node->Data.UncompSize will be 0 if the block is split
1642 (ie. it is the last block in this cabinet) */
1643 if ((Node->Data.UncompSize == 0) ||
1644 ((File->File.FileOffset >= Node->UncompOffset) &&
1645 (File->File.FileOffset < Node->UncompOffset +
1646 Node->Data.UncompSize))) {
1647 File->DataBlock = Node;
1648 return CAB_STATUS_SUCCESS;
1653 return CAB_STATUS_INVALID_CAB;
1657 ULONG CCabinet::LocateFile(LPTSTR FileName,
1660 * FUNCTION: Locates a file in the cabinet
1662 * FileName = Pointer to string with name of file to locate
1663 * File = Address of pointer to CFFILE_NODE structure to fill
1665 * Status of operation
1667 * Current folder is set to the folder of the file
1673 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName));
1675 Node = FileListHead;
1676 while (Node != NULL) {
1677 if (lstrcmpi(FileName, Node->FileName) == 0) {
1679 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
1680 if (!CurrentFolderNode) {
1681 DPRINT(MID_TRACE, ("Folder with index number (%d) not found.\n",
1682 (UINT)Node->File.FileControlID));
1683 return CAB_STATUS_INVALID_CAB;
1686 if (Node->DataBlock == NULL) {
1687 Status = GetAbsoluteOffset(Node);
1689 Status = CAB_STATUS_SUCCESS;
1695 return CAB_STATUS_NOFILE;
1699 ULONG CCabinet::ReadString(LPTSTR String, DWORD MaxLength)
1701 * FUNCTION: Reads a NULL-terminated string from the cabinet
1703 * String = Pointer to buffer to place string
1704 * MaxLength = Maximum length of string
1706 * Status of operation
1718 Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset;
1721 DPRINT(MIN_TRACE, ("Too long a filename.\n"));
1722 return CAB_STATUS_INVALID_CAB;
1725 Status = ReadBlock(&String[Offset], Size, &BytesRead);
1726 if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size)) {
1727 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
1728 return CAB_STATUS_INVALID_CAB;
1731 for (Size = Offset; Size < Offset + BytesRead; Size++) {
1732 if (String[Size] == '\0') {
1738 Offset += BytesRead;
1742 /* Back up some bytes */
1743 Size = (BytesRead - Size) - 1;
1744 SetFilePointer(FileHandle, -(LONG)Size, NULL, FILE_CURRENT);
1745 if (GetLastError() != NO_ERROR) {
1746 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
1747 return CAB_STATUS_INVALID_CAB;
1749 return CAB_STATUS_SUCCESS;
1753 ULONG CCabinet::ReadFileTable()
1755 * FUNCTION: Reads the file table from the cabinet file
1757 * Status of operation
1765 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n",
1766 CABHeader.FileTableOffset));
1768 /* Seek to file table */
1769 SetFilePointer(FileHandle, CABHeader.FileTableOffset, NULL, FILE_BEGIN);
1770 if (GetLastError() != NO_ERROR) {
1771 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
1772 return CAB_STATUS_INVALID_CAB;
1775 for (i = 0; i < CABHeader.FileCount; i++) {
1776 File = NewFileNode();
1778 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1779 return CAB_STATUS_NOMEMORY;
1782 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
1783 &BytesRead)) != CAB_STATUS_SUCCESS) {
1784 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
1785 return CAB_STATUS_INVALID_CAB;
1788 File->FileName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
1789 if (!File->FileName) {
1790 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1791 return CAB_STATUS_NOMEMORY;
1794 /* Read file name */
1795 Status = ReadString(File->FileName, MAX_PATH);
1796 if (Status != CAB_STATUS_SUCCESS)
1799 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
1800 (LPTSTR)File->FileName,
1801 (UINT)File->File.FileOffset,
1802 (UINT)File->File.FileSize,
1803 (UINT)File->File.FileControlID));
1806 return CAB_STATUS_SUCCESS;
1810 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode)
1812 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1814 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1816 * Status of operation
1819 DWORD AbsoluteOffset;
1826 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1827 FolderNode->Index, FolderNode->Folder.DataOffset));
1829 AbsoluteOffset = FolderNode->Folder.DataOffset;
1830 UncompOffset = FolderNode->UncompOffset;
1832 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) {
1833 Node = NewDataNode(FolderNode);
1835 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1836 return CAB_STATUS_NOMEMORY;
1839 /* Seek to data block */
1840 SetFilePointer(FileHandle, AbsoluteOffset, NULL, FILE_BEGIN);
1841 if (GetLastError() != NO_ERROR) {
1842 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
1843 return CAB_STATUS_INVALID_CAB;
1846 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
1847 &BytesRead)) != CAB_STATUS_SUCCESS) {
1848 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (UINT)Status));
1849 return CAB_STATUS_INVALID_CAB;
1852 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1853 (UINT)AbsoluteOffset,
1855 (UINT)Node->Data.Checksum,
1856 (UINT)Node->Data.CompSize,
1857 (UINT)Node->Data.UncompSize));
1859 Node->AbsoluteOffset = AbsoluteOffset;
1860 Node->UncompOffset = UncompOffset;
1862 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
1863 UncompOffset += Node->Data.UncompSize;
1866 FolderUncompSize = UncompOffset;
1868 return CAB_STATUS_SUCCESS;
1872 PCFFOLDER_NODE CCabinet::NewFolderNode()
1874 * FUNCTION: Creates a new folder node
1876 * Pointer to node if there was enough free memory available, otherwise NULL
1879 PCFFOLDER_NODE Node;
1881 Node = (PCFFOLDER_NODE)HeapAlloc(GetProcessHeap(),
1882 0, sizeof(CFFOLDER_NODE));
1886 ZeroMemory(Node, sizeof(CFFOLDER_NODE));
1888 Node->Folder.CompressionType = CAB_COMP_NONE;
1890 Node->Prev = FolderListTail;
1892 if (FolderListTail != NULL) {
1893 FolderListTail->Next = Node;
1895 FolderListHead = Node;
1897 FolderListTail = Node;
1903 PCFFILE_NODE CCabinet::NewFileNode()
1905 * FUNCTION: Creates a new file node
1907 * FolderNode = Pointer to folder node to bind file to
1909 * Pointer to node if there was enough free memory available, otherwise NULL
1914 Node = (PCFFILE_NODE)HeapAlloc(GetProcessHeap(),
1915 0, sizeof(CFFILE_NODE));
1919 ZeroMemory(Node, sizeof(CFFILE_NODE));
1921 Node->Prev = FileListTail;
1923 if (FileListTail != NULL) {
1924 FileListTail->Next = Node;
1926 FileListHead = Node;
1928 FileListTail = Node;
1934 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode)
1936 * FUNCTION: Creates a new data block node
1938 * FolderNode = Pointer to folder node to bind data block to
1940 * Pointer to node if there was enough free memory available, otherwise NULL
1945 Node = (PCFDATA_NODE)HeapAlloc(GetProcessHeap(),
1946 0, sizeof(CFDATA_NODE));
1950 ZeroMemory(Node, sizeof(CFDATA_NODE));
1952 Node->Prev = FolderNode->DataListTail;
1954 if (FolderNode->DataListTail != NULL) {
1955 FolderNode->DataListTail->Next = Node;
1957 FolderNode->DataListHead = Node;
1959 FolderNode->DataListTail = Node;
1965 VOID CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode)
1967 * FUNCTION: Destroys data block nodes bound to a folder node
1969 * FolderNode = Pointer to folder node
1972 PCFDATA_NODE PrevNode;
1973 PCFDATA_NODE NextNode;
1975 NextNode = FolderNode->DataListHead;
1976 while (NextNode != NULL) {
1977 PrevNode = NextNode->Next;
1978 HeapFree(GetProcessHeap(), 0, NextNode);
1979 NextNode = PrevNode;
1981 FolderNode->DataListHead = NULL;
1982 FolderNode->DataListTail = NULL;
1986 VOID CCabinet::DestroyFileNodes()
1988 * FUNCTION: Destroys file nodes
1990 * FolderNode = Pointer to folder node
1993 PCFFILE_NODE PrevNode;
1994 PCFFILE_NODE NextNode;
1996 NextNode = FileListHead;
1997 while (NextNode != NULL) {
1998 PrevNode = NextNode->Next;
1999 if (NextNode->FileName)
2000 HeapFree(GetProcessHeap(), 0, NextNode->FileName);
2001 HeapFree(GetProcessHeap(), 0, NextNode);
2002 NextNode = PrevNode;
2004 FileListHead = NULL;
2005 FileListTail = NULL;
2009 VOID CCabinet::DestroyDeletedFileNodes()
2011 * FUNCTION: Destroys file nodes that are marked for deletion
2014 PCFFILE_NODE CurNode;
2015 PCFFILE_NODE NextNode;
2017 CurNode = FileListHead;
2018 while (CurNode != NULL) {
2019 NextNode = CurNode->Next;
2021 if (CurNode->Delete) {
2022 if (CurNode->Prev != NULL) {
2023 CurNode->Prev->Next = CurNode->Next;
2025 FileListHead = CurNode->Next;
2027 FileListHead->Prev = NULL;
2030 if (CurNode->Next != NULL) {
2031 CurNode->Next->Prev = CurNode->Prev;
2033 FileListTail = CurNode->Prev;
2035 FileListTail->Next = NULL;
2038 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName));
2040 TotalFileSize -= (sizeof(CFFILE) + lstrlen(GetFileName(CurNode->FileName)) + 1);
2042 if (CurNode->FileName)
2043 HeapFree(GetProcessHeap(), 0, CurNode->FileName);
2044 HeapFree(GetProcessHeap(), 0, CurNode);
2051 VOID CCabinet::DestroyFolderNodes()
2053 * FUNCTION: Destroys folder nodes
2056 PCFFOLDER_NODE PrevNode;
2057 PCFFOLDER_NODE NextNode;
2059 NextNode = FolderListHead;
2060 while (NextNode != NULL) {
2061 PrevNode = NextNode->Next;
2062 DestroyDataNodes(NextNode);
2063 HeapFree(GetProcessHeap(), 0, NextNode);
2064 NextNode = PrevNode;
2066 FolderListHead = NULL;
2067 FolderListTail = NULL;
2071 VOID CCabinet::DestroyDeletedFolderNodes()
2073 * FUNCTION: Destroys folder nodes that are marked for deletion
2076 PCFFOLDER_NODE CurNode;
2077 PCFFOLDER_NODE NextNode;
2079 CurNode = FolderListHead;
2080 while (CurNode != NULL) {
2081 NextNode = CurNode->Next;
2083 if (CurNode->Delete) {
2084 if (CurNode->Prev != NULL) {
2085 CurNode->Prev->Next = CurNode->Next;
2087 FolderListHead = CurNode->Next;
2089 FolderListHead->Prev = NULL;
2092 if (CurNode->Next != NULL) {
2093 CurNode->Next->Prev = CurNode->Prev;
2095 FolderListTail = CurNode->Prev;
2097 FolderListTail->Next = NULL;
2100 DestroyDataNodes(CurNode);
2101 HeapFree(GetProcessHeap(), 0, CurNode);
2103 TotalFolderSize -= sizeof(CFFOLDER);
2110 ULONG CCabinet::ComputeChecksum(PVOID Buffer,
2114 * FUNCTION: Computes checksum for data block
2116 * Buffer = Pointer to data buffer
2117 * Size = Length of data buffer
2118 * Seed = Previously computed checksum
2120 * Checksum of buffer
2123 INT UlongCount; // Number of ULONGs in block
2124 ULONG Checksum; // Checksum accumulator
2128 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2129 won't accept checksums computed by this routine */
2131 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%X) Size (%d)\n", (UINT)Buffer, Size));
2133 UlongCount = Size / 4; // Number of ULONGs
2134 Checksum = Seed; // Init checksum
2135 pb = (PBYTE)Buffer; // Start at front of data block
2137 /* Checksum integral multiple of ULONGs */
2138 while (UlongCount-- > 0) {
2139 /* NOTE: Build ULONG in big/little-endian independent manner */
2140 ul = *pb++; // Get low-order byte
2141 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2142 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
2143 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
2145 Checksum ^= ul; // Update checksum
2148 /* Checksum remainder bytes */
2152 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
2154 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2156 ul |= *pb++; // Get low-order byte
2160 Checksum ^= ul; // Update checksum
2162 /* Return computed checksum */
2167 ULONG CCabinet::ReadBlock(PVOID Buffer,
2171 * FUNCTION: Read a block of data from file
2173 * Buffer = Pointer to data buffer
2174 * Size = Length of data buffer
2175 * BytesRead = Pointer to DWORD that on return will contain
2176 * number of bytes read
2178 * Status of operation
2181 if (!ReadFile(FileHandle, Buffer, Size, BytesRead, NULL))
2182 return CAB_STATUS_INVALID_CAB;
2183 return CAB_STATUS_SUCCESS;
2186 #ifndef CAB_READ_ONLY
2188 ULONG CCabinet::InitCabinetHeader()
2190 * FUNCTION: Initializes cabinet header and optional fields
2192 * Status of operation
2195 PCFFOLDER_NODE FolderNode;
2196 PCFFILE_NODE FileNode;
2200 CABHeader.FileTableOffset = 0; // Not known yet
2201 CABHeader.FolderCount = 0; // Not known yet
2202 CABHeader.FileCount = 0; // Not known yet
2203 CABHeader.Flags = 0; // Not known yet
2205 CABHeader.CabinetNumber = CurrentDiskNumber;
2207 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev))) {
2208 CABHeader.Flags |= CAB_FLAG_HASPREV;
2209 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev))
2210 lstrcpy(CabinetPrev, "");
2213 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext)) {
2214 CABHeader.Flags |= CAB_FLAG_HASNEXT;
2215 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext))
2216 lstrcpy(DiskNext, "");
2221 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) {
2223 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
2225 /* Calculate size of name of previous cabinet */
2226 TotalSize += lstrlen(CabinetPrev) + 1;
2228 /* Calculate size of label of previous disk */
2229 TotalSize += lstrlen(DiskPrev) + 1;
2232 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) {
2234 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
2236 /* Calculate size of name of next cabinet */
2237 Size = lstrlen(CabinetNext) + 1;
2239 NextFieldsSize = Size;
2241 /* Calculate size of label of next disk */
2242 Size = lstrlen(DiskNext) + 1;
2244 NextFieldsSize += Size;
2248 DiskSize += TotalSize;
2250 /* FIXME: Add cabinet reserved area size */
2251 TotalHeaderSize = sizeof(CFHEADER) + TotalSize;
2253 return CAB_STATUS_SUCCESS;
2257 ULONG CCabinet::WriteCabinetHeader(BOOL MoreDisks)
2259 * FUNCTION: Writes the cabinet header and optional fields
2261 * MoreDisks = TRUE if next cabinet name should be included
2263 * Status of operation
2266 PCFFOLDER_NODE FolderNode;
2267 PCFFILE_NODE FileNode;
2272 CABHeader.Flags |= CAB_FLAG_HASNEXT;
2273 Size = TotalHeaderSize;
2275 CABHeader.Flags &= ~CAB_FLAG_HASNEXT;
2276 DiskSize -= NextFieldsSize;
2277 Size = TotalHeaderSize - NextFieldsSize;
2280 /* Set absolute folder offsets */
2281 BytesWritten = Size + TotalFolderSize + TotalFileSize;
2282 CABHeader.FolderCount = 0;
2283 FolderNode = FolderListHead;
2284 while (FolderNode != NULL) {
2285 FolderNode->Folder.DataOffset = BytesWritten;
2287 BytesWritten += FolderNode->TotalFolderSize;
2289 CABHeader.FolderCount++;
2291 FolderNode = FolderNode->Next;
2294 /* Set absolute offset of file table */
2295 CABHeader.FileTableOffset = Size + TotalFolderSize;
2297 /* Count number of files to be committed */
2298 CABHeader.FileCount = 0;
2299 FileNode = FileListHead;
2300 while (FileNode != NULL) {
2301 if (FileNode->Commit)
2302 CABHeader.FileCount++;
2303 FileNode = FileNode->Next;
2306 CABHeader.CabinetSize = DiskSize;
2309 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), &BytesWritten, NULL)) {
2310 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2311 return CAB_STATUS_CANNOT_WRITE;
2314 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) {
2316 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
2318 /* Write name of previous cabinet */
2319 Size = lstrlen(CabinetPrev) + 1;
2320 if (!WriteFile(FileHandle, CabinetPrev, Size, &BytesWritten, NULL)) {
2321 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2322 return CAB_STATUS_CANNOT_WRITE;
2325 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev));
2327 /* Write label of previous disk */
2328 Size = lstrlen(DiskPrev) + 1;
2329 if (!WriteFile(FileHandle, DiskPrev, Size, &BytesWritten, NULL)) {
2330 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2331 return CAB_STATUS_CANNOT_WRITE;
2335 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) {
2337 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
2339 /* Write name of next cabinet */
2340 Size = lstrlen(CabinetNext) + 1;
2341 if (!WriteFile(FileHandle, CabinetNext, Size, &BytesWritten, NULL)) {
2342 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2343 return CAB_STATUS_CANNOT_WRITE;
2346 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext));
2348 /* Write label of next disk */
2349 Size = lstrlen(DiskNext) + 1;
2350 if (!WriteFile(FileHandle, DiskNext, Size, &BytesWritten, NULL)) {
2351 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2352 return CAB_STATUS_CANNOT_WRITE;
2356 return CAB_STATUS_SUCCESS;
2360 ULONG CCabinet::WriteFolderEntries()
2362 * FUNCTION: Writes folder entries
2364 * Status of operation
2367 PCFFOLDER_NODE FolderNode;
2370 DPRINT(MAX_TRACE, ("Writing folder table.\n"));
2372 FolderNode = FolderListHead;
2373 while (FolderNode != NULL) {
2374 if (FolderNode->Commit) {
2376 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
2377 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, FolderNode->Folder.DataOffset));
2379 if (!WriteFile(FileHandle,
2380 &FolderNode->Folder,
2384 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2385 return CAB_STATUS_CANNOT_WRITE;
2388 FolderNode = FolderNode->Next;
2391 return CAB_STATUS_SUCCESS;
2395 ULONG CCabinet::WriteFileEntries()
2397 * FUNCTION: Writes file entries for all files
2399 * Status of operation
2406 DPRINT(MAX_TRACE, ("Writing file table.\n"));
2408 File = FileListHead;
2409 while (File != NULL) {
2411 /* Remove any continued files that ends in this disk */
2412 if (File->File.FileControlID == CAB_FILE_CONTINUED)
2413 File->Delete = TRUE;
2415 /* The file could end in the last (split) block and should therefore
2416 appear in the next disk too */
2418 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) &&
2419 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit)) {
2420 File->File.FileControlID = CAB_FILE_SPLIT;
2421 File->Delete = FALSE;
2425 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%d) FileName (%s).\n",
2426 File->File.FileControlID, File->File.FileOffset, File->File.FileSize, File->FileName));
2428 if (!WriteFile(FileHandle,
2433 return CAB_STATUS_CANNOT_WRITE;
2435 if (!WriteFile(FileHandle,
2436 GetFileName(File->FileName),
2437 lstrlen(GetFileName(File->FileName)) + 1, &BytesWritten, NULL))
2438 return CAB_STATUS_CANNOT_WRITE;
2441 File->File.FileControlID = CAB_FILE_CONTINUED;
2448 return CAB_STATUS_SUCCESS;
2452 ULONG CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode)
2454 * FUNCTION: Writes data blocks to the cabinet
2456 * FolderNode = Pointer to folder node containing the data blocks
2458 * Status of operation
2461 PCFDATA_NODE DataNode;
2466 DataNode = FolderNode->DataListHead;
2467 if (DataNode != NULL)
2468 Status = ScratchFile->Seek(DataNode->ScratchFilePosition);
2470 while (DataNode != NULL) {
2471 DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%d) UncompSize (%d).\n",
2472 DataNode->ScratchFilePosition,
2473 DataNode->Data.CompSize,
2474 DataNode->Data.UncompSize));
2476 /* InputBuffer is free for us to use here, so we use it and avoid a
2477 memory allocation. OutputBuffer can't be used here because it may
2478 still contain valid data (if a data block spans two or more disks) */
2479 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead);
2480 if (Status != CAB_STATUS_SUCCESS) {
2481 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%d).\n", (UINT)Status));
2485 if (!WriteFile(FileHandle, &DataNode->Data,
2486 sizeof(CFDATA), &BytesWritten, NULL)) {
2487 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2488 return CAB_STATUS_CANNOT_WRITE;
2491 if (!WriteFile(FileHandle, InputBuffer,
2492 DataNode->Data.CompSize, &BytesWritten, NULL)) {
2493 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2494 return CAB_STATUS_CANNOT_WRITE;
2497 DataNode = DataNode->Next;
2499 return CAB_STATUS_SUCCESS;
2503 ULONG CCabinet::WriteDataBlock()
2505 * FUNCTION: Writes the current data block to the scratch file
2507 * Status of operation
2512 PCFDATA_NODE DataNode;
2514 if (!BlockIsSplit) {
2515 Status = Codec->Compress(OutputBuffer,
2520 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%d) TotalCompSize(%d).\n",
2521 CurrentIBufferSize, TotalCompSize));
2523 CurrentOBuffer = OutputBuffer;
2524 CurrentOBufferSize = TotalCompSize;
2527 DataNode = NewDataNode(CurrentFolderNode);
2529 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2530 return CAB_STATUS_NOMEMORY;
2533 DiskSize += sizeof(CFDATA);
2535 if (MaxDiskSize > 0)
2536 /* Disk size is limited */
2537 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize);
2539 BlockIsSplit = FALSE;
2542 DataNode->Data.CompSize = (WORD)(MaxDiskSize - DiskSize);
2543 DataNode->Data.UncompSize = 0;
2544 CreateNewDisk = TRUE;
2546 DataNode->Data.CompSize = (WORD)CurrentOBufferSize;
2547 DataNode->Data.UncompSize = (WORD)CurrentIBufferSize;
2550 DataNode->Data.Checksum = 0;
2551 DataNode->ScratchFilePosition = ScratchFile->Position();
2553 // FIXME: MAKECAB.EXE does not like this checksum algorithm
2554 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
2556 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2557 (UINT)DataNode->Data.Checksum,
2558 (UINT)DataNode->Data.CompSize,
2559 (UINT)DataNode->Data.UncompSize));
2561 Status = ScratchFile->WriteBlock(&DataNode->Data,
2562 CurrentOBuffer, &BytesWritten);
2563 if (Status != CAB_STATUS_SUCCESS)
2566 DiskSize += BytesWritten;
2568 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA));
2569 CurrentFolderNode->Folder.DataBlockCount++;
2571 (PUCHAR)CurrentOBuffer += DataNode->Data.CompSize;
2572 CurrentOBufferSize -= DataNode->Data.CompSize;
2574 LastBlockStart += DataNode->Data.UncompSize;
2576 if (!BlockIsSplit) {
2577 CurrentIBufferSize = 0;
2578 CurrentIBuffer = InputBuffer;
2581 return CAB_STATUS_SUCCESS;
2585 ULONG CCabinet::GetAttributesOnFile(PCFFILE_NODE File)
2587 * FUNCTION: Returns attributes on a file
2589 * File = Pointer to CFFILE node for file
2591 * Status of operation
2596 Attributes = GetFileAttributes(File->FileName);
2597 if (Attributes == -1)
2598 return CAB_STATUS_CANNOT_READ;
2600 if (Attributes & FILE_ATTRIBUTE_READONLY)
2601 File->File.Attributes |= CAB_ATTRIB_READONLY;
2603 if (Attributes & FILE_ATTRIBUTE_HIDDEN)
2604 File->File.Attributes |= CAB_ATTRIB_HIDDEN;
2606 if (Attributes & FILE_ATTRIBUTE_SYSTEM)
2607 File->File.Attributes |= CAB_ATTRIB_SYSTEM;
2609 if (Attributes & FILE_ATTRIBUTE_DIRECTORY)
2610 File->File.Attributes |= CAB_ATTRIB_DIRECTORY;
2612 if (Attributes & FILE_ATTRIBUTE_ARCHIVE)
2613 File->File.Attributes |= CAB_ATTRIB_ARCHIVE;
2615 return CAB_STATUS_SUCCESS;
2619 ULONG CCabinet::SetAttributesOnFile(PCFFILE_NODE File)
2621 * FUNCTION: Sets attributes on a file
2623 * File = Pointer to CFFILE node for file
2625 * Status of operation
2628 DWORD Attributes = 0;
2630 if (File->File.Attributes & CAB_ATTRIB_READONLY)
2631 Attributes |= FILE_ATTRIBUTE_READONLY;
2633 if (File->File.Attributes & CAB_ATTRIB_HIDDEN)
2634 Attributes |= FILE_ATTRIBUTE_HIDDEN;
2636 if (File->File.Attributes & CAB_ATTRIB_SYSTEM)
2637 Attributes |= FILE_ATTRIBUTE_SYSTEM;
2639 if (File->File.Attributes & CAB_ATTRIB_DIRECTORY)
2640 Attributes |= FILE_ATTRIBUTE_DIRECTORY;
2642 if (File->File.Attributes & CAB_ATTRIB_ARCHIVE)
2643 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
2645 SetFileAttributes(File->FileName, Attributes);
2647 return CAB_STATUS_SUCCESS;
2650 #endif /* CAB_READ_ONLY */