windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/kernel/spcab.c
2020-09-26 16:20:57 +08:00

2687 lines
68 KiB
C

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
spcab.c
Abstract:
Cabinet stuff (file compression/decompression)
Author:
Calin Negreanu (calinn) 27-Apr-2000
Revision History:
Jay Krell (a-JayK) November 2000 -
ported from windows\winstate\cobra\utils\cablib\cablib.c to admin\ntsetup\textmode\kernel\spcab.c
partial nt/unicodification
gas gauge / progress bar support
--*/
#include "spprecmp.h"
#include "fci.h"
#include "fdi.h"
#include "fcntl.h"
#include "crt/sys/stat.h"
#include "spwin.h"
#include "spcab.h"
#include "spcabp.h"
#include <stdarg.h>
#include "fci.h"
#include "spprintf.h"
/*
PathA on decompression looks like it is set wrong, like it is the full path, including the leaf,
to the .cab, when it is only supposed to be to the directory that contains the .cab.
This is ok, we don't end up using the path, because decompress to fullpaths, not relative paths.
*/
//
// NOTE: fdi opens the cab twice. And we allow that they might seek
// the handles. Thus a small amount of complexity.
//
//
// all these globals except the first should be moved into FDI_CAB_HANDLE.
//
PFDI_CAB_HANDLE g_SpCabFdiHandle;
ANSI_STRING g_CabFileFullPath;
typedef struct _SPCAB_CAB_FILE {
ULONGLONG Position;
HANDLE NtHandle;
BOOLEAN Busy;
} SPCAB_CAB_FILE, *PSPCAB_CAB_FILE;
SPCAB_CAB_FILE g_CabFiles[2];
ULONGLONG g_CabFileSize;
ULONGLONG g_CabFileMaximumPosition;
ULONG g_CabLastPercent;
ULONG g_NumberOfOpenCabFiles;
VOID
SpUpdateCabGauge(
ULONGLONG NewPosition
)
{
UINT newPercent;
if (!RTL_SOFT_VERIFY(g_SpCabFdiHandle != NULL))
return;
if (!RTL_SOFT_VERIFY(g_CabFileSize != 0))
return;
if (NewPosition > g_CabFileMaximumPosition) {
g_CabFileMaximumPosition = NewPosition;
newPercent = (ULONG) (NewPosition * 100 / g_CabFileSize);
if (newPercent != g_CabLastPercent) {
g_CabLastPercent = newPercent;
SpFillGauge (g_SpCabFdiHandle->Gauge, newPercent);
SendSetupProgressEvent (
UninstallEvent,
UninstallUpdateEvent,
&newPercent
);
}
}
}
BOOLEAN
SpCabIsCabFileName(
PCSTR FullPath
)
{
return g_CabFileFullPath.Buffer != NULL && _stricmp(FullPath, g_CabFileFullPath.Buffer) == 0;
}
PSPCAB_CAB_FILE
SpCabNewCabFile(
HANDLE NtHandle
)
{
ULONG i;
if (NtHandle == INVALID_HANDLE_VALUE)
return NULL;
for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
if (!g_CabFiles[i].Busy) {
g_NumberOfOpenCabFiles += 1;
g_CabFiles[i].Busy = TRUE;
g_CabFiles[i].NtHandle = NtHandle;
g_CabFiles[i].Position = 0;
return &g_CabFiles[i];
}
}
KdPrint(("SETUP: Ran out of CabFiles g_NumberOfOpenCabFiles:%lu\n", g_NumberOfOpenCabFiles));
return NULL;
}
VOID
SpCabReleaseCabFile(
PSPCAB_CAB_FILE CabFile
)
{
if (CabFile == NULL)
return;
if (!CabFile->Busy)
return;
RtlZeroMemory(CabFile, sizeof(*CabFile));
g_NumberOfOpenCabFiles -= 1;
}
VOID
SpCabCleanupCabGlobals(
)
{
ULONG i;
for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
SpCabReleaseCabFile(&g_CabFiles[i]);
}
ASSERT(g_NumberOfOpenCabFiles == 0);
SpDestroyGauge(g_SpCabFdiHandle->Gauge);
g_SpCabFdiHandle->Gauge = NULL;
SpFreeStringA(&g_CabFileFullPath);
g_CabFileSize = 0;
g_CabFileMaximumPosition = 0;
g_SpCabFdiHandle = NULL;
SendSetupProgressEvent (UninstallEvent, UninstallEndEvent, NULL);
}
PSPCAB_CAB_FILE
SpCabFindCabFile(
HANDLE NtHandle
)
{
ULONG i;
if (NtHandle == INVALID_HANDLE_VALUE)
return NULL;
for (i = 0 ; i < RTL_NUMBER_OF(g_CabFiles) ; ++i) {
if (g_CabFiles[i].NtHandle == NtHandle) {
return &g_CabFiles[i];
}
}
return NULL;
}
VOID
SpCabCloseHandle(
HANDLE* HandlePointer
)
{
HANDLE Handle = *HandlePointer;
ASSERT (Handle); // never NULL
if (Handle != INVALID_HANDLE_VALUE) {
*HandlePointer = INVALID_HANDLE_VALUE;
ZwClose(Handle);
}
}
INT
DIAMONDAPI
pCabFilePlacedW(
IN PCCAB FciCabParams,
IN PSTR FileName,
IN LONG FileSize,
IN BOOL Continuation,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE)Context;
if (CabHandle == NULL)
return 0;
CabHandle->FileCount++;
CabHandle->FileSize += FileSize;
return 0;
}
PVOID
DIAMONDAPI
pCabAlloc(
IN ULONG Size
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
return SpMemAlloc(Size);
}
VOID
DIAMONDAPI
pCabFree(
IN PVOID Memory
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
if (Memory != NULL)
SpMemFree(Memory);
}
INT_PTR
DIAMONDAPI
pCabOpenForWriteA(
IN PSTR FileName,
IN INT oFlag,
IN INT pMode,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
HANDLE FileHandle;
// oFlag and pMode are prepared for using _open. We won't do that
// and it's a terrible waste of time to check each individual flags
// We'll just assert these values.
ASSERT ((oFlag == (_O_CREAT | _O_TRUNC | _O_BINARY | _O_RDWR)) || (oFlag == (_O_CREAT | _O_EXCL | _O_BINARY | _O_RDWR)));
ASSERT (pMode == (_S_IREAD | _S_IWRITE));
FileHandle = SpWin32CreateFileA(
FileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE,
NULL
);
ASSERT (FileHandle); // never NULL
if (FileHandle == INVALID_HANDLE_VALUE) {
*Error = SpGetLastWin32Error();
FileHandle = (HANDLE)(LONG_PTR)-1;
goto Exit;
}
*Error = 0;
Exit:
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(Handle),
"SETUP:"__FUNCTION__"(%s) exiting with FileHandle: %p Status:0x%08lx Error:%d\n",
FileName, FileHandle, SpGetLastNtStatus(), SpGetLastWin32Error()
));
return (INT_PTR)FileHandle;
}
INT_PTR
DIAMONDAPI
pCabOpenForReadA(
IN PSTR FileNameA,
IN INT oFlag,
IN INT pMode
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
const NTSTATUS StatusGaugeInternalError = STATUS_SUCCESS; // STATUS_INTERNAL_ERROR if
// gauge was really critical
const NTSTATUS StatusGaugeNoMemory = STATUS_SUCCESS; // STATUS_NO_MEMORY if
// gauge was really critical
PSPCAB_CAB_FILE CabFile = NULL;
PVOID Gauge = NULL;
// oFlag and pMode are prepared for using _open. We won't do that
// and it's a terrible waste of time to check each individual flags
// We'll just assert these values.
ASSERT (oFlag == _O_BINARY);
FileHandle = SpWin32CreateFileA(
FileNameA,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE,
NULL
);
ASSERT (FileHandle); // never NULL
if (FileHandle == INVALID_HANDLE_VALUE) {
FileHandle = (HANDLE)(LONG_PTR)-1;
goto Exit;
}
if (SpCabIsCabFileName(FileNameA)) {
ULONG CabFileSize32 = 0;
CabFile = SpCabNewCabFile(FileHandle);
if (CabFile == NULL) {
Status = StatusGaugeInternalError;
goto Exit;
}
if (!RTL_VERIFY(g_SpCabFdiHandle != NULL)) {
Status = StatusGaugeInternalError;
goto Exit;
}
ASSERT((g_CabFileSize == 0) == (g_SpCabFdiHandle->Gauge == NULL));
if (g_CabFileSize == 0) {
Status = SpGetFileSize(FileHandle, &CabFileSize32);
//
// 0 file size causes an unhandled divide by zero exception in the gauge code
//
if (NT_SUCCESS(Status) && CabFileSize32 == 0)
Status = STATUS_UNSUCCESSFUL;
if (!NT_SUCCESS(Status)) {
KdPrintEx((
DPFLTR_SETUP_ID,
SpNtStatusToDbgPrintLevel(Status),
__FUNCTION__" SpGetFileSize(.cab:%s, FileHandle:%p) failed Status:0x%08lx\n",
FileNameA,
FileHandle,
Status
));
Status = STATUS_SUCCESS; // gauge is sacrificable
goto Exit;
}
}
if (g_SpCabFdiHandle->Gauge == NULL) {
// need to update the message
SpFormatMessage (TemporaryBuffer, sizeof(TemporaryBuffer), SP_TEXT_SETUP_IS_COPYING);
Gauge =
SpCreateAndDisplayGauge(CabFileSize32, 0, 15,
TemporaryBuffer, NULL, GF_PERCENTAGE, 0);
if (Gauge == NULL) {
Status = StatusGaugeNoMemory;
goto Exit;
}
g_SpCabFdiHandle->Gauge = Gauge;
Gauge = NULL;
g_CabFileSize = CabFileSize32;
SendSetupProgressEvent (UninstallEvent, UninstallStartEvent, NULL);
}
CabFile = NULL;
}
Exit:
if (Gauge != NULL)
SpDestroyGauge(Gauge);
if (CabFile != NULL)
SpCabReleaseCabFile(CabFile);
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(Handle),
__FUNCTION__"(%s) exiting with FileHandle:%p Status:0x%08lx Error:%d\n",
FileNameA, FileHandle, SpGetLastNtStatus(), SpGetLastWin32Error()
));
return (INT_PTR)FileHandle;
}
UINT
DIAMONDAPI
pCabRead(
IN INT_PTR FileHandleInteger,
IN PVOID Buffer,
IN UINT Size,
OUT PINT Error, OPTIONAL
IN PVOID ContextIgnored OPTIONAL
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
BOOL Result;
UINT BytesRead;
HANDLE FileHandle = (HANDLE)FileHandleInteger;
PSPCAB_CAB_FILE CabFile = NULL;
Result = SpWin32ReadFile(FileHandle, Buffer, Size, &BytesRead, NULL);
if (!Result) {
if (Error != NULL) {
*Error = SpGetLastWin32Error();
}
return ((UINT)(-1));
}
if (CabFile = SpCabFindCabFile(FileHandle)) {
CabFile->Position += BytesRead;
SpUpdateCabGauge(CabFile->Position);
}
if (Error != NULL) {
*Error = 0;
}
return BytesRead;
}
UINT
DIAMONDAPI
pCabRead1(
IN INT_PTR FileHandleInteger,
IN PVOID Buffer,
IN UINT Size
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
const UINT i = pCabRead(FileHandleInteger, Buffer, Size, NULL, NULL);
return i;
}
UINT
DIAMONDAPI
pCabWrite(
IN INT_PTR FileHandleInteger,
IN PVOID Buffer,
IN UINT Size,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
BOOL Result;
DWORD BytesWritten;
HANDLE FileHandle = (HANDLE)FileHandleInteger;
//
// g_CabNtFileHandle is only set for reading, so..
//
ASSERT(SpCabFindCabFile(FileHandle) == NULL);
Result = SpWin32WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL/*overlapped*/);
if (!Result) {
*Error = SpGetLastWin32Error();
return (UINT)-1;
}
else if (BytesWritten != Size) {
*Error = -1;
return (UINT)-1;
}
*Error = 0;
return Size;
}
UINT
DIAMONDAPI
pCabWrite1(
IN INT_PTR FileHandle,
IN PVOID Buffer,
IN UINT Size
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
INT ErrorIgnored;
const PVOID ContextIgnored = NULL;
const BOOL Result = pCabWrite(FileHandle, Buffer, Size, &ErrorIgnored, ContextIgnored);
return Result;
}
INT
DIAMONDAPI
pCabClose(
IN INT_PTR FileHandleInteger,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
HANDLE Handle = (HANDLE)FileHandleInteger;
PSPCAB_CAB_FILE CabFile = NULL;
if (CabFile = SpCabFindCabFile(Handle)) {
SpCabReleaseCabFile(CabFile);
}
SpCabCloseHandle(&Handle);
if (Error != NULL) {
*Error = 0;
}
return 0;
}
INT
DIAMONDAPI
pCabClose1(
IN INT_PTR FileHandleInteger
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
const INT Result = pCabClose(FileHandleInteger, NULL, NULL);
return Result;
}
LONG
DIAMONDAPI
pCabSeek(
IN INT_PTR FileHandleInteger,
IN LONG Distance,
IN INT CrtSeekType,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
ULONG NewPosition = 0;
ULONG Win32SeekType = FILE_BEGIN;
HANDLE FileHandle = (HANDLE)FileHandleInteger;
PSPCAB_CAB_FILE CabFile = NULL;
CabFile = SpCabFindCabFile (FileHandle);
switch (CrtSeekType) {
case SEEK_SET:
Win32SeekType = FILE_BEGIN;
break;
case SEEK_CUR:
Win32SeekType = FILE_CURRENT;
break;
case SEEK_END:
Win32SeekType = FILE_END;
break;
}
NewPosition = SpSetFilePointer(FileHandle, Distance, NULL, Win32SeekType);
if (NewPosition == INVALID_SET_FILE_POINTER) {
if (Error != NULL) {
*Error = SpGetLastWin32Error();
}
return -1;
}
if (Error != NULL) {
*Error = 0;
}
if (CabFile != NULL) {
SpUpdateCabGauge(CabFile->Position = NewPosition);
}
return ((LONG)(NewPosition));
}
LONG
DIAMONDAPI
pCabSeek1(
IN INT_PTR FileHandleInteger,
IN LONG Distance,
IN INT CrtSeekType
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
const LONG NewPosition = pCabSeek(FileHandleInteger, Distance, CrtSeekType, NULL, NULL);
return NewPosition;
}
INT
DIAMONDAPI
pCabDeleteA(
IN PSTR FileName,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
if (!SpWin32DeleteFileA(FileName)) {
*Error = SpGetLastWin32Error();
return -1;
}
*Error = 0;
return 0;
}
BOOL
DIAMONDAPI
pCabGetTempFileA(
OUT PSTR FileName,
IN INT FileNameLen,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
static LARGE_INTEGER Counter = { 0 };
PFCI_CAB_HANDLE cabHandle;
cabHandle = (PFCI_CAB_HANDLE) Context;
if (cabHandle == NULL) {
ASSERT (FALSE);
return FALSE;
}
ASSERT(FileNameLen >= 256);
FileName[FileNameLen - 1] = 0;
//
// Seeding the counter based on the time should increase reliability
// in the face of crash/rerun cycles, compared to just starting it at 0.
//
// We should/could also/instead loop while the resulting name exists,
// but I'm putting this in after having tested, so stick with this simpler change.
//
if (Counter.QuadPart == 0) {
KeQuerySystemTime(&Counter); // NtQuerySystemTime in usermode
}
Counter.QuadPart += 1;
_snprintf(FileName, FileNameLen - 1, "%hs\\spcab%I64d", cabHandle->PathA.Buffer, Counter.QuadPart);
KdPrintEx((
DPFLTR_SETUP_ID,
DPFLTR_TRACE_LEVEL,
__FUNCTION__":%s\n",
FileName
));
return TRUE;
}
BOOL
DIAMONDAPI
pCabGetNextCabinet(
IN PCCAB FciCabParams,
IN ULONG PrevCabinetSize,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
ASSERTMSG("We fit in a single cabinet.", FALSE);
return FALSE;
}
LONG
DIAMONDAPI
pCabStatus(
IN UINT StatusType,
IN ULONG Size1,
IN ULONG Size2,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
PFCI_CAB_HANDLE CabHandle = NULL;
if (StatusType == statusCabinet) {
CabHandle = (PFCI_CAB_HANDLE) Context;
if (CabHandle == NULL) {
return 0;
}
CabHandle->CabCount++;
CabHandle->CompressedSize += Size2;
}
return 0;
}
INT_PTR
DIAMONDAPI
pCabGetOpenInfoA(
IN PSTR FileName,
OUT USHORT* Date,
OUT USHORT* Time,
OUT USHORT* Attributes,
OUT PINT Error,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
FILETIME LocalFileTime = { 0 };
HANDLE FileHandle = INVALID_HANDLE_VALUE;
BOOL DoesFileExist = FALSE;
WIN32_FILE_ATTRIBUTE_DATA FileAttributeData = { 0 };
//
// It seems like it'd be better to open the file, and if that succeeds,
// get the information from the handle. Anyway, we just mimic the winstate code for now.
//
DoesFileExist = SpGetFileAttributesExA(FileName, GetFileExInfoStandard, &FileAttributeData);
if (DoesFileExist) {
SpFileTimeToLocalFileTime(&FileAttributeData.ftLastWriteTime, &LocalFileTime);
SpFileTimeToDosDateTime(&LocalFileTime, Date, Time);
/*
* Mask out all other bits except these four, since other
* bits are used by the cabinet format to indicate a
* special meaning.
*/
*Attributes = (USHORT) (FileAttributeData.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
FileHandle = SpWin32CreateFileA(
FileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
ASSERT (FileHandle); // never NULL
if (FileHandle == INVALID_HANDLE_VALUE) {
*Error = SpGetLastWin32Error();
return -1;
}
*Error = 0;
return (INT_PTR)FileHandle;
} else {
*Error = SpGetLastWin32Error();
return -1;
}
}
BOOLEAN
SpCabIsFullPath(
PCANSI_STRING p
)
{
const ULONG Length = p->Length / sizeof(p->Buffer[0]);
if (Length < 4)
return FALSE;
if (p->Buffer[0] == '\\' && p->Buffer[1] == '\\')
return TRUE;
if (p->Buffer[1] == ':' && p->Buffer[2] == '\\')
return TRUE;
if ( p->Buffer[0] == '\\'
&& p->Buffer[1] == '?'
&& p->Buffer[2] == '?'
&& p->Buffer[3] == '\\'
)
return TRUE;
if ( p->Buffer[0] == '\\'
&& (p->Buffer[1] == 'D' || p->Buffer[1] == 'd' )
&& (p->Buffer[2] == 'E' || p->Buffer[2] == 'e' )
&& (p->Buffer[3] == 'V' || p->Buffer[3] == 'v' )
)
return TRUE;
KdPrint(("SETUP: Warning: "__FUNCTION__"(%Z):FALSE\n", p));
return FALSE;
}
VOID
pRecordDataLoss (
VOID
)
/*++
Routine Description:
This routine creates a file called dataloss, so that the backup
CABs don't get removed from the system.
Arguments:
None.
Return Value:
None.
--*/
{
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES obja;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE Handle;
NTSTATUS Status;
//
// We failed to create the subdirectory for this file.
// Put a file in the ~bt directory to prevent the undo
// directory from being removed.
//
wcscpy (TemporaryBuffer, NtBootDevicePath);
SpConcatenatePaths (TemporaryBuffer, DirectoryOnBootDevice);
SpConcatenatePaths (TemporaryBuffer, L"dataloss");
INIT_OBJA (&obja, &UnicodeString, TemporaryBuffer);
Status = ZwCreateFile(
&Handle,
FILE_GENERIC_WRITE|SYNCHRONIZE|FILE_READ_ATTRIBUTES,
&obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,
NULL,
0
);
if (NT_SUCCESS(Status)) {
ZwClose (Handle);
}
}
INT_PTR
DIAMONDAPI
pCabNotification(
IN FDINOTIFICATIONTYPE FdiNotificationType,
IN OUT PFDINOTIFICATION FdiNotification
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
PSTR DestFileA = NULL;
ANSI_STRING DestFileStringA = { 0 };
UNICODE_STRING DestFileStringW = { 0 };
HANDLE DestHandle = INVALID_HANDLE_VALUE;
ULONG FileAttributes = 0;
FILETIME LocalFileTime = { 0 };
FILETIME FileTime = { 0 };
PCAB_DATA CabData = NULL;
INT_PTR Result = 0;
NTSTATUS Status = STATUS_SUCCESS;
PCSTR psz1 = NULL;
ANSI_STRING psz1String = { 0 };
UNICODE_STRING NtPathString = { 0 };
WCHAR ntPathTemp[ACTUAL_MAX_PATH];
CHAR ntPath[ACTUAL_MAX_PATH];
BOOLEAN b;
switch (FdiNotificationType) {
case fdintCABINET_INFO: // General information about cabinet
break;
case fdintCOPY_FILE: // File to be copied
CabData = (PCAB_DATA)FdiNotification->pv;
psz1 = FdiNotification->psz1;
{
RtlInitAnsiString(&psz1String, psz1);
psz1String.Length = psz1String.MaximumLength; // include terminal nul
Status = RtlAnsiStringToUnicodeString(&NtPathString, &psz1String, TRUE);
if (!NT_SUCCESS(Status)) {
KdPrintEx((
DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SETUP: Cannot convert ansi string %s to nt\n",
psz1String.Buffer
));
goto NtExit;
}
b = SpNtNameFromDosPath (
NtPathString.Buffer,
ntPathTemp,
ACTUAL_MAX_PATH * sizeof (WCHAR),
PartitionOrdinalCurrent
);
RtlFreeUnicodeString(&NtPathString);
if (!b) {
KdPrintEx((
DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SETUP: Cannot convert path %ws to an NT path\n",
NtPathString.Buffer
));
goto Exit;
}
RtlInitUnicodeString(&NtPathString, ntPathTemp);
NtPathString.Length = NtPathString.MaximumLength; // include terminal nul
psz1String.Buffer = (PSTR)ntPath;
psz1String.Length = 0;
psz1String.MaximumLength = ACTUAL_MAX_PATH * sizeof (CHAR);
Status = RtlUnicodeStringToAnsiString(&psz1String, &NtPathString, FALSE);
if (!NT_SUCCESS(Status)) {
KdPrintEx((
DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SETUP: Cannot convert nt string %ws to ansi\n",
NtPathString.Buffer
));
goto NtExit;
}
psz1 = psz1String.Buffer;
}
if (SpCabIsFullPath(&psz1String)) {
//
// This is always the case in Win9x uninstall.
//
DestFileA = SpDupString(psz1);
}
else {
DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
}
if (DestFileA == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
if (CabData->NotificationA != NULL) {
if (CabData->NotificationA(DestFileA)) {
Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
if (!NT_SUCCESS(Status)) {
pRecordDataLoss();
Result = 0;
goto Exit;
}
DestHandle = SpCreateFile1A(DestFileA);
ASSERT (DestHandle); // never NULL
}
} else if (CabData->NotificationW != NULL) {
RtlInitAnsiString(&DestFileStringA, DestFileA);
DestFileStringA.Length = DestFileStringA.MaximumLength; // include terminal nul
Status = SpAnsiStringToUnicodeString(&DestFileStringW, &DestFileStringA, TRUE);
if (!NT_SUCCESS(Status)) {
goto NtExit;
}
if (CabData->NotificationW(DestFileStringW.Buffer)) {
//
// Ensure the directory exists. If we can't create the
// dir, then record data loss and skip the file.
//
Status = SpCreateDirectoryForFileA(DestFileA, CREATE_DIRECTORY_FLAG_SKIPPABLE);
if (!NT_SUCCESS(Status)) {
pRecordDataLoss();
Result = 0;
goto Exit;
}
DestHandle = SpCreateFile1A(DestFileA);
ASSERT (DestHandle); // never NULL
}
} else {
DestHandle = SpCreateFile1A(DestFileA);
ASSERT (DestHandle); // never NULL
}
Result = (INT_PTR)DestHandle;
//
// If SpCreateFile1A fails, then enable preservation of
// the backup cabs, but don't fail uninstall.
//
if (Result == -1) {
pRecordDataLoss();
Result = 0;
goto Exit;
}
goto Exit;
case fdintCLOSE_FILE_INFO: // close the file, set relevant info
CabData = (PCAB_DATA)FdiNotification->pv;
if (SpDosDateTimeToFileTime(FdiNotification->date, FdiNotification->time, &LocalFileTime)) {
if (SpLocalFileTimeToFileTime(&LocalFileTime, &FileTime)) {
//
// error here is probably ignorable..
//
SpSetFileTime((HANDLE)FdiNotification->hf, &FileTime, &FileTime, &FileTime);
}
}
SpCabCloseHandle((HANDLE*)(&FdiNotification->hf));
psz1 = FdiNotification->psz1;
RtlInitAnsiString(&psz1String, psz1);
if (SpCabIsFullPath(&psz1String)) {
//
// This is always the case in Win9x uninstall.
//
DestFileA = SpDupString(psz1);
}
else {
DestFileA = SpJoinPathsA(CabData->ExtractPathA.Buffer, psz1);
}
FileAttributes = (FdiNotification->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
if (DestFileA != NULL) {
//
// error here is probably ignorable..
//
SpSetFileAttributesA(DestFileA, FileAttributes);
}
Result = TRUE;
break;
case fdintPARTIAL_FILE: // First file in cabinet is continuation
break;
case fdintENUMERATE: // Enumeration status
break;
case fdintNEXT_CABINET: // File continued to next cabinet
break;
}
Exit:
if (DestFileA != NULL){
SpMemFree(DestFileA);
}
SpFreeStringW(&DestFileStringW);
KdPrintEx((
DPFLTR_SETUP_ID,
SpNtStatusToDbgPrintLevel(Status),
"SETUP:"__FUNCTION__" exiting Status:0x%08lx Error:%d\n",
SpGetLastNtStatus(), SpGetLastWin32Error()));
return Result;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
CCABHANDLE
SppCabCreateCabinet(
PANSI_STRING CabPathA,
PANSI_STRING CabFileFormatA,
PANSI_STRING CabDiskFormatA,
PUNICODE_STRING CabPathW,
PUNICODE_STRING CabFileFormatW,
PUNICODE_STRING CabDiskFormatW,
IN LONG MaxFileSize
)
/*++
Routine Description:
Creates a cabinet context. Caller may use this context for subsequent calls to
CabAddFile.
Arguments:
CabPathA - Specifies the path where the new cabinet file will be.
CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
Return Value:
a valid CCABHANDLE if successful, NULL otherwise.
--*/
{
PFCI_CAB_HANDLE CabHandle = NULL;
PFCI_CAB_HANDLE CabHandleRet = NULL;
NTSTATUS Status = STATUS_SUCCESS;
if (CabFileFormatA == NULL
&& CabFileFormatW == NULL
) {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
if (MaxFileSize < 0) {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
if (MaxFileSize == 0) {
MaxFileSize = 0x7FFFFFFF;
}
CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof (*CabHandle));
if (CabHandle == NULL) {
Status = STATUS_NO_MEMORY;
goto NtExit;
}
RtlZeroMemory(CabHandle, sizeof(*CabHandle));
#if DBG
KeQuerySystemTime(&CabHandle->StartTime);
#endif
SpMoveStringA(&CabHandle->PathA, CabPathA);
SpMoveStringA(&CabHandle->FileFormatA, CabFileFormatA);
SpMoveStringA(&CabHandle->DiskFormatA, CabDiskFormatA);
SpMoveStringW(&CabHandle->PathW, CabPathW);
SpMoveStringW(&CabHandle->FileFormatW, CabFileFormatW);
SpMoveStringW(&CabHandle->DiskFormatW, CabDiskFormatW);
// fill out the CCAB structure (other than the zeros)
CabHandle->FciCabParams.cb = MaxFileSize;
CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
CabHandle->FciCabParams.iCab = 1;
CabHandle->FciCabParams.iDisk = 1;
if (CabHandle->PathA.Buffer != NULL && CabHandle->PathA.Buffer[0] != 0) {
SpStringCopyNA(CabHandle->FciCabParams.szCabPath, CabHandle->PathA.Buffer, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath) - 2);
SpEnsureTrailingBackSlashA(CabHandle->FciCabParams.szCabPath);
}
if (CabHandle->DiskFormatA.Buffer != NULL && CabHandle->DiskFormatA.Buffer[0] != 0) {
SpFormatStringA(CabHandle->FciCabParams.szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk), CabHandle->DiskFormatA.Buffer, CabHandle->FciCabParams.iDisk);
}
if (CabHandle->FileFormatA.Buffer != NULL && CabHandle->FileFormatA.Buffer[0] != 0) {
SpFormatStringA(CabHandle->FciCabParams.szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab), CabHandle->FileFormatA.Buffer, CabHandle->FciCabParams.iCab);
}
CabHandle->FciHandle = FCICreate(
&CabHandle->FciErrorStruct,
pCabFilePlacedA,
pCabAlloc,
pCabFree,
pCabOpenForWriteA,
pCabRead,
pCabWrite,
pCabClose,
pCabSeek,
pCabDeleteA,
pCabGetTempFileA,
&CabHandle->FciCabParams,
CabHandle
);
if (CabHandle->FciHandle == NULL)
goto Exit;
CabHandleRet = CabHandle;
CabHandle = NULL;
Exit:
if (CabHandle != NULL) {
SpCabFlushAndCloseCabinet(CabHandle);
CabHandle = NULL;
}
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(CabHandleRet),
"SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
return CabHandleRet;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
CCABHANDLE
SpCabCreateCabinetW(
IN PCWSTR CabPathW,
IN PCWSTR CabFileFormatW,
IN PCWSTR CabDiskFormatW,
IN LONG MaxFileSize
)
/*++
Routine Description:
Creates a cabinet context. Caller may use this context for subsequent calls to
CabAddFile.
Arguments:
CabPathW - Specifies the path where the new cabinet file will be.
CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.
CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.
MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
Return Value:
a valid CCABHANDLE if successful, NULL otherwise.
--*/
{
ANSI_STRING CabPathStringA = { 0 };
ANSI_STRING CabFileFormatStringA = { 0 };
ANSI_STRING CabDiskFormatStringA = { 0 };
UNICODE_STRING CabPathStringW = { 0 };
UNICODE_STRING CabFileFormatStringW = { 0 };
UNICODE_STRING CabDiskFormatStringW = { 0 };
CCABHANDLE CabHandle = NULL;
NTSTATUS Status = STATUS_SUCCESS;
KdPrintEx((
DPFLTR_SETUP_ID,
DPFLTR_TRACE_LEVEL,
__FUNCTION__"(%ls, %ls, %ls)\n", CabPathW, CabFileFormatW, CabDiskFormatW
));
Status = SpConvertToNulTerminatedNtStringsW(CabPathW, &CabPathStringA, &CabPathStringW);
if (!NT_SUCCESS(Status))
goto NtExit;
Status = SpConvertToNulTerminatedNtStringsW(CabFileFormatW, &CabFileFormatStringA, &CabFileFormatStringW);
if (!NT_SUCCESS(Status))
goto NtExit;
Status = SpConvertToNulTerminatedNtStringsW(CabDiskFormatW, &CabDiskFormatStringA, &CabDiskFormatStringW);
if (!NT_SUCCESS(Status))
goto NtExit;
CabHandle =
SppCabCreateCabinet(
&CabPathStringA,
&CabFileFormatStringA,
&CabDiskFormatStringA,
&CabPathStringW,
&CabFileFormatStringW,
&CabDiskFormatStringW,
MaxFileSize
);
Exit:
SpFreeStringA(&CabDiskFormatStringA);
SpFreeStringA(&CabFileFormatStringA);
SpFreeStringA(&CabPathStringA);
SpFreeStringW(&CabDiskFormatStringW);
SpFreeStringW(&CabFileFormatStringW);
SpFreeStringW(&CabPathStringW);
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(CabHandle),
"SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
CabHandle, SpGetLastNtStatus(), SpGetLastWin32Error()));
return CabHandle;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
CCABHANDLE
SppCabCreateCabinetEx(
IN PCABGETCABINETNAMESA GetCabinetNamesA,
IN PCABGETCABINETNAMESW GetCabinetNamesW,
IN LONG MaxFileSize
)
/*++
Routine Description:
Creates a cabinet context. Caller may use this context for subsequent calls to
CabAddFile.
Arguments:
GetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
Return Value:
a valid CCABHANDLE if successful, NULL otherwise.
--*/
{
PFCI_CAB_HANDLE CabHandle = NULL;
PFCI_CAB_HANDLE CabHandleRet = NULL;
NTSTATUS Status = STATUS_SUCCESS;
typedef struct FRAME {
WCHAR szDisk[CB_MAX_DISK_NAME];
WCHAR szCab[CB_MAX_CABINET_NAME];
WCHAR szCabPath[CB_MAX_CAB_PATH];
} FRAME, *PFRAME;
PFRAME Frame = NULL;
if (GetCabinetNamesA == NULL && GetCabinetNamesW == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
if (MaxFileSize < 0) {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
if (MaxFileSize == 0) {
MaxFileSize = 0x80000000;
}
CabHandle = (PFCI_CAB_HANDLE)SpMemAlloc(sizeof(*CabHandle));
if (CabHandle == NULL) {
Status = STATUS_NO_MEMORY;
goto NtExit;
}
RtlZeroMemory(CabHandle, sizeof(*CabHandle));
CabHandle->GetCabinetNamesA = GetCabinetNamesA;
CabHandle->GetCabinetNamesW = GetCabinetNamesW;
// fill out the CCAB structure
CabHandle->FciCabParams.cb = MaxFileSize;
CabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
CabHandle->FciCabParams.iCab = 1;
CabHandle->FciCabParams.iDisk = 1;
if (GetCabinetNamesA != NULL) {
if (!GetCabinetNamesA(
CabHandle->FciCabParams.szCabPath,
RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath),
CabHandle->FciCabParams.szCab,
RTL_NUMBER_OF(CabHandle->FciCabParams.szCab),
CabHandle->FciCabParams.szDisk,
RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk),
CabHandle->FciCabParams.iCab,
&CabHandle->FciCabParams.iDisk
)) {
goto Exit;
}
}
else if (GetCabinetNamesW != NULL) {
Frame = (PFRAME)SpMemAlloc(sizeof(*Frame));
if (Frame == NULL) {
Status = STATUS_NO_MEMORY;
goto NtExit;
}
if (!GetCabinetNamesW(
Frame->szCabPath,
RTL_NUMBER_OF(Frame->szCabPath),
Frame->szCab,
RTL_NUMBER_OF(Frame->szCab),
Frame->szDisk,
RTL_NUMBER_OF(Frame->szDisk),
CabHandle->FciCabParams.iCab,
&CabHandle->FciCabParams.iDisk
)) {
goto Exit;
Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCabPath, Frame->szCabPath, RTL_NUMBER_OF(CabHandle->FciCabParams.szCabPath));
if (!NT_SUCCESS(Status))
goto NtExit;
Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szCab, Frame->szCab, RTL_NUMBER_OF(CabHandle->FciCabParams.szCab));
if (!NT_SUCCESS(Status))
goto NtExit;
Status = SpKnownSizeUnicodeToDbcsN(CabHandle->FciCabParams.szDisk, Frame->szDisk, RTL_NUMBER_OF(CabHandle->FciCabParams.szDisk));
if (!NT_SUCCESS(Status))
goto NtExit;
}
}
CabHandle->FciHandle = FCICreate(
&CabHandle->FciErrorStruct,
pCabFilePlacedA,
pCabAlloc,
pCabFree,
pCabOpenForWriteA,
pCabRead,
pCabWrite,
pCabClose,
pCabSeek,
pCabDeleteA,
pCabGetTempFileA,
&CabHandle->FciCabParams,
CabHandle
);
if (CabHandle->FciHandle == NULL)
goto Exit;
CabHandleRet = CabHandle;
CabHandle = NULL;
Exit:
if (CabHandle != NULL) {
SpCabFlushAndCloseCabinet(CabHandle);
goto Exit;
}
if (Frame != NULL)
SpMemFree(Frame);
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(CabHandleRet),
"SETUP:"__FUNCTION__" exiting Handle:%p Status:0x%08lx Error:%d\n",
CabHandleRet, SpGetLastNtStatus(), SpGetLastWin32Error()));
return (CCABHANDLE)CabHandleRet;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
CCABHANDLE
SpCabCreateCabinetExW(
IN PCABGETCABINETNAMESW GetCabinetNamesW,
IN LONG MaxFileSize
)
/*++
Routine Description:
Creates a cabinet context. Caller may use this context for subsequent calls to
CabAddFile.
Arguments:
CabGetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.
MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB
Return Value:
a valid CCABHANDLE if successful, NULL otherwise.
--*/
{
const PFCI_CAB_HANDLE CabHandle = SppCabCreateCabinetEx(NULL, GetCabinetNamesW, MaxFileSize);
return CabHandle;
}
TCOMP
SpCabGetCompressionTypeForFile(
PFCI_CAB_HANDLE CabHandle,
IN PCWSTR FileName
)
/*++
don't compress small files
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
TCOMP CompressionType = tcompTYPE_MSZIP;
ULONG FileSize = 0;
ULONG SmallFileSize = 4096;
HANDLE FileHandle = NULL;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes = RTL_INIT_OBJECT_ATTRIBUTES(&UnicodeString, OBJ_CASE_INSENSITIVE);
IO_STATUS_BLOCK IoStatusBlock;
RtlInitUnicodeString(&UnicodeString, FileName);
if (CabHandle != NULL) {
if (CabHandle->SmallFileCompressionType == CabHandle->CompressionType)
return CabHandle->CompressionType;
if (CabHandle->SmallFileSize != 0)
SmallFileSize = CabHandle->SmallFileSize;
CompressionType = CabHandle->CompressionType;
}
Status =
ZwOpenFile(
&FileHandle,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
0,
FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
goto Exit;
Status = SpGetFileSize(FileHandle, &FileSize);
if (!NT_SUCCESS(Status))
goto Exit;
if (FileSize < SmallFileSize)
Status = tcompTYPE_NONE;
Exit:
SpCabCloseHandle(&FileHandle);
return CompressionType;
}
NTSTATUS
SpCabAddFileToCabinetW(
IN CCABHANDLE Handle,
IN PCWSTR FileNameW,
IN PCWSTR StoredNameW
)
/*++
Routine Description:
Compresses and adds a file to a cabinet context.
Arguments:
CabHandle - Specifies cabinet context.
FileNameW - Specifies the file to be added.
StoredNameW - Specifies the name to be stored in the cabinet file.
FileCount - Specifies a count of files, receives the updated count
when cabinet files are created
FileSize - Specifies the number of bytes used by the file, receives
the updated size
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
ANSI_STRING FileNameA = { 0 };
ANSI_STRING StoredNameA = { 0 };
BOOL FreeStoredNameA = FALSE;
PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE)Handle;
NTSTATUS status = STATUS_SUCCESS;
BOOL b;
if (CabHandle == NULL) {
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (CabHandle->FciHandle == NULL) {
status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (StoredNameW == NULL) {
StoredNameW = FileNameW;
}
if (FileNameW == NULL) {
FileNameW = StoredNameW;
}
status = SpConvertToNulTerminatedNtStringsW(FileNameW, &FileNameA, NULL);
if (!NT_SUCCESS(status)) {
goto NtExit;
}
if (FileNameW != StoredNameW) {
FreeStoredNameA = FALSE;
status = SpConvertToNulTerminatedNtStringsW(StoredNameW, &StoredNameA, NULL);
if (!NT_SUCCESS(status)) {
goto NtExit;
}
} else {
StoredNameA = FileNameA;
}
b = FCIAddFile(
CabHandle->FciHandle,
FileNameA.Buffer,
StoredNameA.Buffer,
FALSE,
pCabGetNextCabinet,
pCabStatus,
pCabGetOpenInfoA,
CabHandle->CompressionType
);
if (!b) {
KdPrintEx((
DPFLTR_SETUP_ID,
SpBoolToDbgPrintLevel(b),
"SETUP:"__FUNCTION__" FCIAddFile failed.\n"
));
status = SpGetLastNtStatus();
goto Exit;
}
ASSERT (NT_SUCCESS (status));
Exit:
KdPrintEx((
DPFLTR_SETUP_ID,
SpBoolToDbgPrintLevel(NT_SUCCESS (status)),
"SETUP:"__FUNCTION__" exiting Success:%s Status:0x%08lx Error:%d\n",
SpBoolToStringA(NT_SUCCESS (status)),
SpGetLastNtStatus(),
SpGetLastWin32Error()
));
SpFreeStringA(&FileNameA);
if (FreeStoredNameA) {
SpFreeStringA(&StoredNameA);
}
return status;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(status);
goto Exit;
}
BOOL
SpCabFlushAndCloseCabinetEx(
IN CCABHANDLE Handle,
OUT PUINT FileCount, OPTIONAL
OUT PLONGLONG FileSize, OPTIONAL
OUT PUINT CabFileCount, OPTIONAL
OUT PLONGLONG CabFileSize OPTIONAL
)
/*++
Routine Description:
Completes a cabinet file and closes its context.
Arguments:
CabHandle - Specifies cabinet context.
FileCount - Receives the number of files added to the cab
FileSize - Receives the size of all files before compression
CabFileCount - Receives the number of cabinet files created
CabFileSize - Receives the size of all cabinet files
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
PFCI_CAB_HANDLE CabHandle = (PFCI_CAB_HANDLE) Handle;
BOOL Result = FALSE;
if (CabHandle == NULL) {
goto Exit;
}
if (CabHandle->FciHandle != NULL) {
if (!FCIFlushCabinet(
CabHandle->FciHandle,
FALSE,
pCabGetNextCabinet,
pCabStatus
))
goto Exit;
}
#if DBG
{
TIME_FIELDS TimeFields;
LARGE_INTEGER EndTime = { 0 };
LARGE_INTEGER Duration = { 0 };
KeQuerySystemTime(&EndTime);
Duration.QuadPart = EndTime.QuadPart - CabHandle->StartTime.QuadPart;
RtlTimeToElapsedTimeFields(&Duration, &TimeFields);
KdPrint((
"SETUP: Cab %wZ\\%wZ %lu files compressed from %I64u to %I64u in %d minutes %d seconds\n",
&CabHandle->PathW,
&CabHandle->FileFormatW,
(ULONG)CabHandle->FileCount,
(ULONGLONG)CabHandle->FileSize,
(ULONGLONG)CabHandle->CompressedSize,
(int)TimeFields.Minute,
(int)TimeFields.Second
));
}
#endif
SpFreeStringA(&CabHandle->PathA);
SpFreeStringA(&CabHandle->FileFormatA);
SpFreeStringA(&CabHandle->DiskFormatA);
SpFreeStringW(&CabHandle->PathW);
SpFreeStringW(&CabHandle->FileFormatW);
SpFreeStringW(&CabHandle->DiskFormatW);
if (CabHandle->FciHandle != NULL) {
Result = FCIDestroy(CabHandle->FciHandle);
CabHandle->FciHandle = NULL;
}
if (FileCount)
*FileCount = CabHandle->FileCount;
if (FileSize)
*FileSize = CabHandle->FileSize;
if (CabFileCount)
*CabFileCount = CabHandle->CabCount;
if (CabFileSize)
*CabFileSize = CabHandle->CompressedSize;
Result = TRUE;
Exit:
return Result;
}
OCABHANDLE
SppCabOpenCabinet(
IN PCSTR FileNameA,
IN PCWSTR FileNameW
)
/*++
Routine Description:
Creates a cabinet context for an existent cabinet file.
Arguments:
FileName - Specifies cabinet file name.
Return Value:
a valid OCABHANDLE if successful, NULL otherwise.
--*/
{
PFDI_CAB_HANDLE CabHandleRet = NULL;
PFDI_CAB_HANDLE CabHandle = NULL;
PSTR FilePtrA = NULL;
PWSTR FilePtrW = NULL;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
ANSI_STRING LocalFileNameA = { 0 };
UNICODE_STRING LocalFileNameW = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
CabHandle = (PFDI_CAB_HANDLE) SpMemAlloc(sizeof(*CabHandle));
if (CabHandle == NULL) {
Status = STATUS_NO_MEMORY;
goto NtExit;
}
RtlZeroMemory(CabHandle, sizeof (FDI_CAB_HANDLEW));
CabHandle->FdiHandle = FDICreate(
pCabAlloc,
pCabFree,
pCabOpenForReadA,
pCabRead1,
pCabWrite1,
pCabClose1,
pCabSeek1,
cpuUNKNOWN, // ignored
&CabHandle->FdiErrorStruct
);
if (CabHandle->FdiHandle == NULL) {
goto Exit;
}
if (FileNameW != NULL) {
Status = SpConvertToNulTerminatedNtStringsW(FileNameW, &LocalFileNameA, &LocalFileNameW);
if (!NT_SUCCESS(Status))
goto NtExit;
}
else if (FileNameA != NULL) {
Status = SpConvertToNulTerminatedNtStringsA(FileNameA, &LocalFileNameA, &LocalFileNameW);
if (!NT_SUCCESS(Status))
goto NtExit;
}
else {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
FileHandle = SpOpenFile1W(LocalFileNameW.Buffer);
ASSERT (FileHandle); // never NULL
if (FileHandle == INVALID_HANDLE_VALUE)
goto Exit;
if (!FDIIsCabinet(CabHandle->FdiHandle, (INT_PTR)FileHandle, &CabHandle->FdiCabinetInfo))
goto Exit;
SpCabCloseHandle(&FileHandle);
FilePtrW = (PWSTR)SpGetFileNameFromPathW(LocalFileNameW.Buffer);
if (FilePtrW == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto NtExit;
}
// ok if error, just empty string, no gauge
RtlInitAnsiString(&g_CabFileFullPath, SpDupStringA(LocalFileNameA.Buffer));
SpMoveStringA(&CabHandle->PathA, &LocalFileNameA);
SpMoveStringW(&CabHandle->PathW, &LocalFileNameW);
*FilePtrW = 0;
Status = SpConvertToNulTerminatedNtStringsW(FilePtrW, &CabHandle->FileA, &CabHandle->FileW);
if (!NT_SUCCESS(Status))
goto NtExit;
CabHandleRet = CabHandle;
CabHandle = NULL;
Exit:
ASSERT(g_SpCabFdiHandle == NULL);
if (CabHandleRet != NULL) {
g_SpCabFdiHandle = CabHandleRet;
}
SpCabCloseHandle(&FileHandle);
SpFreeStringA(&LocalFileNameA);
SpFreeStringW(&LocalFileNameW);
if (CabHandle != NULL)
SpCabCloseCabinet(CabHandle);
return (OCABHANDLE)CabHandleRet;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
OCABHANDLE
SpCabOpenCabinetW(
IN PCWSTR FileName
)
/*++
Routine Description:
Creates a cabinet context for an existent cabinet file.
Arguments:
FileName - Specifies cabinet file name.
Return Value:
a valid OCABHANDLE if successful, NULL otherwise.
--*/
{
OCABHANDLE Handle;
KdPrint((__FUNCTION__":%ls\n", FileName));
Handle = SppCabOpenCabinet(NULL, FileName);
return Handle;
}
BOOL
SppCabExtractAllFilesEx(
IN OCABHANDLE Handle,
PCSTR ExtractPathA,
PCWSTR ExtractPathW,
PCABNOTIFICATIONA NotificationA OPTIONAL,
PCABNOTIFICATIONW NotificationW OPTIONAL
)
/*++
Routine Description:
Extracts all files from a cabinet file.
Arguments:
CabHandle - Specifies cabinet context.
ExtractPath - Specifies the path to extract the files to.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
CAB_DATA CabData = { 0 };
BOOL Success = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
if (CabHandle == NULL)
goto Exit;
if (CabHandle->FdiHandle == NULL)
goto Exit;
if (ExtractPathW != NULL) {
Status = SpConvertToNulTerminatedNtStringsW(ExtractPathW, &CabData.ExtractPathA, &CabData.ExtractPathW);
if (!NT_SUCCESS(Status))
goto NtExit;
}
else if (ExtractPathA != NULL) {
Status = SpConvertToNulTerminatedNtStringsA(ExtractPathA, &CabData.ExtractPathA, &CabData.ExtractPathW);
if (!NT_SUCCESS(Status))
goto NtExit;
}
CabData.NotificationA = NotificationA;
CabData.NotificationW = NotificationW;
if (!FDICopy(
CabHandle->FdiHandle,
CabHandle->FileA.Buffer,
CabHandle->PathA.Buffer,
0,
pCabNotification,
NULL,
&CabData
))
goto Exit;
Success = TRUE;
Exit:
return Success;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
BOOL
SpCabExtractAllFilesExW(
IN OCABHANDLE Handle,
IN PCWSTR ExtractPathW,
IN PCABNOTIFICATIONW NotificationW OPTIONAL
)
/*++
Routine Description:
Extracts all files from a cabinet file.
Arguments:
CabHandle - Specifies cabinet context.
ExtractPath - Specifies the path to extract the files to.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
const BOOL Success = SppCabExtractAllFilesEx(Handle, NULL, ExtractPathW, NULL, NotificationW);
return Success;
}
BOOL
SpCabCloseCabinet(
IN OCABHANDLE Handle
)
/*++
Routine Description:
Closes a cabinet file context.
Arguments:
CabHandle - Specifies cabinet context.
Return Value:
TRUE if successful, FALSE otherwise.
Note this function is also used internally to tear down a partially constructed
cab handle, as happens if we fail building it up.
--*/
{
PFDI_CAB_HANDLE CabHandle = (PFDI_CAB_HANDLE)Handle;
BOOL Success = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
if (CabHandle == NULL) {
Success = TRUE;
goto Exit;
}
if (CabHandle->FdiHandle != NULL) {
if (!FDIDestroy(CabHandle->FdiHandle))
goto Exit;
}
SpFreeStringA(&CabHandle->PathA);
SpFreeStringA(&CabHandle->FileA);
SpFreeStringW(&CabHandle->PathW);
SpFreeStringW(&CabHandle->FileW);
if (CabHandle == g_SpCabFdiHandle) {
SpCabCleanupCabGlobals();
}
SpMemFree(CabHandle);
Success = TRUE;
Exit:
return Success;
}
INT
DIAMONDAPI
pCabFilePlacedA(
IN PCCAB FciCabParams,
IN PSTR FileName,
IN LONG FileSize,
IN BOOL Continuation,
IN PVOID Context
)
/*++
Routine Description:
Callback for cabinet compression/decompression. For more information see fci.h/fdi.h
--*/
{
PFCI_CAB_HANDLE CabHandle = NULL;
CabHandle = (PFCI_CAB_HANDLE) Context;
if (CabHandle == NULL) {
return 0;
}
CabHandle->FileCount++;
CabHandle->FileSize += FileSize;
return 0;
}
BOOL
SpCabFlushAndCloseCabinet(
IN CCABHANDLE CabHandle
)
{
return SpCabFlushAndCloseCabinetEx(CabHandle,NULL,NULL,NULL,NULL);
}
VOID
SpFreeStringA(
PANSI_STRING String
)
{
SpFreeStringW((PUNICODE_STRING)String);
}
VOID
SpFreeStringW(
PUNICODE_STRING String
)
{
if (String != NULL) {
if (String->Buffer != NULL) {
SpMemFree(String->Buffer);
}
RtlZeroMemory(String, sizeof(*String));
}
}
NTSTATUS
SpConvertToNulTerminatedNtStringsA(
PCSTR Ansi,
PANSI_STRING OutAnsiString OPTIONAL,
PUNICODE_STRING OutUnicodeString OPTIONAL
)
/*++
Unlike assorted Rtl functions, we are sure that every string is nul terminated.
We also consistently allocate our strings.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Length = 0;
if (Ansi != NULL)
Length = strlen(Ansi);
if (OutAnsiString != NULL)
RtlZeroMemory(OutAnsiString, sizeof(*OutAnsiString));
if (OutUnicodeString != NULL)
RtlZeroMemory(OutUnicodeString, sizeof(*OutUnicodeString));
if (OutAnsiString != NULL) {
if (!(OutAnsiString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutAnsiString->Buffer[0])))) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
RtlCopyMemory(OutAnsiString->Buffer, Ansi, Length * sizeof(OutAnsiString->Buffer[0]));
OutAnsiString->Buffer[Length] = 0;
OutAnsiString->Length = (USHORT)Length * sizeof(OutAnsiString->Buffer[0]);
OutAnsiString->MaximumLength = OutAnsiString->Length + sizeof(OutAnsiString->Buffer[0]);
}
if (OutUnicodeString != NULL) {
ANSI_STRING LocalAnsiString = { 0 };
RtlInitAnsiString(&LocalAnsiString, Ansi);
LocalAnsiString.Length = LocalAnsiString.MaximumLength; // include terminal nul
Status = SpAnsiStringToUnicodeString(OutUnicodeString, &LocalAnsiString, TRUE);
if (!NT_SUCCESS(Status)) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
OutUnicodeString->Length -= sizeof(OutUnicodeString->Buffer[0]);
}
Status = STATUS_SUCCESS;
Exit:
if (!NT_SUCCESS(Status)) {
KdPrintEx((
DPFLTR_SETUP_ID,
SpNtStatusToDbgPrintLevel(Status),
"SETUP:"__FUNCTION__" 0x%08lx\n",
Status
));
SpFreeStringA(OutAnsiString);
SpFreeStringW(OutUnicodeString);
}
return Status;
}
NTSTATUS
SpConvertToNulTerminatedNtStringsW(
PCWSTR Unicode,
PANSI_STRING OutAnsiString OPTIONAL,
PUNICODE_STRING OutUnicodeString OPTIONAL
)
/*++
Unlike assorted Rtl functions, we are sure that every string is nul terminated.
We also consistently allocate our strings.
--*/
{
ULONG Length = 0;
NTSTATUS Status = STATUS_SUCCESS;
if (Unicode != NULL)
Length = wcslen(Unicode);
if (OutUnicodeString != NULL) {
if (!(OutUnicodeString->Buffer = SpMemAlloc((Length + 1) * sizeof(OutUnicodeString->Buffer[0])))) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
RtlCopyMemory(OutUnicodeString->Buffer, Unicode, Length * sizeof(OutUnicodeString->Buffer[0]));
OutUnicodeString->Buffer[Length] = 0;
OutUnicodeString->Length = (USHORT)Length * sizeof(OutUnicodeString->Buffer[0]);
OutUnicodeString->MaximumLength = OutUnicodeString->Length + sizeof(OutUnicodeString->Buffer[0]);
}
if (OutAnsiString != NULL) {
UNICODE_STRING LocalUnicodeString = { 0 };
RtlInitUnicodeString(&LocalUnicodeString, Unicode);
LocalUnicodeString.Length = LocalUnicodeString.MaximumLength; // include terminal nul
Status = SpUnicodeStringToAnsiString(OutAnsiString, &LocalUnicodeString, TRUE);
if (!NT_SUCCESS(Status)) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
OutAnsiString->Length -= sizeof(OutAnsiString->Buffer[0]);
}
Status = STATUS_SUCCESS;
Exit:
if (!NT_SUCCESS(Status)) {
KdPrintEx((
DPFLTR_SETUP_ID,
SpNtStatusToDbgPrintLevel(Status),
"SETUP:"__FUNCTION__" 0x%08lx\n",
Status
));
SpFreeStringA(OutAnsiString);
SpFreeStringW(OutUnicodeString);
}
return Status;
}
VOID
SpStringCopyNA(
PSTR Dest,
PCSTR Source,
SIZE_T Max
)
/*++
Max is a number of chars, as in RTL_NUMBER_OF.
The result is always nul terminated.
--*/
{
SIZE_T Length = strlen(Source);
if (Length >= Max) {
KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
Length = Max - 1;
}
RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
Dest[Length] = 0;
}
VOID
SpStringCopyNW(
PWSTR Dest,
PCWSTR Source,
SIZE_T Max
)
/*++
Max is a number of chars, as in RTL_NUMBER_OF.
The result is always nul terminated.
--*/
{
SIZE_T Length = wcslen(Source);
if (Length >= Max) {
KdPrint(("SETUP:String truncated in "__FUNCTION__".\n"));
Length = Max - 1;
}
RtlCopyMemory(Dest, Source, Length * sizeof(Dest[0]));
Dest[Length] = 0;
}
VOID
SpMoveStringA(
PANSI_STRING Dest,
PANSI_STRING Source
)
{
SpMoveStringW((PUNICODE_STRING)Dest, (PUNICODE_STRING)Source);
}
VOID
SpMoveStringW(
PUNICODE_STRING Dest,
PUNICODE_STRING Source
)
{
if (Source != NULL) {
*Dest = *Source;
RtlZeroMemory(Source, sizeof(*Source));
}
}
NTSTATUS
SpCreateDirectoryForFileA(
IN PCSTR FilePathA,
IN ULONG CreateFlags
)
{
UNICODE_STRING PathW = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
PWSTR LastBackSlash = NULL;
PWSTR BackSlash = NULL;
Status = SpConvertToNulTerminatedNtStringsA(FilePathA, NULL, &PathW);
if (!NT_SUCCESS(Status))
goto Exit;
//
// \device\harddiskn\partitionm\dirs..\file
// or \device\harddiskn\partitionm\file
// calculate \device\hardiskn\partitionm part
//
BackSlash = wcschr(PathW.Buffer + 1, '\\');
if (BackSlash != NULL)
BackSlash = wcschr(BackSlash + 1, '\\');
if (BackSlash != NULL)
BackSlash = wcschr(BackSlash + 1, '\\');
if (BackSlash == NULL) {
Status = STATUS_INVALID_PARAMETER;
KdPrintEx((
DPFLTR_SETUP_ID,
SpNtStatusToDbgPrintLevel(Status),
"SETUP:"__FUNCTION__"(%ls) less than expected number of slashes, expected \\device\\harddiskn\\partitionm\\...\n",
FilePathA
));
goto Exit;
}
*BackSlash = 0;
LastBackSlash = wcsrchr(BackSlash + 1, '\\');
if (LastBackSlash == NULL) {
//
// the file is at the root of a drive, no directory to create, just
// return success
//
Status = STATUS_SUCCESS;
goto Exit;
}
*LastBackSlash = 0;
if (!SpCreateDirectory (PathW.Buffer, NULL, BackSlash + 1, 0, CreateFlags)) {
Status = STATUS_UNSUCCESSFUL;
goto Exit;
}
Status = STATUS_SUCCESS;
Exit:
SpFreeStringW(&PathW);
return Status;
}
NTSTATUS
SpUnicodeStringToAnsiString(
PANSI_STRING DestinationStringA,
PCUNICODE_STRING SourceStringW,
BOOL Allocate
)
/*
This is like RtlUnicodeStringToAnsiString, but it is "setup heap correct".
The result is freed with SpMemFree instead of RtlFreeAnsiString.
I know this is inefficient.
*/
{
NTSTATUS Status = STATUS_SUCCESS;
ANSI_STRING RtlMemDestinationStringA = { 0 };
if (!Allocate) {
Status = RtlUnicodeStringToAnsiString(DestinationStringA, (PUNICODE_STRING)SourceStringW, FALSE);
goto Exit;
}
Status = RtlUnicodeStringToAnsiString(&RtlMemDestinationStringA, (PUNICODE_STRING)SourceStringW, TRUE);
if (!NT_SUCCESS(Status))
goto Exit;
//
// Don't use SpDupString, we might not have a terminal nul (but usually does).
//
DestinationStringA->Buffer = SpMemAlloc(RtlMemDestinationStringA.MaximumLength);
if (DestinationStringA->Buffer == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
DestinationStringA->MaximumLength = RtlMemDestinationStringA.MaximumLength;
DestinationStringA->Length = RtlMemDestinationStringA.Length;
RtlCopyMemory(DestinationStringA->Buffer, RtlMemDestinationStringA.Buffer, DestinationStringA->Length);
if (DestinationStringA->MaximumLength >= (DestinationStringA->Length + sizeof(DestinationStringA->Buffer[0])))
DestinationStringA->Buffer[DestinationStringA->Length / sizeof(DestinationStringA->Buffer[0])] = 0;
Status = STATUS_SUCCESS;
Exit:
RtlFreeAnsiString(&RtlMemDestinationStringA);
return Status;
}
NTSTATUS
SpAnsiStringToUnicodeString(
PUNICODE_STRING DestinationStringW,
PCANSI_STRING SourceStringA,
BOOL Allocate
)
/*
This is like RtlAnsiStringToUnicodeString, but it is "setup heap correct".
The result is freed with SpMemFree instead of RtlFreeUnicodeString.
I know this is inefficient.
*/
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING RtlMemDestinationStringW = { 0 };
if (!Allocate) {
Status = RtlAnsiStringToUnicodeString(DestinationStringW, (PANSI_STRING)SourceStringA, FALSE);
goto Exit;
}
Status = RtlAnsiStringToUnicodeString(&RtlMemDestinationStringW, (PANSI_STRING)SourceStringA, TRUE);
if (!NT_SUCCESS(Status))
goto Exit;
//
// Don't use SpDupString, we might not have a terminal nul (but usually does).
//
DestinationStringW->Buffer = SpMemAlloc(RtlMemDestinationStringW.MaximumLength);
if (DestinationStringW->Buffer == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
DestinationStringW->MaximumLength = RtlMemDestinationStringW.MaximumLength;
DestinationStringW->Length = RtlMemDestinationStringW.Length;
RtlCopyMemory(DestinationStringW->Buffer, RtlMemDestinationStringW.Buffer, DestinationStringW->Length);
if (DestinationStringW->MaximumLength >= (DestinationStringW->Length + sizeof(DestinationStringW->Buffer[0])))
DestinationStringW->Buffer[DestinationStringW->Length / sizeof(DestinationStringW->Buffer[0])] = 0;
Status = STATUS_SUCCESS;
Exit:
RtlFreeUnicodeString(&RtlMemDestinationStringW);
return Status;
}
NTSTATUS
SpKnownSizeUnicodeToDbcsN(
OUT PSTR Ansi,
IN PCWSTR Unicode,
IN SIZE_T AnsiSize
)
/*++
based on windows\winstate\cobra\utils\...
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ANSI_STRING AnsiString;
UNICODE_STRING UnicodeString;
AnsiString.Buffer = Ansi;
AnsiString.Length = 0;
AnsiString.MaximumLength = (USHORT)AnsiSize;
RtlInitUnicodeString(&UnicodeString, Unicode);
UnicodeString.Length = UnicodeString.MaximumLength; // include terminal nul
Status = SpUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
if (!NT_SUCCESS(Status))
goto Exit;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
VOID
SpEnsureTrailingBackSlashA(
PSTR Path
)
/*++
based on windows\winstate\cobra\utils\...
--*/
{
if (*Path == 0 || *((Path += strlen(Path)) - 1) != '\\') {
*Path = '\\';
*(Path + 1) = 0;
}
}
PCWSTR
SpGetFileNameFromPathW(
IN PCWSTR PathSpec
)
/*++
based on windows\winstate\cobra\utils\...
--*/
{
PCWSTR p;
p = wcsrchr(PathSpec, L'\\');
if (p) {
p++;
} else {
p = PathSpec;
}
return p;
}
HANDLE
SpCreateFile1A(
IN PCSTR FileName
)
/*++
based on windows\winstate\cobra\utils\...
--*/
{
HANDLE Handle;
DWORD orgAttributes;
WIN32_FILE_ATTRIBUTE_DATA fileAttributeData = { 0 };
//
// Reset the file attributes, then do a CREATE_ALWAYS. FileName is an NT path.
//
// We do this because some of the files are replacing have had their
// system|hidden attributes changed, and you can get access denied if you
// try to replace these files with mismatching attributes.
//
if (!SpGetFileAttributesExA (FileName, GetFileExInfoStandard, &fileAttributeData)) {
orgAttributes = FILE_ATTRIBUTE_NORMAL;
} else {
orgAttributes = fileAttributeData.dwFileAttributes;
}
SpSetFileAttributesA (FileName, FILE_ATTRIBUTE_NORMAL);
Handle = SpWin32CreateFileA(
FileName,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
ASSERT (Handle); // never NULL
if (Handle == INVALID_HANDLE_VALUE) {
SpSetFileAttributesA (FileName, orgAttributes);
}
return Handle;
}
PSTR
SpJoinPathsA(
PCSTR a,
PCSTR b
)
/*++
based on windows\winstate\cobra\utils\...
--*/
{
// find code elsewhere in setup that does this already..
PSTR Result = NULL;
SIZE_T alen = 0;
SIZE_T blen = 0;
if (a == NULL)
goto Exit;
if (b == NULL)
goto Exit;
alen = strlen(a);
blen = strlen(b);
Result = SpMemAlloc((alen + blen + 2) * sizeof(*Result));
if (Result == NULL) {
SpSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
goto Exit;
}
if (alen != 0) {
strcpy(Result, a);
if (a[alen - 1] != '\\')
strcat(Result, "\\");
}
strcat(Result, b);
Exit:
KdPrintEx((DPFLTR_SETUP_ID, SpPointerToDbgPrintLevel(Result), "SETUP:"__FUNCTION__" exiting\n"));
return Result;
}
HANDLE
SpOpenFile1A(
IN PCSTR Ansi
)
/*++
based on windows\winstate\cobra\utils\main\basefile.c
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOL Success = FALSE;
ANSI_STRING AnsiString = { 0 };
UNICODE_STRING UnicodeString = { 0 };
HANDLE Handle = INVALID_HANDLE_VALUE;
RtlInitAnsiString(&AnsiString, Ansi);
AnsiString.Length = AnsiString.MaximumLength; // include terminal nul
if (!NT_SUCCESS(Status = SpAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE)))
goto NtExit;
Handle = SpOpenFile1W(UnicodeString.Buffer);
ASSERT (Handle); // never NULL
if (Handle == INVALID_HANDLE_VALUE)
goto Exit;
Exit:
SpFreeStringW(&UnicodeString);
KdPrintEx((
DPFLTR_SETUP_ID,
SpHandleToDbgPrintLevel(Handle),
"SETUP:"__FUNCTION__"(%s) exiting %p\n", Ansi, Handle
));
return Handle;
NtExit:
SpSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
goto Exit;
}
HANDLE
SpOpenFile1W(
IN PCWSTR FileName
)
/*++
based on windows\winstate\cobra\utils\main\basefile.c
--*/
{
HANDLE Handle;
Handle = SpWin32CreateFileW(
FileName,
GENERIC_READ|GENERIC_WRITE,
0, // no share
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
ASSERT (Handle); // never NULL
return Handle;
}