windows-nt/Source/XPSP1/NT/base/ntos/io/pnpmgr/pplastgood.c

556 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
PpLastGood.c
Abstract:
This module handles last known good processing for the IO subsystem.
Author:
Adrian J. Oney - April 4, 2000
Revision History:
--*/
#include "pnpmgrp.h"
#include "pilastgood.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, PpLastGoodDoBootProcessing)
#pragma alloc_text(INIT, PiLastGoodRevertLastKnownDirectory)
#pragma alloc_text(INIT, PiLastGoodRevertCopyCallback)
#pragma alloc_text(INIT, PiLastGoodCopyKeyContents)
#endif
#define POOLTAG_LASTGOOD ('gLpP')
VOID
PpLastGoodDoBootProcessing(
VOID
)
/*++
Routine Description:
This rolls back the system files to the state they were during the last
known good boot. It should only be called from within a last known good
boot, and at the earliest point possible.
Arguments:
None.
Return Value:
None.
--*/
{
UNICODE_STRING lastKnownGoodPath, lastKnownGoodTmpPath;
UNICODE_STRING lastKnownGoodDelKey, lastKnownGoodTmpDelKey;
NTSTATUS status;
RtlInitUnicodeString(
&lastKnownGoodPath,
L"\\SystemRoot\\LastGood"
);
RtlInitUnicodeString(
&lastKnownGoodDelKey,
CM_REGISTRY_MACHINE(REGSTR_PATH_LASTGOOD)
);
RtlInitUnicodeString(
&lastKnownGoodTmpPath,
L"\\SystemRoot\\LastGood.Tmp"
);
RtlInitUnicodeString(
&lastKnownGoodTmpDelKey,
CM_REGISTRY_MACHINE(REGSTR_PATH_LASTGOODTMP)
);
if (!CmIsLastKnownGoodBoot()) {
//
// If we are in safe mode we don't do anything to commit the current
// boot.
//
if (InitSafeBootMode) {
return;
}
//
// We are in a non-last known good boot. We immediately move all the
// previous last known good info into the tmp subtree. We do this
// because we will taint the normal LKG path prior to marking it good
// (eg pre-logon server side install of PnP devices). Note that if the
// tmp directory already exists, we *don't* perform the copy, as a good
// boot is signified by deleting that directory.
//
status = IopFileUtilRename(
&lastKnownGoodPath,
&lastKnownGoodTmpPath,
FALSE
);
if (!NT_SUCCESS(status)) {
return;
}
//
// It worked, now we also take care of the registry info.
//
PiLastGoodCopyKeyContents(
&lastKnownGoodDelKey,
&lastKnownGoodTmpDelKey,
TRUE
);
return;
}
//
// Revert the LastGood tree. This tree contains the changes made after
// SMSS.EXE's initialization.
//
PiLastGoodRevertLastKnownDirectory(
&lastKnownGoodPath,
&lastKnownGoodDelKey
);
//
// Revert the LastGood.Tmp tree. This tree contains the changes made on
// a prior boot if we crashed between SMSS.EXE's initialization and login.
//
PiLastGoodRevertLastKnownDirectory(
&lastKnownGoodTmpPath,
&lastKnownGoodTmpDelKey);
}
VOID
PiLastGoodRevertLastKnownDirectory(
IN PUNICODE_STRING LastKnownGoodDirectory,
IN PUNICODE_STRING LastKnownGoodRegPath
)
/*++
Routine Description:
This function commits the changes specified by a given last known good
directory and reg key. All files in the directory are first copied over any
existing files. Subsequently, any files specified in the reg key are
deleted.
Arguments:
LastKnownGoodDirectory - Directory subtree to copy over \SystemRoot. This
path is emptied when the copy is complete.
LastKnownGoodRegPath - Key containing files to delete. Each value entry
is relative to \SystemRoot, and the value itself
contains the name of the file to delete.
Return Value:
None.
--*/
{
NTSTATUS status;
UNICODE_STRING fileToDelete, fileName;
OBJECT_ATTRIBUTES lastKnownGoodKeyAttributes;
OBJECT_ATTRIBUTES fileAttributes;
HANDLE lastGoodRegHandle;
UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 256*sizeof(WCHAR) + sizeof(ULONG)];
WCHAR filePathName[255 + sizeof("\\SystemRoot\\")];
PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
ULONG resultLength, i, j, optionValue;
//
// Preinit our pointer to the full information buffer.
//
pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
//
// Preform the file copy.
//
IopFileUtilWalkDirectoryTreeTopDown(
LastKnownGoodDirectory,
( DIRWALK_INCLUDE_FILES | DIRWALK_CULL_DOTPATHS | DIRWALK_TRAVERSE ),
PiLastGoodRevertCopyCallback,
(PVOID) LastKnownGoodDirectory
);
//
// Delete all the files specified in by the registry keys.
//
InitializeObjectAttributes(
&lastKnownGoodKeyAttributes,
LastKnownGoodRegPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status = ZwOpenKey(
&lastGoodRegHandle,
KEY_ALL_ACCESS,
&lastKnownGoodKeyAttributes
);
if (!NT_SUCCESS(status)) {
return;
}
i = 0;
while (1) {
status = ZwEnumerateValueKey(
lastGoodRegHandle,
i++,
KeyValueFullInformation,
pFullKeyInformation,
sizeof(keyBuffer),
&resultLength
);
if (!NT_SUCCESS(status)) {
if (status == STATUS_NO_MORE_ENTRIES) {
status = STATUS_SUCCESS;
}
break;
}
if (resultLength == 0) {
continue;
}
if (pFullKeyInformation->Type != REG_DWORD) {
continue;
}
if (pFullKeyInformation->DataLength != sizeof(ULONG)) {
continue;
}
optionValue = *((PULONG) (((PUCHAR) pFullKeyInformation) +
pFullKeyInformation->DataOffset));
//
// We only understand deletes (and no flags).
//
if ((optionValue & 0xFF) != 1) {
continue;
}
fileToDelete.Buffer = filePathName;
fileToDelete.Length = (USHORT) 0;
fileToDelete.MaximumLength = sizeof(filePathName);
fileName.Buffer = (PWSTR) pFullKeyInformation->Name;
fileName.Length = (USHORT) pFullKeyInformation->NameLength;
fileName.MaximumLength = fileName.Length;
RtlAppendUnicodeToString(&fileToDelete, L"\\SystemRoot\\");
RtlAppendUnicodeStringToString(&fileToDelete, &fileName);
//
// Note that the key name has all '\'s changed to '/'s. Here we change
// them back as the file systems are *almost* but not quite slash-tilt
// agnostic.
//
for(j = sizeof(L"\\SystemRoot\\")/sizeof(WCHAR);
j < fileToDelete.Length/sizeof(WCHAR);
j++) {
if (filePathName[j] == L'/') {
filePathName[j] = L'\\';
}
}
IopFileUtilClearAttributes(
&fileToDelete,
( FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM )
);
InitializeObjectAttributes(
&fileAttributes,
&fileToDelete,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL
);
ZwDeleteFile(&fileAttributes);
}
ZwDeleteKey(&lastGoodRegHandle);
ZwClose(lastGoodRegHandle);
}
NTSTATUS
PiLastGoodRevertCopyCallback(
IN PUNICODE_STRING FullPathName,
IN PUNICODE_STRING FileName,
IN ULONG FileAttributes,
IN PVOID Context
)
/*++
Routine Description:
This function is called back for each file in each of the appropriate
LastKnownGood directories. It's job is to move the specified file into the
appropriate mainline directory.
Arguments:
FullPathName - Full path name of the identified file, relative to SystemRoot
FileName - Filename portion, exempts directory.
Context - Unicode string name of the root directory scanned. The string
should not have a trailing '\\'
Return Value:
NTSTATUS (Unsuccessful statusi abort further copies).
--*/
{
NTSTATUS status;
const USHORT rootLength = sizeof(L"\\SystemRoot\\")-sizeof(WCHAR);
USHORT lastGoodLength;
UNICODE_STRING targetFile;
PWCHAR newPathText;
//
// Add in an extra character to skip past the '\\'
//
lastGoodLength = ((PUNICODE_STRING) Context)->Length + sizeof(WCHAR);
newPathText = ExAllocatePoolWithTag(
PagedPool,
FullPathName->Length,
POOLTAG_LASTGOOD
);
if (newPathText == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Change \\SystemRoot\LastGood\Blah... to \\SystemRoot\Blah...
//
RtlCopyMemory(
newPathText,
FullPathName->Buffer,
rootLength
);
RtlCopyMemory(
newPathText + rootLength/sizeof(WCHAR),
FullPathName->Buffer + lastGoodLength/sizeof(WCHAR),
FullPathName->Length - lastGoodLength
);
//
// Setup our unicode string path.
//
targetFile.Length = FullPathName->Length - lastGoodLength + rootLength;
targetFile.MaximumLength = targetFile.Length;
targetFile.Buffer = newPathText;
//
// Perform the rename.
//
status = IopFileUtilRename(FullPathName, &targetFile, TRUE);
//
// Cleanup and exit.
//
ExFreePool(newPathText);
return status;
}
NTSTATUS
PiLastGoodCopyKeyContents(
IN PUNICODE_STRING SourceRegPath,
IN PUNICODE_STRING DestinationRegPath,
IN BOOLEAN DeleteSourceKey
)
/*++
Routine Description:
This function copies all the value keys in one source path to the
destination path.
NOTE: This function's implementation currently restricts the total of value
and name lengths to 512 bytes, and is therefore not a generic key
copy function.
Arguments:
SourcePath - Registry path to enumerate and copy keys from.
DestinationPath - Registry path to receive new value keys. This key will
be created if it does not exist.
DeleteSourceKey - If TRUE, source key is deleted upn successful completion
of copy.
Return Value:
None.
--*/
{
NTSTATUS status;
OBJECT_ATTRIBUTES sourceKeyAttributes, destinationKeyAttributes;
HANDLE sourceRegHandle, destinationRegHandle;
UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 512*sizeof(WCHAR)];
PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
ULONG resultLength, i, disposition;
UNICODE_STRING valueName;
//
// Prep the buffer.
//
pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
//
// Open the source key.
//
InitializeObjectAttributes(
&sourceKeyAttributes,
SourceRegPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status = ZwOpenKey(
&sourceRegHandle,
KEY_ALL_ACCESS,
&sourceKeyAttributes
);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Open or create the destination key.
//
InitializeObjectAttributes(
&destinationKeyAttributes,
DestinationRegPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status = ZwCreateKey(
&destinationRegHandle,
KEY_ALL_ACCESS,
&destinationKeyAttributes,
0,
NULL,
REG_OPTION_NON_VOLATILE,
&disposition
);
if (!NT_SUCCESS(status)) {
ZwClose(sourceRegHandle);
return status;
}
//
// Iterate over all the value keys, copying each.
//
i = 0;
while (1) {
status = ZwEnumerateValueKey(
sourceRegHandle,
i++,
KeyValueFullInformation,
pFullKeyInformation,
sizeof(keyBuffer),
&resultLength
);
if (!NT_SUCCESS(status)) {
if (status == STATUS_NO_MORE_ENTRIES) {
status = STATUS_SUCCESS;
}
break;
}
valueName.Buffer = pFullKeyInformation->Name;
valueName.Length = (USHORT) pFullKeyInformation->NameLength;
valueName.MaximumLength = valueName.Length;
status = ZwSetValueKey(
destinationRegHandle,
&valueName,
0,
pFullKeyInformation->Type,
((PUCHAR) pFullKeyInformation) + pFullKeyInformation->DataOffset,
pFullKeyInformation->DataLength
);
if (!NT_SUCCESS(status)) {
break;
}
}
//
// Cleanup time.
//
if (NT_SUCCESS(status) && DeleteSourceKey) {
ZwDeleteKey(sourceRegHandle);
}
ZwClose(sourceRegHandle);
ZwClose(destinationRegHandle);
return status;
}