X-Git-Url: http://git.jankratochvil.net/?p=reactos.git;a=blobdiff_plain;f=subsys%2Fsystem%2Fusetup%2Fcabinet.c;fp=subsys%2Fsystem%2Fusetup%2Fcabinet.c;h=129241e172559c2a8cf90cf291eab0f3e6e0d5dd;hp=0000000000000000000000000000000000000000;hb=a3df8bf1429570e0bd6c6428f6ed80073578cf4b;hpb=7c0db166f81fbe8c8b913d7f26048e337d383605 diff --git a/subsys/system/usetup/cabinet.c b/subsys/system/usetup/cabinet.c new file mode 100755 index 0000000..129241e --- /dev/null +++ b/subsys/system/usetup/cabinet.c @@ -0,0 +1,2094 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS text-mode setup + * FILE: subsys/system/usetup/cabinet.c + * PURPOSE: Cabinet routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 15/08-2003 Created + */ +#include +#include +#include +#include +#include "cabinet.h" +#include "usetup.h" + +#define NDEBUG +#include + +#define SEEK_BEGIN 0 +#define SEEK_CURRENT 1 +#define SEEK_END 2 + +typedef struct __DOSTIME +{ + WORD Second:5; + WORD Minute:6; + WORD Hour:5; +} DOSTIME, *PDOSTIME; + + +typedef struct __DOSDATE +{ + WORD Day:5; + WORD Month:4; + WORD Year:5; +} DOSDATE, *PDOSDATE; + +static WCHAR CabinetName[256]; // Filename of current cabinet +static WCHAR CabinetPrev[256]; // Filename of previous cabinet +static WCHAR DiskPrev[256]; // Label of cabinet in file CabinetPrev +static WCHAR CabinetNext[256]; // Filename of next cabinet +static WCHAR DiskNext[256]; // Label of cabinet in file CabinetNext +static ULONG FolderUncompSize = 0; // Uncompressed size of folder +static ULONG BytesLeftInBlock = 0; // Number of bytes left in current block +static BOOL ReuseBlock = FALSE; +static WCHAR DestPath[MAX_PATH]; +static HANDLE FileHandle; +static BOOL FileOpen = FALSE; +static CFHEADER CABHeader; +static ULONG CabinetReserved = 0; +static ULONG FolderReserved = 0; +static ULONG DataReserved = 0; +static PCFFOLDER_NODE FolderListHead = NULL; +static PCFFOLDER_NODE FolderListTail = NULL; +static PCFFOLDER_NODE CurrentFolderNode = NULL; +static PCFDATA_NODE CurrentDataNode = NULL; +static PCFFILE_NODE FileListHead = NULL; +static PCFFILE_NODE FileListTail = NULL; +static ULONG CodecId; +static PCABINET_CODEC_UNCOMPRESS CodecUncompress = NULL; +static BOOL CodecSelected = FALSE; +static PVOID InputBuffer = NULL; +static PVOID CurrentIBuffer = NULL; // Current offset in input buffer +static ULONG CurrentIBufferSize = 0; // Bytes left in input buffer +static PVOID OutputBuffer = NULL; +static PVOID CurrentOBuffer = NULL; // Current offset in output buffer +static ULONG CurrentOBufferSize = 0; // Bytes left in output buffer +static BOOL RestartSearch = FALSE; +static ULONG LastFileOffset = 0; // Uncompressed offset of last extracted file +static PCABINET_OVERWRITE OverwriteHandler = NULL; +static PCABINET_EXTRACT ExtractHandler = NULL; +static PCABINET_DISK_CHANGE DiskChangeHandler = NULL; +static z_stream ZStream; +static PVOID CabinetReservedArea = NULL; + + +/* Needed by zlib, but we don't want the dependency on msvcrt.dll */ + +void free(void* _ptr) +{ + RtlFreeHeap(ProcessHeap, 0, _ptr); +} + +void* calloc(size_t _nmemb, size_t _size) +{ + return (void*)RtlAllocateHeap (ProcessHeap, HEAP_ZERO_MEMORY, _size); +} + +/* RAW codec */ + +ULONG +RawCodecUncompress(PVOID OutputBuffer, + PVOID InputBuffer, + ULONG InputLength, + PULONG OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + memcpy(OutputBuffer, InputBuffer, InputLength); + *OutputLength = InputLength; + return CS_SUCCESS; +} + + +/* MSZIP codec */ + +ULONG +MSZipCodecUncompress(PVOID OutputBuffer, + PVOID InputBuffer, + ULONG InputLength, + PULONG OutputLength) +/* + * FUNCTION: Uncompresses data in a buffer + * ARGUMENTS: + * OutputBuffer = Pointer to buffer to place uncompressed data + * InputBuffer = Pointer to buffer with data to be uncompressed + * InputLength = Length of input buffer + * OutputLength = Address of buffer to place size of uncompressed data + */ +{ + USHORT Magic; + INT Status; + + DPRINT("InputLength (%d).\n", InputLength); + + Magic = *((PUSHORT)InputBuffer); + + if (Magic != MSZIP_MAGIC) + { + DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic); + return CS_BADSTREAM; + } + + ZStream.next_in = (PUCHAR)((ULONG)InputBuffer + 2); + ZStream.avail_in = InputLength - 2; + ZStream.next_out = (PUCHAR)OutputBuffer; + ZStream.avail_out = CAB_BLOCKSIZE + 12; + + /* WindowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + */ + Status = inflateInit2(&ZStream, -MAX_WBITS); + if (Status != Z_OK) + { + DPRINT("inflateInit2() returned (%d).\n", Status); + return CS_BADSTREAM; + } + + while ((ZStream.total_out < CAB_BLOCKSIZE + 12) && + (ZStream.total_in < InputLength - 2)) + { + Status = inflate(&ZStream, Z_NO_FLUSH); + if (Status == Z_STREAM_END) break; + if (Status != Z_OK) + { + DPRINT("inflate() returned (%d) (%s).\n", Status, ZStream.msg); + if (Status == Z_MEM_ERROR) + return CS_NOMEMORY; + return CS_BADSTREAM; + } + } + + *OutputLength = ZStream.total_out; + + Status = inflateEnd(&ZStream); + if (Status != Z_OK) + { + DPRINT("inflateEnd() returned (%d).\n", Status); + return CS_BADSTREAM; + } + return CS_SUCCESS; +} + + + +/* Memory functions */ + +voidpf MSZipAlloc(voidpf opaque, uInt items, uInt size) +{ + return (voidpf)RtlAllocateHeap (ProcessHeap, 0, items * size); +} + +void MSZipFree (voidpf opaque, voidpf address) +{ + RtlFreeHeap(ProcessHeap, 0, address); +} + + +static DWORD +SeekInFile(HANDLE hFile, + LONG lDistanceToMove, + PLONG lpDistanceToMoveHigh, + DWORD dwMoveMethod, + PNTSTATUS Status) +{ + FILE_POSITION_INFORMATION FilePosition; + FILE_STANDARD_INFORMATION FileStandart; + NTSTATUS errCode; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER Distance; + + DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n", + hFile,lDistanceToMove,dwMoveMethod); + + Distance.u.LowPart = lDistanceToMove; + if (lpDistanceToMoveHigh) + { + Distance.u.HighPart = *lpDistanceToMoveHigh; + } + else if (lDistanceToMove >= 0) + { + Distance.u.HighPart = 0; + } + else + { + Distance.u.HighPart = -1; + } + + if (dwMoveMethod == SEEK_CURRENT) + { + NtQueryInformationFile(hFile, + &IoStatusBlock, + &FilePosition, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + FilePosition.CurrentByteOffset.QuadPart += Distance.QuadPart; + } + else if (dwMoveMethod == SEEK_END) + { + NtQueryInformationFile(hFile, + &IoStatusBlock, + &FileStandart, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); + FilePosition.CurrentByteOffset.QuadPart = + FileStandart.EndOfFile.QuadPart + Distance.QuadPart; + } + else if ( dwMoveMethod == SEEK_BEGIN ) + { + FilePosition.CurrentByteOffset.QuadPart = Distance.QuadPart; + } + +// DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart); + + errCode = NtSetInformationFile(hFile, + &IoStatusBlock, + &FilePosition, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + if (!NT_SUCCESS(errCode)) + { + if (Status != NULL) + { + *Status = errCode; + } + return -1; + } + + if (lpDistanceToMoveHigh != NULL) + { + *lpDistanceToMoveHigh = FilePosition.CurrentByteOffset.u.HighPart; + } + if (Status != NULL) + { + *Status = STATUS_SUCCESS; + } + return FilePosition.CurrentByteOffset.u.LowPart; +} + + +static BOOL +ConvertSystemTimeToFileTime( + CONST SYSTEMTIME * lpSystemTime, + LPFILETIME lpFileTime) +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER liTime; + + TimeFields.Year = lpSystemTime->wYear; + TimeFields.Month = lpSystemTime->wMonth; + TimeFields.Day = lpSystemTime->wDay; + TimeFields.Hour = lpSystemTime->wHour; + TimeFields.Minute = lpSystemTime->wMinute; + TimeFields.Second = lpSystemTime->wSecond; + TimeFields.Milliseconds = lpSystemTime->wMilliseconds; + + if (RtlTimeFieldsToTime(&TimeFields, &liTime)) + { + lpFileTime->dwLowDateTime = liTime.u.LowPart; + lpFileTime->dwHighDateTime = liTime.u.HighPart; + return TRUE; + } + return FALSE; +} + + +static BOOL +ConvertDosDateTimeToFileTime( + WORD wFatDate, + WORD wFatTime, + LPFILETIME lpFileTime) +{ + PDOSTIME pdtime = (PDOSTIME) &wFatTime; + PDOSDATE pddate = (PDOSDATE) &wFatDate; + SYSTEMTIME SystemTime; + + if (lpFileTime == NULL) + return FALSE; + + SystemTime.wMilliseconds = 0; + SystemTime.wSecond = pdtime->Second; + SystemTime.wMinute = pdtime->Minute; + SystemTime.wHour = pdtime->Hour; + + SystemTime.wDay = pddate->Day; + SystemTime.wMonth = pddate->Month; + SystemTime.wYear = 1980 + pddate->Year; + + ConvertSystemTimeToFileTime(&SystemTime,lpFileTime); + + return TRUE; +} + + +static PWCHAR +GetFileName(PWCHAR Path) +/* + * FUNCTION: Returns a pointer to file name + * ARGUMENTS: + * Path = Pointer to string with pathname + * RETURNS: + * Pointer to filename + */ +{ + ULONG i, j; + + j = i = 0; + + while (Path [i++]) + { + if (Path[i - 1] == L'\\') j = i; + } + return Path + j; +} + + +static VOID +RemoveFileName(PWCHAR Path) +/* + * FUNCTION: Removes a file name from a path + * ARGUMENTS: + * Path = Pointer to string with path + */ +{ + PWCHAR FileName; + DWORD i; + + i = 0; + FileName = GetFileName(Path + i); + + if ((FileName != (Path + i)) && (FileName [-1] == L'\\')) + FileName--; + if ((FileName == (Path + i)) && (FileName [0] == L'\\')) + FileName++; + FileName[0] = 0; +} + + +static BOOL +SetAttributesOnFile(PCFFILE_NODE File, HANDLE hFile) +/* + * FUNCTION: Sets attributes on a file + * ARGUMENTS: + * File = Pointer to CFFILE node for file + * RETURNS: + * Status of operation + */ +{ + FILE_BASIC_INFORMATION FileBasic; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS NtStatus; + ULONG Attributes = 0; + + if (File->File.Attributes & CAB_ATTRIB_READONLY) + Attributes |= FILE_ATTRIBUTE_READONLY; + + if (File->File.Attributes & CAB_ATTRIB_HIDDEN) + Attributes |= FILE_ATTRIBUTE_HIDDEN; + + if (File->File.Attributes & CAB_ATTRIB_SYSTEM) + Attributes |= FILE_ATTRIBUTE_SYSTEM; + + if (File->File.Attributes & CAB_ATTRIB_DIRECTORY) + Attributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (File->File.Attributes & CAB_ATTRIB_ARCHIVE) + Attributes |= FILE_ATTRIBUTE_ARCHIVE; + + NtStatus = NtQueryInformationFile(hFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus); + } + else + { + FileBasic.FileAttributes = Attributes; + + NtStatus = NtSetInformationFile(hFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus); + } + } + + return NT_SUCCESS(NtStatus); +} + + +static ULONG +ReadBlock(PVOID Buffer, + ULONG Size, + PULONG BytesRead) +/* + * FUNCTION: Read a block of data from file + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * BytesRead = Pointer to ULONG that on return will contain + * number of bytes read + * RETURNS: + * Status of operation + */ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS NtStatus; + + NtStatus = NtReadFile(FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + Buffer, + Size, + NULL, + NULL); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("ReadBlock for %d bytes failed (%x)\n", Size, NtStatus); + *BytesRead = 0; + return CAB_STATUS_INVALID_CAB; + } + *BytesRead = Size; + return CAB_STATUS_SUCCESS; +} + + +static PCFFOLDER_NODE +NewFolderNode() +/* + * FUNCTION: Creates a new folder node + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFOLDER_NODE Node; + + Node = (PCFFOLDER_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFOLDER_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFFOLDER_NODE)); + + Node->Folder.CompressionType = CAB_COMP_NONE; + + Node->Prev = FolderListTail; + + if (FolderListTail != NULL) + { + FolderListTail->Next = Node; + } + else + { + FolderListHead = Node; + } + FolderListTail = Node; + + return Node; +} + + +static PCFFILE_NODE +NewFileNode() +/* + * FUNCTION: Creates a new file node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind file to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFFILE_NODE Node; + + Node = (PCFFILE_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFILE_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFFILE_NODE)); + + Node->Prev = FileListTail; + + if (FileListTail != NULL) + { + FileListTail->Next = Node; + } + else + { + FileListHead = Node; + } + FileListTail = Node; + + return Node; +} + + +static PCFDATA_NODE +NewDataNode(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Creates a new data block node + * ARGUMENTS: + * FolderNode = Pointer to folder node to bind data block to + * RETURNS: + * Pointer to node if there was enough free memory available, otherwise NULL + */ +{ + PCFDATA_NODE Node; + + Node = (PCFDATA_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFDATA_NODE)); + if (!Node) + return NULL; + + RtlZeroMemory(Node, sizeof(CFDATA_NODE)); + + Node->Prev = FolderNode->DataListTail; + + if (FolderNode->DataListTail != NULL) + { + FolderNode->DataListTail->Next = Node; + } + else + { + FolderNode->DataListHead = Node; + } + FolderNode->DataListTail = Node; + + return Node; +} + + +static VOID +DestroyDataNodes(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Destroys data block nodes bound to a folder node + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFDATA_NODE PrevNode; + PCFDATA_NODE NextNode; + + NextNode = FolderNode->DataListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FolderNode->DataListHead = NULL; + FolderNode->DataListTail = NULL; +} + + +static VOID +DestroyFileNodes() +/* + * FUNCTION: Destroys file nodes + * ARGUMENTS: + * FolderNode = Pointer to folder node + */ +{ + PCFFILE_NODE PrevNode; + PCFFILE_NODE NextNode; + + NextNode = FileListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + if (NextNode->FileName) + RtlFreeHeap(ProcessHeap, 0, NextNode->FileName); + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FileListHead = NULL; + FileListTail = NULL; +} + + +static VOID +DestroyDeletedFileNodes() +/* + * FUNCTION: Destroys file nodes that are marked for deletion + */ +{ + PCFFILE_NODE CurNode; + PCFFILE_NODE NextNode; + + CurNode = FileListHead; + while (CurNode != NULL) + { + NextNode = CurNode->Next; + + if (CurNode->Delete) + { + if (CurNode->Prev != NULL) + { + CurNode->Prev->Next = CurNode->Next; + } + else + { + FileListHead = CurNode->Next; + if (FileListHead) + FileListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) + { + CurNode->Next->Prev = CurNode->Prev; + } + else + { + FileListTail = CurNode->Prev; + if (FileListTail) + FileListTail->Next = NULL; + } + + DPRINT("Deleting file: '%S'\n", CurNode->FileName); + + if (CurNode->FileName) + RtlFreeHeap(ProcessHeap, 0, CurNode->FileName); + RtlFreeHeap(ProcessHeap, 0, CurNode); + } + CurNode = NextNode; + } +} + + +static VOID +DestroyFolderNodes() +/* + * FUNCTION: Destroys folder nodes + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + + NextNode = FolderListHead; + while (NextNode != NULL) + { + PrevNode = NextNode->Next; + DestroyDataNodes(NextNode); + RtlFreeHeap(ProcessHeap, 0, NextNode); + NextNode = PrevNode; + } + FolderListHead = NULL; + FolderListTail = NULL; +} + + +static VOID +DestroyDeletedFolderNodes() +/* + * FUNCTION: Destroys folder nodes that are marked for deletion + */ +{ + PCFFOLDER_NODE CurNode; + PCFFOLDER_NODE NextNode; + + CurNode = FolderListHead; + while (CurNode != NULL) + { + NextNode = CurNode->Next; + + if (CurNode->Delete) + { + if (CurNode->Prev != NULL) + { + CurNode->Prev->Next = CurNode->Next; + } + else + { + FolderListHead = CurNode->Next; + if (FolderListHead) + FolderListHead->Prev = NULL; + } + + if (CurNode->Next != NULL) + { + CurNode->Next->Prev = CurNode->Prev; + } + else + { + FolderListTail = CurNode->Prev; + if (FolderListTail) + FolderListTail->Next = NULL; + } + + DestroyDataNodes(CurNode); + RtlFreeHeap(ProcessHeap, 0, CurNode); + } + CurNode = NextNode; + } +} + + +static PCFFOLDER_NODE +LocateFolderNode(ULONG Index) +/* + * FUNCTION: Locates a folder node + * ARGUMENTS: + * Index = Folder index + * RETURNS: + * Pointer to folder node or NULL if the folder node was not found + */ +{ + PCFFOLDER_NODE Node; + + switch (Index) + { + case CAB_FILE_SPLIT: + return FolderListTail; + + case CAB_FILE_CONTINUED: + case CAB_FILE_PREV_NEXT: + return FolderListHead; + } + + Node = FolderListHead; + while (Node != NULL) + { + if (Node->Index == Index) + return Node; + Node = Node->Next; + } + return NULL; +} + + +static ULONG +GetAbsoluteOffset(PCFFILE_NODE File) +/* + * FUNCTION: Returns the absolute offset of a file + * ARGUMENTS: + * File = Pointer to CFFILE_NODE structure for file + * RETURNS: + * Status of operation + */ +{ + PCFDATA_NODE Node; + + DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n", + (PWCHAR)File->FileName, + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize); + + Node = CurrentFolderNode->DataListHead; + while (Node != NULL) + { + DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n", + (UINT)Node->UncompOffset, + (UINT)Node->UncompOffset + Node->Data.UncompSize, + (UINT)Node->Data.UncompSize); + + /* Node->Data.UncompSize will be 0 if the block is split + (ie. it is the last block in this cabinet) */ + if ((Node->Data.UncompSize == 0) || + ((File->File.FileOffset >= Node->UncompOffset) && + (File->File.FileOffset < Node->UncompOffset + + Node->Data.UncompSize))) + { + File->DataBlock = Node; + return CAB_STATUS_SUCCESS; + } + + Node = Node->Next; + } + return CAB_STATUS_INVALID_CAB; +} + + +static ULONG +LocateFile(PWCHAR FileName, + PCFFILE_NODE *File) +/* + * FUNCTION: Locates a file in the cabinet + * ARGUMENTS: + * FileName = Pointer to string with name of file to locate + * File = Address of pointer to CFFILE_NODE structure to fill + * RETURNS: + * Status of operation + * NOTES: + * Current folder is set to the folder of the file + */ +{ + PCFFILE_NODE Node; + ULONG Status; + + DPRINT("FileName '%S'\n", FileName); + + Node = FileListHead; + while (Node != NULL) + { + if (_wcsicmp(FileName, Node->FileName) == 0) + { + CurrentFolderNode = LocateFolderNode(Node->File.FileControlID); + if (!CurrentFolderNode) + { + DPRINT("Folder with index number (%d) not found.\n", + (UINT)Node->File.FileControlID); + return CAB_STATUS_INVALID_CAB; + } + + if (Node->DataBlock == NULL) + { + Status = GetAbsoluteOffset(Node); + } + else + Status = CAB_STATUS_SUCCESS; + *File = Node; + return Status; + } + Node = Node->Next; + } + return CAB_STATUS_NOFILE; +} + + +static ULONG +ReadString(PWCHAR String, ULONG MaxLength) +/* + * FUNCTION: Reads a NULL-terminated string from the cabinet + * ARGUMENTS: + * String = Pointer to buffer to place string + * MaxLength = Maximum length of string + * RETURNS: + * Status of operation + */ +{ + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Offset; + ULONG Status; + ULONG Size; + BOOL Found; + CHAR buf[MAX_PATH]; + ANSI_STRING as; + UNICODE_STRING us; + + Offset = 0; + Found = FALSE; + do + { + Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset; + + if (Size == 0) + { + DPRINT("Too long a filename.\n"); + return CAB_STATUS_INVALID_CAB; + } + + Status = ReadBlock((PCFDATA)&buf[Offset], Size, &BytesRead); + if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size)) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + for (Size = Offset; Size < Offset + BytesRead; Size++) + { + if (buf[Size] == '\0') + { + Found = TRUE; + break; + } + } + + Offset += BytesRead; + } while (!Found); + + /* Back up some bytes */ + Size = (BytesRead - Size) - 1; + SeekInFile(FileHandle, -(LONG)Size, NULL, SEEK_CURRENT, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + RtlInitAnsiString(&as, buf); + us.Buffer = String; + us.MaximumLength = MaxLength * sizeof(WCHAR); + us.Length = 0; + + RtlAnsiStringToUnicodeString(&us, &as, FALSE); + + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ReadFileTable() +/* + * FUNCTION: Reads the file table from the cabinet file + * RETURNS: + * Status of operation + */ +{ + ULONG i; + ULONG Status; + ULONG BytesRead; + PCFFILE_NODE File; + NTSTATUS NtStatus; + + DPRINT("Reading file table at absolute offset (0x%X).\n", + CABHeader.FileTableOffset); + + /* Seek to file table */ + SeekInFile(FileHandle, CABHeader.FileTableOffset, NULL, SEEK_BEGIN, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + for (i = 0; i < CABHeader.FileCount; i++) + { + File = NewFileNode(); + if (!File) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + if ((Status = ReadBlock(&File->File, sizeof(CFFILE), + &BytesRead)) != CAB_STATUS_SUCCESS) { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + File->FileName = (PWCHAR)RtlAllocateHeap(ProcessHeap, 0, MAX_PATH * sizeof(WCHAR)); + if (!File->FileName) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Read file name */ + Status = ReadString(File->FileName, MAX_PATH); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n", + (PWCHAR)File->FileName, + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->File.FileControlID); + } + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ReadDataBlocks(PCFFOLDER_NODE FolderNode) +/* + * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file + * ARGUMENTS: + * FolderNode = Pointer to CFFOLDER_NODE structure for folder + * RETURNS: + * Status of operation + */ +{ + ULONG AbsoluteOffset; + ULONG UncompOffset; + PCFDATA_NODE Node; + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Status; + ULONG i; + + DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n", + FolderNode->Index, FolderNode->Folder.DataOffset); + + AbsoluteOffset = FolderNode->Folder.DataOffset; + UncompOffset = FolderNode->UncompOffset; + + for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) + { + Node = NewDataNode(FolderNode); + if (!Node) + { + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Seek to data block */ + SeekInFile(FileHandle, AbsoluteOffset, NULL, SEEK_BEGIN, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA), + &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n", + (UINT)AbsoluteOffset, + (UINT)UncompOffset, + (UINT)Node->Data.Checksum, + (UINT)Node->Data.CompSize, + (UINT)Node->Data.UncompSize); + + Node->AbsoluteOffset = AbsoluteOffset; + Node->UncompOffset = UncompOffset; + + AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize; + UncompOffset += Node->Data.UncompSize; + } + + FolderUncompSize = UncompOffset; + + return CAB_STATUS_SUCCESS; +} + + +static ULONG +ComputeChecksum(PVOID Buffer, + UINT Size, + ULONG Seed) +/* + * FUNCTION: Computes checksum for data block + * ARGUMENTS: + * Buffer = Pointer to data buffer + * Size = Length of data buffer + * Seed = Previously computed checksum + * RETURNS: + * Checksum of buffer + */ +{ + INT UlongCount; // Number of ULONGs in block + ULONG Checksum; // Checksum accumulator + PBYTE pb; + ULONG ul; + + /* FIXME: Doesn't seem to be correct. EXTRACT.EXE + won't accept checksums computed by this routine */ + + DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT)Buffer, Size); + + UlongCount = Size / 4; // Number of ULONGs + Checksum = Seed; // Init checksum + pb = (PBYTE)Buffer; // Start at front of data block + + /* Checksum integral multiple of ULONGs */ + while (UlongCount-- > 0) + { + /* NOTE: Build ULONG in big/little-endian independent manner */ + ul = *pb++; // Get low-order byte + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte + ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte + + Checksum ^= ul; // Update checksum + } + + /* Checksum remainder bytes */ + ul = 0; + switch (Size % 4) + { + case 3: + ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte + case 2: + ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte + case 1: + ul |= *pb++; // Get low-order byte + default: + break; + } + Checksum ^= ul; // Update checksum + + /* Return computed checksum */ + return Checksum; +} + + +static ULONG +CloseCabinet() +/* + * FUNCTION: Closes the current cabinet + * RETURNS: + * Status of operation + */ +{ + PCFFOLDER_NODE PrevNode; + PCFFOLDER_NODE NextNode; + ULONG Status; + + DestroyFileNodes(); + + DestroyFolderNodes(); + + if (InputBuffer) + { + RtlFreeHeap(ProcessHeap, 0, InputBuffer); + InputBuffer = NULL; + } + + if (OutputBuffer) + { + RtlFreeHeap(ProcessHeap, 0, OutputBuffer); + OutputBuffer = NULL; + } + + NtClose(FileHandle); +} + + +VOID +CabinetInitialize() +/* + * FUNCTION: Initialize archiver + */ +{ + ZStream.zalloc = MSZipAlloc; + ZStream.zfree = MSZipFree; + ZStream.opaque = (voidpf)0; + + FileOpen = FALSE; + wcscpy(DestPath, L""); + + FolderListHead = NULL; + FolderListTail = NULL; + FileListHead = NULL; + FileListTail = NULL; + + CodecId = CAB_CODEC_RAW; + CodecSelected = TRUE; + + OutputBuffer = NULL; + CurrentOBuffer = NULL; + CurrentOBufferSize = 0; + InputBuffer = NULL; + CurrentIBuffer = NULL; + CurrentIBufferSize = 0; + + FolderUncompSize = 0; + BytesLeftInBlock = 0; + CabinetReserved = 0; + FolderReserved = 0; + DataReserved = 0; + ReuseBlock = FALSE; + CurrentFolderNode = NULL; + CurrentDataNode = NULL; + CabinetReservedArea = NULL; + RestartSearch = FALSE; + LastFileOffset = 0; +} + + +VOID +CabinetCleanup() +/* + * FUNCTION: Cleanup archiver + */ +{ + CabinetClose(); +} + + +BOOL +CabinetNormalizePath(PWCHAR Path, + ULONG Length) +/* + * FUNCTION: Normalizes a path + * ARGUMENTS: + * Path = Pointer to string with pathname + * Length = Number of characters in Path + * RETURNS: + * TRUE if there was enough room in Path, or FALSE + */ +{ + ULONG n; + BOOL OK = TRUE; + + if ((n = wcslen(Path)) && + (Path[n - 1] != L'\\') && + (OK = ((n + 1) < Length))) + { + Path[n] = L'\\'; + Path[n + 1] = 0; + } + return OK; +} + + +PWCHAR +CabinetGetCabinetName() +/* + * FUNCTION: Returns pointer to cabinet file name + * RETURNS: + * Pointer to string with name of cabinet + */ +{ + return CabinetName; +} + + +VOID +CabinetSetCabinetName(PWCHAR FileName) +/* + * FUNCTION: Sets cabinet file name + * ARGUMENTS: + * FileName = Pointer to string with name of cabinet + */ +{ + wcscpy(CabinetName, FileName); +} + + +VOID +CabinetSetDestinationPath(PWCHAR DestinationPath) +/* + * FUNCTION: Sets destination path + * ARGUMENTS: + * DestinationPath = Pointer to string with name of destination path + */ +{ + wcscpy(DestPath, DestinationPath); + if (wcslen(DestPath) > 0) + CabinetNormalizePath(DestPath, MAX_PATH); +} + + +PWCHAR +CabinetGetDestinationPath() +/* + * FUNCTION: Returns destination path + * RETURNS: + * Pointer to string with name of destination path + */ +{ + return DestPath; +} + + +ULONG +CabinetOpen() +/* + * FUNCTION: Opens a cabinet file + * RETURNS: + * Status of operation + */ +{ + WCHAR CabinetFileName[256]; + PCFFOLDER_NODE FolderNode; + ULONG Status; + ULONG Index; + + if (!FileOpen) + { + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING FileName; + NTSTATUS NtStatus; + ULONG BytesRead; + ULONG Size; + + OutputBuffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough + if (!OutputBuffer) + return CAB_STATUS_NOMEMORY; + + RtlInitUnicodeString(&FileName, + CabinetName); + + InitializeObjectAttributes(&ObjectAttributes, + &FileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + NtStatus = NtOpenFile(&FileHandle, + FILE_READ_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_ALERT); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("Cannot open file (%S) (%x).\n", CabinetName, NtStatus); + return CAB_STATUS_CANNOT_OPEN; + } + + FileOpen = TRUE; + + /* Load CAB header */ + if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + /* Check header */ + if ((BytesRead != sizeof(CFHEADER)) || + (CABHeader.Signature != CAB_SIGNATURE ) || + (CABHeader.Version != CAB_VERSION ) || + (CABHeader.FolderCount == 0 ) || + (CABHeader.FileCount == 0 ) || + (CABHeader.FileTableOffset < sizeof(CFHEADER))) { + CloseCabinet(); + DPRINT("File has invalid header.\n"); + return CAB_STATUS_INVALID_CAB; + } + + Size = 0; + + /* Read/skip any reserved bytes */ + if (CABHeader.Flags & CAB_FLAG_RESERVE) + { + if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + CabinetReserved = Size & 0xFFFF; + FolderReserved = (Size >> 16) & 0xFF; + DataReserved = (Size >> 24) & 0xFF; + + if (CabinetReserved > 0) + { + CabinetReservedArea = RtlAllocateHeap(ProcessHeap, 0, CabinetReserved); + if (!CabinetReservedArea) + { + return CAB_STATUS_NOMEMORY; + } + + if ((Status = ReadBlock(CabinetReservedArea, CabinetReserved, &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + } +#if 0 + SeekInFile(FileHandle, CabinetReserved, NULL, SEEK_CURRENT, &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } +#endif + } + + if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) + { + /* Read name of previous cabinet */ + Status = ReadString(CabinetFileName, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The previous cabinet file is in the same directory as the current */ + wcscpy(CabinetPrev, CabinetName); + RemoveFileName(CabinetPrev); + CabinetNormalizePath(CabinetPrev, 256); + wcscat(CabinetPrev, CabinetFileName); + + /* Read label of previous disk */ + Status = ReadString(DiskPrev, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + else + { + wcscpy(CabinetPrev, L""); + wcscpy(DiskPrev, L""); + } + + if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) + { + /* Read name of next cabinet */ + Status = ReadString(CabinetFileName, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The next cabinet file is in the same directory as the previous */ + wcscpy(CabinetNext, CabinetName); + RemoveFileName(CabinetNext); + CabinetNormalizePath(CabinetNext, 256); + wcscat(CabinetNext, CabinetFileName); + + /* Read label of next disk */ + Status = ReadString(DiskNext, 256); + if (Status != CAB_STATUS_SUCCESS) + return Status; + } + else + { + wcscpy(CabinetNext, L""); + wcscpy(DiskNext, L""); + } + + /* Read all folders */ + for (Index = 0; Index < CABHeader.FolderCount; Index++) + { + FolderNode = NewFolderNode(); + if (!FolderNode) + { + DPRINT("Insufficient resources.\n"); + return CAB_STATUS_NOMEMORY; + } + + if (Index == 0) + FolderNode->UncompOffset = FolderUncompSize; + + FolderNode->Index = Index; + + if ((Status = ReadBlock(&FolderNode->Folder, + sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + } + + /* Read file entries */ + Status = ReadFileTable(); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("ReadFileTable() failed (%d).\n", (UINT)Status); + return Status; + } + + /* Read data blocks for all folders */ + FolderNode = FolderListHead; + while (FolderNode != NULL) + { + Status = ReadDataBlocks(FolderNode); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("ReadDataBlocks() failed (%d).\n", (UINT)Status); + return Status; + } + FolderNode = FolderNode->Next; + } + } + return CAB_STATUS_SUCCESS; +} + + +VOID +CabinetClose() +/* + * FUNCTION: Closes the cabinet file + */ +{ + if (FileOpen) + { + CloseCabinet(); + + if (CabinetReservedArea != NULL) + { + RtlFreeHeap(ProcessHeap, 0, CabinetReservedArea); + CabinetReservedArea = NULL; + } + + FileOpen = FALSE; + } +} + + +ULONG +CabinetFindFirst(PWCHAR FileName, + PCAB_SEARCH Search) +/* + * FUNCTION: Finds the first file in the cabinet that matches a search criteria + * ARGUMENTS: + * FileName = Pointer to search criteria + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + RestartSearch = FALSE; + wcsncpy(Search->Search, FileName, MAX_PATH); + Search->Next = FileListHead; + return CabinetFindNext(Search); +} + + +ULONG +CabinetFindNext(PCAB_SEARCH Search) +/* + * FUNCTION: Finds next file in the cabinet that matches a search criteria + * ARGUMENTS: + * Search = Pointer to search structure + * RETURNS: + * Status of operation + */ +{ + ULONG Status; + + if (RestartSearch) + { + Search->Next = FileListHead; + + /* Skip split files already extracted */ + while ((Search->Next) && + (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) && + (Search->Next->File.FileOffset <= LastFileOffset)) + { + DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n", + Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset); + Search->Next = Search->Next->Next; + } + + RestartSearch = FALSE; + } + + /* FIXME: Check search criteria */ + + if (!Search->Next) + { + if (wcslen(DiskNext) > 0) + { + CloseCabinet(); + + CabinetSetCabinetName(CabinetNext); + + if (DiskChangeHandler != NULL) + { + DiskChangeHandler(CabinetNext, DiskNext); + } + + Status = CabinetOpen(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + Search->Next = FileListHead; + if (!Search->Next) + return CAB_STATUS_NOFILE; + } + else + { + return CAB_STATUS_NOFILE; + } + } + + Search->File = &Search->Next->File; + Search->FileName = Search->Next->FileName; + Search->Next = Search->Next->Next; + return CAB_STATUS_SUCCESS; +} + + +ULONG +CabinetExtractFile(PWCHAR FileName) +/* + * FUNCTION: Extracts a file from the cabinet + * ARGUMENTS: + * FileName = Pointer to buffer with name of file + * RETURNS + * Status of operation + */ +{ + ULONG Size; + ULONG Offset; + ULONG BytesRead; + ULONG BytesToRead; + ULONG BytesWritten; + ULONG BytesSkipped; + ULONG BytesToWrite; + ULONG TotalBytesRead; + ULONG CurrentOffset; + PUCHAR Buffer; + PUCHAR CurrentBuffer; + HANDLE DestFile; + PCFFILE_NODE File; + CFDATA CFData; + ULONG Status; + BOOL Skip; + FILETIME FileTime; + WCHAR DestName[MAX_PATH]; + WCHAR TempName[MAX_PATH]; + NTSTATUS NtStatus; + UNICODE_STRING UnicodeString; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + FILE_BASIC_INFORMATION FileBasic; + + Status = LocateFile(FileName, &File); + if (Status != CAB_STATUS_SUCCESS) + { + DPRINT("Cannot locate file (%d).\n", (UINT)Status); + return Status; + } + + LastFileOffset = File->File.FileOffset; + + switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) + { + case CAB_COMP_NONE: + CabinetSelectCodec(CAB_CODEC_RAW); + break; + case CAB_COMP_MSZIP: + CabinetSelectCodec(CAB_CODEC_MSZIP); + break; + default: + return CAB_STATUS_UNSUPPCOMP; + } + + DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->DataBlock->AbsoluteOffset, + (UINT)File->DataBlock->UncompOffset); + + wcscpy(DestName, DestPath); + wcscat(DestName, FileName); + + /* Create destination file, fail if it already exists */ + RtlInitUnicodeString(&UnicodeString, + DestName); + + InitializeObjectAttributes(&ObjectAttributes, + &UnicodeString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + NtStatus = NtCreateFile(&DestFile, + FILE_WRITE_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_CREATE, + FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName, NtStatus); + + /* If file exists, ask to overwrite file */ + if (OverwriteHandler == NULL || OverwriteHandler(&File->File, FileName)) + { + /* Create destination file, overwrite if it already exists */ + NtStatus = NtCreateFile(&DestFile, + FILE_WRITE_ACCESS, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OVERWRITE, + FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName, NtStatus); + return CAB_STATUS_CANNOT_CREATE; + } + } + else + { + DPRINT("File (%S) exists.\n", DestName); + return CAB_STATUS_FILE_EXISTS; + } + } + + if (!ConvertDosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) + { + NtClose(DestFile); + DPRINT("DosDateTimeToFileTime() failed.\n"); + return CAB_STATUS_CANNOT_WRITE; + } + + NtStatus = NtQueryInformationFile(DestFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus); + } + else + { + memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME)); + + NtStatus = NtSetInformationFile(DestFile, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus); + } + } + + SetAttributesOnFile(File, DestFile); + + Buffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough + if (!Buffer) + { + NtClose(DestFile); + DPRINT("Insufficient memory.\n"); + return CAB_STATUS_NOMEMORY; + } + + /* Call extract event handler */ + if (ExtractHandler != NULL) + { + ExtractHandler(&File->File, FileName); + } + + /* Search to start of file */ + Offset = SeekInFile(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + Size = File->File.FileSize; + Offset = File->File.FileOffset; + CurrentOffset = File->DataBlock->UncompOffset; + + Skip = TRUE; + + ReuseBlock = (CurrentDataNode == File->DataBlock); + if (Size > 0) + { + do + { + DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n", + File->DataBlock->UncompOffset, (UINT)ReuseBlock, Offset, Size, + BytesLeftInBlock); + + if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) + { + DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock); + + CurrentBuffer = Buffer; + TotalBytesRead = 0; + do + { + DPRINT("Size (%d bytes).\n", Size); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n", + (UINT)CFData.Checksum, + (UINT)CFData.CompSize, + (UINT)CFData.UncompSize, + (UINT)SeekInFile(FileHandle, 0, NULL, SEEK_CURRENT, &NtStatus)); + + //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12); + + BytesToRead = CFData.CompSize; + + DPRINT("Read: (0x%X,0x%X).\n", + CurrentBuffer, Buffer); + + if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + /* FIXME: Does not work with files generated by makecab.exe */ +/* + if (CFData.Checksum != 0) + { + ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0); + if (Checksum != CFData.Checksum) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n", + Checksum, CFData.Checksum); + return CAB_STATUS_INVALID_CAB; + } + } +*/ + TotalBytesRead += BytesRead; + + CurrentBuffer += BytesRead; + + if (CFData.UncompSize == 0) + { + if (wcslen(DiskNext) == 0) + return CAB_STATUS_NOFILE; + + /* CloseCabinet() will destroy all file entries so in case + FileName refers to the FileName field of a CFFOLDER_NODE + structure, we have to save a copy of the filename */ + wcscpy(TempName, FileName); + + CloseCabinet(); + + CabinetSetCabinetName(CabinetNext); + + if (DiskChangeHandler != NULL) + { + DiskChangeHandler(CabinetNext, DiskNext); + } + + Status = CabinetOpen(); + if (Status != CAB_STATUS_SUCCESS) + return Status; + + /* The first data block of the file will not be + found as it is located in the previous file */ + Status = LocateFile(TempName, &File); + if (Status == CAB_STATUS_NOFILE) + { + DPRINT("Cannot locate file (%d).\n", (UINT)Status); + return Status; + } + + /* The file is continued in the first data block in the folder */ + File->DataBlock = CurrentFolderNode->DataListHead; + + /* Search to start of file */ + SeekInFile(FileHandle, + File->DataBlock->AbsoluteOffset, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n", + (UINT)File->File.FileOffset, + (UINT)File->File.FileSize, + (UINT)File->DataBlock->AbsoluteOffset, + (UINT)File->DataBlock->UncompOffset); + + CurrentDataNode = File->DataBlock; + ReuseBlock = TRUE; + + RestartSearch = TRUE; + } + } while (CFData.UncompSize == 0); + + DPRINT("TotalBytesRead (%d).\n", TotalBytesRead); + + Status = CodecUncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite); + if (Status != CS_SUCCESS) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot uncompress block.\n"); + if (Status == CS_NOMEMORY) + return CAB_STATUS_NOMEMORY; + return CAB_STATUS_INVALID_CAB; + } + + if (BytesToWrite != CFData.UncompSize) + { + DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n", + BytesToWrite, CFData.UncompSize); + return CAB_STATUS_INVALID_CAB; + } + + BytesLeftInBlock = BytesToWrite; + } + else + { + DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock); + + BytesToWrite = BytesLeftInBlock; + + DPRINT("Seeking to absolute offset 0x%X.\n", + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize); + + if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != + CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) + { + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot read from file (%d).\n", (UINT)Status); + return CAB_STATUS_INVALID_CAB; + } + + DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n", + CFData.CompSize, CFData.UncompSize); + + /* Go to next data block */ + SeekInFile(FileHandle, + CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + + CurrentDataNode->Data.CompSize, + NULL, + SEEK_BEGIN, + &NtStatus); + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("SeekInFile() failed (%x).\n", NtStatus); + return CAB_STATUS_INVALID_CAB; + } + + ReuseBlock = FALSE; + } + + if (Skip) + BytesSkipped = (Offset - CurrentOffset); + else + BytesSkipped = 0; + + BytesToWrite -= BytesSkipped; + + if (Size < BytesToWrite) + BytesToWrite = Size; + + DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n", + (UINT)Offset, + (UINT)CurrentOffset, + (UINT)BytesToWrite, + (UINT)BytesSkipped, (UINT)Skip, + (UINT)Size); + +// if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped), +// BytesToWrite, &BytesWritten, NULL) || +// (BytesToWrite != BytesWritten)) + + NtStatus = NtWriteFile(DestFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + (PVOID)((ULONG)OutputBuffer + BytesSkipped), + BytesToWrite, + NULL, + NULL); + BytesWritten = BytesToWrite; + if (!NT_SUCCESS(NtStatus)) + { + DPRINT("Status 0x%X.\n", NtStatus); + + NtClose(DestFile); + RtlFreeHeap(ProcessHeap, 0, Buffer); + DPRINT("Cannot write to file.\n"); + return CAB_STATUS_CANNOT_WRITE; + } + Size -= BytesToWrite; + + CurrentOffset += BytesToWrite; + + /* Don't skip any more bytes */ + Skip = FALSE; + } while (Size > 0); + } + + NtClose(DestFile); + + RtlFreeHeap(ProcessHeap, 0, Buffer); + + return CAB_STATUS_SUCCESS; +} + + +VOID +CabinetSelectCodec(ULONG Id) +/* + * FUNCTION: Selects codec engine to use + * ARGUMENTS: + * Id = Codec identifier + */ +{ + if (CodecSelected) + { + if (Id == CodecId) + return; + + CodecSelected = FALSE; + } + + switch (Id) + { + case CAB_CODEC_RAW: + CodecUncompress = RawCodecUncompress; + break; + case CAB_CODEC_MSZIP: + CodecUncompress = MSZipCodecUncompress; + break; + default: + return; + } + + CodecId = Id; + CodecSelected = TRUE; +} + + +VOID +CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite, + PCABINET_EXTRACT Extract, + PCABINET_DISK_CHANGE DiskChange) +/* + * FUNCTION: Set event handlers + * ARGUMENTS: + * Overwrite = Handler called when a file is to be overwritten + * Extract = Handler called when a file is to be extracted + * DiskChange = Handler called when changing the disk + */ +{ + OverwriteHandler = Overwrite; + ExtractHandler = Extract; + DiskChangeHandler = DiskChange; +} + + +PVOID +CabinetGetCabinetReservedArea(PULONG Size) +/* + * FUNCTION: Get pointer to cabinet reserved area. NULL if none + */ +{ + if (CabinetReservedArea != NULL) + { + if (Size != NULL) + { + *Size = CabinetReserved; + } + return CabinetReservedArea; + } + else + { + if (Size != NULL) + { + *Size = 0; + } + return NULL; + } +}