2687 lines
68 KiB
C
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;
|
|
}
|