You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1309 lines
36 KiB
1309 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sprestrt.c
|
|
|
|
Abstract:
|
|
|
|
This program is used to help make GUI Setup restartable,
|
|
if setup was started in restartable mode.
|
|
|
|
Text mode setup will create a system hive containing the value
|
|
|
|
HKLM\System\Setup:RestartSpSetup = REG_DWORD FALSE
|
|
|
|
and a system.sav with RestartSpSetup set to TRUE. In both hives
|
|
the session manager key will be written such that this program
|
|
runs at autochk time.
|
|
|
|
When this program starts, it checks the RestartSpSetup flag.
|
|
If FALSE, then this is the first boot into GUI Setup, and we change it
|
|
to TRUE and we're done here. If TRUE, then GUI setup needs to be
|
|
restarted, and we clean out the config directory, copying *.sav to *.
|
|
and erase everything else in there. System.sav has RestartSpSetup = TRUE,
|
|
so GUI setup will be restarted over and over again until it succeeds.
|
|
|
|
At the end of GUI Setup, sprestrt.exe is removed from the list of
|
|
autochk programs and RestartSpSetup is set to FALSE.
|
|
|
|
The boot loader looks at RestartSpSetup to see whether it needs to unload
|
|
system and load system.sav instead. On the first boot into gui setup,
|
|
we don't want to do this but on subsequent boots we do. The logic above
|
|
makes this work correctly.
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) Feb 1996
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include "msg.h"
|
|
#include "psp.h"
|
|
|
|
//
|
|
// Define result codes.
|
|
//
|
|
#define SUCCESS 0
|
|
#define FAILURE 1
|
|
|
|
#define SIZEOFARRAY(a) (sizeof(a)/sizeof(a[0]))
|
|
#define BACKUP_EXTENSION L".sav"
|
|
#define BACKUP_EXTENSION_LEN 4
|
|
#define SPS_EXTENSION L".sps"
|
|
#define SPS_EXTENSION_LEN 4
|
|
|
|
PCWSTR g_RestartHiveNames[] = {
|
|
L"default",
|
|
L"security",
|
|
L"software",
|
|
L"system",
|
|
};
|
|
|
|
//
|
|
// Define helper macro to deal with subtleties of NT-level programming.
|
|
//
|
|
#define INIT_OBJA(Obja,UnicodeString,UnicodeText) \
|
|
\
|
|
RtlInitUnicodeString((UnicodeString),(UnicodeText)); \
|
|
\
|
|
InitializeObjectAttributes( \
|
|
(Obja), \
|
|
(UnicodeString), \
|
|
OBJ_CASE_INSENSITIVE, \
|
|
NULL, \
|
|
NULL \
|
|
)
|
|
//
|
|
// Relevent registry key and values.
|
|
//
|
|
const PCWSTR SetupRegistryKeyName = L"\\Registry\\Machine\\SYSTEM\\Setup";
|
|
const PCWSTR RestartSpSetupValueName = L"RestartSpSetup";
|
|
const PCWSTR ConfigDirectory =L"\\SystemRoot\\System32\\Config";
|
|
const PCWSTR ProgressIndicator = L".";
|
|
|
|
//
|
|
// Copy buffer. What the heck, it doesn't take up any space in the image.
|
|
//
|
|
#define COPYBUF_SIZE 65536
|
|
UCHAR CopyBuffer[COPYBUF_SIZE];
|
|
|
|
//
|
|
// Tristate value, where a boolean just won't do.
|
|
//
|
|
typedef enum {
|
|
xFALSE,
|
|
xTRUE,
|
|
xUNKNOWN
|
|
} TriState;
|
|
|
|
|
|
//
|
|
// Define structure for keeping a linked list of unicode strings.
|
|
//
|
|
typedef struct _COPY_LIST_NODE {
|
|
LONGLONG FileSize;
|
|
UNICODE_STRING UnicodeString;
|
|
struct _COPY_LIST_NODE *Next;
|
|
} COPY_LIST_NODE, *PCOPY_LIST_NODE;
|
|
|
|
//
|
|
// Memory routines
|
|
//
|
|
#define MALLOC(size) RtlAllocateHeap(RtlProcessHeap(),0,(size))
|
|
#define FREE(block) RtlFreeHeap(RtlProcessHeap(),0,(block))
|
|
|
|
//
|
|
// Forward references
|
|
//
|
|
TriState
|
|
CheckRestartValue(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
SetRestartValue(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
SaveConfigForSpSetupRestart (
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
RestoreConfigForSpSetupRestart(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
RestoreConfigDirectory(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
CopyAFile(
|
|
IN HANDLE DirectoryHandle,
|
|
IN LONGLONG FileSize,
|
|
IN PCWSTR ExistingFile,
|
|
IN PCWSTR NewFile,
|
|
IN BOOLEAN BackupTargetIfExists
|
|
);
|
|
|
|
BOOLEAN
|
|
AreStringsEqual(
|
|
IN PCWSTR String1,
|
|
IN PCWSTR String2
|
|
);
|
|
|
|
BOOLEAN
|
|
Message(
|
|
IN ULONG MessageId,
|
|
IN ULONG DotCount,
|
|
...
|
|
);
|
|
|
|
|
|
BOOLEAN
|
|
pIsRestartHive (
|
|
IN PCWSTR HiveName
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SIZEOFARRAY(g_RestartHiveNames); i++) {
|
|
if (AreStringsEqual (g_RestartHiveNames[i], HiveName)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
__cdecl
|
|
main(
|
|
VOID
|
|
)
|
|
{
|
|
int Result = FAILURE;
|
|
|
|
//
|
|
// Check the status of the RestartSpSetup flag.
|
|
// If not present, do nothing.
|
|
// If FALSE, set to TRUE.
|
|
// If TRUE, clean up config directory.
|
|
//
|
|
|
|
switch(CheckRestartValue()) {
|
|
|
|
case xFALSE:
|
|
|
|
if (SaveConfigForSpSetupRestart () && SetRestartValue()) {
|
|
Result = SUCCESS;
|
|
} else {
|
|
Message(MSG_WARNING_CANT_SET_RESTART,0);
|
|
}
|
|
break;
|
|
|
|
case xTRUE:
|
|
|
|
Result = RestoreConfigForSpSetupRestart();
|
|
Message(MSG_CRLF,0);
|
|
if(!Result) {
|
|
Message(MSG_WARNING_CANT_CLEAN_UP,0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
|
|
|
|
TriState
|
|
CheckRestartValue(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if HKLM\System\Setup:RestartSpSetup is present as a REG_DWORD
|
|
and if so get its value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Value indicating whether the flag is set (xTrue), not set (xFalse),
|
|
or in an unknown state (ie, not present or not REG_DWORD, etc; xUnknown).
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
ULONG DataLength;
|
|
UCHAR Buffer[1024];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
|
|
TriState b;
|
|
|
|
//
|
|
// Assume not present.
|
|
//
|
|
b = xUNKNOWN;
|
|
|
|
//
|
|
// Attempt to open the key.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,SetupRegistryKeyName);
|
|
|
|
Status = NtOpenKey(
|
|
&KeyHandle,
|
|
READ_CONTROL | KEY_QUERY_VALUE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to open %ws (%lx)\n",
|
|
SetupRegistryKeyName,
|
|
Status));
|
|
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// Attempt to get the value of "RestartSpSetup"
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,RestartSpSetupValueName);
|
|
|
|
Status = NtQueryValueKey(
|
|
KeyHandle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
Buffer,
|
|
sizeof(Buffer),
|
|
&DataLength
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to get value of %ws (%lx)\n",
|
|
RestartSpSetupValueName,
|
|
Status));
|
|
|
|
goto c1;
|
|
}
|
|
|
|
//
|
|
// Check for a REG_DWORD value and fetch.
|
|
//
|
|
KeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
|
|
|
|
if((KeyInfo->Type == REG_DWORD) && (KeyInfo->DataLength == sizeof(ULONG))) {
|
|
|
|
b = *(PULONG)KeyInfo->Data ? xTRUE : xFALSE;
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: Restart value is %u\n",
|
|
b));
|
|
|
|
} else {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: %ws is corrupt!\n",
|
|
RestartSpSetupValueName));
|
|
}
|
|
|
|
c1:
|
|
NtClose(KeyHandle);
|
|
c0:
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SetRestartValue(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set HKLM\System\Setup:RestartSpSetup to REG_DWORD 1.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the operation was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
BOOLEAN b;
|
|
ULONG One;
|
|
|
|
//
|
|
// Assume failure.
|
|
//
|
|
b = FALSE;
|
|
|
|
//
|
|
// Attempt to open the key, which must already be present.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,SetupRegistryKeyName);
|
|
|
|
Status = NtOpenKey(
|
|
&KeyHandle,
|
|
READ_CONTROL | KEY_SET_VALUE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to open %ws (%lx)\n",
|
|
SetupRegistryKeyName,
|
|
Status));
|
|
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// Attempt to set the value of "RestartSpSetup" to REG_DWORD 1.
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,RestartSpSetupValueName);
|
|
One = 1;
|
|
|
|
Status = NtSetValueKey(
|
|
KeyHandle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_DWORD,
|
|
&One,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to set value of %ws (%lx)\n",
|
|
RestartSpSetupValueName,
|
|
Status));
|
|
|
|
goto c1;
|
|
}
|
|
|
|
//
|
|
// Success.
|
|
//
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: Value of %ws set to 1\n",
|
|
RestartSpSetupValueName));
|
|
|
|
b = TRUE;
|
|
|
|
c1:
|
|
NtClose(KeyHandle);
|
|
c0:
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SaveConfigForSpSetupRestart (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares the system for restartability
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether we were successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE DirectoryHandle;
|
|
HANDLE FileHandle;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LONGLONG Buffer[2048/8];
|
|
BOOLEAN FirstQuery;
|
|
PFILE_DIRECTORY_INFORMATION FileInfo;
|
|
USHORT LengthChars;
|
|
BOOLEAN b;
|
|
FILE_DISPOSITION_INFORMATION Disposition;
|
|
BOOLEAN AnyErrors;
|
|
PCOPY_LIST_NODE CopyList,CopyNode,NextNode;
|
|
|
|
//
|
|
// Open \SystemRoot\system32\config for list access.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,ConfigDirectory);
|
|
|
|
Status = NtOpenFile(
|
|
&DirectoryHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: unable to open system32\\config for list access (%lx)\n",
|
|
Status));
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
FirstQuery = TRUE;
|
|
FileInfo = (PFILE_DIRECTORY_INFORMATION)Buffer;
|
|
AnyErrors = FALSE;
|
|
CopyList = NULL;
|
|
do {
|
|
|
|
Status = NtQueryDirectoryFile(
|
|
DirectoryHandle,
|
|
NULL, // no event to signal
|
|
NULL, // no apc routine
|
|
NULL, // no apc context
|
|
&IoStatusBlock,
|
|
Buffer,
|
|
sizeof(Buffer)-sizeof(WCHAR), // leave room for terminating nul
|
|
FileDirectoryInformation,
|
|
TRUE, // want single entry
|
|
NULL, // get 'em all
|
|
FirstQuery
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
if(!(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
LengthChars = (USHORT)FileInfo->FileNameLength / sizeof(WCHAR);
|
|
if (FileInfo->FileName[LengthChars]) {
|
|
FileInfo->FileName[LengthChars] = 0;
|
|
}
|
|
if (pIsRestartHive (FileInfo->FileName)) {
|
|
//
|
|
// remember .sav files for later.
|
|
//
|
|
if(CopyNode = MALLOC(sizeof(COPY_LIST_NODE))) {
|
|
if(RtlCreateUnicodeString(&CopyNode->UnicodeString,FileInfo->FileName)) {
|
|
CopyNode->FileSize = FileInfo->EndOfFile.QuadPart;
|
|
CopyNode->Next = CopyList;
|
|
CopyList = CopyNode;
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
FREE(CopyNode);
|
|
}
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
FirstQuery = FALSE;
|
|
}
|
|
} while(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Check for normal loop termination.
|
|
//
|
|
if(Status == STATUS_NO_MORE_FILES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Even if we got errors, try to keep going.
|
|
//
|
|
if(!NT_SUCCESS(Status)) {
|
|
AnyErrors = TRUE;
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Status %lx enumerating files\n",
|
|
Status));
|
|
}
|
|
|
|
//
|
|
// Now run down our list of *.sav and copy to *.
|
|
//
|
|
for(CopyNode=CopyList; CopyNode; CopyNode=NextNode) {
|
|
|
|
//
|
|
// Remember next node, because we're going to free this one.
|
|
//
|
|
NextNode = CopyNode->Next;
|
|
|
|
//
|
|
// Create the target name, which is the same as the source name
|
|
// with the .sav appended.
|
|
//
|
|
LengthChars = wcslen (CopyNode->UnicodeString.Buffer) + 1 + BACKUP_EXTENSION_LEN;
|
|
UnicodeString.Buffer = MALLOC(LengthChars * sizeof(WCHAR));
|
|
if(UnicodeString.Buffer) {
|
|
UnicodeString.Length = UnicodeString.MaximumLength = LengthChars * sizeof(WCHAR);
|
|
|
|
RtlCopyMemory (UnicodeString.Buffer, CopyNode->UnicodeString.Buffer, CopyNode->UnicodeString.Length);
|
|
RtlCopyMemory (UnicodeString.Buffer + CopyNode->UnicodeString.Length, BACKUP_EXTENSION, BACKUP_EXTENSION_LEN * sizeof(WCHAR));
|
|
UnicodeString.Buffer[LengthChars] = 0;
|
|
|
|
Status = CopyAFile(
|
|
DirectoryHandle,
|
|
CopyNode->FileSize,
|
|
CopyNode->UnicodeString.Buffer,
|
|
UnicodeString.Buffer,
|
|
TRUE
|
|
);
|
|
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
AnyErrors = TRUE;
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to copy %ws (%lx)\n",
|
|
CopyNode->UnicodeString.Buffer,Status));
|
|
}
|
|
|
|
FREE(CopyNode->UnicodeString.Buffer);
|
|
FREE(CopyNode);
|
|
}
|
|
|
|
NtClose(DirectoryHandle);
|
|
return((BOOLEAN)!AnyErrors);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RestoreConfigForSpSetupRestart(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares the system for restarting gui mode setup.
|
|
Currently this consists of erasing %sysroot%\system32\config\*,
|
|
except *.sav, then copying *.sav to *.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether we were successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN b;
|
|
|
|
//
|
|
// Display a message indicating that we are rolling back to the
|
|
// start of gui mode setup.
|
|
//
|
|
Message(MSG_CRLF,0);
|
|
Message(MSG_RESTARTING_SETUP,0);
|
|
|
|
b = RestoreConfigDirectory();
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOLEAN
|
|
RestoreConfigDirectory(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Erase %sysroot%\system32\config\*, except *.sav, and userdiff,
|
|
then copy *.sav to *.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether we were successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE DirectoryHandle;
|
|
HANDLE FileHandle;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LONGLONG Buffer[2048/8];
|
|
BOOLEAN FirstQuery;
|
|
PFILE_DIRECTORY_INFORMATION FileInfo;
|
|
ULONG LengthChars;
|
|
BOOLEAN b;
|
|
FILE_DISPOSITION_INFORMATION Disposition;
|
|
BOOLEAN AnyErrors;
|
|
PCOPY_LIST_NODE CopyList,CopyNode,NextNode;
|
|
ULONG DotCount;
|
|
|
|
//
|
|
// Open \SystemRoot\system32\config for list access.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,ConfigDirectory);
|
|
|
|
Status = NtOpenFile(
|
|
&DirectoryHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
DotCount = 0;
|
|
Message(MSG_RESTARTING_SETUP,++DotCount);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: unable to open system32\\config for list access (%lx)\n",
|
|
Status));
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
FirstQuery = TRUE;
|
|
FileInfo = (PFILE_DIRECTORY_INFORMATION)Buffer;
|
|
AnyErrors = FALSE;
|
|
CopyList = NULL;
|
|
do {
|
|
|
|
Status = NtQueryDirectoryFile(
|
|
DirectoryHandle,
|
|
NULL, // no event to signal
|
|
NULL, // no apc routine
|
|
NULL, // no apc context
|
|
&IoStatusBlock,
|
|
Buffer,
|
|
sizeof(Buffer)-sizeof(WCHAR), // leave room for terminating nul
|
|
FileDirectoryInformation,
|
|
TRUE, // want single entry
|
|
NULL, // get 'em all
|
|
FirstQuery
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
if(!(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
LengthChars = FileInfo->FileNameLength / sizeof(WCHAR);
|
|
if (FileInfo->FileName[LengthChars]) {
|
|
FileInfo->FileName[LengthChars] = 0;
|
|
}
|
|
if (LengthChars > BACKUP_EXTENSION_LEN &&
|
|
AreStringsEqual (FileInfo->FileName + LengthChars - BACKUP_EXTENSION_LEN, BACKUP_EXTENSION)
|
|
) {
|
|
FileInfo->FileName[LengthChars - BACKUP_EXTENSION_LEN] = 0;
|
|
b = pIsRestartHive (FileInfo->FileName);
|
|
FileInfo->FileName[LengthChars - BACKUP_EXTENSION_LEN] = L'.';
|
|
if (b) {
|
|
//
|
|
// remember .sav files for later.
|
|
//
|
|
if(CopyNode = MALLOC(sizeof(COPY_LIST_NODE))) {
|
|
if(RtlCreateUnicodeString(&CopyNode->UnicodeString,FileInfo->FileName)) {
|
|
CopyNode->FileSize = FileInfo->EndOfFile.QuadPart;
|
|
CopyNode->Next = CopyList;
|
|
CopyList = CopyNode;
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
FREE(CopyNode);
|
|
}
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FirstQuery = FALSE;
|
|
}
|
|
} while(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Check for normal loop termination.
|
|
//
|
|
if(Status == STATUS_NO_MORE_FILES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Even if we got errors, try to keep going.
|
|
//
|
|
if(!NT_SUCCESS(Status)) {
|
|
AnyErrors = TRUE;
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Status %lx enumerating files\n",
|
|
Status));
|
|
}
|
|
|
|
//
|
|
// Now run down our list of *.sav and copy to *.
|
|
//
|
|
for(CopyNode=CopyList; CopyNode; CopyNode=NextNode) {
|
|
|
|
Message(MSG_RESTARTING_SETUP,++DotCount);
|
|
|
|
//
|
|
// Remember next node, because we're going to free this one.
|
|
//
|
|
NextNode = CopyNode->Next;
|
|
|
|
//
|
|
// Create the target name, which is the same as the source name
|
|
// with the .sav stripped off.
|
|
//
|
|
if(RtlCreateUnicodeString(&UnicodeString,CopyNode->UnicodeString.Buffer)) {
|
|
|
|
UnicodeString.Buffer[(UnicodeString.Length/sizeof(WCHAR))-4] = 0;
|
|
UnicodeString.Length -= 4*sizeof(WCHAR);
|
|
|
|
Status = CopyAFile(
|
|
DirectoryHandle,
|
|
CopyNode->FileSize,
|
|
CopyNode->UnicodeString.Buffer,
|
|
UnicodeString.Buffer,
|
|
FALSE
|
|
);
|
|
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
AnyErrors = TRUE;
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Unable to copy %ws (%lx)\n",
|
|
CopyNode->UnicodeString.Buffer,Status));
|
|
}
|
|
|
|
RtlFreeUnicodeString(&CopyNode->UnicodeString);
|
|
FREE(CopyNode);
|
|
}
|
|
|
|
NtClose(DirectoryHandle);
|
|
return((BOOLEAN)!AnyErrors);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CopyAFile(
|
|
IN HANDLE DirectoryHandle,
|
|
IN LONGLONG FileSize,
|
|
IN PCWSTR ExistingFile,
|
|
IN PCWSTR NewFile,
|
|
IN BOOLEAN BackupTargetIfExists
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs a simple file copy within a directory.
|
|
The target file must either not exist or be writable.
|
|
Only the default stream is copied.
|
|
|
|
Arguments:
|
|
|
|
DirectoryHandle - supplies handle to directory within which
|
|
the file is to be copied. The handle must have appropriate
|
|
access to allow this.
|
|
|
|
FileSize - supplies size of file to be copied.
|
|
|
|
ExistingFile - supplies filename of file within directory to
|
|
be copied.
|
|
|
|
NewFile - supplies name of file to be created as a copy of
|
|
the existing file.
|
|
|
|
BackupTargetIfExists - specifies if a backup of the target should
|
|
be created (if target file exists) by appending ".sps"
|
|
|
|
Return Value:
|
|
|
|
NT Status code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS Status;
|
|
HANDLE SourceHandle;
|
|
HANDLE TargetHandle;
|
|
HANDLE SetAttributesHandle;
|
|
ULONG XFerSize;
|
|
PCWSTR NewFileBackup;
|
|
UNICODE_STRING NewFileString;
|
|
USHORT Length;
|
|
PFILE_RENAME_INFORMATION RenameInformation;
|
|
FILE_INFORMATION_CLASS SetInfoClass;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
ULONG SetInfoLength;
|
|
PVOID SetInfoBuffer;
|
|
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: Copying %ws to %ws\n",
|
|
ExistingFile,
|
|
NewFile));
|
|
|
|
//
|
|
// backup the target fisrt, if it exists and the caller wanted that
|
|
//
|
|
if (BackupTargetIfExists) {
|
|
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,NewFile);
|
|
ObjectAttributes.RootDirectory = DirectoryHandle;
|
|
Status = NtOpenFile(&TargetHandle,
|
|
(ACCESS_MASK)DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// the *.sav file does exist; NewFileString is *.sav.psp
|
|
//
|
|
Length = UnicodeString.Length + (SPS_EXTENSION_LEN + 1) * sizeof(WCHAR);
|
|
NewFileString.Buffer = MALLOC(Length);
|
|
if(!NewFileString.Buffer) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
NewFileString.Length = NewFileString.MaximumLength = Length;
|
|
|
|
RtlCopyMemory (NewFileString.Buffer, UnicodeString.Buffer, UnicodeString.Length);
|
|
RtlCopyMemory (NewFileString.Buffer + UnicodeString.Length / sizeof(WCHAR), SPS_EXTENSION, SPS_EXTENSION_LEN * sizeof(WCHAR));
|
|
NewFileString.Buffer[Length / sizeof(WCHAR)] = 0;
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: Backing up %ws to %ws\n",
|
|
NewFile,
|
|
NewFileString.Buffer
|
|
));
|
|
|
|
SetInfoClass = FileRenameInformation;
|
|
SetInfoLength = NewFileString.Length + sizeof(*RenameInformation);
|
|
SetInfoBuffer = MALLOC(SetInfoLength);
|
|
if (!SetInfoBuffer) {
|
|
FREE(NewFileString.Buffer);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
RenameInformation = (PFILE_RENAME_INFORMATION)SetInfoBuffer;
|
|
RenameInformation->ReplaceIfExists = TRUE;
|
|
RenameInformation->RootDirectory = DirectoryHandle;
|
|
RenameInformation->FileNameLength = NewFileString.Length;
|
|
RtlMoveMemory(RenameInformation->FileName,
|
|
NewFileString.Buffer,
|
|
NewFileString.Length);
|
|
|
|
Status = NtSetInformationFile(TargetHandle,
|
|
&IoStatusBlock,
|
|
SetInfoBuffer,
|
|
SetInfoLength,
|
|
SetInfoClass);
|
|
if (Status == STATUS_OBJECT_NAME_COLLISION) {
|
|
|
|
//
|
|
// oops, the *.sav.sps file does exist and it's read-only;
|
|
// we must force the rename
|
|
//
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: %ws exists and is read-only; resetting attribs\n",
|
|
NewFileString.Buffer
|
|
));
|
|
|
|
//
|
|
// Open the file for Write Attributes access
|
|
//
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&NewFileString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DirectoryHandle,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenFile(&SetAttributesHandle,
|
|
(ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if(NT_SUCCESS(Status)){
|
|
|
|
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
|
|
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
Status = NtSetInformationFile(SetAttributesHandle,
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation);
|
|
NtClose(SetAttributesHandle);
|
|
if(NT_SUCCESS(Status)){
|
|
Status = NtSetInformationFile(TargetHandle,
|
|
&IoStatusBlock,
|
|
SetInfoBuffer,
|
|
SetInfoLength,
|
|
SetInfoClass);
|
|
|
|
if(NT_SUCCESS(Status)){
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"RestartSpSetup: Re-Rename Worked OK\n"));
|
|
}
|
|
else {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Re-Rename Failed - Status == %x\n",
|
|
Status));
|
|
}
|
|
}
|
|
else {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Set To NORMAL Failed - Status == %x\n",
|
|
Status));
|
|
}
|
|
}
|
|
else {
|
|
KdPrintEx((DPFLTR_SETUP_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"RestartSpSetup: Open Existing file %ws Failed - Status == %x\n",
|
|
NewFileString.Buffer,
|
|
Status));
|
|
}
|
|
}
|
|
|
|
NtClose(TargetHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the source for reading. The source must exist.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,ExistingFile);
|
|
ObjectAttributes.RootDirectory = DirectoryHandle;
|
|
|
|
Status = NtOpenFile(
|
|
&SourceHandle,
|
|
FILE_READ_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// Open/create the target for writing.
|
|
//
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,NewFile);
|
|
ObjectAttributes.RootDirectory = DirectoryHandle;
|
|
|
|
Status = NtCreateFile(
|
|
&TargetHandle,
|
|
FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto c1;
|
|
}
|
|
|
|
//
|
|
// Read/write buffers while there's still data to copy.
|
|
//
|
|
while(NT_SUCCESS(Status) && FileSize) {
|
|
|
|
XFerSize = (FileSize < COPYBUF_SIZE) ? (ULONG)FileSize : COPYBUF_SIZE;
|
|
|
|
Status = NtReadFile(
|
|
SourceHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
CopyBuffer,
|
|
XFerSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
Status = NtWriteFile(
|
|
TargetHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
CopyBuffer,
|
|
XFerSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
FileSize -= XFerSize;
|
|
}
|
|
}
|
|
|
|
NtClose(TargetHandle);
|
|
c1:
|
|
NtClose(SourceHandle);
|
|
c0:
|
|
return(Status);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AreStringsEqual(
|
|
IN PCWSTR String1,
|
|
IN PCWSTR String2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compare 2 0-terminated unicode strings, case insensitively.
|
|
|
|
Arguments:
|
|
|
|
String1 - supplies first string for comparison
|
|
|
|
String2 - supplies second string for comparison
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether strings are equal.
|
|
TRUE = yes; FALSE = no.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING u1;
|
|
UNICODE_STRING u2;
|
|
|
|
RtlInitUnicodeString(&u1,String1);
|
|
RtlInitUnicodeString(&u2,String2);
|
|
|
|
return((BOOLEAN)(RtlCompareUnicodeString(&u1,&u2,TRUE) == 0));
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Message(
|
|
IN ULONG MessageId,
|
|
IN ULONG DotCount,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Format and display a message, which is retreived from
|
|
the image's message resources.
|
|
|
|
Arguments:
|
|
|
|
MessageId - Supplies the message id of the message resource.
|
|
|
|
DotCount - Supplies number of trailing dots to be appended to
|
|
the message text prior to display. If this value is non-0,
|
|
then the message shouldn't have a trailing cr/lf!
|
|
|
|
Additional arguments specify message-specific inserts.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the message was displayed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID ImageBase;
|
|
NTSTATUS Status;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
va_list arglist;
|
|
WCHAR Buffer[1024];
|
|
ULONG u;
|
|
|
|
//
|
|
// Get our image base address
|
|
//
|
|
ImageBase = NtCurrentPeb()->ImageBaseAddress;
|
|
if(!ImageBase) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Find the message.
|
|
// For DBCS codepages we will use English resources instead of
|
|
// default resource because we can only display ASCII characters onto
|
|
// blue Screen via HalDisplayString()
|
|
//
|
|
Status = RtlFindMessage(
|
|
ImageBase,
|
|
11,
|
|
NLS_MB_CODE_PAGE_TAG ? MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) : 0,
|
|
MessageId,
|
|
&MessageEntry
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// If the message is not unicode, convert to unicode.
|
|
// Let the conversion routine allocate the buffer.
|
|
//
|
|
if(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
|
|
|
|
RtlInitAnsiString(&AnsiString,MessageEntry->Text);
|
|
Status = RtlAnsiStringToUnicodeString(&UnicodeString,&AnsiString,TRUE);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Message is already unicode. Make a copy.
|
|
//
|
|
if(!RtlCreateUnicodeString(&UnicodeString,(PWSTR)MessageEntry->Text)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Format the message.
|
|
//
|
|
va_start(arglist,DotCount);
|
|
|
|
Status = RtlFormatMessage(
|
|
UnicodeString.Buffer,
|
|
0, // max width
|
|
FALSE, // don't ignore inserts
|
|
FALSE, // args are not ansi
|
|
FALSE, // args are not an array
|
|
&arglist,
|
|
Buffer,
|
|
sizeof(Buffer)/sizeof(Buffer[0]),
|
|
NULL
|
|
);
|
|
|
|
va_end(arglist);
|
|
|
|
//
|
|
// We don't need the message source any more. Free it.
|
|
//
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
//
|
|
// Add dots and cr.
|
|
//
|
|
for(u=0; u<DotCount; u++) {
|
|
wcscat(Buffer,L".");
|
|
}
|
|
wcscat(Buffer,L"\r");
|
|
|
|
//
|
|
// Print out the message
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,Buffer);
|
|
Status = NtDisplayString(&UnicodeString);
|
|
|
|
return(NT_SUCCESS(Status));
|
|
}
|
|
|