/* * 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; } }