mirror of https://github.com/tongzx/nt5src
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.
4920 lines
140 KiB
4920 lines
140 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmapi.c
|
|
|
|
Abstract:
|
|
|
|
This module contains CM level entry points for the registry.
|
|
|
|
Author:
|
|
|
|
Bryan M. Willman (bryanwi) 30-Aug-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmp.h"
|
|
|
|
|
|
|
|
extern BOOLEAN CmpNoWrite;
|
|
|
|
extern LIST_ENTRY CmpHiveListHead;
|
|
|
|
extern BOOLEAN CmpProfileLoaded;
|
|
extern BOOLEAN CmpWasSetupBoot;
|
|
|
|
extern UNICODE_STRING CmSymbolicLinkValueName;
|
|
|
|
extern ULONG CmpGlobalQuotaAllowed;
|
|
extern ULONG CmpGlobalQuotaWarning;
|
|
extern PCMHIVE CmpMasterHive;
|
|
extern HIVE_LIST_ENTRY CmpMachineHiveList[];
|
|
|
|
VOID
|
|
CmpDereferenceNameControlBlockWithLock(
|
|
PCM_NAME_CONTROL_BLOCK Ncb
|
|
);
|
|
|
|
//
|
|
// procedures private to this file
|
|
//
|
|
NTSTATUS
|
|
CmpSetValueKeyExisting(
|
|
IN PHHIVE Hive,
|
|
IN HCELL_INDEX OldChild,
|
|
IN PCM_KEY_VALUE Value,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize,
|
|
IN ULONG StorageType,
|
|
IN ULONG TempData
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
CmpSetValueKeyNew(
|
|
IN PHHIVE Hive,
|
|
IN PCM_KEY_NODE Parent,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG Index,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize,
|
|
IN ULONG StorageType,
|
|
IN ULONG TempData
|
|
);
|
|
|
|
VOID
|
|
CmpRemoveKeyHash(
|
|
IN PCM_KEY_HASH KeyHash
|
|
);
|
|
|
|
PCM_KEY_CONTROL_BLOCK
|
|
CmpInsertKeyHash(
|
|
IN PCM_KEY_HASH KeyHash,
|
|
IN BOOLEAN FakeKey
|
|
);
|
|
|
|
#if DBG
|
|
ULONG
|
|
CmpUnloadKeyWorker(
|
|
PCM_KEY_CONTROL_BLOCK Current,
|
|
PVOID Context1,
|
|
PVOID Context2
|
|
);
|
|
#endif
|
|
|
|
ULONG
|
|
CmpCompressKeyWorker(
|
|
PCM_KEY_CONTROL_BLOCK Current,
|
|
PVOID Context1,
|
|
PVOID Context2
|
|
);
|
|
|
|
NTSTATUS
|
|
CmpDuplicateKey(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX OldKeyCell,
|
|
PHCELL_INDEX NewKeyCell
|
|
);
|
|
|
|
|
|
VOID
|
|
CmpDestroyTemporaryHive(
|
|
PCMHIVE CmHive
|
|
);
|
|
|
|
BOOLEAN
|
|
CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
PUNICODE_STRING ValueName,
|
|
ULONG Type,
|
|
PVOID Data,
|
|
ULONG DataSize
|
|
);
|
|
BOOLEAN
|
|
CmpGetValueDataFromCache(
|
|
IN PHHIVE Hive,
|
|
IN PPCM_CACHED_VALUE ContainingList,
|
|
IN PCELL_DATA ValueKey,
|
|
IN BOOLEAN ValueCached,
|
|
OUT PUCHAR *DataPointer,
|
|
OUT PBOOLEAN Allocated,
|
|
OUT PHCELL_INDEX CellToRelease
|
|
);
|
|
|
|
BOOLEAN
|
|
CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
PUNICODE_STRING ValueName,
|
|
ULONG Type,
|
|
PVOID Data,
|
|
ULONG DataSize
|
|
);
|
|
BOOLEAN
|
|
CmpGetValueDataFromCache(
|
|
IN PHHIVE Hive,
|
|
IN PPCM_CACHED_VALUE ContainingList,
|
|
IN PCELL_DATA ValueKey,
|
|
IN BOOLEAN ValueCached,
|
|
OUT PUCHAR *DataPointer,
|
|
OUT PBOOLEAN Allocated,
|
|
OUT PHCELL_INDEX CellToRelease
|
|
);
|
|
|
|
BOOLEAN
|
|
CmpIsHiveAlreadyLoaded( IN HANDLE KeyHandle,
|
|
IN POBJECT_ATTRIBUTES SourceFile
|
|
);
|
|
|
|
NTSTATUS
|
|
static
|
|
__forceinline
|
|
CmpCheckReplaceHive( IN PHHIVE Hive,
|
|
OUT PHCELL_INDEX Key
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmDeleteValueKey)
|
|
#pragma alloc_text(PAGE,CmEnumerateKey)
|
|
#pragma alloc_text(PAGE,CmEnumerateValueKey)
|
|
#pragma alloc_text(PAGE,CmFlushKey)
|
|
#pragma alloc_text(PAGE,CmQueryKey)
|
|
#pragma alloc_text(PAGE,CmQueryValueKey)
|
|
#pragma alloc_text(PAGE,CmQueryMultipleValueKey)
|
|
#pragma alloc_text(PAGE,CmSetValueKey)
|
|
#pragma alloc_text(PAGE,CmpSetValueKeyExisting)
|
|
#pragma alloc_text(PAGE,CmpSetValueKeyNew)
|
|
#pragma alloc_text(PAGE,CmSetLastWriteTimeKey)
|
|
#pragma alloc_text(PAGE,CmSetKeyUserFlags)
|
|
#pragma alloc_text(PAGE,CmLoadKey)
|
|
#pragma alloc_text(PAGE,CmUnloadKey)
|
|
|
|
#ifdef NT_UNLOAD_KEY_EX
|
|
#pragma alloc_text(PAGE,CmUnloadKeyEx)
|
|
#endif //NT_UNLOAD_KEY_EX
|
|
|
|
#pragma alloc_text(PAGE,CmpDoFlushAll)
|
|
#pragma alloc_text(PAGE,CmReplaceKey)
|
|
|
|
#ifdef WRITE_PROTECTED_REGISTRY_POOL
|
|
#pragma alloc_text(PAGE,CmpMarkAllBinsReadOnly)
|
|
#endif //WRITE_PROTECTED_REGISTRY_POOL
|
|
|
|
#ifdef NT_RENAME_KEY
|
|
#pragma alloc_text(PAGE,CmRenameKey)
|
|
#endif //NT_RENAME_KEY
|
|
|
|
#pragma alloc_text(PAGE,CmLockKcbForWrite)
|
|
|
|
#if DBG
|
|
#pragma alloc_text(PAGE,CmpUnloadKeyWorker)
|
|
#endif
|
|
|
|
#pragma alloc_text(PAGE,CmMoveKey)
|
|
#pragma alloc_text(PAGE,CmpDuplicateKey)
|
|
#pragma alloc_text(PAGE,CmCompressKey)
|
|
#pragma alloc_text(PAGE,CmpCompressKeyWorker)
|
|
#pragma alloc_text(PAGE,CmpCompareNewValueDataAgainstKCBCache)
|
|
#pragma alloc_text(PAGE,CmpIsHiveAlreadyLoaded)
|
|
#pragma alloc_text(PAGE,CmpCheckReplaceHive)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
CmDeleteValueKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN UNICODE_STRING ValueName // RAW
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
One of the value entries of a registry key may be removed with this call.
|
|
|
|
The value entry with ValueName matching ValueName is removed from the key.
|
|
If no such entry exists, an error is returned.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for key to operate on
|
|
|
|
ValueName - The name of the value to be deleted. NULL is a legal name.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PCM_KEY_NODE pcell = NULL;
|
|
PCHILD_LIST plist;
|
|
PCM_KEY_VALUE Value = NULL;
|
|
ULONG targetindex;
|
|
HCELL_INDEX ChildCell;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
ULONG realsize;
|
|
LARGE_INTEGER systemtime;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmDeleteValueKey\n"));
|
|
|
|
CmpLockRegistryExclusive();
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
PERFINFO_REG_DELETE_VALUE(KeyControlBlock, &ValueName);
|
|
|
|
try {
|
|
//
|
|
// no edits, not even this one, on keys marked for deletion
|
|
//
|
|
if (KeyControlBlock->Delete) {
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
|
|
pcell = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if( pcell == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
plist = &(pcell->ValueList);
|
|
ChildCell = HCELL_NIL;
|
|
|
|
if (plist->Count != 0) {
|
|
|
|
//
|
|
// The parent has at least one value, map in the list of
|
|
// values and call CmpFindChildInList
|
|
//
|
|
|
|
//
|
|
// plist -> the CHILD_LIST structure
|
|
// pchild -> the child node structure being examined
|
|
//
|
|
|
|
if( CmpFindNameInList(Hive,
|
|
plist,
|
|
&ValueName,
|
|
&targetindex,
|
|
&ChildCell) == FALSE ) {
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (ChildCell != HCELL_NIL) {
|
|
|
|
//
|
|
// 1. the desired target was found
|
|
// 2. ChildCell is it's HCELL_INDEX
|
|
// 3. targetaddress points to it
|
|
// 4. targetindex is it's index
|
|
//
|
|
|
|
//
|
|
// attempt to mark all relevent cells dirty
|
|
//
|
|
if (!(HvMarkCellDirty(Hive, Cell) &&
|
|
HvMarkCellDirty(Hive, pcell->ValueList.List) &&
|
|
HvMarkCellDirty(Hive, ChildCell)))
|
|
|
|
{
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
|
|
if( Value == NULL ) {
|
|
//
|
|
// could not map view inside
|
|
// this is impossible as we just dirtied the view
|
|
//
|
|
ASSERT( FALSE );
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if( !CmpMarkValueDataDirty(Hive,Value) ) {
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return(STATUS_NO_LOG_SPACE);
|
|
}
|
|
|
|
// sanity
|
|
ASSERT_CELL_DIRTY(Hive,pcell->ValueList.List);
|
|
ASSERT_CELL_DIRTY(Hive,ChildCell);
|
|
|
|
if( !NT_SUCCESS(CmpRemoveValueFromList(Hive,targetindex,plist)) ) {
|
|
//
|
|
// bail out !
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if( CmpFreeValue(Hive, ChildCell) == FALSE ) {
|
|
//
|
|
// we couldn't map a view inside above call
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeQuerySystemTime(&systemtime);
|
|
pcell->LastWriteTime = systemtime;
|
|
// cache it in the kcb too.
|
|
KeyControlBlock->KcbLastWriteTime = systemtime;
|
|
|
|
// some sanity asserts
|
|
ASSERT( pcell->MaxValueNameLen == KeyControlBlock->KcbMaxValueNameLen );
|
|
ASSERT( pcell->MaxValueDataLen == KeyControlBlock->KcbMaxValueDataLen );
|
|
ASSERT_CELL_DIRTY(Hive,Cell);
|
|
|
|
if (pcell->ValueList.Count == 0) {
|
|
pcell->MaxValueNameLen = 0;
|
|
pcell->MaxValueDataLen = 0;
|
|
// update the kcb cache too
|
|
KeyControlBlock->KcbMaxValueNameLen = 0;
|
|
KeyControlBlock->KcbMaxValueDataLen = 0;
|
|
}
|
|
|
|
//
|
|
// We are changing the KCB cache. Since the registry is locked exclusively,
|
|
// we do not need a KCB lock.
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
//
|
|
// Invalidate and rebuild the cache
|
|
//
|
|
CmpCleanUpKcbValueCache(KeyControlBlock);
|
|
CmpSetUpKcbValueCache(KeyControlBlock,plist->Count,plist->List);
|
|
|
|
CmpReportNotify(
|
|
KeyControlBlock,
|
|
KeyControlBlock->KeyHive,
|
|
KeyControlBlock->KeyCell,
|
|
REG_NOTIFY_CHANGE_LAST_SET
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
} finally {
|
|
if(pcell != NULL){
|
|
HvReleaseCell(Hive, Cell);
|
|
}
|
|
if(Value != NULL){
|
|
ASSERT( ChildCell != HCELL_NIL );
|
|
HvReleaseCell(Hive, ChildCell);
|
|
}
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmEnumerateKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN ULONG Index,
|
|
IN KEY_INFORMATION_CLASS KeyInformationClass,
|
|
IN PVOID KeyInformation,
|
|
IN ULONG Length,
|
|
IN PULONG ResultLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerate sub keys, return data on Index'th entry.
|
|
|
|
CmEnumerateKey returns the name of the Index'th sub key of the open
|
|
key specified. The value STATUS_NO_MORE_ENTRIES will be
|
|
returned if value of Index is larger than the number of sub keys.
|
|
|
|
Note that Index is simply a way to select among child keys. Two calls
|
|
to CmEnumerateKey with the same Index are NOT guaranteed to return
|
|
the same results.
|
|
|
|
If KeyInformation is not long enough to hold all requested data,
|
|
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
|
|
set to the number of bytes actually required.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to the KCB that describes the key
|
|
|
|
Index - Specifies the (0-based) number of the sub key to be returned.
|
|
|
|
KeyInformationClass - Specifies the type of information returned in
|
|
Buffer. One of the following types:
|
|
|
|
KeyBasicInformation - return last write time, title index, and name.
|
|
(see KEY_BASIC_INFORMATION structure)
|
|
|
|
KeyNodeInformation - return last write time, title index, name, class.
|
|
(see KEY_NODE_INFORMATION structure)
|
|
|
|
KeyInformation -Supplies pointer to buffer to receive the data.
|
|
|
|
Length - Length of KeyInformation in bytes.
|
|
|
|
ResultLength - Number of bytes actually written into KeyInformation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HCELL_INDEX childcell;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
PCM_KEY_NODE Node;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmEnumerateKey\n"));
|
|
|
|
|
|
CmpLockRegistry();
|
|
|
|
PERFINFO_REG_ENUM_KEY(KeyControlBlock, Index);
|
|
|
|
if (KeyControlBlock->Delete) {
|
|
CmpUnlockRegistry();
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
//
|
|
// fetch the child of interest
|
|
//
|
|
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
CmpUnlockRegistry();
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
childcell = CmpFindSubKeyByNumber(Hive, Node, Index);
|
|
|
|
// release this cell here as we don't need this Node anymore
|
|
HvReleaseCell(Hive, Cell);
|
|
|
|
if (childcell == HCELL_NIL) {
|
|
//
|
|
// no such child, clean up and return error
|
|
//
|
|
// we cannot return STATUS_INSUFFICIENT_RESOURCES because of Iop
|
|
// subsystem which treats INSUFFICIENT RESOURCES as no fatal error
|
|
//
|
|
CmpUnlockRegistry();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive,childcell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
CmpUnlockRegistry();
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// call a worker to perform data transfer
|
|
//
|
|
|
|
status = CmpQueryKeyData(Hive,
|
|
Node,
|
|
KeyInformationClass,
|
|
KeyInformation,
|
|
Length,
|
|
ResultLength
|
|
#if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
|
|
,
|
|
NULL
|
|
#endif
|
|
);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
HvReleaseCell(Hive, childcell);
|
|
|
|
CmpUnlockRegistry();
|
|
status = GetExceptionCode();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return status;
|
|
}
|
|
|
|
HvReleaseCell(Hive, childcell);
|
|
|
|
CmpUnlockRegistry();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CmEnumerateValueKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN ULONG Index,
|
|
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
|
|
IN PVOID KeyValueInformation,
|
|
IN ULONG Length,
|
|
IN PULONG ResultLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The value entries of an open key may be enumerated.
|
|
|
|
CmEnumerateValueKey returns the name of the Index'th value
|
|
entry of the open key specified by KeyHandle. The value
|
|
STATUS_NO_MORE_ENTRIES will be returned if value of Index is
|
|
larger than the number of sub keys.
|
|
|
|
Note that Index is simply a way to select among value
|
|
entries. Two calls to NtEnumerateValueKey with the same Index
|
|
are NOT guaranteed to return the same results.
|
|
|
|
If KeyValueInformation is not long enough to hold all requested data,
|
|
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
|
|
set to the number of bytes actually required.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to the KCB that describes the key
|
|
|
|
Index - Specifies the (0-based) number of the sub key to be returned.
|
|
|
|
KeyValueInformationClass - Specifies the type of information returned
|
|
in Buffer. One of the following types:
|
|
|
|
KeyValueBasicInformation - return time of last write,
|
|
title index, and name. (See KEY_VALUE_BASIC_INFORMATION)
|
|
|
|
KeyValueFullInformation - return time of last write,
|
|
title index, name, class. (See KEY_VALUE_FULL_INFORMATION)
|
|
|
|
KeyValueInformation -Supplies pointer to buffer to receive the data.
|
|
|
|
Length - Length of KeyValueInformation in bytes.
|
|
|
|
ResultLength - Number of bytes actually written into KeyValueInformation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PHHIVE Hive;
|
|
PCM_KEY_NODE Node;
|
|
PCELL_DATA ChildList;
|
|
PCM_KEY_VALUE ValueData;
|
|
BOOLEAN IndexCached;
|
|
BOOLEAN ValueCached;
|
|
PPCM_CACHED_VALUE ContainingList;
|
|
HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmEnumerateValueKey\n"));
|
|
|
|
|
|
//
|
|
// lock the parent cell
|
|
//
|
|
|
|
CmpLockRegistry();
|
|
|
|
PERFINFO_REG_ENUM_VALUE(KeyControlBlock, Index);
|
|
|
|
if (KeyControlBlock->Delete) {
|
|
CmpUnlockRegistry();
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, KeyControlBlock->KeyCell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
CmpUnlockRegistry();
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// fetch the child of interest
|
|
//
|
|
//
|
|
// Do it using the cache
|
|
//
|
|
if (Index >= KeyControlBlock->ValueCache.Count) {
|
|
//
|
|
// No such child, clean up and return error.
|
|
//
|
|
HvReleaseCell(Hive, KeyControlBlock->KeyCell);
|
|
CmpUnlockRegistry();
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
BEGIN_KCB_LOCK_GUARD;
|
|
CmpLockKCBTreeExclusive();
|
|
|
|
if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) {
|
|
//
|
|
// The value list is now set to the KCB for symbolic link,
|
|
// Clean it up and set the value right before we do the query.
|
|
//
|
|
CmpCleanUpKcbValueCache(KeyControlBlock);
|
|
CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
|
|
}
|
|
|
|
ChildList = CmpGetValueListFromCache(Hive, &(KeyControlBlock->ValueCache), &IndexCached);
|
|
if( ChildList == NULL ) {
|
|
//
|
|
// couldn't map view; treat it as insufficient resources
|
|
//
|
|
|
|
HvReleaseCell(Hive, KeyControlBlock->KeyCell);
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
CmpUnlockKCBTree();
|
|
CmpUnlockRegistry();
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
}
|
|
ValueData = CmpGetValueKeyFromCache(Hive, ChildList, Index, &ContainingList, IndexCached, &ValueCached,&ValueDataCellToRelease);
|
|
if( ValueData == NULL ) {
|
|
//
|
|
// couldn't map view; treat it as insufficient resources
|
|
//
|
|
|
|
HvReleaseCell(Hive, KeyControlBlock->KeyCell);
|
|
if( ValueDataCellToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ValueDataCellToRelease);
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
CmpUnlockKCBTree();
|
|
CmpUnlockRegistry();
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
END_KCB_LOCK_GUARD;
|
|
|
|
|
|
// Trying to catch the BAD guy who writes over our pool.
|
|
CmpMakeValueCacheReadWrite(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
|
|
|
|
try {
|
|
|
|
//
|
|
// call a worker to perform data transfer; we are touching user-mode address; do it in a try/except
|
|
//
|
|
status = CmpQueryKeyValueData(Hive,
|
|
ContainingList,
|
|
ValueData,
|
|
ValueCached,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmEnumerateValueKey: code:%08lx\n", GetExceptionCode()));
|
|
status = GetExceptionCode();
|
|
}
|
|
|
|
// Trying to catch the BAD guy who writes over our pool.
|
|
CmpMakeValueCacheReadOnly(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
|
|
|
|
HvReleaseCell(Hive, KeyControlBlock->KeyCell);
|
|
|
|
if( ValueDataCellToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ValueDataCellToRelease);
|
|
}
|
|
|
|
CmpUnlockKCBTree();
|
|
CmpUnlockRegistry();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CmFlushKey(
|
|
IN PHHIVE Hive,
|
|
IN HCELL_INDEX Cell
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Forces changes made to a key to disk.
|
|
|
|
CmFlushKey will not return to its caller until any changed data
|
|
associated with the key has been written out.
|
|
|
|
WARNING: CmFlushKey will flush the entire registry tree, and thus will
|
|
burn cycles and I/O.
|
|
|
|
Arguments:
|
|
|
|
Hive - supplies a pointer to the hive control structure for the hive
|
|
|
|
Cell - supplies index of node to whose sub keys are to be found
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE CmHive;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmFlushKey\n"));
|
|
|
|
//
|
|
// If writes are not working, lie and say we succeeded, will
|
|
// clean up in a short time. Only early system init code
|
|
// will ever know the difference.
|
|
//
|
|
if (CmpNoWrite) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
|
|
//
|
|
// Don't flush the master hive. If somebody asks for a flushkey on
|
|
// the master hive, do a CmpDoFlushAll instead. CmpDoFlushAll flushes
|
|
// every hive except the master hive, which is what they REALLY want.
|
|
//
|
|
if (CmHive == CmpMasterHive) {
|
|
CmpDoFlushAll(FALSE);
|
|
} else {
|
|
DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
|
|
|
|
CmLockHive (CmHive);
|
|
CmLockHiveViews (CmHive);
|
|
|
|
if( HvHiveWillShrink( &(CmHive->Hive) ) ) {
|
|
//
|
|
// we may end up here is when the hive shrinks and we need
|
|
// exclusive access over the registry, as we are going to CcPurge !
|
|
//
|
|
CmUnlockHiveViews (CmHive);
|
|
CmUnlockHive (CmHive);
|
|
CmpUnlockRegistry();
|
|
CmpLockRegistryExclusive();
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
CmLockHive (CmHive);
|
|
|
|
if( CmHive->UseCount != 0) {
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
CmpFixHiveUsageCount(CmHive);
|
|
ASSERT( CmHive->UseCount == 0 );
|
|
}
|
|
} else {
|
|
//
|
|
// release the views
|
|
//
|
|
CmUnlockHiveViews (CmHive);
|
|
}
|
|
|
|
if (! HvSyncHive(Hive)) {
|
|
|
|
status = STATUS_REGISTRY_IO_FAILED;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmFlushKey: HvSyncHive failed\n"));
|
|
}
|
|
|
|
CmUnlockHive (CmHive);
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmQueryKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN KEY_INFORMATION_CLASS KeyInformationClass,
|
|
IN PVOID KeyInformation,
|
|
IN ULONG Length,
|
|
IN PULONG ResultLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Data about the class of a key, and the numbers and sizes of its
|
|
children and value entries may be queried with CmQueryKey.
|
|
|
|
NOTE: The returned lengths are guaranteed to be at least as
|
|
long as the described values, but may be longer in
|
|
some circumstances.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to the KCB that describes the key
|
|
|
|
KeyInformationClass - Specifies the type of information
|
|
returned in Buffer. One of the following types:
|
|
|
|
KeyBasicInformation - return last write time, title index, and name.
|
|
(See KEY_BASIC_INFORMATION)
|
|
|
|
KeyNodeInformation - return last write time, title index, name, class.
|
|
(See KEY_NODE_INFORMATION)
|
|
|
|
KeyFullInformation - return all data except for name and security.
|
|
(See KEY_FULL_INFORMATION)
|
|
|
|
KeyInformation -Supplies pointer to buffer to receive the data.
|
|
|
|
Length - Length of KeyInformation in bytes.
|
|
|
|
ResultLength - Number of bytes actually written into KeyInformation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PCM_KEY_NODE Node = NULL;
|
|
PUNICODE_STRING Name = NULL;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryKey\n"));
|
|
|
|
CmpLockRegistry();
|
|
|
|
PERFINFO_REG_QUERY_KEY(KeyControlBlock);
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
try {
|
|
|
|
//
|
|
// request for the FULL path of the key
|
|
//
|
|
if( KeyInformationClass == KeyNameInformation ) {
|
|
if (KeyControlBlock->Delete ) {
|
|
//
|
|
// special case: return key deleted status, but still fill the full name of the key.
|
|
//
|
|
status = STATUS_KEY_DELETED;
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if( KeyControlBlock->NameBlock ) {
|
|
|
|
Name = CmpConstructName(KeyControlBlock);
|
|
if (Name == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
ULONG requiredlength;
|
|
ULONG minimumlength;
|
|
USHORT NameLength;
|
|
LONG leftlength;
|
|
PKEY_INFORMATION pbuffer = (PKEY_INFORMATION)KeyInformation;
|
|
|
|
NameLength = Name->Length;
|
|
|
|
requiredlength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + NameLength;
|
|
|
|
minimumlength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name);
|
|
|
|
*ResultLength = requiredlength;
|
|
if (Length < minimumlength) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else {
|
|
//
|
|
// Fill in the length of the name
|
|
//
|
|
pbuffer->KeyNameInformation.NameLength = NameLength;
|
|
|
|
//
|
|
// Now copy the full name into the user buffer, if enough space
|
|
//
|
|
leftlength = Length - minimumlength;
|
|
requiredlength = NameLength;
|
|
if (leftlength < (LONG)requiredlength) {
|
|
requiredlength = leftlength;
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
//
|
|
// If not enough space, copy how much we can and return overflow
|
|
//
|
|
RtlCopyMemory(
|
|
&(pbuffer->KeyNameInformation.Name[0]),
|
|
Name->Buffer,
|
|
requiredlength
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} else if(KeyControlBlock->Delete ) {
|
|
//
|
|
// key already deleted
|
|
//
|
|
status = STATUS_KEY_DELETED;
|
|
} else if( KeyInformationClass == KeyFlagsInformation ) {
|
|
//
|
|
// we only want to get the user defined flags;
|
|
//
|
|
PKEY_INFORMATION pbuffer = (PKEY_INFORMATION)KeyInformation;
|
|
ULONG requiredlength;
|
|
|
|
requiredlength = sizeof(KEY_FLAGS_INFORMATION);
|
|
|
|
*ResultLength = requiredlength;
|
|
|
|
if (Length < requiredlength) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
pbuffer->KeyFlagsInformation.UserFlags = (ULONG)((USHORT)KeyControlBlock->Flags >> KEY_USER_FLAGS_SHIFT);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
//
|
|
// call a worker to perform data transfer
|
|
//
|
|
|
|
if( KeyInformationClass == KeyCachedInformation ) {
|
|
//
|
|
// call the fast version
|
|
//
|
|
status = CmpQueryKeyDataFromCache( KeyControlBlock,
|
|
KeyInformationClass,
|
|
KeyInformation,
|
|
Length,
|
|
ResultLength );
|
|
} else {
|
|
//
|
|
// old'n plain slow version
|
|
//
|
|
Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
status = CmpQueryKeyData(KeyControlBlock->KeyHive,
|
|
Node,
|
|
KeyInformationClass,
|
|
KeyInformation,
|
|
Length,
|
|
ResultLength
|
|
#if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
|
|
,
|
|
KeyControlBlock
|
|
#endif
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
if( Node != NULL ) {
|
|
HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
}
|
|
|
|
if( Name != NULL ) {
|
|
ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
|
|
}
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmQueryValueKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN UNICODE_STRING ValueName,
|
|
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
|
|
IN PVOID KeyValueInformation,
|
|
IN ULONG Length,
|
|
IN PULONG ResultLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The ValueName, TitleIndex, Type, and Data for any one of a key's
|
|
value entries may be queried with CmQueryValueKey.
|
|
|
|
If KeyValueInformation is not long enough to hold all requested data,
|
|
STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
|
|
set to the number of bytes actually required.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to the KCB that describes the key
|
|
|
|
ValueName - The name of the value entry to return data for.
|
|
|
|
KeyValueInformationClass - Specifies the type of information
|
|
returned in KeyValueInformation. One of the following types:
|
|
|
|
KeyValueBasicInformation - return time of last write, title
|
|
index, and name. (See KEY_VALUE_BASIC_INFORMATION)
|
|
|
|
KeyValueFullInformation - return time of last write, title
|
|
index, name, class. (See KEY_VALUE_FULL_INFORMATION)
|
|
|
|
KeyValueInformation -Supplies pointer to buffer to receive the data.
|
|
|
|
Length - Length of KeyValueInformation in bytes.
|
|
|
|
ResultLength - Number of bytes actually written into KeyValueInformation.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
HCELL_INDEX childcell;
|
|
PHCELL_INDEX childindex;
|
|
HCELL_INDEX Cell;
|
|
PCM_KEY_VALUE ValueData;
|
|
ULONG Index;
|
|
BOOLEAN ValueCached;
|
|
PPCM_CACHED_VALUE ContainingList;
|
|
HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
|
|
|
|
PAGED_CODE();
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryValueKey\n"));
|
|
|
|
|
|
CmpLockRegistry();
|
|
|
|
PERFINFO_REG_QUERY_VALUE(KeyControlBlock, &ValueName);
|
|
|
|
if (KeyControlBlock->Delete) {
|
|
|
|
CmpUnlockRegistry();
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
BEGIN_KCB_LOCK_GUARD;
|
|
|
|
CmpLockKCBTreeExclusive();
|
|
|
|
if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) {
|
|
//
|
|
// The value list is now set to the KCB for symbolic link,
|
|
// Clean it up and set the value right before we do the query.
|
|
//
|
|
CmpCleanUpKcbValueCache(KeyControlBlock);
|
|
|
|
{
|
|
PCM_KEY_NODE Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
CmpUnlockKCBTree();
|
|
CmpUnlockRegistry();
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
|
|
|
|
HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the data
|
|
//
|
|
|
|
ValueData = CmpFindValueByNameFromCache(KeyControlBlock->KeyHive,
|
|
&(KeyControlBlock->ValueCache),
|
|
&ValueName,
|
|
&ContainingList,
|
|
&Index,
|
|
&ValueCached,
|
|
&ValueDataCellToRelease
|
|
);
|
|
|
|
END_KCB_LOCK_GUARD;
|
|
|
|
if (ValueData) {
|
|
|
|
// Trying to catch the BAD guy who writes over our pool.
|
|
CmpMakeValueCacheReadWrite(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
|
|
|
|
try {
|
|
|
|
//
|
|
// call a worker to perform data transfer; we are touching user-mode address; do it in a try/except
|
|
//
|
|
|
|
status = CmpQueryKeyValueData(KeyControlBlock->KeyHive,
|
|
ContainingList,
|
|
ValueData,
|
|
ValueCached,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmQueryValueKey: code:%08lx\n", GetExceptionCode()));
|
|
status = GetExceptionCode();
|
|
}
|
|
|
|
// Trying to catch the BAD guy who writes over our pool.
|
|
CmpMakeValueCacheReadOnly(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
|
|
} else {
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
|
|
if(ValueDataCellToRelease != HCELL_NIL) {
|
|
HvReleaseCell(KeyControlBlock->KeyHive,ValueDataCellToRelease);
|
|
}
|
|
CmpUnlockKCBTree();
|
|
CmpUnlockRegistry();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmQueryMultipleValueKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN PKEY_VALUE_ENTRY ValueEntries,
|
|
IN ULONG EntryCount,
|
|
IN PVOID ValueBuffer,
|
|
IN OUT PULONG BufferLength,
|
|
IN OPTIONAL PULONG ResultLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Multiple values of any key may be queried atomically with
|
|
this api.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - Supplies the key to be queried.
|
|
|
|
ValueEntries - Returns an array of KEY_VALUE_ENTRY structures, one for each value.
|
|
|
|
EntryCount - Supplies the number of entries in the ValueNames and ValueEntries arrays
|
|
|
|
ValueBuffer - Returns the value data for each value.
|
|
|
|
BufferLength - Supplies the length of the ValueBuffer array in bytes.
|
|
Returns the length of the ValueBuffer array that was filled in.
|
|
|
|
ResultLength - if present, Returns the length in bytes of the ValueBuffer
|
|
array required to return the requested values of this key.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PHHIVE Hive;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
UNICODE_STRING CurrentName;
|
|
HCELL_INDEX ValueCell = HCELL_NIL;
|
|
PCM_KEY_VALUE ValueNode;
|
|
ULONG RequiredLength = 0;
|
|
ULONG UsedLength = 0;
|
|
ULONG DataLength;
|
|
BOOLEAN BufferFull = FALSE;
|
|
BOOLEAN Small;
|
|
PUCHAR Data;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PCM_KEY_NODE Node;
|
|
|
|
PAGED_CODE();
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryMultipleValueKey\n"));
|
|
|
|
|
|
CmpLockRegistry();
|
|
|
|
if (KeyControlBlock->Delete) {
|
|
CmpUnlockRegistry();
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, KeyControlBlock->KeyCell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
CmpUnlockRegistry();
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
try {
|
|
for (i=0; i < EntryCount; i++) {
|
|
//
|
|
// find the data
|
|
//
|
|
if (PreviousMode == UserMode) {
|
|
CurrentName = ProbeAndReadUnicodeString(ValueEntries[i].ValueName);
|
|
ProbeForRead(CurrentName.Buffer,CurrentName.Length,sizeof(WCHAR));
|
|
} else {
|
|
CurrentName = *(ValueEntries[i].ValueName);
|
|
}
|
|
|
|
PERFINFO_REG_QUERY_MULTIVALUE(KeyControlBlock, &CurrentName);
|
|
|
|
ValueCell = CmpFindValueByName(Hive,
|
|
Node,
|
|
&CurrentName);
|
|
if (ValueCell != HCELL_NIL) {
|
|
|
|
ValueNode = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
|
|
if( ValueNode == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
Small = CmpIsHKeyValueSmall(DataLength, ValueNode->DataLength);
|
|
|
|
//
|
|
// Round up UsedLength and RequiredLength to a ULONG boundary
|
|
//
|
|
UsedLength = (UsedLength + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);
|
|
RequiredLength = (RequiredLength + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);
|
|
|
|
//
|
|
// If there is enough room for this data value in the buffer,
|
|
// fill it in now. Otherwise, mark the buffer as full. We must
|
|
// keep iterating through the values in order to determine the
|
|
// RequiredLength.
|
|
//
|
|
if ((UsedLength + DataLength <= *BufferLength) &&
|
|
(!BufferFull)) {
|
|
PCELL_DATA Buffer;
|
|
BOOLEAN BufferAllocated;
|
|
HCELL_INDEX CellToRelease;
|
|
//
|
|
// get the data from source, regardless of the size
|
|
//
|
|
if( CmpGetValueData(Hive,ValueNode,&DataLength,&Buffer,&BufferAllocated,&CellToRelease) == FALSE ) {
|
|
//
|
|
// insufficient resources; return NULL
|
|
//
|
|
ASSERT( BufferAllocated == FALSE );
|
|
ASSERT( Buffer == NULL );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory((PUCHAR)ValueBuffer + UsedLength,
|
|
Buffer,
|
|
DataLength);
|
|
//
|
|
// cleanup the temporary buffer
|
|
//
|
|
if( BufferAllocated == TRUE ) {
|
|
ExFreePool( Buffer );
|
|
}
|
|
//
|
|
// release the buffer in case we are using hive storage
|
|
//
|
|
if( CellToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,CellToRelease);
|
|
}
|
|
|
|
ValueEntries[i].Type = ValueNode->Type;
|
|
ValueEntries[i].DataLength = DataLength;
|
|
ValueEntries[i].DataOffset = UsedLength;
|
|
UsedLength += DataLength;
|
|
} else {
|
|
BufferFull = TRUE;
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
RequiredLength += DataLength;
|
|
HvReleaseCell(Hive, ValueCell);
|
|
ValueCell = HCELL_NIL;
|
|
} else {
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) ||
|
|
(Status == STATUS_BUFFER_OVERFLOW)) {
|
|
*BufferLength = UsedLength;
|
|
if (ARGUMENT_PRESENT(ResultLength)) {
|
|
*ResultLength = RequiredLength;
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
if( ValueCell != HCELL_NIL) {
|
|
HvReleaseCell(Hive, ValueCell);
|
|
}
|
|
HvReleaseCell(Hive, KeyControlBlock->KeyCell);
|
|
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(Hive);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmSetValueKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A value entry may be created or replaced with CmSetValueKey.
|
|
|
|
If a value entry with a Value ID (i.e. name) matching the
|
|
one specified by ValueName exists, it is deleted and replaced
|
|
with the one specified. If no such value entry exists, a new
|
|
one is created. NULL is a legal Value ID. While Value IDs must
|
|
be unique within any given key, the same Value ID may appear
|
|
in many different keys.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for the key to operate on
|
|
|
|
ValueName - The unique (relative to the containing key) name
|
|
of the value entry. May be NULL.
|
|
|
|
Type - The integer type number of the value entry.
|
|
|
|
Data - Pointer to buffer with actual data for the value entry.
|
|
|
|
DataSize - Size of Data buffer.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PCM_KEY_NODE parent = NULL;
|
|
HCELL_INDEX oldchild;
|
|
ULONG count;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
ULONG StorageType;
|
|
ULONG TempData;
|
|
BOOLEAN found;
|
|
PCM_KEY_VALUE Value = NULL;
|
|
LARGE_INTEGER systemtime;
|
|
ULONG mustChange=FALSE;
|
|
ULONG ChildIndex;
|
|
HCELL_INDEX ParentToRelease = HCELL_NIL;
|
|
HCELL_INDEX ChildToRelease = HCELL_NIL;
|
|
|
|
PERFINFO_REG_SET_VALUE_DECL();
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetValueKey\n"));
|
|
|
|
CmpLockRegistry();
|
|
|
|
ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
|
|
|
|
PERFINFO_REG_SET_VALUE(KeyControlBlock);
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
while (TRUE) {
|
|
//
|
|
// Check that we are not being asked to add a value to a key
|
|
// that has been deleted
|
|
//
|
|
if (KeyControlBlock->Delete == TRUE) {
|
|
status = STATUS_KEY_DELETED;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Check to see if this is a symbolic link node. If so caller
|
|
// is only allowed to create/change the SymbolicLinkValue
|
|
// value name
|
|
//
|
|
|
|
#ifdef CMP_KCB_CACHE_VALIDATION
|
|
{
|
|
PCM_KEY_NODE Node;
|
|
Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
ASSERT( Node->Flags == KeyControlBlock->Flags );
|
|
HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
|
|
}
|
|
#endif
|
|
if (KeyControlBlock->Flags & KEY_SYM_LINK &&
|
|
(( (Type != REG_LINK)
|
|
#ifdef CM_DYN_SYM_LINK
|
|
&& (Type != REG_DYN_LINK)
|
|
#endif //CM_DYN_SYM_LINK
|
|
) ||
|
|
ValueName == NULL ||
|
|
!RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE)))
|
|
{
|
|
//
|
|
// Disallow attempts to manipulate any value names under a symbolic link
|
|
// except for the "SymbolicLinkValue" value name or type other than REG_LINK
|
|
//
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
status = STATUS_ACCESS_DENIED;
|
|
goto Exit;
|
|
}
|
|
|
|
if( mustChange == FALSE ) {
|
|
//
|
|
// first iteration; look inside the kcb cache
|
|
//
|
|
|
|
if( CmpCompareNewValueDataAgainstKCBCache(KeyControlBlock,ValueName,Type,Data,DataSize) == TRUE ) {
|
|
//
|
|
// the value is in the cache and is the same; make this call a noop
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
//
|
|
// To Get here, we must either be changing a value, or setting a new one
|
|
//
|
|
mustChange=TRUE;
|
|
} else {
|
|
//
|
|
// second iteration; look inside the hive
|
|
//
|
|
|
|
|
|
//
|
|
// get reference to parent key,
|
|
//
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
if( ParentToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ParentToRelease);
|
|
ParentToRelease = HCELL_NIL;
|
|
}
|
|
parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if( parent == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
ParentToRelease = Cell;
|
|
//
|
|
// try to find an existing value entry by the same name
|
|
//
|
|
count = parent->ValueList.Count;
|
|
found = FALSE;
|
|
|
|
if (count > 0) {
|
|
if( CmpFindNameInList(Hive,
|
|
&parent->ValueList,
|
|
ValueName,
|
|
&ChildIndex,
|
|
&oldchild) == FALSE ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
if (oldchild != HCELL_NIL) {
|
|
if( ChildToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ChildToRelease);
|
|
ChildToRelease = HCELL_NIL;
|
|
}
|
|
Value = (PCM_KEY_VALUE)HvGetCell(Hive,oldchild);
|
|
if( Value == NULL ) {
|
|
//
|
|
// could no map view
|
|
//
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
ChildToRelease = oldchild;
|
|
found = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// empty list; add it first
|
|
//
|
|
ChildIndex = 0;
|
|
}
|
|
|
|
//
|
|
// Performance Hack:
|
|
// If a Set is asking us to set a key to the current value (IE does this a lot)
|
|
// drop it (and, therefore, the last modified time) on the floor, but return success
|
|
// this stops the page from being dirtied, and us having to flush the registry.
|
|
//
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We're going through these gyrations so that if someone does come in and try and delete the
|
|
// key we're setting we're safe. Once we know we have to change the key, take the
|
|
// Exclusive (write) lock then restart
|
|
//
|
|
//
|
|
CmpUnlockRegistry();
|
|
CmpLockRegistryExclusive();
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
}// while
|
|
|
|
ASSERT( mustChange == TRUE );
|
|
|
|
// It's a different or new value, mark it dirty, since we'll
|
|
// at least set its time stamp
|
|
|
|
if (! HvMarkCellDirty(Hive, Cell)) {
|
|
status = STATUS_NO_LOG_SPACE;
|
|
goto Exit;
|
|
}
|
|
|
|
StorageType = HvGetCellType(Cell);
|
|
|
|
//
|
|
// stash small data if relevent
|
|
//
|
|
TempData = 0;
|
|
if ((DataSize <= CM_KEY_VALUE_SMALL) &&
|
|
(DataSize > 0))
|
|
{
|
|
try {
|
|
RtlMoveMemory( // yes, move memory, could be 1 byte
|
|
&TempData, // at the end of a page.
|
|
Data,
|
|
DataSize
|
|
);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmSetValueKey: code:%08lx\n", GetExceptionCode()));
|
|
status = GetExceptionCode();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
|
|
//
|
|
// ----- Existing Value Entry Path -----
|
|
//
|
|
|
|
//
|
|
// An existing value entry of the specified name exists,
|
|
// set our data into it.
|
|
//
|
|
status = CmpSetValueKeyExisting(Hive,
|
|
oldchild,
|
|
Value,
|
|
Type,
|
|
Data,
|
|
DataSize,
|
|
StorageType,
|
|
TempData);
|
|
|
|
PERFINFO_REG_SET_VALUE_EXIST();
|
|
} else {
|
|
|
|
//
|
|
// ----- New Value Entry Path -----
|
|
//
|
|
|
|
//
|
|
// Either there are no existing value entries, or the one
|
|
// specified is not in the list. In either case, create and
|
|
// fill a new one, and add it to the list
|
|
//
|
|
status = CmpSetValueKeyNew(Hive,
|
|
parent,
|
|
ValueName,
|
|
ChildIndex,
|
|
Type,
|
|
Data,
|
|
DataSize,
|
|
StorageType,
|
|
TempData);
|
|
PERFINFO_REG_SET_VALUE_NEW();
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
// sanity assert
|
|
ASSERT( parent->MaxValueNameLen == KeyControlBlock->KcbMaxValueNameLen );
|
|
if (parent->MaxValueNameLen < ValueName->Length) {
|
|
parent->MaxValueNameLen = ValueName->Length;
|
|
// update the kcb cache too
|
|
KeyControlBlock->KcbMaxValueNameLen = ValueName->Length;
|
|
}
|
|
|
|
//sanity assert
|
|
ASSERT( parent->MaxValueDataLen == KeyControlBlock->KcbMaxValueDataLen );
|
|
if (parent->MaxValueDataLen < DataSize) {
|
|
parent->MaxValueDataLen = DataSize;
|
|
// update the kcb cache too
|
|
KeyControlBlock->KcbMaxValueDataLen = parent->MaxValueDataLen;
|
|
}
|
|
|
|
KeQuerySystemTime(&systemtime);
|
|
parent->LastWriteTime = systemtime;
|
|
// update the kcb cache too.
|
|
KeyControlBlock->KcbLastWriteTime = systemtime;
|
|
|
|
//
|
|
// Update the cache, no need for KCB lock as the registry is locked exclusively.
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
if( found && (CMP_IS_CELL_CACHED(KeyControlBlock->ValueCache.ValueList)) ) {
|
|
//
|
|
// invalidate only the entry we changed.
|
|
//
|
|
PULONG_PTR CachedList = (PULONG_PTR) CMP_GET_CACHED_CELLDATA(KeyControlBlock->ValueCache.ValueList);
|
|
if (CMP_IS_CELL_CACHED(CachedList[ChildIndex])) {
|
|
|
|
ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(CachedList[ChildIndex]));
|
|
}
|
|
CachedList[ChildIndex] = oldchild;
|
|
|
|
} else {
|
|
//
|
|
// rebuild ALL KCB cache
|
|
//
|
|
CmpCleanUpKcbValueCache(KeyControlBlock);
|
|
CmpSetUpKcbValueCache(KeyControlBlock,parent->ValueList.Count,parent->ValueList.List);
|
|
}
|
|
CmpReportNotify(KeyControlBlock,
|
|
KeyControlBlock->KeyHive,
|
|
KeyControlBlock->KeyCell,
|
|
REG_NOTIFY_CHANGE_LAST_SET);
|
|
}
|
|
|
|
Exit:
|
|
PERFINFO_REG_SET_VALUE_DONE(ValueName);
|
|
|
|
if( ParentToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ParentToRelease);
|
|
}
|
|
if( ChildToRelease != HCELL_NIL ) {
|
|
HvReleaseCell(Hive,ChildToRelease);
|
|
}
|
|
|
|
CmpUnlockRegistry();
|
|
|
|
// Mark the hive as read only
|
|
CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmpSetValueKeyExisting(
|
|
IN PHHIVE Hive,
|
|
IN HCELL_INDEX OldChild,
|
|
IN PCM_KEY_VALUE Value,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize,
|
|
IN ULONG StorageType,
|
|
IN ULONG TempData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper for CmSetValueKey, implements the case where the value entry
|
|
being set already exists.
|
|
|
|
Arguments:
|
|
|
|
Hive - hive of interest
|
|
|
|
OldChild - hcell_index of the value entry body to which we are to
|
|
set new data
|
|
|
|
Type - The integer type number of the value entry.
|
|
|
|
Data - Pointer to buffer with actual data for the value entry.
|
|
|
|
DataSize - Size of Data buffer.
|
|
|
|
StorageType - stable or volatile
|
|
|
|
TempData - small values are passed here
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if it worked, appropriate status code if it did not
|
|
|
|
Note:
|
|
|
|
For new hives format, we have the following cases:
|
|
|
|
New Data Old Data
|
|
-------- --------
|
|
|
|
1. small small
|
|
2. small normal
|
|
3. small bigdata
|
|
4. normal small
|
|
5. normal normal
|
|
6. normal bigdata
|
|
7. bigdata small
|
|
8. bigdata normal
|
|
9. bigdata bigdata
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
HCELL_INDEX DataCell;
|
|
HCELL_INDEX OldDataCell;
|
|
PCELL_DATA pdata;
|
|
HCELL_INDEX NewCell;
|
|
ULONG OldRealSize;
|
|
USHORT OldSizeType; // 0 - small
|
|
USHORT NewSizeType; // 1 - normal
|
|
// 2 - bigdata
|
|
HANDLE hSecure = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
|
|
//
|
|
// value entry by the specified name already exists
|
|
// oldchild is hcell_index of its value entry body
|
|
// which we will always edit, so mark it dirty
|
|
//
|
|
if (! HvMarkCellDirty(Hive, OldChild)) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
if(CmpIsHKeyValueSmall(OldRealSize, Value->DataLength) == TRUE ) {
|
|
//
|
|
// old data was small
|
|
//
|
|
OldSizeType = 0;
|
|
} else if( CmpIsHKeyValueBig(Hive,OldRealSize) == TRUE ) {
|
|
//
|
|
// old data was big
|
|
//
|
|
OldSizeType = 2;
|
|
} else {
|
|
//
|
|
// old data was normal
|
|
//
|
|
OldSizeType = 1;
|
|
}
|
|
|
|
if( DataSize <= CM_KEY_VALUE_SMALL ) {
|
|
//
|
|
// new data is small
|
|
//
|
|
NewSizeType = 0;
|
|
} else if( CmpIsHKeyValueBig(Hive,DataSize) == TRUE ) {
|
|
//
|
|
// new data is big
|
|
//
|
|
NewSizeType = 2;
|
|
} else {
|
|
//
|
|
// new data is normal
|
|
//
|
|
NewSizeType = 1;
|
|
}
|
|
|
|
|
|
//
|
|
// this will handle all cases and will make sure data is marked dirty
|
|
//
|
|
if( !CmpMarkValueDataDirty(Hive,Value) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
//
|
|
// cases 1,2,3
|
|
//
|
|
if( NewSizeType == 0 ) {
|
|
if( ((OldSizeType == 1) && (OldRealSize > 0) ) ||
|
|
(OldSizeType == 2)
|
|
) {
|
|
CmpFreeValueData(Hive,Value->Data,OldRealSize);
|
|
}
|
|
|
|
//
|
|
// write our new small data into value entry body
|
|
//
|
|
Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
|
|
Value->Data = TempData;
|
|
Value->Type = Type;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// secure the user buffer so we don't get inconsistencies.
|
|
// ONLY if we are called with a user mode buffer !!!
|
|
//
|
|
|
|
if ( (ULONG_PTR)Data <= (ULONG_PTR)MM_HIGHEST_USER_ADDRESS ) {
|
|
hSecure = MmSecureVirtualMemory(Data,DataSize, PAGE_READONLY);
|
|
if (hSecure == 0) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// store it to be freed if the allocation succeeds
|
|
//
|
|
OldDataCell = Value->Data;
|
|
|
|
//
|
|
// cases 4,5,6
|
|
//
|
|
if( NewSizeType == 1 ){
|
|
|
|
if( (OldSizeType == 1) && (OldRealSize > 0)) {
|
|
//
|
|
// we already have a cell; see if we can reuse it !
|
|
//
|
|
DataCell = Value->Data;
|
|
ASSERT(DataCell != HCELL_NIL);
|
|
pdata = HvGetCell(Hive, DataCell);
|
|
if( pdata == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
// release it right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, DataCell);
|
|
|
|
ASSERT(HvGetCellSize(Hive, pdata) > 0);
|
|
|
|
if (DataSize <= (ULONG)(HvGetCellSize(Hive, pdata))) {
|
|
|
|
//
|
|
// The existing data cell is big enough to hold the new data.
|
|
//
|
|
|
|
//
|
|
// we'll keep this cell
|
|
//
|
|
NewCell = DataCell;
|
|
|
|
} else {
|
|
//
|
|
// grow the existing cell
|
|
//
|
|
NewCell = HvReallocateCell(Hive,DataCell,DataSize);
|
|
if (NewCell == HCELL_NIL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// allocate a new cell
|
|
//
|
|
NewCell = HvAllocateCell(Hive, DataSize, StorageType,(HvGetCellType(OldChild)==StorageType)?OldChild:HCELL_NIL);
|
|
|
|
if (NewCell == HCELL_NIL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// now we have a cell that can accomodate the data
|
|
//
|
|
pdata = HvGetCell(Hive, NewCell);
|
|
if( pdata == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
// this shouldn't happen as we just allocated/ reallocated/ marked dirty this cell
|
|
//
|
|
ASSERT( FALSE );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
// release it right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, NewCell);
|
|
|
|
//
|
|
// copy the actual data
|
|
//
|
|
RtlCopyMemory(pdata,Data,DataSize);
|
|
Value->Data = NewCell;
|
|
Value->DataLength = DataSize;
|
|
Value->Type = Type;
|
|
|
|
// sanity
|
|
ASSERT_CELL_DIRTY(Hive,NewCell);
|
|
|
|
if( OldSizeType == 2 ) {
|
|
//
|
|
// old data was big; free it
|
|
//
|
|
ASSERT( OldDataCell != NewCell );
|
|
CmpFreeValueData(Hive,OldDataCell,OldRealSize);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// cases 7,8,9
|
|
//
|
|
if( NewSizeType == 2 ) {
|
|
|
|
if( OldSizeType == 2 ) {
|
|
//
|
|
// data was previously big; grow it!
|
|
//
|
|
|
|
status =CmpSetValueDataExisting(Hive,Data,DataSize,StorageType,OldDataCell);
|
|
if( !NT_SUCCESS(status) ) {
|
|
goto Exit;
|
|
}
|
|
NewCell = OldDataCell;
|
|
|
|
} else {
|
|
//
|
|
// data was small or normal.
|
|
// allocate and copy to a new big data cell;
|
|
// then free the old cell
|
|
//
|
|
status = CmpSetValueDataNew(Hive,Data,DataSize,StorageType,OldChild,&NewCell);
|
|
if( !NT_SUCCESS(status) ) {
|
|
//
|
|
// We have bombed out loading user data, clean up and exit.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
if( (OldSizeType != 0) && (OldRealSize != 0) ) {
|
|
//
|
|
// there is something to free
|
|
//
|
|
HvFreeCell(Hive, Value->Data);
|
|
}
|
|
}
|
|
|
|
Value->DataLength = DataSize;
|
|
Value->Data = NewCell;
|
|
Value->Type = Type;
|
|
|
|
// sanity
|
|
ASSERT_CELL_DIRTY(Hive,NewCell);
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// we shouldn't go here
|
|
//
|
|
ASSERT( FALSE );
|
|
|
|
Exit:
|
|
if( hSecure) {
|
|
MmUnsecureVirtualMemory(hSecure);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpSetValueKeyNew(
|
|
IN PHHIVE Hive,
|
|
IN PCM_KEY_NODE Parent,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG Index,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize,
|
|
IN ULONG StorageType,
|
|
IN ULONG TempData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper for CmSetValueKey, implements the case where the value entry
|
|
being set does not exist. Will create new value entry and data,
|
|
place in list (which may be created)
|
|
|
|
Arguments:
|
|
|
|
Hive - hive of interest
|
|
|
|
Parent - pointer to key node value entry is for
|
|
|
|
ValueName - The unique (relative to the containing key) name
|
|
of the value entry. May be NULL.
|
|
|
|
Index - where in the list should this value be inserted
|
|
|
|
Type - The integer type number of the value entry.
|
|
|
|
Data - Pointer to buffer with actual data for the value entry.
|
|
|
|
DataSize - Size of Data buffer.
|
|
|
|
StorageType - stable or volatile
|
|
|
|
TempData - small data values passed here
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if it worked, appropriate status code if it did not
|
|
|
|
--*/
|
|
{
|
|
PCELL_DATA pvalue;
|
|
HCELL_INDEX ValueCell;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
//
|
|
// Either Count == 0 (no list) or our entry is simply not in
|
|
// the list. Create a new value entry body, and data. Add to list.
|
|
// (May create the list.)
|
|
//
|
|
if (Parent->ValueList.Count != 0) {
|
|
ASSERT(Parent->ValueList.List != HCELL_NIL);
|
|
if (! HvMarkCellDirty(Hive, Parent->ValueList.List)) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate the body of the value entry, and the data
|
|
//
|
|
ValueCell = HvAllocateCell(
|
|
Hive,
|
|
CmpHKeyValueSize(Hive, ValueName),
|
|
StorageType,
|
|
HCELL_NIL
|
|
);
|
|
|
|
if (ValueCell == HCELL_NIL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// map in the body, and fill in its fixed portion
|
|
//
|
|
pvalue = HvGetCell(Hive, ValueCell);
|
|
if( pvalue == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
//
|
|
// normally this shouldn't happen as we just allocated ValueCell
|
|
// i.e. the bin containing ValueCell should be mapped in memory at this point.
|
|
//
|
|
ASSERT( FALSE );
|
|
HvFreeCell(Hive, ValueCell);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
// release it right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, ValueCell);
|
|
|
|
// sanity
|
|
ASSERT_CELL_DIRTY(Hive,ValueCell);
|
|
|
|
pvalue->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
|
|
|
|
//
|
|
// fill in the variable portions of the new value entry, name and
|
|
// and data are copied from caller space, could fault.
|
|
//
|
|
try {
|
|
|
|
//
|
|
// fill in the name
|
|
//
|
|
pvalue->u.KeyValue.NameLength = CmpCopyName(Hive,
|
|
pvalue->u.KeyValue.Name,
|
|
ValueName);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmSetValueKey: code:%08lx\n", GetExceptionCode()));
|
|
|
|
//
|
|
// We have bombed out loading user data, clean up and exit.
|
|
//
|
|
HvFreeCell(Hive, ValueCell);
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
if (pvalue->u.KeyValue.NameLength < ValueName->Length) {
|
|
pvalue->u.KeyValue.Flags = VALUE_COMP_NAME;
|
|
} else {
|
|
pvalue->u.KeyValue.Flags = 0;
|
|
}
|
|
|
|
//
|
|
// fill in the data
|
|
//
|
|
if (DataSize > CM_KEY_VALUE_SMALL) {
|
|
Status = CmpSetValueDataNew(Hive,Data,DataSize,StorageType,ValueCell,&(pvalue->u.KeyValue.Data));
|
|
if( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// We have bombed out loading user data, clean up and exit.
|
|
//
|
|
HvFreeCell(Hive, ValueCell);
|
|
return Status;
|
|
}
|
|
|
|
pvalue->u.KeyValue.DataLength = DataSize;
|
|
// sanity
|
|
ASSERT_CELL_DIRTY(Hive,pvalue->u.KeyValue.Data);
|
|
|
|
} else {
|
|
pvalue->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
|
|
pvalue->u.KeyValue.Data = TempData;
|
|
}
|
|
pvalue->u.KeyValue.Type = Type;
|
|
|
|
if( !NT_SUCCESS(CmpAddValueToList(Hive,ValueCell,Index,StorageType,&(Parent->ValueList)) ) ) {
|
|
// out of space, free all allocated stuff
|
|
// this will free embeded cigdata cell info too (if any)
|
|
CmpFreeValue(Hive,ValueCell);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmSetLastWriteTimeKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN PLARGE_INTEGER LastWriteTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The LastWriteTime associated with a key node can be set with
|
|
CmSetLastWriteTimeKey
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for the key to operate on
|
|
|
|
LastWriteTime - new time for key
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE parent;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetLastWriteTimeKey\n"));
|
|
|
|
CmpLockRegistryExclusive();
|
|
|
|
//
|
|
// Check that we are not being asked to modify a key
|
|
// that has been deleted
|
|
//
|
|
if (KeyControlBlock->Delete == TRUE) {
|
|
status = STATUS_KEY_DELETED;
|
|
goto Exit;
|
|
}
|
|
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if( parent == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, Cell);
|
|
|
|
if (! HvMarkCellDirty(Hive, Cell)) {
|
|
status = STATUS_NO_LOG_SPACE;
|
|
goto Exit;
|
|
}
|
|
|
|
parent->LastWriteTime = *LastWriteTime;
|
|
// update the kcb cache too.
|
|
KeyControlBlock->KcbLastWriteTime = *LastWriteTime;
|
|
|
|
Exit:
|
|
|
|
CmpUnlockRegistry();
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmSetKeyUserFlags(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN ULONG UserFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the user defined flags for the key; At this point there are only
|
|
4 bits reserved for user defined flags. kcb and knode must be kept in
|
|
sync.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for the key to operate on
|
|
|
|
UserFlags - user defined flags to be set on this key.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE Node;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
LARGE_INTEGER LastWriteTime;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetKeyUserFlags\n"));
|
|
|
|
CmpLockRegistryExclusive();
|
|
|
|
//
|
|
// Check that we are not being asked to modify a key
|
|
// that has been deleted
|
|
//
|
|
if (KeyControlBlock->Delete == TRUE) {
|
|
status = STATUS_KEY_DELETED;
|
|
goto Exit;
|
|
}
|
|
|
|
if( UserFlags & (~((ULONG)KEY_USER_FLAGS_VALID_MASK)) ) {
|
|
//
|
|
// number of user defined flags exceeded; punt
|
|
//
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map a view for the bin containing this cell
|
|
//
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, Cell);
|
|
|
|
if (! HvMarkCellDirty(Hive, Cell)) {
|
|
status = STATUS_NO_LOG_SPACE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// shift/(pack) the user defined flags and
|
|
// update knode and kcb cache
|
|
//
|
|
// first, erase the old flags
|
|
Node->Flags &= KEY_USER_FLAGS_CLEAR_MASK;
|
|
Node->Flags |= (USHORT)(UserFlags<<KEY_USER_FLAGS_SHIFT);
|
|
// update the kcb cache
|
|
KeyControlBlock->Flags = Node->Flags;
|
|
|
|
//
|
|
// we need to update the LstWriteTime as well
|
|
//
|
|
KeQuerySystemTime(&LastWriteTime);
|
|
Node->LastWriteTime = LastWriteTime;
|
|
// update the kcb cache too.
|
|
KeyControlBlock->KcbLastWriteTime = LastWriteTime;
|
|
|
|
Exit:
|
|
CmpUnlockRegistry();
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
CmpIsHiveAlreadyLoaded( IN HANDLE KeyHandle,
|
|
IN POBJECT_ATTRIBUTES SourceFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if the SourceFile is already loaded in the same spot as KeyHandle.
|
|
|
|
Arguments:
|
|
|
|
KeyHandle - should be the root of a hive. We'll query the name of the primary file
|
|
and compare it against the name of SourceFile
|
|
|
|
SourceFile - specifies a file. while file could be remote,
|
|
that is strongly discouraged.
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PCM_KEY_BODY KeyBody;
|
|
BOOLEAN Result = FALSE; // pesimistic
|
|
PCMHIVE CmHive;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
status = ObReferenceObjectByHandle(KeyHandle,
|
|
0,
|
|
CmpKeyObjectType,
|
|
KernelMode,
|
|
(PVOID *)(&KeyBody),
|
|
NULL);
|
|
if(!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
|
|
|
|
//
|
|
// should be the root of a hive
|
|
//
|
|
if( !(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) || // not root of a hive
|
|
(CmHive->FileUserName.Buffer == NULL)// no name captured
|
|
) {
|
|
goto ExitCleanup;
|
|
}
|
|
|
|
if( RtlCompareUnicodeString(&(CmHive->FileUserName),
|
|
SourceFile->ObjectName,
|
|
TRUE) == 0 ) {
|
|
//
|
|
// same file; same spot
|
|
//
|
|
Result = TRUE;
|
|
//
|
|
// unfreeze the hive;hive will become just a regular hive from now on
|
|
// it is safe to do this because we hold an extra refcount on the root of the hive
|
|
// as we have specifically opened the root to check if it's already loaded
|
|
//
|
|
if( IsHiveFrozen(CmHive) ) {
|
|
CmHive->Frozen = FALSE;
|
|
if( CmHive->UnloadWorkItem != NULL ) {
|
|
ExFreePool( CmHive->UnloadWorkItem );
|
|
CmHive->UnloadWorkItem = NULL;
|
|
}
|
|
if( CmHive->RootKcb ) {
|
|
CmpDereferenceKeyControlBlockWithLock(CmHive->RootKcb);
|
|
CmHive->RootKcb = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExitCleanup:
|
|
ObDereferenceObject((PVOID)KeyBody);
|
|
return Result;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmLoadKey(
|
|
IN POBJECT_ATTRIBUTES TargetKey,
|
|
IN POBJECT_ATTRIBUTES SourceFile,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A hive (file in the format created by NtSaveKey) may be linked
|
|
into the active registry with this call. UNLIKE NtRestoreKey,
|
|
the file specified to NtLoadKey will become the actual backing
|
|
store of part of the registry (that is, it will NOT be copied.)
|
|
|
|
The file may have an associated .log file.
|
|
|
|
If the hive file is marked as needing a .log file, and one is
|
|
not present, the call will fail.
|
|
|
|
The name specified by SourceFile must be such that ".log" can
|
|
be appended to it to generate the name of the log file. Thus,
|
|
on FAT file systems, the hive file may not have an extension.
|
|
|
|
This call is used by logon to make the user's profile available
|
|
in the registry. It is not intended for use doing backup,
|
|
restore, etc. Use NtRestoreKey for that.
|
|
|
|
N.B. This routine assumes that the object attributes for the file
|
|
to be opened have been captured into kernel space so that
|
|
they can safely be passed to the worker thread to open the file
|
|
and do the actual I/O.
|
|
|
|
Arguments:
|
|
|
|
TargetKey - specifies the path to a key to link the hive to.
|
|
path must be of the form "\registry\user\<username>"
|
|
|
|
SourceFile - specifies a file. while file could be remote,
|
|
that is strongly discouraged.
|
|
|
|
Flags - specifies any flags that should be used for the load operation.
|
|
The only valid flag is REG_NO_LAZY_FLUSH.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - values TBS.
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE NewHive;
|
|
NTSTATUS Status;
|
|
BOOLEAN Allocate;
|
|
BOOLEAN RegistryLockAquired;
|
|
SECURITY_QUALITY_OF_SERVICE ServiceQos;
|
|
SECURITY_CLIENT_CONTEXT ClientSecurityContext;
|
|
HANDLE KeyHandle;
|
|
|
|
|
|
|
|
//
|
|
// Obtain the security context here so we can use it
|
|
// later to impersonate the user, which we will do
|
|
// if we cannot access the file as SYSTEM. This
|
|
// usually occurs if the file is on a remote machine.
|
|
//
|
|
ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
ServiceQos.ImpersonationLevel = SecurityImpersonation;
|
|
ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
ServiceQos.EffectiveOnly = TRUE;
|
|
Status = SeCreateClientSecurity(CONTAINING_RECORD(KeGetCurrentThread(),ETHREAD,Tcb),
|
|
&ServiceQos,
|
|
FALSE,
|
|
&ClientSecurityContext);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// we open the root of the hive here. if it already exists,this will prevent it from going
|
|
// away from under us while we are doing the "already loaded" check (due to delay unload logic)
|
|
//
|
|
Status = ObOpenObjectByName(TargetKey,
|
|
CmpKeyObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
KEY_READ,
|
|
NULL,
|
|
&KeyHandle);
|
|
if(!NT_SUCCESS(Status)) {
|
|
KeyHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// Do not lock the registry; Instead set the RegistryLockAquired member
|
|
// of REGISTRY_COMMAND so CmpWorker can lock it after opening the hive files
|
|
//
|
|
//CmpLockRegistryExclusive();
|
|
//
|
|
|
|
RegistryLockAquired = FALSE;
|
|
Allocate = TRUE;
|
|
Status = CmpCmdHiveOpen( SourceFile, // FileAttributes
|
|
&ClientSecurityContext, // ImpersonationContext
|
|
&Allocate, // Allocate
|
|
&RegistryLockAquired, // RegistryLockAquired
|
|
&NewHive, // NewHive
|
|
CM_CHECK_REGISTRY_CHECK_CLEAN //CheckFlags
|
|
);
|
|
|
|
SeDeleteClientSecurity( &ClientSecurityContext );
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if( KeyHandle != NULL ) {
|
|
//
|
|
// lock the registry exclusive while we are checking attempt to load same file into the same spot
|
|
//
|
|
if( !RegistryLockAquired ) {
|
|
CmpLockRegistryExclusive();
|
|
RegistryLockAquired = TRUE;
|
|
}
|
|
|
|
//
|
|
// check if the same file is loaded in the same spot
|
|
//
|
|
if( CmpIsHiveAlreadyLoaded(KeyHandle,SourceFile) ) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if( RegistryLockAquired ) {
|
|
// if CmpWorker has locked the registry, unlock it now.
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
if( KeyHandle != NULL ) {
|
|
ZwClose(KeyHandle);
|
|
}
|
|
return(Status);
|
|
} else {
|
|
//
|
|
// if we got here, CmpWorker should have locked the registry exclusive.
|
|
//
|
|
ASSERT( RegistryLockAquired );
|
|
}
|
|
|
|
//
|
|
// if this is a NO_LAZY_FLUSH hive, set the appropriate bit.
|
|
//
|
|
if (Flags & REG_NO_LAZY_FLUSH) {
|
|
NewHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
|
|
}
|
|
|
|
//
|
|
// We now have a succesfully loaded and initialized CmHive, so we
|
|
// just need to link that into the appropriate spot in the master hive.
|
|
//
|
|
Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
|
|
TargetKey->RootDirectory,
|
|
NewHive,
|
|
Allocate,
|
|
TargetKey->SecurityDescriptor);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//
|
|
// add new hive to hivelist
|
|
//
|
|
CmpAddToHiveFileList(NewHive);
|
|
//
|
|
// flush the hive right here if just created; this is to avoid situations where
|
|
// the lazy flusher doesn't get a chance to flush the hive, or it can't (because
|
|
// the hive is a no_lazy_flush hive and it is never explicitly flushed)
|
|
//
|
|
if( Allocate == TRUE ) {
|
|
HvSyncHive(&(NewHive->Hive));
|
|
}
|
|
|
|
} else {
|
|
LOCK_HIVE_LIST();
|
|
CmpRemoveEntryList(&(NewHive->HiveList));
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
CmpCheckForOrphanedKcbs((PHHIVE)NewHive);
|
|
|
|
CmpDestroyHiveViewList(NewHive);
|
|
CmpDestroySecurityCache (NewHive);
|
|
CmpDropFileObjectForHive(NewHive);
|
|
|
|
HvFreeHive((PHHIVE)NewHive);
|
|
|
|
//
|
|
// Close the hive files
|
|
//
|
|
CmpCmdHiveClose(NewHive);
|
|
|
|
//
|
|
// free the cm level structure
|
|
//
|
|
ASSERT( NewHive->HiveLock );
|
|
ExFreePool(NewHive->HiveLock);
|
|
ASSERT( NewHive->ViewLock );
|
|
ExFreePool(NewHive->ViewLock);
|
|
CmpFree(NewHive, sizeof(CMHIVE));
|
|
}
|
|
|
|
//
|
|
// We've given user chance to log on, so turn on quota
|
|
//
|
|
if ((CmpProfileLoaded == FALSE) &&
|
|
(CmpWasSetupBoot == FALSE)) {
|
|
CmpProfileLoaded = TRUE;
|
|
CmpSetGlobalQuotaAllowed();
|
|
}
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
CmpUnlockRegistry();
|
|
|
|
if( KeyHandle != NULL ) {
|
|
ZwClose(KeyHandle);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
#if DBG
|
|
ULONG
|
|
CmpUnloadKeyWorker(
|
|
PCM_KEY_CONTROL_BLOCK Current,
|
|
PVOID Context1,
|
|
PVOID Context2
|
|
)
|
|
{
|
|
PUNICODE_STRING ConstructedName;
|
|
if (Current->KeyHive == Context1) {
|
|
ConstructedName = CmpConstructName(Current);
|
|
|
|
if (ConstructedName) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"%wZ\n", ConstructedName));
|
|
ExFreePoolWithTag(ConstructedName, CM_NAME_TAG | PROTECTED_POOL);
|
|
}
|
|
}
|
|
return KCB_WORKER_CONTINUE; // always keep searching
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
CmUnloadKey(
|
|
IN PHHIVE Hive,
|
|
IN HCELL_INDEX Cell,
|
|
IN PCM_KEY_CONTROL_BLOCK Kcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlinks a hive from its location in the registry, closes its file
|
|
handles, and deallocates all its memory.
|
|
|
|
There must be no key control blocks currently referencing the hive
|
|
to be unloaded.
|
|
|
|
Arguments:
|
|
|
|
Hive - Supplies a pointer to the hive control structure for the
|
|
hive to be unloaded
|
|
|
|
Cell - supplies the HCELL_INDEX for the root cell of the hive.
|
|
|
|
Kcb - Supplies the key control block
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCMHIVE CmHive;
|
|
BOOLEAN Success;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmUnloadKey\n"));
|
|
|
|
//
|
|
// Make sure the cell passed in is the root cell of the hive.
|
|
//
|
|
if (Cell != Hive->BaseBlock->RootCell) {
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Make sure there are no open references to key control blocks
|
|
// for this hive. If there are none, then we can unload the hive.
|
|
//
|
|
|
|
CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
if(Kcb->RefCount != 1) {
|
|
Success = (CmpSearchForOpenSubKeys(Kcb,SearchIfExist) == 0);
|
|
Success = Success && (Kcb->RefCount == 1);
|
|
|
|
if( Success == FALSE) {
|
|
#if DBG
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"List of keys open against hive unload was attempted on:\n"));
|
|
CmpSearchKeyControlBlockTree(
|
|
CmpUnloadKeyWorker,
|
|
Hive,
|
|
NULL
|
|
);
|
|
#endif
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
}
|
|
|
|
ASSERT( Kcb->RefCount == 1 );
|
|
|
|
//
|
|
// Flush any dirty data to disk. If this fails, too bad.
|
|
//
|
|
CmFlushKey(Hive, Cell);
|
|
|
|
//
|
|
// Remove the hive from the HiveFileList
|
|
//
|
|
CmpRemoveFromHiveFileList((PCMHIVE)Hive);
|
|
|
|
//
|
|
// Unlink from master hive, remove from list
|
|
//
|
|
Success = CmpDestroyHive(Hive, Cell);
|
|
|
|
if (Success) {
|
|
//
|
|
// signal the user event (if any), then do the cleanup (i.e. deref the event
|
|
// and the artificial refcount we set on the root kcb)
|
|
//
|
|
if( CmHive->UnloadEvent != NULL ) {
|
|
KeSetEvent(CmHive->UnloadEvent,0,FALSE);
|
|
ObDereferenceObject(CmHive->UnloadEvent);
|
|
}
|
|
|
|
CmpDestroyHiveViewList(CmHive);
|
|
CmpDestroySecurityCache (CmHive);
|
|
CmpDropFileObjectForHive(CmHive);
|
|
|
|
HvFreeHive(Hive);
|
|
|
|
//
|
|
// Close the hive files
|
|
//
|
|
CmpCmdHiveClose(CmHive);
|
|
|
|
//
|
|
// free the cm level structure
|
|
//
|
|
ASSERT( CmHive->HiveLock );
|
|
ExFreePool(CmHive->HiveLock);
|
|
ASSERT( CmHive->ViewLock );
|
|
ExFreePool(CmHive->ViewLock);
|
|
CmpFree(CmHive, sizeof(CMHIVE));
|
|
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef NT_UNLOAD_KEY_EX
|
|
NTSTATUS
|
|
CmUnloadKeyEx(
|
|
IN PCM_KEY_CONTROL_BLOCK kcb,
|
|
IN PKEVENT UserEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
First tries to unlink the hive, by calling the sync version
|
|
|
|
If the hive cannot be unloaded (there are open handles inside it),
|
|
reference the root of the hive (i.e. kcb) and freeze the hive.
|
|
|
|
Arguments:
|
|
|
|
Kcb - Supplies the key control block
|
|
|
|
UserEvent - the event to be signaled after the hive was unloaded
|
|
(only if late - unload is needed)
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING - the hive was frozen and it'll be unloaded later
|
|
|
|
STATUS_SUCCESS - the hive was successfully sync-unloaded (no need
|
|
to signal for UserEvent)
|
|
|
|
<other> - an error occured, operation failed
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE CmHive;
|
|
HCELL_INDEX Cell;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Cell = kcb->KeyCell;
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(kcb->KeyHive, CMHIVE, Hive);
|
|
|
|
if( IsHiveFrozen(CmHive) ) {
|
|
//
|
|
// don't let them hurt themselves by calling it twice
|
|
//
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
//
|
|
// first, try out he sync routine; this may or may not unload the hive,
|
|
// but at least will kick kcbs with refcount = 0 out of cache
|
|
//
|
|
Status = CmUnloadKey(&(CmHive->Hive),Cell,kcb);
|
|
if( Status != STATUS_CANNOT_DELETE ) {
|
|
//
|
|
// the hive was either unloaded, or some bad thing happened
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
ASSERT( kcb->RefCount > 1 );
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
//
|
|
// Prepare for late-unloading:
|
|
// 1. reference the kcb, to make sure it won't go away without us noticing
|
|
// (we have the registry locked in exclusive mode, so we don't need to lock the kcbtree
|
|
//
|
|
if (!CmpReferenceKeyControlBlock(kcb)) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// parse the kcb tree and mark all open kcbs inside this hive and "no delay close"
|
|
//
|
|
CmpSearchForOpenSubKeys(kcb,SearchAndTagNoDelayClose);
|
|
kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
|
|
|
|
//
|
|
// 2. Freeze the hive
|
|
//
|
|
CmHive->RootKcb = kcb;
|
|
CmHive->Frozen = TRUE;
|
|
CmHive->UnloadEvent = UserEvent;
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
#endif //NT_UNLOAD_KEY_EX
|
|
|
|
// define in cmworker.c
|
|
extern BOOLEAN CmpForceForceFlush;
|
|
|
|
BOOLEAN
|
|
CmpDoFlushAll(
|
|
BOOLEAN ForceFlush
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flush all hives.
|
|
|
|
Runs in the context of the CmpWorkerThread.
|
|
|
|
Runs down list of Hives and applies HvSyncHive to them.
|
|
|
|
NOTE: Hives which are marked as HV_NOLAZYFLUSH are *NOT* flushed
|
|
by this call. You must call HvSyncHive explicitly to flush
|
|
a hive marked as HV_NOLAZYFLUSH.
|
|
|
|
Arguments:
|
|
|
|
ForceFlush - used as a contingency plan when a prior exception left
|
|
some hive in a used state. When set to TRUE, assumes the
|
|
registry is locked exclusive. It also repairs the broken
|
|
hives.
|
|
|
|
- When FALSE saves only the hives with UseCount == 0.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Notes:
|
|
|
|
If any of the hives is about to shrink CmpForceForceFlush is set to TRUE,
|
|
otherwise, it is set to FALSE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY p;
|
|
PCMHIVE h;
|
|
BOOLEAN Result = TRUE;
|
|
/*
|
|
ULONG rc;
|
|
*/
|
|
extern PCMHIVE CmpMasterHive;
|
|
|
|
//
|
|
// If writes are not working, lie and say we succeeded, will
|
|
// clean up in a short time. Only early system init code
|
|
// will ever know the difference.
|
|
//
|
|
if (CmpNoWrite) {
|
|
return TRUE;
|
|
}
|
|
|
|
CmpForceForceFlush = FALSE;
|
|
|
|
//
|
|
// traverse list of hives, sync each one
|
|
//
|
|
LOCK_HIVE_LIST();
|
|
p = CmpHiveListHead.Flink;
|
|
while (p != &CmpHiveListHead) {
|
|
|
|
h = CONTAINING_RECORD(p, CMHIVE, HiveList);
|
|
|
|
if (!(h->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) {
|
|
|
|
//
|
|
//Lock the hive before we flush it.
|
|
//-- since we now allow multiple readers
|
|
// during a flush (a flush is considered a read)
|
|
// we have to force a serialization on the vector table
|
|
//
|
|
CmLockHive (h);
|
|
|
|
if( (ForceFlush == TRUE) && (h->UseCount != 0) ) {
|
|
//
|
|
// hive was left in an instable state by a prior exception raised
|
|
// somewhere inside a CM function.
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
CmpFixHiveUsageCount(h);
|
|
ASSERT( h->UseCount == 0 );
|
|
}
|
|
|
|
|
|
if( (ForceFlush == TRUE) || (!HvHiveWillShrink((PHHIVE)h)) ) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpDoFlushAll hive = %p ForceFlush = %lu IsHiveShrinking = %lu BaseLength = %lx StableLength = %lx\n",
|
|
h,(ULONG)ForceFlush,(ULONG)HvHiveWillShrink((PHHIVE)h),((PHHIVE)h)->BaseBlock->Length,((PHHIVE)h)->Storage[Stable].Length));
|
|
Status = HvSyncHive((PHHIVE)h);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
Result = FALSE;
|
|
}
|
|
} else {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpDoFlushAll: Fail to flush hive %p because is shrinking\n",h));
|
|
Result = FALSE;
|
|
//
|
|
// another unsuccessful attempt to save this hive, because we needed the reglock exclisive
|
|
//
|
|
CmpForceForceFlush = TRUE;
|
|
}
|
|
|
|
CmUnlockHive (h);
|
|
//
|
|
// WARNNOTE - the above means that a lazy flush or
|
|
// or shutdown flush did not work. we don't
|
|
// know why. there is noone to report an error
|
|
// to, so continue on and hope for the best.
|
|
// (in theory, worst that can happen is user changes
|
|
// are lost.)
|
|
//
|
|
}
|
|
|
|
|
|
p = p->Flink;
|
|
}
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmReplaceKey(
|
|
IN PHHIVE Hive,
|
|
IN HCELL_INDEX Cell,
|
|
IN PUNICODE_STRING NewHiveName,
|
|
IN PUNICODE_STRING OldFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Renames the hive file for a running system and replaces it with a new
|
|
file. The new file is not actually used until the next boot.
|
|
|
|
Arguments:
|
|
|
|
Hive - Supplies a hive control structure for the hive to be replaced.
|
|
|
|
Cell - Supplies the HCELL_INDEX of the root cell of the hive to be
|
|
replaced.
|
|
|
|
NewHiveName - Supplies the name of the file which is to be installed
|
|
as the new hive.
|
|
|
|
OldFileName - Supplies the name of the file which the existing hive
|
|
file is to be renamed to.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
CHAR ObjectInfoBuffer[512];
|
|
NTSTATUS Status;
|
|
NTSTATUS Status2;
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
PCMHIVE NewHive;
|
|
PCMHIVE CmHive;
|
|
POBJECT_NAME_INFORMATION NameInfo;
|
|
ULONG OldQuotaAllowed;
|
|
ULONG OldQuotaWarning;
|
|
BOOLEAN Allocate;
|
|
BOOLEAN RegistryLockAquired;
|
|
|
|
UNREFERENCED_PARAMETER (Cell);
|
|
CmpLockRegistryExclusive();
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
if (Hive->HiveFlags & HIVE_HAS_BEEN_REPLACED) {
|
|
CmpUnlockRegistry();
|
|
return STATUS_FILE_RENAMED;
|
|
}
|
|
|
|
//
|
|
// temporarily disable registry quota as we will be giving this memory back immediately!
|
|
//
|
|
OldQuotaAllowed = CmpGlobalQuotaAllowed;
|
|
OldQuotaWarning = CmpGlobalQuotaWarning;
|
|
CmpGlobalQuotaAllowed = CM_WRAP_LIMIT;
|
|
CmpGlobalQuotaWarning = CM_WRAP_LIMIT;
|
|
|
|
//
|
|
// First open the new hive file and check to make sure it is valid.
|
|
//
|
|
InitializeObjectAttributes(&Attributes,
|
|
NewHiveName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Allocate = FALSE;
|
|
RegistryLockAquired = TRUE;
|
|
Status = CmpCmdHiveOpen( &Attributes, // FileAttributes
|
|
NULL, // ImpersonationContext
|
|
&Allocate, // Allocate
|
|
&RegistryLockAquired, // RegistryLockAquired
|
|
&NewHive, // NewHive
|
|
CM_CHECK_REGISTRY_CHECK_CLEAN // CheckFlags
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit;
|
|
}
|
|
ASSERT(Allocate == FALSE);
|
|
|
|
if( Hive == (PHHIVE)(CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive) ) {
|
|
//
|
|
// Somebody attempts to replace the system hive: do the WPA test
|
|
//
|
|
HCELL_INDEX Src,Dest;
|
|
|
|
Status = CmpCheckReplaceHive(Hive,&Src);
|
|
if( !NT_SUCCESS(Status) ) {
|
|
goto ErrorCleanup;
|
|
}
|
|
Status = CmpCheckReplaceHive((PHHIVE)NewHive,&Dest);
|
|
if( !NT_SUCCESS(Status) ) {
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
ASSERT( Src != HCELL_NIL );
|
|
ASSERT( Dest != HCELL_NIL );
|
|
//
|
|
// now stuff the current WPA subtree into the new hive
|
|
//
|
|
if( !CmpSyncTrees(Hive, Src, (PHHIVE)NewHive, Dest, FALSE ) ) {
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
//
|
|
// commit the changes we've made in the destination hive
|
|
//
|
|
if( !HvSyncHive((PHHIVE)NewHive) ) {
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
//
|
|
// The new hive exists, and is consistent, and we have it open.
|
|
// Now rename the current hive file.
|
|
//
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
Status = CmpCmdRenameHive( CmHive, // CmHive
|
|
(POBJECT_NAME_INFORMATION)ObjectInfoBuffer, // OldName
|
|
OldFileName, // NewName
|
|
sizeof(ObjectInfoBuffer) // NameInfoLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// rename failed, close the files associated with the new hive
|
|
//
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
//
|
|
// The existing hive was successfully renamed, so try to rename the
|
|
// new file to what the old hive file was named. (which was returned
|
|
// into ObjectInfoBuffer by the worker thread)
|
|
//
|
|
Hive->HiveFlags |= HIVE_HAS_BEEN_REPLACED;
|
|
NameInfo = (POBJECT_NAME_INFORMATION)ObjectInfoBuffer;
|
|
|
|
Status = CmpCmdRenameHive( NewHive, // CmHive
|
|
NULL, // OldName
|
|
&NameInfo->Name,// NewName
|
|
0 // NameInfoLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// We are in trouble now. We have renamed the existing hive file,
|
|
// but we couldn't rename the new hive file! Try to rename the
|
|
// existing hive file back to where it was.
|
|
//
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
Status2 = CmpCmdRenameHive( CmHive, // CmHive
|
|
NULL, // OldName
|
|
&NameInfo->Name, // NewName
|
|
0 // NameInfoLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status2)) {
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmReplaceKey: renamed existing hive file, but couldn't\n"));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," rename new hive file (%08lx) ",Status));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," or replace old hive file (%08lx)!\n",Status2));
|
|
|
|
//
|
|
// WARNNOTE:
|
|
// To get into this state, the user must have relevent
|
|
// privileges, deliberately mess with system in an attempt
|
|
// to defeat it, AND get it done in a narrow timing window.
|
|
//
|
|
// Further, if it's a user profile, the system will
|
|
// still come up.
|
|
//
|
|
// Therefore, return an error code and go on.
|
|
//
|
|
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
|
|
}
|
|
} else {
|
|
//
|
|
// flush file buffers (we are particulary interested in ValidDataLength to be updated on-disk)
|
|
//
|
|
IO_STATUS_BLOCK IoStatus;
|
|
Status = ZwFlushBuffersFile(NewHive->FileHandles[HFILE_TYPE_PRIMARY],&IoStatus);
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// failed to set ValidDataLength, close the files associated with the new hive
|
|
//
|
|
|
|
//
|
|
// We are in trouble now. We have renamed the existing hive file,
|
|
// but we couldn't rename the new hive file! Try to rename the
|
|
// existing hive file back to where it was.
|
|
//
|
|
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
Status2 = CmpCmdRenameHive( CmHive, // CmHive
|
|
NULL, // OldName
|
|
&NameInfo->Name, // NewName
|
|
0 // NameInfoLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status2)) {
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmReplaceKey: renamed existing hive file, but couldn't\n"));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," rename new hive file (%08lx) ",Status));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," or replace old hive file (%08lx)!\n",Status2));
|
|
|
|
//
|
|
// WARNNOTE:
|
|
// To get into this state, the user must have relevent
|
|
// privileges, deliberately mess with system in an attempt
|
|
// to defeat it, AND get it done in a narrow timing window.
|
|
//
|
|
// Further, if it's a user profile, the system will
|
|
// still come up.
|
|
//
|
|
// Therefore, return an error code and go on.
|
|
//
|
|
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// All of the renaming is done. However, we are holding an in-memory
|
|
// image of the new hive. Release it, since it will not actually
|
|
// be used until next boot.
|
|
//
|
|
// Do not close the open file handles to the new hive, we need to
|
|
// keep it locked exclusively until the system is rebooted to prevent
|
|
// people from mucking with it.
|
|
//
|
|
ErrorCleanup:
|
|
|
|
LOCK_HIVE_LIST();
|
|
CmpRemoveEntryList(&(NewHive->HiveList));
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
CmpDestroyHiveViewList(NewHive);
|
|
CmpDestroySecurityCache(NewHive);
|
|
CmpDropFileObjectForHive(NewHive);
|
|
|
|
HvFreeHive((PHHIVE)NewHive);
|
|
|
|
//
|
|
// only close handles on error
|
|
//
|
|
if( !NT_SUCCESS(Status) ) {
|
|
CmpCmdHiveClose(NewHive);
|
|
}
|
|
|
|
ASSERT( NewHive->HiveLock );
|
|
ExFreePool(NewHive->HiveLock);
|
|
ASSERT( NewHive->ViewLock );
|
|
ExFreePool(NewHive->ViewLock);
|
|
CmpFree(NewHive, sizeof(CMHIVE));
|
|
|
|
ErrorExit:
|
|
//
|
|
// Set global quota back to what it was.
|
|
//
|
|
CmpGlobalQuotaAllowed = OldQuotaAllowed;
|
|
CmpGlobalQuotaWarning = OldQuotaWarning;
|
|
|
|
#ifdef CHECK_REGISTRY_USECOUNT
|
|
CmpCheckRegistryUseCount();
|
|
#endif //CHECK_REGISTRY_USECOUNT
|
|
|
|
CmpUnlockRegistry();
|
|
return(Status);
|
|
}
|
|
|
|
#ifdef NT_RENAME_KEY
|
|
|
|
ULONG
|
|
CmpComputeKcbConvKey(
|
|
PCM_KEY_CONTROL_BLOCK KeyControlBlock
|
|
);
|
|
|
|
NTSTATUS
|
|
CmRenameKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
IN UNICODE_STRING NewKeyName // RAW
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Changes the name of the key to the given one.
|
|
|
|
What needs to be done:
|
|
|
|
1. Allocate a cell big enough to accomodate new knode
|
|
2. make a duplicate of the index in subkeylist of kcb's parent
|
|
3. replace parent's subkeylist with the duplicate
|
|
4. add new subkey to parent
|
|
5. remove old subkey
|
|
6. free storage.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for key to operate on
|
|
|
|
NewKeyName - The new name to be given to this key
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
Comments:
|
|
|
|
What do we do with symbolic links?
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX Cell;
|
|
PCM_KEY_NODE Node;
|
|
PCM_KEY_NODE ParentNode;
|
|
ULONG NodeSize;
|
|
HCELL_INDEX NewKeyCell = HCELL_NIL;
|
|
HSTORAGE_TYPE StorageType;
|
|
HCELL_INDEX OldSubKeyList = HCELL_NIL;
|
|
PCM_KEY_NODE NewKeyNode;
|
|
PCM_KEY_INDEX Index;
|
|
ULONG i;
|
|
LARGE_INTEGER TimeStamp;
|
|
ULONG NameLength;
|
|
PCM_NAME_CONTROL_BLOCK OldNcb;
|
|
ULONG ConvKey;
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmRenameKey\n"));
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
//
|
|
// no edits, on keys marked for deletion
|
|
//
|
|
if (KeyControlBlock->Delete) {
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
|
|
//
|
|
// see if the newName is not already a subkey of parentKcb
|
|
//
|
|
Hive = KeyControlBlock->KeyHive;
|
|
Cell = KeyControlBlock->KeyCell;
|
|
StorageType = HvGetCellType(Cell);
|
|
|
|
//
|
|
// OBS. we could have worked with the kcb tree instead, but if this is not
|
|
// going to work, we are in trouble anyway, so it's better to find out soon
|
|
//
|
|
Node = (PCM_KEY_NODE)HvGetCell(Hive,Cell);
|
|
if( Node == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, Cell);
|
|
|
|
//
|
|
// cannot rename the root of a hive; or anything in the master hive !!!
|
|
//
|
|
if((Hive == &CmpMasterHive->Hive) || (KeyControlBlock->ParentKcb == NULL) || (KeyControlBlock->ParentKcb->KeyHive == &CmpMasterHive->Hive) ) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
ParentNode = (PCM_KEY_NODE)HvGetCell(Hive,Node->Parent);
|
|
if( ParentNode == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, Node->Parent);
|
|
|
|
try {
|
|
if( CmpFindSubKeyByName(Hive,ParentNode,&NewKeyName) != HCELL_NIL ) {
|
|
//
|
|
// a subkey with this name already exists
|
|
//
|
|
return STATUS_CANNOT_DELETE;
|
|
}
|
|
|
|
//
|
|
// since we are in try-except, compute the new node size
|
|
//
|
|
NodeSize = CmpHKeyNodeSize(Hive, &NewKeyName);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
//
|
|
// 1. Allocate the new knode cell and copy the data from the old one, updating
|
|
// the name.
|
|
|
|
//
|
|
// mark the parent dirty, as we will modify its SubkeyLists
|
|
//
|
|
if(!HvMarkCellDirty(Hive, Node->Parent)) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
//
|
|
// mark the index dirty as we are going to free it on success
|
|
//
|
|
if ( !CmpMarkIndexDirty(Hive, Node->Parent, Cell) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
//
|
|
// mark key_node as dirty as we are going to free it if we succeed
|
|
//
|
|
if(!HvMarkCellDirty(Hive, Cell)) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
OldSubKeyList = ParentNode->SubKeyLists[StorageType];
|
|
ASSERT( OldSubKeyList != HCELL_NIL );
|
|
if(!HvMarkCellDirty(Hive, OldSubKeyList)) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive,OldSubKeyList);
|
|
if( Index == NULL ) {
|
|
//
|
|
// this is a bad joke; we just marked this dirty
|
|
//
|
|
ASSERT( FALSE );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, OldSubKeyList);
|
|
|
|
//
|
|
// mark all the index cells dirty
|
|
//
|
|
if( Index->Signature == CM_KEY_INDEX_ROOT ) {
|
|
//
|
|
// it's a root
|
|
//
|
|
for(i=0;i<Index->Count;i++) {
|
|
// common sense
|
|
ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
|
|
if(!HvMarkCellDirty(Hive, Index->List[i])) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NewKeyCell = HvAllocateCell(
|
|
Hive,
|
|
NodeSize,
|
|
StorageType,
|
|
Cell // in the same vicinity
|
|
);
|
|
if( NewKeyCell == HCELL_NIL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,NewKeyCell);
|
|
if( NewKeyNode == NULL ) {
|
|
//
|
|
// cannot map view; this shouldn't happen as we just allocated
|
|
// this cell (i.e. it should be dirty/pinned into memory)
|
|
//
|
|
ASSERT( FALSE );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, NewKeyCell);
|
|
|
|
//
|
|
// copy old keynode info onto the new cell and update the name
|
|
//
|
|
// first everything BUT the name
|
|
RtlCopyMemory(NewKeyNode,Node,FIELD_OFFSET(CM_KEY_NODE, Name));
|
|
// second, the new name
|
|
try {
|
|
NewKeyNode->NameLength = CmpCopyName( Hive,
|
|
NewKeyNode->Name,
|
|
&NewKeyName);
|
|
NameLength = NewKeyName.Length;
|
|
|
|
if (NewKeyNode->NameLength < NameLength ) {
|
|
NewKeyNode->Flags |= KEY_COMP_NAME;
|
|
} else {
|
|
NewKeyNode->Flags &= ~KEY_COMP_NAME;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
|
|
Status = GetExceptionCode();
|
|
goto ErrorExit;
|
|
}
|
|
// third, the timestamp
|
|
KeQuerySystemTime(&TimeStamp);
|
|
NewKeyNode->LastWriteTime = TimeStamp;
|
|
|
|
//
|
|
// at this point we have the new key_node all built up.
|
|
//
|
|
|
|
//
|
|
// 2.3. Make a duplicate of the parent's subkeylist and replace the original
|
|
//
|
|
ParentNode->SubKeyLists[StorageType] = CmpDuplicateIndex(Hive,OldSubKeyList,StorageType);
|
|
if( ParentNode->SubKeyLists[StorageType] == HCELL_NIL ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// 4. Add new subkey to the parent. This will take care of index
|
|
// grow and rebalance problems.
|
|
// Note: the index is at this point a duplicate, so if we fail, we still have the
|
|
// original one handy to recover
|
|
//
|
|
if( !CmpAddSubKey(Hive,Node->Parent,NewKeyCell) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// 5. remove old subkey;
|
|
//
|
|
if( !CmpRemoveSubKey(Hive,Node->Parent,Cell) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// 5'. update the parent on each and every son.
|
|
//
|
|
if( !CmpUpdateParentForEachSon(Hive,NewKeyCell) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// update the NCB in the kcb; at the end of this function, the kcbs underneath this
|
|
// will eventually get rehashed
|
|
//
|
|
OldNcb = KeyControlBlock->NameBlock;
|
|
try {
|
|
KeyControlBlock->NameBlock = CmpGetNameControlBlock (&NewKeyName);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
|
|
Status = GetExceptionCode();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// 6. At this point we have it all done. We just need to free the old index and key_cell
|
|
//
|
|
|
|
//
|
|
// free old index
|
|
//
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive,OldSubKeyList);
|
|
if( Index == NULL ) {
|
|
//
|
|
// this is a bad joke; we just marked this dirty
|
|
//
|
|
ASSERT( FALSE );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, OldSubKeyList);
|
|
|
|
if( Index->Signature == CM_KEY_INDEX_ROOT ) {
|
|
//
|
|
// it's a root
|
|
//
|
|
for(i=0;i<Index->Count;i++) {
|
|
// common sense
|
|
ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
|
|
HvFreeCell(Hive, Index->List[i]);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// should be a leaf
|
|
//
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF)
|
|
);
|
|
ASSERT(Index->Count != 0);
|
|
}
|
|
HvFreeCell(Hive, OldSubKeyList);
|
|
|
|
//
|
|
// free old cell
|
|
//
|
|
HvFreeCell(Hive,Cell);
|
|
|
|
//
|
|
// update the node KeyCell for this kcb and the timestamp on the kcb;
|
|
//
|
|
KeyControlBlock->KeyCell = NewKeyCell;
|
|
KeyControlBlock->KcbLastWriteTime = TimeStamp;
|
|
|
|
//
|
|
// and one last "little" thing: update parent's maxnamelen and reset parents cache
|
|
//
|
|
CmpCleanUpSubKeyInfo (KeyControlBlock->ParentKcb);
|
|
|
|
if (ParentNode->MaxNameLen < NameLength) {
|
|
ParentNode->MaxNameLen = NameLength;
|
|
KeyControlBlock->ParentKcb->KcbMaxNameLen = (USHORT)NameLength;
|
|
}
|
|
|
|
//
|
|
// rehash this kcb
|
|
//
|
|
ConvKey = CmpComputeKcbConvKey(KeyControlBlock);
|
|
if( ConvKey != KeyControlBlock->ConvKey ) {
|
|
//
|
|
// rehash the kcb by removing it from hash, and then inserting it
|
|
// again with th new ConvKey
|
|
//
|
|
CmpRemoveKeyHash(&(KeyControlBlock->KeyHash));
|
|
KeyControlBlock->ConvKey = ConvKey;
|
|
CmpInsertKeyHash(&(KeyControlBlock->KeyHash),FALSE);
|
|
}
|
|
|
|
//
|
|
// Aditional work: take care of the kcb subtree; this cannot fail, punt
|
|
//
|
|
CmpSearchForOpenSubKeys(KeyControlBlock,SearchAndRehash);
|
|
|
|
//
|
|
// last, dereference the OldNcb for this kcb
|
|
//
|
|
ASSERT( OldNcb != NULL );
|
|
CmpDereferenceNameControlBlockWithLock(OldNcb);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if( OldSubKeyList != HCELL_NIL ) {
|
|
//
|
|
// we have attempted (maybe even succedded) to duplicate parent's index)
|
|
//
|
|
if( ParentNode->SubKeyLists[StorageType] != HCELL_NIL ) {
|
|
//
|
|
// we need to free this as it is a duplicate
|
|
//
|
|
Index = (PCM_KEY_INDEX)HvGetCell(Hive,ParentNode->SubKeyLists[StorageType]);
|
|
if( Index == NULL ) {
|
|
//
|
|
// could not map view;this shouldn't happen as we just allocated this cell
|
|
//
|
|
ASSERT( FALSE );
|
|
} else {
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
HvReleaseCell(Hive, ParentNode->SubKeyLists[StorageType]);
|
|
|
|
if( Index->Signature == CM_KEY_INDEX_ROOT ) {
|
|
//
|
|
// it's a root
|
|
//
|
|
for(i=0;i<Index->Count;i++) {
|
|
// common sense
|
|
ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
|
|
HvFreeCell(Hive, Index->List[i]);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// should be a leaf
|
|
//
|
|
ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
|
|
(Index->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Index->Signature == CM_KEY_HASH_LEAF)
|
|
);
|
|
ASSERT(Index->Count != 0);
|
|
}
|
|
HvFreeCell(Hive, ParentNode->SubKeyLists[StorageType]);
|
|
}
|
|
|
|
}
|
|
//
|
|
// restore the parent's index
|
|
//
|
|
ParentNode->SubKeyLists[StorageType] = OldSubKeyList;
|
|
}
|
|
ASSERT( NewKeyCell != HCELL_NIL );
|
|
HvFreeCell(Hive,NewKeyCell);
|
|
|
|
if( OldNcb != NULL ) {
|
|
KeyControlBlock->NameBlock = OldNcb;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
CmMoveKey(
|
|
IN PCM_KEY_CONTROL_BLOCK KeyControlBlock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Moves all the cells related to this kcb above the specified fileoffset.
|
|
|
|
What needs to be done:
|
|
|
|
1. mark all data that we are going to touch dirty
|
|
2. Duplicate the key_node (and values and all cells involved)
|
|
3. Update the parent for all children
|
|
4. replace the new Key_cell in the parent's subkeylist
|
|
5. Update the kcb and the kcb cache
|
|
6. remove old subkey
|
|
|
|
WARNING:
|
|
after 3 we cannot fail anymore. if we do, we'll leak cells.
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for key to operate on
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PHHIVE Hive;
|
|
HCELL_INDEX OldKeyCell;
|
|
HCELL_INDEX NewKeyCell = HCELL_NIL;
|
|
HCELL_INDEX ParentKeyCell;
|
|
HSTORAGE_TYPE StorageType;
|
|
PCM_KEY_NODE OldKeyNode;
|
|
PCM_KEY_NODE ParentKeyNode;
|
|
PCM_KEY_NODE NewKeyNode;
|
|
PCM_KEY_INDEX ParentIndex;
|
|
PCM_KEY_INDEX OldIndex;
|
|
ULONG i,j;
|
|
HCELL_INDEX LeafCell;
|
|
PCM_KEY_INDEX Leaf;
|
|
PCM_KEY_FAST_INDEX FastIndex;
|
|
PHCELL_INDEX ParentIndexLocation = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmMoveKey\n"));
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
//
|
|
// no edits, on keys marked for deletion
|
|
//
|
|
if (KeyControlBlock->Delete) {
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
|
|
//
|
|
// see if the newName is not already a subkey of parentKcb
|
|
//
|
|
Hive = KeyControlBlock->KeyHive;
|
|
OldKeyCell = KeyControlBlock->KeyCell;
|
|
StorageType = HvGetCellType(OldKeyCell);
|
|
|
|
if( StorageType != Stable ) {
|
|
//
|
|
// nop the volatiles
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if( OldKeyCell == Hive->BaseBlock->RootCell ) {
|
|
//
|
|
// this works only for stable keys.
|
|
//
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// 1. mark all data that we are going to touch dirty
|
|
//
|
|
// parent's index, as we will replace the key node cell in it
|
|
// we only search in the Stable storage. It is supposed to be there
|
|
//
|
|
OldKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,OldKeyCell);
|
|
if( OldKeyNode == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (! CmpMarkKeyDirty(Hive, OldKeyCell
|
|
#if DBG
|
|
,FALSE
|
|
#endif //DBG
|
|
)) {
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
// release the cell right here, as the registry is locked exclusively, and the key_cell is marked as dirty
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
|
|
if( OldKeyNode->Flags & KEY_SYM_LINK ) {
|
|
//
|
|
// we do not compact links
|
|
//
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
|
|
//
|
|
// mark the index dirty
|
|
//
|
|
OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, OldKeyNode->SubKeyLists[Stable]);
|
|
if( OldIndex == NULL ) {
|
|
//
|
|
// we couldn't map the bin containing this cell
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
HvReleaseCell(Hive, OldKeyNode->SubKeyLists[Stable]);
|
|
if( !HvMarkCellDirty(Hive, OldKeyNode->SubKeyLists[Stable]) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
|
|
if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
|
|
for (i = 0; i < OldIndex->Count; i++) {
|
|
if( !HvMarkCellDirty(Hive, OldIndex->List[i]) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ParentKeyCell = OldKeyNode->Parent;
|
|
//
|
|
// now in the parent's spot
|
|
//
|
|
ParentKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,ParentKeyCell);
|
|
if( ParentKeyNode == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
if( !HvMarkCellDirty(Hive, ParentKeyCell) ) {
|
|
HvReleaseCell(Hive, ParentKeyCell);
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
// release the cell right here, as the registry is locked exclusively, so we don't care
|
|
// Key_cell is marked dirty to keep the parent knode mapped
|
|
HvReleaseCell(Hive, ParentKeyCell);
|
|
|
|
ParentIndex = (PCM_KEY_INDEX)HvGetCell(Hive, ParentKeyNode->SubKeyLists[Stable]);
|
|
if( ParentIndex == NULL ) {
|
|
//
|
|
// we couldn't map the bin containing this cell
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
HvReleaseCell(Hive, ParentKeyNode->SubKeyLists[Stable]);
|
|
|
|
if(ParentIndex->Signature == CM_KEY_INDEX_ROOT) {
|
|
|
|
//
|
|
// step through root, till we find the right leaf
|
|
//
|
|
for (i = 0; i < ParentIndex->Count; i++) {
|
|
LeafCell = ParentIndex->List[i];
|
|
Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
|
|
if( Leaf == NULL ) {
|
|
//
|
|
// we couldn't map the bin containing this cell
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
HvReleaseCell(Hive, LeafCell);
|
|
|
|
if ( (Leaf->Signature == CM_KEY_FAST_LEAF) ||
|
|
(Leaf->Signature == CM_KEY_HASH_LEAF)
|
|
) {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
|
|
for(j=0;j<FastIndex->Count;j++) {
|
|
if( FastIndex->List[j].Cell == OldKeyCell ) {
|
|
//
|
|
// found it! remember the locations we want to update later and break the loop
|
|
//
|
|
if( !HvMarkCellDirty(Hive, LeafCell) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
ParentIndexLocation = &(FastIndex->List[j].Cell);
|
|
break;
|
|
}
|
|
}
|
|
if( ParentIndexLocation != NULL ) {
|
|
break;
|
|
}
|
|
} else {
|
|
for(j=0;j<Leaf->Count;j++) {
|
|
if( Leaf->List[j] == OldKeyCell ) {
|
|
//
|
|
// found it! remember the locations we want to update later and break the loop
|
|
//
|
|
if( !HvMarkCellDirty(Hive, LeafCell) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
ParentIndexLocation = &(Leaf->List[j]);
|
|
break;
|
|
}
|
|
}
|
|
if( ParentIndexLocation != NULL ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if ( (ParentIndex->Signature == CM_KEY_FAST_LEAF) ||
|
|
(ParentIndex->Signature == CM_KEY_HASH_LEAF)
|
|
) {
|
|
FastIndex = (PCM_KEY_FAST_INDEX)ParentIndex;
|
|
for(j=0;j<FastIndex->Count;j++) {
|
|
if( FastIndex->List[j].Cell == OldKeyCell ) {
|
|
//
|
|
// found it! remember the locations we want to update later and break the loop
|
|
//
|
|
if( !HvMarkCellDirty(Hive, ParentKeyNode->SubKeyLists[Stable]) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
ParentIndexLocation = &(FastIndex->List[j].Cell);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for(j=0;j<ParentIndex->Count;j++) {
|
|
if( ParentIndex->List[j] == OldKeyCell ) {
|
|
//
|
|
// found it! remember the locations we want to update later and break the loop
|
|
//
|
|
if( !HvMarkCellDirty(Hive, ParentKeyNode->SubKeyLists[Stable]) ) {
|
|
return STATUS_NO_LOG_SPACE;
|
|
}
|
|
ParentIndexLocation = &(ParentIndex->List[j]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we should've find it !!!
|
|
ASSERT( ParentIndexLocation != NULL );
|
|
|
|
//
|
|
// 2. Duplicate the key_node (and values and all cells involved)
|
|
//
|
|
Status = CmpDuplicateKey(Hive,OldKeyCell,&NewKeyCell);
|
|
if( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
// sanity
|
|
ASSERT( (NewKeyCell != HCELL_NIL) && (StorageType == (HSTORAGE_TYPE)HvGetCellType(NewKeyCell)));
|
|
|
|
//
|
|
// 3. update the parent on each and every son.
|
|
//
|
|
if( !CmpUpdateParentForEachSon(Hive,NewKeyCell) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// 4. replace the new Key_cell in the parent's subkeylist
|
|
// From now on, WE CANNOT fails. we have everything marked dirty
|
|
// we just update some fields. no resources required !
|
|
// If we fail to free some cells, too bad, we'll leak some cells.
|
|
//
|
|
*ParentIndexLocation = NewKeyCell;
|
|
|
|
//
|
|
// 5. Update the kcb and the kcb cache
|
|
//
|
|
CmpCleanUpSubKeyInfo(KeyControlBlock->ParentKcb);
|
|
KeyControlBlock->KeyCell = NewKeyCell;
|
|
CmpRebuildKcbCache(KeyControlBlock);
|
|
|
|
//
|
|
// 6. remove old subkey
|
|
//
|
|
// First the Index; it's already marked dirty (i.e. PINNED)
|
|
//
|
|
if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
|
|
OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, OldKeyNode->SubKeyLists[Stable]);
|
|
ASSERT( OldIndex != NULL );
|
|
HvReleaseCell(Hive, OldKeyNode->SubKeyLists[Stable]);
|
|
if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
|
|
for (i = 0; i < OldIndex->Count; i++) {
|
|
HvFreeCell(Hive, OldIndex->List[i]);
|
|
}
|
|
}
|
|
HvFreeCell(Hive,OldKeyNode->SubKeyLists[Stable]);
|
|
}
|
|
|
|
OldKeyNode->SubKeyCounts[Stable] = 0;
|
|
OldKeyNode->SubKeyCounts[Volatile] = 0;
|
|
|
|
CmpFreeKeyByCell(Hive,OldKeyCell,FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
ErrorExit:
|
|
//
|
|
// we need to free the new knode allocated
|
|
//
|
|
NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,NewKeyCell);
|
|
// must be dirty
|
|
ASSERT( NewKeyNode != NULL );
|
|
HvReleaseCell(Hive, NewKeyCell);
|
|
if( NewKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
|
|
OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, NewKeyNode->SubKeyLists[Stable]);
|
|
ASSERT( OldIndex != NULL );
|
|
HvReleaseCell(Hive, NewKeyNode->SubKeyLists[Stable]);
|
|
if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
|
|
for (i = 0; i < OldIndex->Count; i++) {
|
|
HvFreeCell(Hive, OldIndex->List[i]);
|
|
}
|
|
}
|
|
HvFreeCell(Hive,NewKeyNode->SubKeyLists[Stable]);
|
|
}
|
|
NewKeyNode->SubKeyCounts[Stable] = 0;
|
|
NewKeyNode->SubKeyCounts[Volatile] = 0;
|
|
|
|
CmpFreeKeyByCell(Hive,NewKeyCell,FALSE);
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpDuplicateKey(
|
|
PHHIVE Hive,
|
|
HCELL_INDEX OldKeyCell,
|
|
PHCELL_INDEX NewKeyCell
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes an exact clone of OldKeyCell key_node in the
|
|
space above AboveFileOffset.
|
|
Operates on Stable storage ONLY!!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_NODE OldKeyNode;
|
|
PCM_KEY_NODE NewKeyNode;
|
|
ULONG i;
|
|
PRELEASE_CELL_ROUTINE TargetReleaseCellRoutine;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
ASSERT( HvGetCellType(OldKeyCell) == Stable );
|
|
|
|
OldKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,OldKeyCell);
|
|
if( OldKeyNode == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// since the registry is locked exclusively here, we don't need to lock/release cells
|
|
// while copying the trees; So, we just set the release routines to NULL and restore after
|
|
// the copy is complete; this saves some pain
|
|
//
|
|
TargetReleaseCellRoutine = Hive->ReleaseCellRoutine;
|
|
Hive->ReleaseCellRoutine = NULL;
|
|
|
|
*NewKeyCell = CmpCopyKeyPartial(Hive,OldKeyCell,Hive,OldKeyNode->Parent,TRUE);
|
|
Hive->ReleaseCellRoutine = TargetReleaseCellRoutine;
|
|
|
|
if( *NewKeyCell == HCELL_NIL ) {
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,*NewKeyCell);
|
|
if( NewKeyNode == NULL ) {
|
|
//
|
|
// cannot map view
|
|
//
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
CmpFreeKeyByCell(Hive,*NewKeyCell,FALSE);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// now we have the key_cell duplicated. Values and security has also been taken care of
|
|
// Go ahead and duplicate the Index.
|
|
//
|
|
if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
|
|
NewKeyNode->SubKeyLists[Stable] = CmpDuplicateIndex(Hive,OldKeyNode->SubKeyLists[Stable],Stable);
|
|
if( NewKeyNode->SubKeyLists[Stable] == HCELL_NIL ) {
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
CmpFreeKeyByCell(Hive,*NewKeyCell,FALSE);
|
|
HvReleaseCell(Hive, *NewKeyCell);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
ASSERT( OldKeyNode->SubKeyCounts[Stable] == 0 );
|
|
NewKeyNode->SubKeyLists[Stable] = HCELL_NIL;
|
|
}
|
|
NewKeyNode->SubKeyCounts[Stable] = OldKeyNode->SubKeyCounts[Stable];
|
|
NewKeyNode->SubKeyLists[Volatile] = OldKeyNode->SubKeyLists[Volatile];
|
|
NewKeyNode->SubKeyCounts[Volatile] = OldKeyNode->SubKeyCounts[Volatile];
|
|
|
|
HvReleaseCell(Hive, *NewKeyCell);
|
|
HvReleaseCell(Hive, OldKeyCell);
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
#ifdef WRITE_PROTECTED_REGISTRY_POOL
|
|
|
|
VOID
|
|
CmpMarkAllBinsReadOnly(
|
|
PHHIVE Hive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Marks the memory allocated for all the stable bins in this hive as read only.
|
|
|
|
Arguments:
|
|
|
|
Hive - supplies a pointer to the hive control structure for the
|
|
hive of interest
|
|
|
|
Return Value:
|
|
|
|
NONE (It should work!)
|
|
|
|
--*/
|
|
{
|
|
PHMAP_ENTRY t;
|
|
PHBIN Bin;
|
|
HCELL_INDEX p;
|
|
ULONG Length;
|
|
|
|
//
|
|
// we are only interested in the stable storage
|
|
//
|
|
Length = Hive->Storage[Stable].Length;
|
|
|
|
p = 0;
|
|
|
|
//
|
|
// for each bin in the space
|
|
//
|
|
while (p < Length) {
|
|
t = HvpGetCellMap(Hive, p);
|
|
VALIDATE_CELL_MAP(__LINE__,t,Hive,p);
|
|
|
|
Bin = (PHBIN)HBIN_BASE(t->BinAddress);
|
|
|
|
if (t->BinAddress & HMAP_NEWALLOC) {
|
|
|
|
//
|
|
// Mark it as read Only
|
|
//
|
|
HvpChangeBinAllocation(Bin,TRUE);
|
|
}
|
|
|
|
// next one, please
|
|
p = (ULONG)p + Bin->Size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif //WRITE_PROTECTED_REGISTRY_POOL
|
|
|
|
ULONG
|
|
CmpCompressKeyWorker(
|
|
PCM_KEY_CONTROL_BLOCK Current,
|
|
PVOID Context1,
|
|
PVOID Context2
|
|
)
|
|
{
|
|
PLIST_ENTRY pListHead;
|
|
PCM_KCB_REMAP_BLOCK kcbRemapBlock;
|
|
//PLIST_ENTRY AnchorAddr;
|
|
|
|
if (Current->KeyHive == Context1) {
|
|
|
|
pListHead = (PLIST_ENTRY)Context2;
|
|
ASSERT( pListHead );
|
|
/*
|
|
//
|
|
// check if we didn't already recorded this kcb
|
|
//
|
|
AnchorAddr = pListHead;
|
|
kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)(pListHead->Flink);
|
|
|
|
while ( kcbRemapBlock != (PCM_KCB_REMAP_BLOCK)AnchorAddr ) {
|
|
kcbRemapBlock = CONTAINING_RECORD(
|
|
kcbRemapBlock,
|
|
CM_KCB_REMAP_BLOCK,
|
|
RemapList
|
|
);
|
|
if( kcbRemapBlock->KeyControlBlock == Current ) {
|
|
//
|
|
// we already have this kcb
|
|
//
|
|
return KCB_WORKER_CONTINUE;
|
|
}
|
|
//
|
|
// skip to the next element
|
|
//
|
|
kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)(kcbRemapBlock->RemapList.Flink);
|
|
}
|
|
*/
|
|
|
|
kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)ExAllocatePool(PagedPool, sizeof(CM_KCB_REMAP_BLOCK));
|
|
if( kcbRemapBlock == NULL ) {
|
|
return KCB_WORKER_ERROR;
|
|
}
|
|
kcbRemapBlock->KeyControlBlock = Current;
|
|
kcbRemapBlock->NewCellIndex = HCELL_NIL;
|
|
kcbRemapBlock->OldCellIndex = Current->KeyCell;
|
|
kcbRemapBlock->ValueCount = 0;
|
|
kcbRemapBlock->ValueList = HCELL_NIL;
|
|
InsertTailList(pListHead,&(kcbRemapBlock->RemapList));
|
|
|
|
}
|
|
return KCB_WORKER_CONTINUE; // always keep searching
|
|
}
|
|
|
|
NTSTATUS
|
|
CmCompressKey(
|
|
IN PHHIVE Hive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compresses the kcb, by means of simulating an "in-place" SaveKey
|
|
|
|
What needs to be done:
|
|
|
|
1. iterate through the kcb tree and make a list of all the kcbs
|
|
that need to be changed (their keycell will change during the process)
|
|
2. iterate through the cache and compute an array of security cells.
|
|
We'll need it to map security cells into the new hive.
|
|
3. Save the hive into a temporary hive, preserving
|
|
the volatile info in keynodes and updating the cell mappings.
|
|
4. Update the cache by adding volatile security cells from the old hive.
|
|
5. Dump temporary (compressed) hive over to the old file.
|
|
6. Switch hive data from the compressed one to the existing one and update
|
|
the kcb KeyCell and security mapping
|
|
7. Invalidate the map and drop paged bins.
|
|
8. Free storage for the new hive (OK if we fail)
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive to operate on
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Result code from call, among the following:
|
|
|
|
<TBS>
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HCELL_INDEX KeyCell;
|
|
PCMHIVE CmHive;
|
|
PCM_KCB_REMAP_BLOCK RemapBlock;
|
|
PCMHIVE NewHive = NULL;
|
|
HCELL_INDEX LinkCell;
|
|
PCM_KEY_NODE LinkNode;
|
|
PCM_KNODE_REMAP_BLOCK KnodeRemapBlock;
|
|
ULONG OldLength;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmCompressKey\n"));
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
if( HvAutoCompressCheck(Hive) == FALSE ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
KeyCell = Hive->BaseBlock->RootCell;
|
|
CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
|
|
//
|
|
// Make sure the cell passed in is the root cell of the hive.
|
|
//
|
|
if ( CmHive == CmpMasterHive ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// 0. Get the cells we need to relink the compressed hive
|
|
//
|
|
LinkNode = (PCM_KEY_NODE)HvGetCell(Hive,KeyCell);
|
|
if( LinkNode == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
LinkCell = LinkNode->Parent;
|
|
HvReleaseCell(Hive,KeyCell);
|
|
LinkNode = (PCM_KEY_NODE)HvGetCell((PHHIVE)CmpMasterHive,LinkCell);
|
|
// master storage is paged pool
|
|
ASSERT(LinkNode != NULL);
|
|
HvReleaseCell((PHHIVE)CmpMasterHive,LinkCell);
|
|
|
|
|
|
OldLength = Hive->BaseBlock->Length;
|
|
//
|
|
// 1. iterate through the kcb tree and make a list of all the kcbs
|
|
// that need to be changed (their keycell will change during the process)
|
|
//
|
|
ASSERT( IsListEmpty(&(CmHive->KcbConvertListHead)) );
|
|
//
|
|
// this will kick all kcb with refcount == 0 out of cache, so we can use
|
|
// CmpSearchKeyControlBlockTree for recording referenced kcbs
|
|
//
|
|
CmpCleanUpKCBCacheTable();
|
|
//CmpSearchForOpenSubKeys(KeyControlBlock,SearchIfExist);
|
|
if( !CmpSearchKeyControlBlockTree(CmpCompressKeyWorker,(PVOID)Hive,(PVOID)(&(CmHive->KcbConvertListHead))) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 2. iterate through the cache and compute an array of security cells.
|
|
// We'll need it to map security cells into the new hive.
|
|
//
|
|
if( !CmpBuildSecurityCellMappingArray(CmHive) ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 3. Save the hive into a temporary hive , preserving
|
|
// the volatile info in keynodes and updating the cell mappings.
|
|
//
|
|
Status = CmpShiftHiveFreeBins(CmHive,&NewHive);
|
|
if( !NT_SUCCESS(Status) ) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// 5. Dump temporary (compressed) hive over to the old file.
|
|
//
|
|
Status = CmpOverwriteHive(CmHive,NewHive,LinkCell);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// From this point on, we WILL NOT FAIL!
|
|
//
|
|
|
|
//
|
|
// get the root node and link it into the master storage
|
|
//
|
|
LinkNode->ChildHiveReference.KeyCell = NewHive->Hive.BaseBlock->RootCell;
|
|
|
|
//
|
|
// 6. Switch hive data from the compressed one to the existing one and update
|
|
// the kcb KeyCell and security mapping
|
|
// This should better NOT fail!!! If it does, we are doomed, as we have partial
|
|
// data => bugcheck
|
|
//
|
|
CmpSwitchStorageAndRebuildMappings(CmHive,NewHive);
|
|
|
|
|
|
//
|
|
// 7. Invalidate the map and drop paged bins. If system hive, check for the hysteresis callback.
|
|
//
|
|
HvpDropAllPagedBins(&(CmHive->Hive));
|
|
if( OldLength < CmHive->Hive.BaseBlock->Length ) {
|
|
CmpUpdateSystemHiveHysteresis(&(CmHive->Hive),CmHive->Hive.BaseBlock->Length,OldLength);
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
//
|
|
// 8. Free storage for the new hive (OK if we fail)
|
|
//
|
|
if( NewHive != NULL ) {
|
|
CmpDestroyTemporaryHive(NewHive);
|
|
}
|
|
|
|
if( CmHive->CellRemapArray != NULL ) {
|
|
ExFreePool(CmHive->CellRemapArray);
|
|
CmHive->CellRemapArray = NULL;
|
|
}
|
|
//
|
|
// remove all remap blocks and free them
|
|
//
|
|
while (IsListEmpty(&(CmHive->KcbConvertListHead)) == FALSE) {
|
|
RemapBlock = (PCM_KCB_REMAP_BLOCK)RemoveHeadList(&(CmHive->KcbConvertListHead));
|
|
RemapBlock = CONTAINING_RECORD(
|
|
RemapBlock,
|
|
CM_KCB_REMAP_BLOCK,
|
|
RemapList
|
|
);
|
|
ExFreePool(RemapBlock);
|
|
}
|
|
while (IsListEmpty(&(CmHive->KnodeConvertListHead)) == FALSE) {
|
|
KnodeRemapBlock = (PCM_KNODE_REMAP_BLOCK)RemoveHeadList(&(CmHive->KnodeConvertListHead));
|
|
KnodeRemapBlock = CONTAINING_RECORD(
|
|
KnodeRemapBlock,
|
|
CM_KNODE_REMAP_BLOCK,
|
|
RemapList
|
|
);
|
|
ExFreePool(KnodeRemapBlock);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmLockKcbForWrite(PCM_KEY_CONTROL_BLOCK KeyControlBlock)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tags the kcb as being read-only and no-delay-close
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock
|
|
|
|
Return Value:
|
|
|
|
TBS
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CmpLockKCBTreeExclusive();
|
|
|
|
ASSERT_KCB(KeyControlBlock);
|
|
if( KeyControlBlock->Delete ) {
|
|
CmpUnlockKCBTree();
|
|
return STATUS_KEY_DELETED;
|
|
}
|
|
//
|
|
// sanity check in case we are called twice
|
|
//
|
|
ASSERT( ((KeyControlBlock->ExtFlags&CM_KCB_READ_ONLY_KEY) && (KeyControlBlock->ExtFlags&CM_KCB_NO_DELAY_CLOSE)) ||
|
|
(!(KeyControlBlock->ExtFlags&CM_KCB_READ_ONLY_KEY))
|
|
);
|
|
|
|
//
|
|
// tag the kcb as read-only; also make it no-delay close so it can revert to the normal state after all handles are closed.
|
|
//
|
|
KeyControlBlock->ExtFlags |= (CM_KCB_READ_ONLY_KEY|CM_KCB_NO_DELAY_CLOSE);
|
|
|
|
//
|
|
// add an artificial refcount on this kcb. This will keep the kcb (and the read only flag set in memory for as long as the system is up)
|
|
//
|
|
InterlockedIncrement( (PLONG)&KeyControlBlock->RefCount );
|
|
|
|
CmpUnlockKCBTree();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
|
|
PUNICODE_STRING ValueName,
|
|
ULONG Type,
|
|
PVOID Data,
|
|
ULONG DataSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Most of the SetValue calls are noops (i.e. they are setting the same
|
|
value name to the same value data). By comparing against the data already
|
|
in the kcb cache (i.e. faulted in) we can save page faults.
|
|
|
|
|
|
Arguments:
|
|
|
|
KeyControlBlock - pointer to kcb for the key to operate on
|
|
|
|
ValueName - The unique (relative to the containing key) name
|
|
of the value entry. May be NULL.
|
|
|
|
Type - The integer type number of the value entry.
|
|
|
|
Data - Pointer to buffer with actual data for the value entry.
|
|
|
|
DataSize - Size of Data buffer.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - same value with the same data exist in the cache.
|
|
|
|
--*/
|
|
{
|
|
PCM_KEY_VALUE Value;
|
|
ULONG Index;
|
|
BOOLEAN ValueCached;
|
|
PPCM_CACHED_VALUE ContainingList;
|
|
HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
|
|
BOOLEAN Result = FALSE;
|
|
PUCHAR datapointer;
|
|
BOOLEAN BufferAllocated = FALSE;
|
|
HCELL_INDEX CellToRelease = HCELL_NIL;
|
|
ULONG compareSize;
|
|
ULONG realsize;
|
|
BOOLEAN small;
|
|
|
|
PAGED_CODE();
|
|
|
|
BEGIN_KCB_LOCK_GUARD;
|
|
CmpLockKCBTreeExclusive();
|
|
|
|
if( KeyControlBlock->Flags & KEY_SYM_LINK ) {
|
|
//
|
|
// need to rebuild the value cache, so we could runt the same code
|
|
//
|
|
PCM_KEY_NODE Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell);
|
|
|
|
if( Node == NULL ) {
|
|
//
|
|
// we couldn't map the bin containing this cell
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
CmpCleanUpKcbValueCache(KeyControlBlock);
|
|
CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
|
|
|
|
HvReleaseCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell);
|
|
}
|
|
|
|
Value = CmpFindValueByNameFromCache(KeyControlBlock->KeyHive,
|
|
&(KeyControlBlock->ValueCache),
|
|
ValueName,
|
|
&ContainingList,
|
|
&Index,
|
|
&ValueCached,
|
|
&ValueDataCellToRelease
|
|
);
|
|
|
|
if(Value) {
|
|
if( (Type == Value->Type) && (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE)) ) {
|
|
|
|
small = CmpIsHKeyValueSmall(realsize, Value->DataLength);
|
|
if (small == TRUE) {
|
|
datapointer = (PUCHAR)(&(Value->Data));
|
|
} else if( CmpGetValueDataFromCache(KeyControlBlock->KeyHive, ContainingList,(PCELL_DATA)Value,
|
|
ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
|
|
//
|
|
// we couldn't map view for cell; treat it as insufficient resources problem
|
|
//
|
|
ASSERT( datapointer == NULL );
|
|
ASSERT( BufferAllocated == FALSE );
|
|
goto Exit;
|
|
}
|
|
//
|
|
// compare data
|
|
//
|
|
if (DataSize > 0) {
|
|
|
|
try {
|
|
compareSize = (ULONG)RtlCompareMemory ((PVOID)datapointer,Data,(DataSize & ~CM_KEY_VALUE_SPECIAL_SIZE));
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
goto Exit;
|
|
}
|
|
|
|
} else {
|
|
compareSize = 0;
|
|
}
|
|
|
|
if (compareSize == DataSize) {
|
|
Result = TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
CmpUnlockKCBTree();
|
|
END_KCB_LOCK_GUARD;
|
|
|
|
if(ValueDataCellToRelease != HCELL_NIL) {
|
|
HvReleaseCell(KeyControlBlock->KeyHive,ValueDataCellToRelease);
|
|
}
|
|
if( BufferAllocated == TRUE ) {
|
|
ExFreePool(datapointer);
|
|
}
|
|
if(CellToRelease != HCELL_NIL) {
|
|
HvReleaseCell(KeyControlBlock->KeyHive,CellToRelease);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
NTSTATUS
|
|
static
|
|
__forceinline
|
|
CmpCheckReplaceHive( IN PHHIVE Hive,
|
|
OUT PHCELL_INDEX Key
|
|
)
|
|
{
|
|
HCELL_INDEX RootCell;
|
|
UNICODE_STRING Name;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PRELEASE_CELL_ROUTINE TargetReleaseCellRoutine;
|
|
WCHAR Buffer[4];
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
//
|
|
// disable refcounting
|
|
//
|
|
TargetReleaseCellRoutine = Hive->ReleaseCellRoutine;
|
|
Hive->ReleaseCellRoutine = NULL;
|
|
|
|
Buffer[3] = 0;
|
|
*Key = HCELL_NIL;
|
|
Buffer[1] = (WCHAR)'P';
|
|
|
|
RootCell = Hive->BaseBlock->RootCell;
|
|
Buffer[2] = (WCHAR)'A';
|
|
|
|
if( RootCell == HCELL_NIL ) {
|
|
//
|
|
// could not find root cell. Bogus.
|
|
//
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
goto Exit;
|
|
}
|
|
Buffer[0] = (WCHAR)'W';
|
|
|
|
RtlInitUnicodeString(&Name, Buffer);
|
|
RootCell = CmpFindSubKeyByName(Hive,
|
|
(PCM_KEY_NODE)HvGetCell(Hive,RootCell),
|
|
&Name);
|
|
|
|
|
|
if( RootCell != HCELL_NIL ) {
|
|
//
|
|
// found it.
|
|
//
|
|
*Key = RootCell;
|
|
} else {
|
|
//
|
|
// WPA key should be present; it's created by GUI mode.
|
|
//
|
|
Status = STATUS_REGISTRY_CORRUPT;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
Hive->ReleaseCellRoutine = TargetReleaseCellRoutine;
|
|
return Status;
|
|
}
|