mirror of
https://github.com/reactos/reactos.git
synced 2026-09-12 20:59:19 +08:00
- Fixed class name cell allocation. - Renamed a lot of variables and functions. svn path=/trunk/; revision=6274
574 lines
13 KiB
C
574 lines
13 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/cm/regobj.c
|
|
* PURPOSE: Registry object manipulation routines.
|
|
* UPDATE HISTORY:
|
|
*/
|
|
|
|
#define NTOS_MODE_KERNEL
|
|
#include <ntos.h>
|
|
#include <string.h>
|
|
#include <roscfg.h>
|
|
#include <internal/ob.h>
|
|
#include <internal/registry.h>
|
|
#include <ntos/minmax.h>
|
|
|
|
#define NDEBUG
|
|
#include <internal/debug.h>
|
|
|
|
#include "cm.h"
|
|
|
|
|
|
static NTSTATUS
|
|
CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
|
|
PKEY_CELL KeyCell,
|
|
PUNICODE_STRING TargetPath);
|
|
|
|
/* FUNCTONS *****************************************************************/
|
|
|
|
NTSTATUS STDCALL
|
|
CmiObjectParse(PVOID ParsedObject,
|
|
PVOID *NextObject,
|
|
PUNICODE_STRING FullPath,
|
|
PWSTR *Path,
|
|
ULONG Attributes)
|
|
{
|
|
BLOCK_OFFSET BlockOffset;
|
|
PKEY_OBJECT FoundObject;
|
|
PKEY_OBJECT ParsedKey;
|
|
PKEY_CELL SubKeyCell;
|
|
NTSTATUS Status;
|
|
PWSTR StartPtr;
|
|
PWSTR EndPtr;
|
|
ULONG Length;
|
|
UNICODE_STRING LinkPath;
|
|
UNICODE_STRING TargetPath;
|
|
UNICODE_STRING KeyName;
|
|
|
|
ParsedKey = ParsedObject;
|
|
|
|
VERIFY_KEY_OBJECT(ParsedKey);
|
|
|
|
*NextObject = NULL;
|
|
|
|
if ((*Path) == NULL)
|
|
{
|
|
DPRINT("*Path is NULL\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
DPRINT("Path '%S'\n", *Path);
|
|
|
|
/* Extract relevant path name */
|
|
StartPtr = *Path;
|
|
if (*StartPtr == L'\\')
|
|
StartPtr++;
|
|
|
|
EndPtr = wcschr(StartPtr, L'\\');
|
|
if (EndPtr != NULL)
|
|
Length = ((PCHAR)EndPtr - (PCHAR)StartPtr) / sizeof(WCHAR);
|
|
else
|
|
Length = wcslen(StartPtr);
|
|
|
|
|
|
KeyName.Length = Length * sizeof(WCHAR);
|
|
KeyName.MaximumLength = KeyName.Length + sizeof(WCHAR);
|
|
KeyName.Buffer = ExAllocatePool(NonPagedPool,
|
|
KeyName.MaximumLength);
|
|
RtlCopyMemory(KeyName.Buffer,
|
|
StartPtr,
|
|
KeyName.Length);
|
|
KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] = 0;
|
|
|
|
|
|
FoundObject = CmiScanKeyList(ParsedKey,
|
|
&KeyName,
|
|
Attributes);
|
|
if (FoundObject == NULL)
|
|
{
|
|
Status = CmiScanForSubKey(ParsedKey->RegistryHive,
|
|
ParsedKey->KeyCell,
|
|
&SubKeyCell,
|
|
&BlockOffset,
|
|
&KeyName,
|
|
0,
|
|
Attributes);
|
|
if (!NT_SUCCESS(Status) || (SubKeyCell == NULL))
|
|
{
|
|
RtlFreeUnicodeString(&KeyName);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
if ((SubKeyCell->Flags & REG_KEY_LINK_CELL) &&
|
|
!((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
|
|
{
|
|
RtlInitUnicodeString(&LinkPath, NULL);
|
|
Status = CmiGetLinkTarget(ParsedKey->RegistryHive,
|
|
SubKeyCell,
|
|
&LinkPath);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("LinkPath '%wZ'\n", &LinkPath);
|
|
|
|
/* build new FullPath for reparsing */
|
|
TargetPath.MaximumLength = LinkPath.MaximumLength;
|
|
if (EndPtr != NULL)
|
|
{
|
|
TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
|
|
}
|
|
TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
|
|
TargetPath.Buffer = ExAllocatePool(NonPagedPool,
|
|
TargetPath.MaximumLength);
|
|
wcscpy(TargetPath.Buffer, LinkPath.Buffer);
|
|
if (EndPtr != NULL)
|
|
{
|
|
wcscat(TargetPath.Buffer, EndPtr);
|
|
}
|
|
|
|
RtlFreeUnicodeString(FullPath);
|
|
RtlFreeUnicodeString(&LinkPath);
|
|
FullPath->Length = TargetPath.Length;
|
|
FullPath->MaximumLength = TargetPath.MaximumLength;
|
|
FullPath->Buffer = TargetPath.Buffer;
|
|
|
|
DPRINT("FullPath '%wZ'\n", FullPath);
|
|
|
|
/* reinitialize Path for reparsing */
|
|
*Path = FullPath->Buffer;
|
|
|
|
*NextObject = NULL;
|
|
|
|
RtlFreeUnicodeString(&KeyName);
|
|
return(STATUS_REPARSE);
|
|
}
|
|
}
|
|
|
|
/* Create new key object and put into linked list */
|
|
DPRINT("CmiObjectParse: %s\n", Path);
|
|
Status = ObCreateObject(KernelMode,
|
|
CmiKeyType,
|
|
NULL,
|
|
KernelMode,
|
|
NULL,
|
|
sizeof(KEY_OBJECT),
|
|
0,
|
|
0,
|
|
(PVOID*)&FoundObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlFreeUnicodeString(&KeyName);
|
|
return(Status);
|
|
}
|
|
|
|
FoundObject->Flags = 0;
|
|
FoundObject->KeyCell = SubKeyCell;
|
|
FoundObject->KeyCellOffset = BlockOffset;
|
|
FoundObject->RegistryHive = ParsedKey->RegistryHive;
|
|
RtlCreateUnicodeString(&FoundObject->Name,
|
|
KeyName.Buffer);
|
|
CmiAddKeyToList(ParsedKey, FoundObject);
|
|
DPRINT("Created object 0x%x\n", FoundObject);
|
|
}
|
|
else
|
|
{
|
|
if ((FoundObject->KeyCell->Flags & REG_KEY_LINK_CELL) &&
|
|
!((Attributes & OBJ_OPENLINK) && (EndPtr == NULL)))
|
|
{
|
|
DPRINT("Found link\n");
|
|
|
|
RtlInitUnicodeString(&LinkPath, NULL);
|
|
Status = CmiGetLinkTarget(FoundObject->RegistryHive,
|
|
FoundObject->KeyCell,
|
|
&LinkPath);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("LinkPath '%wZ'\n", &LinkPath);
|
|
|
|
/* build new FullPath for reparsing */
|
|
TargetPath.MaximumLength = LinkPath.MaximumLength;
|
|
if (EndPtr != NULL)
|
|
{
|
|
TargetPath.MaximumLength += (wcslen(EndPtr) * sizeof(WCHAR));
|
|
}
|
|
TargetPath.Length = TargetPath.MaximumLength - sizeof(WCHAR);
|
|
TargetPath.Buffer = ExAllocatePool(NonPagedPool,
|
|
TargetPath.MaximumLength);
|
|
wcscpy(TargetPath.Buffer, LinkPath.Buffer);
|
|
if (EndPtr != NULL)
|
|
{
|
|
wcscat(TargetPath.Buffer, EndPtr);
|
|
}
|
|
|
|
RtlFreeUnicodeString(FullPath);
|
|
RtlFreeUnicodeString(&LinkPath);
|
|
FullPath->Length = TargetPath.Length;
|
|
FullPath->MaximumLength = TargetPath.MaximumLength;
|
|
FullPath->Buffer = TargetPath.Buffer;
|
|
|
|
DPRINT("FullPath '%wZ'\n", FullPath);
|
|
|
|
/* reinitialize Path for reparsing */
|
|
*Path = FullPath->Buffer;
|
|
|
|
*NextObject = NULL;
|
|
|
|
RtlFreeUnicodeString(&KeyName);
|
|
return(STATUS_REPARSE);
|
|
}
|
|
}
|
|
|
|
ObReferenceObjectByPointer(FoundObject,
|
|
STANDARD_RIGHTS_REQUIRED,
|
|
NULL,
|
|
UserMode);
|
|
}
|
|
|
|
DPRINT("CmiObjectParse: %s\n", FoundObject->Name);
|
|
|
|
*Path = EndPtr;
|
|
|
|
VERIFY_KEY_OBJECT(FoundObject);
|
|
|
|
*NextObject = FoundObject;
|
|
|
|
RtlFreeUnicodeString(&KeyName);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS STDCALL
|
|
CmiObjectCreate(PVOID ObjectBody,
|
|
PVOID Parent,
|
|
PWSTR RemainingPath,
|
|
POBJECT_ATTRIBUTES ObjectAttributes)
|
|
{
|
|
PKEY_OBJECT KeyObject = ObjectBody;
|
|
PWSTR Start;
|
|
|
|
KeyObject->ParentKey = Parent;
|
|
if (RemainingPath)
|
|
{
|
|
Start = RemainingPath;
|
|
if(*Start == L'\\')
|
|
Start++;
|
|
RtlCreateUnicodeString(&KeyObject->Name,
|
|
Start);
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&KeyObject->Name,
|
|
NULL);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID STDCALL
|
|
CmiObjectDelete(PVOID DeletedObject)
|
|
{
|
|
PKEY_OBJECT KeyObject;
|
|
|
|
DPRINT("Delete key object (%p)\n", DeletedObject);
|
|
|
|
KeyObject = (PKEY_OBJECT) DeletedObject;
|
|
|
|
ObReferenceObject(KeyObject->ParentKey);
|
|
|
|
if (!NT_SUCCESS(CmiRemoveKeyFromList(KeyObject)))
|
|
{
|
|
DPRINT1("Key not found in parent list ???\n");
|
|
}
|
|
|
|
RtlFreeUnicodeString(&KeyObject->Name);
|
|
|
|
if (KeyObject->Flags & KO_MARKED_FOR_DELETE)
|
|
{
|
|
DPRINT("delete really key\n");
|
|
|
|
CmiRemoveSubKey(KeyObject->RegistryHive,
|
|
KeyObject->ParentKey,
|
|
KeyObject);
|
|
|
|
if (!IsNoFileHive(KeyObject->RegistryHive))
|
|
{
|
|
CmiSyncHives();
|
|
}
|
|
}
|
|
ObDereferenceObject(KeyObject->ParentKey);
|
|
if (KeyObject->NumberOfSubKeys)
|
|
{
|
|
KEBUGCHECK(0);
|
|
}
|
|
if (KeyObject->SizeOfSubKeys)
|
|
{
|
|
ExFreePool(KeyObject->SubKeys);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS STDCALL
|
|
CmiObjectSecurity(PVOID ObjectBody,
|
|
SECURITY_OPERATION_CODE OperationCode,
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
PULONG BufferLength)
|
|
{
|
|
DPRINT1 ("CmiObjectSecurity() called\n");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS STDCALL
|
|
CmiObjectQueryName (PVOID ObjectBody,
|
|
POBJECT_NAME_INFORMATION ObjectNameInfo,
|
|
ULONG Length,
|
|
PULONG ReturnLength)
|
|
{
|
|
POBJECT_NAME_INFORMATION LocalInfo;
|
|
PKEY_OBJECT KeyObject;
|
|
ULONG LocalReturnLength;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT ("CmiObjectQueryName() called\n");
|
|
|
|
KeyObject = (PKEY_OBJECT)ObjectBody;
|
|
|
|
LocalInfo = ExAllocatePool (NonPagedPool,
|
|
sizeof(OBJECT_NAME_INFORMATION) +
|
|
MAX_PATH * sizeof(WCHAR));
|
|
if (LocalInfo == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (KeyObject->ParentKey != KeyObject)
|
|
{
|
|
Status = ObQueryNameString (KeyObject->ParentKey,
|
|
LocalInfo,
|
|
MAX_PATH * sizeof(WCHAR),
|
|
&LocalReturnLength);
|
|
}
|
|
else
|
|
{
|
|
/* KeyObject is the root key */
|
|
Status = ObQueryNameString (BODY_TO_HEADER(KeyObject)->Parent,
|
|
LocalInfo,
|
|
MAX_PATH * sizeof(WCHAR),
|
|
&LocalReturnLength);
|
|
}
|
|
|
|
if (!NT_SUCCESS (Status))
|
|
{
|
|
ExFreePool (LocalInfo);
|
|
return Status;
|
|
}
|
|
DPRINT ("Parent path: %wZ\n", &LocalInfo->Name);
|
|
|
|
Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
|
|
&LocalInfo->Name);
|
|
ExFreePool (LocalInfo);
|
|
if (!NT_SUCCESS (Status))
|
|
return Status;
|
|
|
|
Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
|
|
L"\\");
|
|
if (!NT_SUCCESS (Status))
|
|
return Status;
|
|
|
|
Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
|
|
&KeyObject->Name);
|
|
if (NT_SUCCESS (Status))
|
|
{
|
|
DPRINT ("Total path: %wZ\n", &ObjectNameInfo->Name);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmiAddKeyToList(PKEY_OBJECT ParentKey,
|
|
PKEY_OBJECT NewKey)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
DPRINT("ParentKey %.08x\n", ParentKey);
|
|
|
|
KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
|
|
|
|
if (ParentKey->SizeOfSubKeys <= ParentKey->NumberOfSubKeys)
|
|
{
|
|
PKEY_OBJECT *tmpSubKeys = ExAllocatePool(NonPagedPool,
|
|
(ParentKey->NumberOfSubKeys + 1) * sizeof(ULONG));
|
|
|
|
if (ParentKey->NumberOfSubKeys > 0)
|
|
{
|
|
RtlCopyMemory (tmpSubKeys,
|
|
ParentKey->SubKeys,
|
|
ParentKey->NumberOfSubKeys * sizeof(ULONG));
|
|
}
|
|
|
|
if (ParentKey->SubKeys)
|
|
ExFreePool(ParentKey->SubKeys);
|
|
|
|
ParentKey->SubKeys = tmpSubKeys;
|
|
ParentKey->SizeOfSubKeys = ParentKey->NumberOfSubKeys + 1;
|
|
}
|
|
|
|
/* FIXME: Please maintain the list in alphabetic order */
|
|
/* to allow a dichotomic search */
|
|
ParentKey->SubKeys[ParentKey->NumberOfSubKeys++] = NewKey;
|
|
|
|
DPRINT("Reference parent key: 0x%x\n", ParentKey);
|
|
|
|
ObReferenceObjectByPointer(ParentKey,
|
|
STANDARD_RIGHTS_REQUIRED,
|
|
NULL,
|
|
UserMode);
|
|
NewKey->ParentKey = ParentKey;
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmiRemoveKeyFromList(PKEY_OBJECT KeyToRemove)
|
|
{
|
|
PKEY_OBJECT ParentKey;
|
|
KIRQL OldIrql;
|
|
DWORD Index;
|
|
|
|
ParentKey = KeyToRemove->ParentKey;
|
|
KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
|
|
/* FIXME: If list maintained in alphabetic order, use dichotomic search */
|
|
for (Index = 0; Index < ParentKey->NumberOfSubKeys; Index++)
|
|
{
|
|
if (ParentKey->SubKeys[Index] == KeyToRemove)
|
|
{
|
|
if (Index < ParentKey->NumberOfSubKeys-1)
|
|
RtlMoveMemory(&ParentKey->SubKeys[Index],
|
|
&ParentKey->SubKeys[Index + 1],
|
|
(ParentKey->NumberOfSubKeys - Index - 1) * sizeof(PKEY_OBJECT));
|
|
ParentKey->NumberOfSubKeys--;
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
|
|
DPRINT("Dereference parent key: 0x%x\n", ParentKey);
|
|
|
|
ObDereferenceObject(ParentKey);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
PKEY_OBJECT
|
|
CmiScanKeyList(PKEY_OBJECT Parent,
|
|
PUNICODE_STRING KeyName,
|
|
ULONG Attributes)
|
|
{
|
|
PKEY_OBJECT CurKey;
|
|
KIRQL OldIrql;
|
|
ULONG Index;
|
|
|
|
DPRINT("Scanning key list for: %wZ (Parent: %wZ)\n",
|
|
KeyName, &Parent->Name);
|
|
|
|
KeAcquireSpinLock(&CmiKeyListLock, &OldIrql);
|
|
/* FIXME: if list maintained in alphabetic order, use dichotomic search */
|
|
for (Index=0; Index < Parent->NumberOfSubKeys; Index++)
|
|
{
|
|
CurKey = Parent->SubKeys[Index];
|
|
if (Attributes & OBJ_CASE_INSENSITIVE)
|
|
{
|
|
if ((KeyName->Length == CurKey->Name.Length)
|
|
&& (_wcsicmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
|
|
{
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
return CurKey;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((KeyName->Length == CurKey->Name.Length)
|
|
&& (wcscmp(KeyName->Buffer, CurKey->Name.Buffer) == 0))
|
|
{
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
return CurKey;
|
|
}
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&CmiKeyListLock, OldIrql);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
CmiGetLinkTarget(PREGISTRY_HIVE RegistryHive,
|
|
PKEY_CELL KeyCell,
|
|
PUNICODE_STRING TargetPath)
|
|
{
|
|
UNICODE_STRING LinkName = UNICODE_STRING_INITIALIZER(L"SymbolicLinkValue");
|
|
PVALUE_CELL ValueCell;
|
|
PDATA_CELL DataCell;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("CmiGetLinkTarget() called\n");
|
|
|
|
/* Get Value block of interest */
|
|
Status = CmiScanKeyForValue(RegistryHive,
|
|
KeyCell,
|
|
&LinkName,
|
|
&ValueCell,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("CmiScanKeyForValue() failed (Status %lx)\n", Status);
|
|
return(Status);
|
|
}
|
|
|
|
if (ValueCell->DataType != REG_LINK)
|
|
{
|
|
DPRINT1("Type != REG_LINK\n!");
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
if (TargetPath->Buffer == NULL && TargetPath->MaximumLength == 0)
|
|
{
|
|
TargetPath->Length = 0;
|
|
TargetPath->MaximumLength = ValueCell->DataSize + sizeof(WCHAR);
|
|
TargetPath->Buffer = ExAllocatePool(NonPagedPool,
|
|
TargetPath->MaximumLength);
|
|
}
|
|
|
|
TargetPath->Length = min(TargetPath->MaximumLength - sizeof(WCHAR),
|
|
(ULONG) ValueCell->DataSize);
|
|
|
|
if (ValueCell->DataSize > 0)
|
|
{
|
|
DataCell = CmiGetCell (RegistryHive, ValueCell->DataOffset, NULL);
|
|
RtlCopyMemory(TargetPath->Buffer,
|
|
DataCell->Data,
|
|
TargetPath->Length);
|
|
TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(TargetPath->Buffer,
|
|
&ValueCell->DataOffset,
|
|
TargetPath->Length);
|
|
TargetPath->Buffer[TargetPath->Length / sizeof(WCHAR)] = 0;
|
|
}
|
|
|
|
DPRINT("TargetPath '%wZ'\n", TargetPath);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
/* EOF */
|