mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 19:02:32 +00:00
509 lines
16 KiB
C
509 lines
16 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Setup Library
|
|
* FILE: base/setup/lib/regutil.c
|
|
* PURPOSE: Registry utility functions
|
|
* PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#include "filesup.h"
|
|
|
|
#include "regutil.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
static UNICODE_STRING SymbolicLinkValueName =
|
|
RTL_CONSTANT_STRING(L"SymbolicLinkValue");
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/*
|
|
* This function is similar to the one in dlls/win32/advapi32/reg/reg.c
|
|
* TODO: I should review both of them very carefully, because they may need
|
|
* some adjustments in their NtCreateKey calls, especially for CreateOptions
|
|
* stuff etc...
|
|
*/
|
|
NTSTATUS
|
|
CreateNestedKey(PHANDLE KeyHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
ULONG CreateOptions)
|
|
{
|
|
OBJECT_ATTRIBUTES LocalObjectAttributes;
|
|
UNICODE_STRING LocalKeyName;
|
|
ULONG Disposition;
|
|
NTSTATUS Status;
|
|
USHORT FullNameLength;
|
|
PWCHAR Ptr;
|
|
HANDLE LocalKeyHandle;
|
|
|
|
Status = NtCreateKey(KeyHandle,
|
|
KEY_ALL_ACCESS,
|
|
ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
CreateOptions,
|
|
&Disposition);
|
|
DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
|
|
if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
if (!NT_SUCCESS(Status))
|
|
DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", ObjectAttributes->ObjectName, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* Copy object attributes */
|
|
RtlCopyMemory(&LocalObjectAttributes,
|
|
ObjectAttributes,
|
|
sizeof(OBJECT_ATTRIBUTES));
|
|
RtlCreateUnicodeString(&LocalKeyName,
|
|
ObjectAttributes->ObjectName->Buffer);
|
|
LocalObjectAttributes.ObjectName = &LocalKeyName;
|
|
FullNameLength = LocalKeyName.Length;
|
|
|
|
/* Remove the last part of the key name and try to create the key again. */
|
|
while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
Ptr = wcsrchr(LocalKeyName.Buffer, '\\');
|
|
if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
*Ptr = (WCHAR)0;
|
|
LocalKeyName.Length = (Ptr - LocalKeyName.Buffer) * sizeof(WCHAR);
|
|
|
|
Status = NtCreateKey(&LocalKeyHandle,
|
|
KEY_CREATE_SUB_KEY,
|
|
&LocalObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE, // FIXME ?
|
|
&Disposition);
|
|
DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeUnicodeString(&LocalKeyName);
|
|
return Status;
|
|
}
|
|
|
|
/* Add removed parts of the key name and create them too. */
|
|
while (TRUE)
|
|
{
|
|
if (LocalKeyName.Length == FullNameLength)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
*KeyHandle = LocalKeyHandle;
|
|
break;
|
|
}
|
|
NtClose(LocalKeyHandle);
|
|
|
|
LocalKeyName.Buffer[LocalKeyName.Length / sizeof(WCHAR)] = L'\\';
|
|
LocalKeyName.Length = (USHORT)wcslen(LocalKeyName.Buffer) * sizeof(WCHAR);
|
|
|
|
Status = NtCreateKey(&LocalKeyHandle,
|
|
KEY_ALL_ACCESS,
|
|
&LocalObjectAttributes,
|
|
0,
|
|
NULL,
|
|
CreateOptions,
|
|
&Disposition);
|
|
DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlFreeUnicodeString(&LocalKeyName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Should be called under SE_BACKUP_PRIVILEGE privilege
|
|
*/
|
|
NTSTATUS
|
|
CreateRegistryFile(
|
|
IN PUNICODE_STRING NtSystemRoot,
|
|
IN PCWSTR RegistryKey,
|
|
IN BOOLEAN IsHiveNew,
|
|
IN HANDLE ProtoKeyHandle
|
|
/*
|
|
IN PUCHAR Descriptor,
|
|
IN ULONG DescriptorLength
|
|
*/
|
|
)
|
|
{
|
|
/* '.old' is for old valid hives, while '.brk' is for old broken hives */
|
|
static PCWSTR Extensions[] = {L"old", L"brk"};
|
|
|
|
NTSTATUS Status;
|
|
HANDLE FileHandle;
|
|
UNICODE_STRING FileName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PCWSTR Extension;
|
|
WCHAR PathBuffer[MAX_PATH];
|
|
WCHAR PathBuffer2[MAX_PATH];
|
|
|
|
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
|
|
NtSystemRoot->Buffer, L"System32\\config", RegistryKey);
|
|
|
|
Extension = Extensions[IsHiveNew ? 0 : 1];
|
|
|
|
//
|
|
// FIXME: The best, actually, would be to rename (move) the existing
|
|
// System32\config\RegistryKey file to System32\config\RegistryKey.old,
|
|
// and if it already existed some System32\config\RegistryKey.old, we should
|
|
// first rename this one into System32\config\RegistryKey_N.old before
|
|
// performing the original rename.
|
|
//
|
|
|
|
/* Check whether the registry hive file already existed, and if so, rename it */
|
|
if (DoesFileExist(NULL, PathBuffer))
|
|
{
|
|
// UINT i;
|
|
|
|
DPRINT1("Registry hive '%S' already exists, rename it\n", PathBuffer);
|
|
|
|
// i = 1;
|
|
/* Try first by just appending the '.old' extension */
|
|
RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
|
|
L"%s.%s", PathBuffer, Extension);
|
|
#if 0
|
|
while (DoesFileExist(NULL, PathBuffer2))
|
|
{
|
|
/* An old file already exists, increments its index, but not too much */
|
|
if (i <= 0xFFFF)
|
|
{
|
|
/* Append '_N.old' extension */
|
|
RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
|
|
L"%s_%lu.%s", PathBuffer, i, Extension);
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Too many old files exist, we will rename the file
|
|
* using the name of the oldest one.
|
|
*/
|
|
RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
|
|
L"%s.%s", PathBuffer, Extension);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Now rename the file (force the move) */
|
|
Status = SetupMoveFile(PathBuffer, PathBuffer2, MOVEFILE_REPLACE_EXISTING);
|
|
}
|
|
|
|
/* Create the file */
|
|
RtlInitUnicodeString(&FileName, PathBuffer);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, // Could have been NtSystemRoot, etc...
|
|
NULL); // Descriptor
|
|
|
|
Status = NtCreateFile(&FileHandle,
|
|
FILE_GENERIC_WRITE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Save the selected hive into the file */
|
|
Status = NtSaveKeyEx(ProtoKeyHandle, FileHandle, REG_LATEST_FORMAT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
|
|
}
|
|
|
|
/* Close the file and return */
|
|
NtClose(FileHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Adapted from ntoskrnl/config/cmsysini.c:CmpLinkKeyToHive() */
|
|
NTSTATUS
|
|
CreateSymLinkKey(
|
|
IN HANDLE RootKey OPTIONAL,
|
|
IN PCWSTR LinkKeyName,
|
|
IN PCWSTR TargetKeyName)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyName;
|
|
HANDLE LinkKeyHandle;
|
|
ULONG Disposition;
|
|
|
|
/* Initialize the object attributes */
|
|
RtlInitUnicodeString(&KeyName, LinkKeyName);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL);
|
|
|
|
/* Create the link key */
|
|
Status = NtCreateKey(&LinkKeyHandle,
|
|
KEY_SET_VALUE | KEY_CREATE_LINK,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
|
|
&Disposition);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CreateSymLinkKey: couldn't create '%S', Status = 0x%08lx\n",
|
|
LinkKeyName, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if the new key was actually created */
|
|
if (Disposition != REG_CREATED_NEW_KEY)
|
|
{
|
|
DPRINT1("CreateSymLinkKey: %S already exists!\n", LinkKeyName);
|
|
NtClose(LinkKeyHandle);
|
|
return STATUS_OBJECT_NAME_EXISTS; // STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
/* Set the target key name as link target */
|
|
RtlInitUnicodeString(&KeyName, TargetKeyName);
|
|
Status = NtSetValueKey(LinkKeyHandle,
|
|
&SymbolicLinkValueName,
|
|
0,
|
|
REG_LINK,
|
|
KeyName.Buffer,
|
|
KeyName.Length);
|
|
|
|
/* Close the link key handle */
|
|
NtClose(LinkKeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CreateSymLinkKey: couldn't create symbolic link '%S' for '%S', Status = 0x%08lx\n",
|
|
LinkKeyName, TargetKeyName, Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
DeleteSymLinkKey(
|
|
IN HANDLE RootKey OPTIONAL,
|
|
IN PCWSTR LinkKeyName)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyName;
|
|
HANDLE LinkKeyHandle;
|
|
// ULONG Disposition;
|
|
|
|
/* Initialize the object attributes */
|
|
RtlInitUnicodeString(&KeyName, LinkKeyName);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
/* Open the symlink key itself if it exists, and not its target */
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
|
|
RootKey,
|
|
NULL);
|
|
|
|
/*
|
|
* Note: We could use here NtOpenKey() but it does not allow to pass
|
|
* opening options. NtOpenKeyEx() could do it but is Windows 7+.
|
|
* So we use the good old NtCreateKey() that can open the key.
|
|
*/
|
|
#if 0
|
|
Status = NtCreateKey(&LinkKeyHandle,
|
|
DELETE | KEY_SET_VALUE | KEY_CREATE_LINK,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
/*REG_OPTION_VOLATILE |*/ REG_OPTION_OPEN_LINK,
|
|
&Disposition);
|
|
#else
|
|
Status = NtOpenKey(&LinkKeyHandle,
|
|
DELETE | KEY_SET_VALUE | KEY_CREATE_LINK,
|
|
&ObjectAttributes);
|
|
#endif
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Delete the special "SymbolicLinkValue" value.
|
|
* This is technically not needed since we are going to remove
|
|
* the key anyways, but it is good practice to do it.
|
|
*/
|
|
Status = NtDeleteValueKey(LinkKeyHandle, &SymbolicLinkValueName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtDeleteValueKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
|
|
NtClose(LinkKeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Finally delete the key itself and close the link key handle */
|
|
Status = NtDeleteKey(LinkKeyHandle);
|
|
NtClose(LinkKeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("DeleteSymLinkKey: couldn't delete symbolic link '%S', Status = 0x%08lx\n",
|
|
LinkKeyName, Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* Should be called under SE_RESTORE_PRIVILEGE privilege
|
|
*/
|
|
NTSTATUS
|
|
ConnectRegistry(
|
|
IN HANDLE RootKey OPTIONAL,
|
|
IN PCWSTR RegMountPoint,
|
|
// IN HANDLE RootDirectory OPTIONAL,
|
|
IN PUNICODE_STRING NtSystemRoot,
|
|
IN PCWSTR RegistryKey
|
|
/*
|
|
IN PUCHAR Descriptor,
|
|
IN ULONG DescriptorLength
|
|
*/
|
|
)
|
|
{
|
|
UNICODE_STRING KeyName, FileName;
|
|
OBJECT_ATTRIBUTES KeyObjectAttributes;
|
|
OBJECT_ATTRIBUTES FileObjectAttributes;
|
|
WCHAR PathBuffer[MAX_PATH];
|
|
|
|
RtlInitUnicodeString(&KeyName, RegMountPoint);
|
|
InitializeObjectAttributes(&KeyObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL); // Descriptor
|
|
|
|
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
|
|
NtSystemRoot->Buffer, L"System32\\config", RegistryKey);
|
|
RtlInitUnicodeString(&FileName, PathBuffer);
|
|
InitializeObjectAttributes(&FileObjectAttributes,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, // RootDirectory,
|
|
NULL);
|
|
|
|
/* Mount the registry hive in the registry namespace */
|
|
return NtLoadKey(&KeyObjectAttributes, &FileObjectAttributes);
|
|
}
|
|
|
|
/*
|
|
* Should be called under SE_RESTORE_PRIVILEGE privilege
|
|
*/
|
|
NTSTATUS
|
|
DisconnectRegistry(
|
|
IN HANDLE RootKey OPTIONAL,
|
|
IN PCWSTR RegMountPoint,
|
|
IN ULONG Flags)
|
|
{
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
RtlInitUnicodeString(&KeyName, RegMountPoint);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RootKey,
|
|
NULL);
|
|
|
|
// NOTE: NtUnloadKey == NtUnloadKey2 with Flags == 0.
|
|
return NtUnloadKey2(&ObjectAttributes, Flags);
|
|
}
|
|
|
|
/*
|
|
* Should be called under SE_RESTORE_PRIVILEGE privilege
|
|
*/
|
|
NTSTATUS
|
|
VerifyRegistryHive(
|
|
// IN HANDLE RootKey OPTIONAL,
|
|
// // IN HANDLE RootDirectory OPTIONAL,
|
|
IN PUNICODE_STRING NtSystemRoot,
|
|
IN PCWSTR RegistryKey /* ,
|
|
IN PCWSTR RegMountPoint */)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Try to mount the specified registry hive */
|
|
Status = ConnectRegistry(NULL,
|
|
L"\\Registry\\Machine\\USetup_VerifyHive",
|
|
NtSystemRoot,
|
|
RegistryKey
|
|
/* NULL, 0 */);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n", RegistryKey, Status);
|
|
}
|
|
|
|
DPRINT1("VerifyRegistryHive: ConnectRegistry(%S) returns Status 0x%08lx\n", RegistryKey, Status);
|
|
|
|
//
|
|
// TODO: Check the Status error codes: STATUS_SUCCESS, STATUS_REGISTRY_RECOVERED,
|
|
// STATUS_REGISTRY_HIVE_RECOVERED, STATUS_REGISTRY_CORRUPT, STATUS_REGISTRY_IO_FAILED,
|
|
// STATUS_NOT_REGISTRY_FILE, STATUS_CANNOT_LOAD_REGISTRY_FILE ;
|
|
//(STATUS_HIVE_UNLOADED) ; STATUS_SYSTEM_HIVE_TOO_LARGE
|
|
//
|
|
|
|
if (Status == STATUS_REGISTRY_HIVE_RECOVERED) // NT_SUCCESS is still FALSE in this case!
|
|
DPRINT1("VerifyRegistryHive: Registry hive %S was recovered but some data may be lost (Status 0x%08lx)\n", RegistryKey, Status);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("VerifyRegistryHive: Registry hive %S is corrupted (Status 0x%08lx)\n", RegistryKey, Status);
|
|
return Status;
|
|
}
|
|
|
|
if (Status == STATUS_REGISTRY_RECOVERED)
|
|
DPRINT1("VerifyRegistryHive: Registry hive %S succeeded recovered (Status 0x%08lx)\n", RegistryKey, Status);
|
|
|
|
/* Unmount the hive */
|
|
Status = DisconnectRegistry(NULL,
|
|
L"\\Registry\\Machine\\USetup_VerifyHive",
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("DisconnectRegistry(%S) failed, Status 0x%08lx\n", RegistryKey, Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|