windows-nt/Source/XPSP1/NT/base/cluster/service/dm/dmreg.c

1991 lines
45 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
dmreg.c
Abstract:
Contains the registry access routines for the Config Database Manager
Author:
John Vert (jvert) 24-Apr-1996
Revision History:
--*/
#include "dmp.h"
#include <align.h>
#if NO_SHARED_LOCKS
extern CRITICAL_SECTION gLockDmpRoot;
#else
extern RTL_RESOURCE gLockDmpRoot;
#endif
HDMKEY
DmGetRootKey(
IN DWORD samDesired
)
/*++
Routine Description:
Opens the registry key at the root of the cluster registry database
Arguments:
samDesired - Supplies requested security access
Return Value:
A handle to the opened registry key.
NULL on error. LastError will be set to the specific error code.
--*/
{
DWORD Error;
PDMKEY Key;
Key = LocalAlloc(LMEM_FIXED, sizeof(DMKEY)+sizeof(WCHAR));
if (Key == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
//
// Acquire DM root lock to synchronize with DmRollbackRegistry.
//
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
DmpClusterParametersKeyName,
0,
samDesired,
&Key->hKey);
if (Error != ERROR_SUCCESS) {
LocalFree(Key);
SetLastError(Error);
Key = NULL;
goto FnExit;
}
Key->Name[0] = '\0';
Key->GrantedAccess = samDesired;
EnterCriticalSection(&KeyLock);
InsertHeadList(&KeyList, &Key->ListEntry);
InitializeListHead(&Key->NotifyList);
LeaveCriticalSection(&KeyLock);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return((HDMKEY)Key);
}
DWORD
DmCloseKey(
IN HDMKEY hKey
)
/*++
Routine Description:
Closes a handle to an open HDMKEY key.
Arguments:
hKey - Supplies the handle to be closed.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise.
--*/
{
DWORD Error = ERROR_SUCCESS;
PDMKEY Key;
//
// Nobody better EVER close one of the global keys.
//
CL_ASSERT(hKey != DmClusterParametersKey);
CL_ASSERT(hKey != DmResourcesKey);
CL_ASSERT(hKey != DmResourceTypesKey);
CL_ASSERT(hKey != DmQuorumKey);
CL_ASSERT(hKey != DmGroupsKey);
CL_ASSERT(hKey != DmNodesKey);
CL_ASSERT(hKey != DmNetworksKey);
CL_ASSERT(hKey != DmNetInterfacesKey);
Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//if the key was deleted and invalidated and couldnt be reopened
// it will be set to NULL, in this case we dont call regclosekey
if( Key == NULL ) goto FnExit;
if (ISKEYDELETED(Key))
goto CleanupKey;
Error = RegCloseKey(Key->hKey);
if (Error != ERROR_SUCCESS)
{
CL_LOGFAILURE(Error);
goto FnExit;
}
CleanupKey:
EnterCriticalSection(&KeyLock);
RemoveEntryList(&Key->ListEntry);
DmpRundownNotify(Key);
LeaveCriticalSection(&KeyLock);
LocalFree(Key);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Error);
}
HDMKEY
DmCreateKey(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey,
IN DWORD dwOptions,
IN DWORD samDesired,
IN OPTIONAL LPVOID lpSecurityDescriptor,
OUT LPDWORD lpDisposition
)
/*++
Routine Description:
Creates a key in the cluster registry. If the key exists, it
is opened. If it does not exist, it is created on all nodes in
the cluster.
Arguments:
hKey - Supplies the key that the create is relative to.
lpSubKey - Supplies the key name relative to hKey
dwOptions - Supplies any registry option flags.
samDesired - Supplies desired security access mask
lpSecurityDescriptor - Supplies security for the newly created key.
Disposition - Returns whether the key was opened (REG_OPENED_EXISTING_KEY)
or created (REG_CREATED_NEW_KEY)
Return Value:
A handle to the specified key if successful
NULL otherwise. LastError will be set to the specific error code.
--*/
{
PDMKEY Parent;
PDMKEY Key=NULL;
DWORD NameLength;
DWORD Status = ERROR_SUCCESS;
HDMKEY NewKey;
PDM_CREATE_KEY_UPDATE CreateUpdate = NULL;
DWORD SecurityLength;
// if this is a request to create a volatile key, refuse it
// we dont support volatile keys in the cluster hive since
// we cant roll back the cluster hive then.
if (dwOptions == REG_OPTION_VOLATILE)
{
Status = ERROR_INVALID_PARAMETER;
goto FnExit;
}
//
// Issue a global update to create the key.
//
Parent = (PDMKEY)hKey;
//
// Allocate the DMKEY structure.
//
NameLength = (lstrlenW(Parent->Name) + 1 + lstrlenW(lpSubKey) + 1)*sizeof(WCHAR);
Key = LocalAlloc(LMEM_FIXED, sizeof(DMKEY)+NameLength);
if (Key == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
Status = ERROR_NOT_ENOUGH_MEMORY;
goto FnExit;
}
//
// Create the key name
//
lstrcpyW(Key->Name, Parent->Name);
if (Key->Name[0] != UNICODE_NULL) {
lstrcatW(Key->Name, L"\\");
}
lstrcatW(Key->Name, lpSubKey);
Key->GrantedAccess = samDesired;
//get the length of the security structure
if (ARGUMENT_PRESENT(lpSecurityDescriptor)) {
SecurityLength = GetSecurityDescriptorLength(lpSecurityDescriptor);
} else {
SecurityLength = 0;
}
CreateUpdate = (PDM_CREATE_KEY_UPDATE)LocalAlloc(LMEM_FIXED, sizeof(DM_CREATE_KEY_UPDATE));
if (CreateUpdate == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
Status = ERROR_NOT_ENOUGH_MEMORY;
goto FnExit;
}
//
// Issue the update.
//
CreateUpdate->lpDisposition = lpDisposition;
CreateUpdate->phKey = &Key->hKey;
CreateUpdate->samDesired = samDesired;
CreateUpdate->dwOptions = dwOptions;
if (ARGUMENT_PRESENT(lpSecurityDescriptor)) {
CreateUpdate->SecurityPresent = TRUE;
} else {
CreateUpdate->SecurityPresent = FALSE;
}
Status = GumSendUpdateEx(GumUpdateRegistry,
DmUpdateCreateKey,
3,
sizeof(DM_CREATE_KEY_UPDATE),
CreateUpdate,
(lstrlenW(Key->Name)+1)*sizeof(WCHAR),
Key->Name,
SecurityLength,
lpSecurityDescriptor);
if (Status != ERROR_SUCCESS)
{
goto FnExit;
}
EnterCriticalSection(&KeyLock);
InsertHeadList(&KeyList, &Key->ListEntry);
InitializeListHead(&Key->NotifyList);
LeaveCriticalSection(&KeyLock);
FnExit:
if (CreateUpdate) LocalFree(CreateUpdate);
if (Status != ERROR_SUCCESS)
{
if (Key) LocalFree(Key);
SetLastError(Status);
return(NULL);
}
else
{
return ((HDMKEY)Key);
}
}
HDMKEY
DmOpenKey(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey,
IN DWORD samDesired
)
/*++
Routine Description:
Opens a key in the cluster registry. If the key exists, it
is opened. If it does not exist, the call fails.
Arguments:
hKey - Supplies the key that the open is relative to.
lpSubKey - Supplies the key name relative to hKey
samDesired - Supplies desired security access mask
Return Value:
A handle to the specified key if successful
NULL otherwise. LastError will be set to the specific error code.
--*/
{
PDMKEY Parent;
PDMKEY Key=NULL;
DWORD NameLength;
DWORD Status = ERROR_SUCCESS;
Parent = (PDMKEY)hKey;
//hold the shared lock
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//check if the key was deleted and invalidated
if (ISKEYDELETED(Parent))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
//
// Allocate the DMKEY structure.
//
NameLength = (lstrlenW(Parent->Name) + 1 + lstrlenW(lpSubKey) + 1)*sizeof(WCHAR);
Key = LocalAlloc(LMEM_FIXED, sizeof(DMKEY)+NameLength);
if (Key == NULL) {
Status = ERROR_NOT_ENOUGH_MEMORY;
CL_UNEXPECTED_ERROR(Status);
goto FnExit;
}
//
// Open the key on the local machine.
//
Status = RegOpenKeyEx(Parent->hKey,
lpSubKey,
0,
samDesired,
&Key->hKey);
if (Status != ERROR_SUCCESS) {
goto FnExit;
}
//
// Create the key name. only append a trailing backslash
// if a parent name and subkey are non-null
//
lstrcpyW(Key->Name, Parent->Name);
if ((Key->Name[0] != UNICODE_NULL) && (lpSubKey[0] != UNICODE_NULL)) {
lstrcatW(Key->Name, L"\\");
}
lstrcatW(Key->Name, lpSubKey);
Key->GrantedAccess = samDesired;
EnterCriticalSection(&KeyLock);
InsertHeadList(&KeyList, &Key->ListEntry);
InitializeListHead(&Key->NotifyList);
LeaveCriticalSection(&KeyLock);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
if (Status != ERROR_SUCCESS)
{
if (Key) LocalFree(Key);
SetLastError(Status);
return(NULL);
}
else
return((HDMKEY)Key);
}
DWORD
DmEnumKey(
IN HDMKEY hKey,
IN DWORD dwIndex,
OUT LPWSTR lpName,
IN OUT LPDWORD lpcbName,
OUT OPTIONAL PFILETIME lpLastWriteTime
)
/*++
Routine Description:
Enumerates the subkeys of a cluster registry key.
Arguments:
hKey - Supplies the registry key for which the subkeys should
be enumerated.
dwIndex - Supplies the index to be enumerated.
KeyName - Returns the name of the dwIndex subkey. The memory
allocated for this buffer must be freed by the client.
lpLastWriteTime - Returns the last write time.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PDMKEY Key;
DWORD Status;
FILETIME LastTime;
Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//check if the key was deleted and invalidated
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegEnumKeyExW(Key->hKey,
dwIndex,
lpName,
lpcbName,
NULL,
NULL,
NULL,
&LastTime);
if (lpLastWriteTime != NULL) {
*lpLastWriteTime = LastTime;
}
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
}
DWORD
DmSetValue(
IN HDMKEY hKey,
IN LPCWSTR lpValueName,
IN DWORD dwType,
IN CONST BYTE *lpData,
IN DWORD cbData
)
/*++
Routine Description:
This routine sets the named value for the specified
cluster registry key.
Arguments:
hKey - Supplies the cluster registry subkey whose value is to be set
lpValueName - Supplies the name of the value to be set.
dwType - Supplies the value data type
lpData - Supplies a pointer to the value data
cbData - Supplies the length of the value data.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status= ERROR_SUCCESS; //initialize to success
PDMKEY Key;
DWORD NameLength;
DWORD ValueNameLength;
DWORD UpdateLength;
PDM_SET_VALUE_UPDATE Update;
PUCHAR Dest;
Key = (PDMKEY)hKey;
if (ISKEYDELETED(Key))
return(ERROR_KEY_DELETED);
//
// round lengths such that pointers to the data trailing the structure are
// aligned on the architecture's natural boundary
//
NameLength = (lstrlenW(Key->Name)+1)*sizeof(WCHAR);
NameLength = ROUND_UP_COUNT( NameLength, sizeof( DWORD_PTR ));
ValueNameLength = (lstrlenW(lpValueName)+1)*sizeof(WCHAR);
ValueNameLength = ROUND_UP_COUNT( ValueNameLength, sizeof( DWORD_PTR ));
UpdateLength = sizeof(DM_SET_VALUE_UPDATE) +
NameLength +
ValueNameLength +
cbData;
Update = (PDM_SET_VALUE_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength);
if (Update == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
return(ERROR_NOT_ENOUGH_MEMORY);
}
Update->lpStatus = &Status;
Update->NameOffset = FIELD_OFFSET(DM_SET_VALUE_UPDATE, KeyName)+NameLength;
Update->DataOffset = Update->NameOffset + ValueNameLength;
Update->DataLength = cbData;
Update->Type = dwType;
CopyMemory(Update->KeyName, Key->Name, NameLength);
Dest = (PUCHAR)Update + Update->NameOffset;
CopyMemory(Dest, lpValueName, ValueNameLength);
Dest = (PUCHAR)Update + Update->DataOffset;
CopyMemory(Dest, lpData, cbData);
Status = GumSendUpdate(GumUpdateRegistry,
DmUpdateSetValue,
UpdateLength,
Update);
LocalFree(Update);
return(Status);
}
DWORD
DmDeleteValue(
IN HDMKEY hKey,
IN LPCWSTR lpValueName
)
/*++
Routine Description:
Removes the specified value from a given registry subkey
Arguments:
hKey - Supplies the key whose value is to be deleted.
lpValueName - Supplies the name of the value to be removed.
Return Value:
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is an error value.
--*/
{
PDMKEY Key;
DWORD NameLength;
DWORD ValueNameLength;
DWORD UpdateLength;
PDM_DELETE_VALUE_UPDATE Update;
PUCHAR Dest;
DWORD Status;
Key = (PDMKEY)hKey;
if (ISKEYDELETED(Key))
return(ERROR_KEY_DELETED);
//
// round up length to align pointer to ValueName on natural architecture
// boundary
//
NameLength = (lstrlenW(Key->Name)+1)*sizeof(WCHAR);
NameLength = ROUND_UP_COUNT( NameLength, sizeof( DWORD_PTR ));
ValueNameLength = (lstrlenW(lpValueName)+1)*sizeof(WCHAR);
UpdateLength = sizeof(DM_DELETE_VALUE_UPDATE) +
NameLength +
ValueNameLength;
Update = (PDM_DELETE_VALUE_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength);
if (Update == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
return(ERROR_NOT_ENOUGH_MEMORY);
}
Update->lpStatus = &Status;
Update->NameOffset = FIELD_OFFSET(DM_DELETE_VALUE_UPDATE, KeyName)+NameLength;
CopyMemory(Update->KeyName, Key->Name, NameLength);
Dest = (PUCHAR)Update + Update->NameOffset;
CopyMemory(Dest, lpValueName, ValueNameLength);
Status = GumSendUpdate(GumUpdateRegistry,
DmUpdateDeleteValue,
UpdateLength,
Update);
LocalFree(Update);
return(Status);
}
DWORD
DmQueryValue(
IN HDMKEY hKey,
IN LPCWSTR lpValueName,
OUT LPDWORD lpType,
OUT LPBYTE lpData,
IN OUT LPDWORD lpcbData
)
/*++
Routine Description:
Queries a named value for the specified cluster registry subkey
Arguments:
hKey - Supplies the subkey whose value should be queried
lpValueName - Supplies the named value to be queried
lpType - Returns the type of the value's data
lpData - Returns the value's data
lpcbData - Supplies the size (in bytes) of the lpData buffer
Returns the number of bytes copied into the lpData buffer
If lpData==NULL, cbData is set to the required buffer
size and the function returns ERROR_SUCCESS
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PDMKEY Key;
DWORD Status;
Key = (PDMKEY)hKey;
//check if the key was deleted and invalidated
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegQueryValueEx(Key->hKey,
lpValueName,
NULL,
lpType,
lpData,
lpcbData);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
}
DWORD
DmQueryDword(
IN HDMKEY hKey,
IN LPCWSTR lpValueName,
OUT LPDWORD lpValue,
IN LPDWORD lpDefaultValue OPTIONAL
)
/*++
Routine Description:
Reads a REG_DWORD registry value. If the value is not present, then
default to the value supplied in lpDefaultValue (if present).
Arguments:
hKey - Open key for the value to be read.
lpValueName - Unicode name of the value to be read.
lpValue - Pointer to the DWORD into which to read the value.
lpDefaultValue - Optional pointer to a DWORD to use as a default value.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PDMKEY Key;
DWORD Status;
DWORD ValueType;
DWORD ValueSize = sizeof(DWORD);
Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegQueryValueEx(Key->hKey,
lpValueName,
NULL,
&ValueType,
(LPBYTE)lpValue,
&ValueSize);
if ( Status == ERROR_SUCCESS ) {
if ( ValueType != REG_DWORD ) {
Status = ERROR_INVALID_PARAMETER;
}
} else {
if ( ARGUMENT_PRESENT( lpDefaultValue ) ) {
*lpValue = *lpDefaultValue;
Status = ERROR_SUCCESS;
}
}
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
} // DmQueryDword
DWORD
DmQueryString(
IN HDMKEY Key,
IN LPCWSTR ValueName,
IN DWORD ValueType,
IN LPWSTR *StringBuffer,
IN OUT LPDWORD StringBufferSize,
OUT LPDWORD StringSize
)
/*++
Routine Description:
Reads a REG_SZ or REG_MULTI_SZ registry value. If the StringBuffer is
not large enough to hold the data, it is reallocated.
Arguments:
Key - Open key for the value to be read.
ValueName - Unicode name of the value to be read.
ValueType - REG_SZ or REG_MULTI_SZ.
StringBuffer - Buffer into which to place the value data.
StringBufferSize - Pointer to the size of the StringBuffer. This parameter
is updated if StringBuffer is reallocated.
StringSize - The size of the data returned in StringBuffer, including
the terminating null character.
Return Value:
The status of the registry query.
--*/
{
DWORD status;
DWORD valueType;
WCHAR *temp;
DWORD oldBufferSize = *StringBufferSize;
BOOL noBuffer = FALSE;
if (*StringBufferSize == 0) {
noBuffer = TRUE;
}
*StringSize = *StringBufferSize;
status = DmQueryValue( Key,
ValueName,
&valueType,
(LPBYTE) *StringBuffer,
StringSize
);
if (status == NO_ERROR) {
if (!noBuffer ) {
if (valueType == ValueType) {
return(NO_ERROR);
}
else {
return(ERROR_INVALID_PARAMETER);
}
}
if (*StringSize) status = ERROR_MORE_DATA;
}
if (status == ERROR_MORE_DATA) {
temp = LocalAlloc(LMEM_FIXED, *StringSize);
if (temp == NULL) {
*StringSize = 0;
return(ERROR_NOT_ENOUGH_MEMORY);
}
if (!noBuffer) {
LocalFree(*StringBuffer);
}
*StringBuffer = temp;
*StringBufferSize = *StringSize;
status = DmQueryValue( Key,
ValueName,
&valueType,
(LPBYTE) *StringBuffer,
StringSize
);
if (status == NO_ERROR) {
if (valueType == ValueType) {
return(NO_ERROR);
}
else {
*StringSize = 0;
return(ERROR_INVALID_PARAMETER);
}
}
}
return(status);
} // DmQueryString
VOID
DmEnumKeys(
IN HDMKEY RootKey,
IN PENUM_KEY_CALLBACK Callback,
IN PVOID Context
)
/*++
Routine Description:
Enumerates the subkeys of the given registry key. For each
subkey, a string is allocated to hold the subkey name and
the subkey is opened. The specified callback function is
called and passed the subkey handle and subkey name.
The callback function is responsible for closing the subkey
handle and freeing the subkey name.
Arguments:
RootKey - Supplies a handle to the key whose subkeys are to
be enumerated.
Callback - Supplies the callback routine.
Context - Supplies an arbitrary context to be passed to the
callback routine.
Return Value:
None.
--*/
{
PWSTR KeyName;
HDMKEY SubKey;
DWORD Index;
DWORD Status;
FILETIME FileTime;
PWSTR NameBuf;
DWORD NameBufSize;
DWORD OrigNameBufSize;
//
// Find the length of the longest subkey name.
//
Status = DmQueryInfoKey(RootKey,
NULL,
&NameBufSize,
NULL,
NULL,
NULL,
NULL,
NULL);
if (Status != ERROR_SUCCESS) {
CL_UNEXPECTED_ERROR(Status);
return;
}
NameBufSize = (NameBufSize + 1)*sizeof(WCHAR);
NameBuf = LocalAlloc(LMEM_FIXED, NameBufSize);
if (NameBuf == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
}
OrigNameBufSize = NameBufSize;
//
// Enumerate the subkeys
//
Index = 0;
do {
NameBufSize = OrigNameBufSize;
Status = DmEnumKey( RootKey,
Index,
NameBuf,
&NameBufSize,
NULL);
if (Status == ERROR_SUCCESS) {
KeyName = LocalAlloc(LMEM_FIXED, (wcslen(NameBuf)+1)*sizeof(WCHAR));
if (KeyName != NULL) {
wcscpy(KeyName, NameBuf);
//
// Open the key
//
SubKey = DmOpenKey( RootKey,
KeyName,
MAXIMUM_ALLOWED);
if (SubKey == NULL) {
Status = GetLastError();
CL_UNEXPECTED_ERROR(Status);
LocalFree(KeyName);
} else {
(Callback)(SubKey,
KeyName,
Context);
}
} else {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
}
}
Index++;
} while ( Status == ERROR_SUCCESS );
LocalFree(NameBuf);
} // DmEnumKeys
VOID
DmEnumValues(
IN HDMKEY RootKey,
IN PENUM_VALUE_CALLBACK Callback,
IN PVOID Context
)
/*++
Routine Description:
Enumerates the values of the given registry key. For each
value, a string is allocated to hold the value name and a
buffer is allocated to hold its data. The specified callback
function is called and passed the value name and data.
The callback function must not free either the value name
or its buffer. If it needs this data after the callback
returns, it must copy it.
Arguments:
RootKey - Supplies a handle to the key whose values are to
be enumerated.
Callback - Supplies the callback routine.
Context - Supplies an arbitrary context to be passed to the
callback routine.
Return Value:
None.
--*/
{
DWORD Index;
DWORD Status;
PWSTR NameBuf;
DWORD NameBufSize;
DWORD ValueCount;
DWORD MaxValueLen;
DWORD MaxNameLen;
PVOID ValueBuf;
DWORD cbName;
DWORD cbData;
DWORD dwType;
BOOL Continue;
//
// Find the length of the longest value name and data.
//
Status = DmQueryInfoKey(RootKey,
NULL,
NULL,
&ValueCount,
&MaxNameLen,
&MaxValueLen,
NULL,
NULL);
if (Status != ERROR_SUCCESS) {
CL_UNEXPECTED_ERROR(Status);
return;
}
NameBuf = CsAlloc((MaxNameLen+1)*sizeof(WCHAR));
ValueBuf = CsAlloc(MaxValueLen);
//
// Enumerate the values
//
for (Index=0; Index<ValueCount; Index++) {
cbName = MaxNameLen+1;
cbData = MaxValueLen;
Status = DmEnumValue(RootKey,
Index,
NameBuf,
&cbName,
&dwType,
ValueBuf,
&cbData);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmEnumValue for index %1!d! of key %2!ws! failed %3!d!\n",
Index,
((PDMKEY)(RootKey))->Name,
Status);
} else {
Continue = (Callback)(NameBuf,
ValueBuf,
dwType,
cbData,
Context);
if (!Continue) {
break;
}
}
}
CsFree(NameBuf);
CsFree(ValueBuf);
} // DmEnumValues
DWORD
DmQueryInfoKey(
IN HDMKEY hKey,
OUT LPDWORD SubKeys,
OUT LPDWORD MaxSubKeyLen,
OUT LPDWORD Values,
OUT LPDWORD MaxValueNameLen,
OUT LPDWORD MaxValueLen,
OUT LPDWORD lpcbSecurityDescriptor,
OUT PFILETIME FileTime
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
PDMKEY Key;
DWORD Ignored;
DWORD Status;
Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegQueryInfoKeyW( Key->hKey,
NULL,
&Ignored,
NULL,
SubKeys,
MaxSubKeyLen,
&Ignored,
Values,
MaxValueNameLen,
MaxValueLen,
lpcbSecurityDescriptor,
FileTime);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
} // DmQueryInfoKey
DWORD
DmDeleteKey(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey
)
/*++
Routine Description:
Deletes the specified key. A key that has subkeys cannot
be deleted.
Arguments:
hKey - Supplies a handle to a currently open key.
lpSubKey - Points to a null-terminated string specifying the
name of the key to delete. This parameter cannot be NULL,
and the specified key must not have subkeys.
Return Value:
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is an error value.
--*/
{
PDMKEY Key;
DWORD NameLength;
DWORD UpdateLength;
PDM_DELETE_KEY_UPDATE Update;
DWORD Status;
Key = (PDMKEY)hKey;
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
return(ERROR_KEY_DELETED);
NameLength = (lstrlenW(Key->Name) + 1 + lstrlenW(lpSubKey) + 1)*sizeof(WCHAR);
UpdateLength = NameLength + sizeof(DM_DELETE_KEY_UPDATE);
Update = (PDM_DELETE_KEY_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength);
if (Update == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
return(ERROR_NOT_ENOUGH_MEMORY);
}
Update->lpStatus = &Status;
CopyMemory(Update->Name, Key->Name, (lstrlenW(Key->Name) + 1) * sizeof(WCHAR));
if (Update->Name[0] != '\0') {
lstrcatW(Update->Name, L"\\");
}
lstrcatW(Update->Name, lpSubKey);
Status = GumSendUpdate(GumUpdateRegistry,
DmUpdateDeleteKey,
sizeof(DM_DELETE_KEY_UPDATE)+NameLength,
Update);
LocalFree(Update);
return(Status);
}
DWORD
DmDeleteTree(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey
)
/*++
Routine Description:
Deletes the specified registry subtree. All subkeys are
deleted.
Arguments:
hKey - Supplies a handle to a currently open key.
lpSubKey - Points to a null-terminated string specifying the
name of the key to delete. This parameter cannot be NULL.
Any subkeys of the specified key will also be deleted.
Return Value:
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is an error value.
--*/
{
HDMKEY Subkey;
DWORD i;
DWORD Status;
LPWSTR KeyBuffer=NULL;
DWORD MaxKeyLen;
DWORD NeededSize;
Subkey = DmOpenKey(hKey,
lpSubKey,
MAXIMUM_ALLOWED);
if (Subkey == NULL) {
Status = GetLastError();
return(Status);
}
//
// Get the size of name buffer we will need.
//
Status = DmQueryInfoKey(Subkey,
NULL,
&MaxKeyLen,
NULL,
NULL,
NULL,
NULL,
NULL);
if (Status != ERROR_SUCCESS) {
CL_UNEXPECTED_ERROR( Status );
DmCloseKey(Subkey);
return(Status);
}
KeyBuffer = LocalAlloc(LMEM_FIXED, (MaxKeyLen+1)*sizeof(WCHAR));
if (KeyBuffer == NULL) {
CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
DmCloseKey(Subkey);
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Enumerate the subkeys and apply ourselves recursively to each one.
//
i=0;
do {
NeededSize = MaxKeyLen+1;
Status = DmEnumKey(Subkey,
i,
KeyBuffer,
&NeededSize,
NULL);
if (Status == ERROR_SUCCESS) {
//
// Call ourselves recursively on this keyname.
//
DmDeleteTree(Subkey, KeyBuffer);
} else {
//
// Some odd error, keep going with the next key.
//
++i;
}
} while ( Status != ERROR_NO_MORE_ITEMS );
DmCloseKey(Subkey);
Status = DmDeleteKey(hKey, lpSubKey);
if (KeyBuffer != NULL) {
LocalFree(KeyBuffer);
}
return(Status);
}
DWORD
DmEnumValue(
IN HDMKEY hKey,
IN DWORD dwIndex,
OUT LPWSTR lpValueName,
IN OUT LPDWORD lpcbValueName,
OUT LPDWORD lpType,
OUT LPBYTE lpData,
IN OUT LPDWORD lpcbData
)
/*++
Routine Description:
Enumerates the specified value of a registry subkey
Arguments:
hKey - Supplies the registry key handle
dwIndex - Supplies the index of the value to be enumerated
lpValueName - Points to a buffer that receives the name of the value,
including the terminating null character
lpcbValueName - Points to a variable that specifies the size, in characters,
of the buffer pointed to by the lpValueName parameter. This size should
include the terminating null character. When the function returns, the
variable pointed to by lpcbValueName contains the number of characters
stored in the buffer. The count returned does not include the terminating
null character.
lpType - Returns the value data type
lpData - Points to a buffer that receives the data for the value entry. This
parameter can be NULL if the data is not required.
lpcbData - Points to a variable that specifies the size, in bytes, of the
buffer pointed to by the lpData parameter. When the function returns, the
variable pointed to by the lpcbData parameter contains the number of bytes
stored in the buffer. This parameter can be NULL, only if lpData is NULL.
Return Value:
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is an error value.
--*/
{
PDMKEY Key;
DWORD Status;
DWORD cbValueName = *lpcbValueName;
Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegEnumValueW(Key->hKey,
dwIndex,
lpValueName,
lpcbValueName,
NULL,
lpType,
lpData,
lpcbData);
//
// The following code is to mask the registry behavior by which RegEnumValue does not necessarily
// fill in lpValueName (even though we specify a large enough buffer) when lpData buffer is a
// valid buffer but a little too small.
//
if ( Status == ERROR_MORE_DATA )
{
DWORD dwError;
dwError = RegEnumValueW( Key->hKey,
dwIndex,
lpValueName,
&cbValueName,
NULL,
NULL,
NULL,
NULL );
if ( ( dwError != ERROR_SUCCESS ) &&
( dwError != ERROR_MORE_DATA ) )
{
Status = dwError;
} else
{
*lpcbValueName = cbValueName;
}
}
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
}
DWORD
DmAppendToMultiSz(
IN HDMKEY hKey,
IN LPCWSTR lpValueName,
IN LPCWSTR lpString
)
/*++
Routine Description:
Adds another string to a REG_MULTI_SZ value. If the value does
not exist, it will be created.
Arguments:
hKey - Supplies the key where the value exists. This key must
have been opened with KEY_READ | KEY_SET_VALUE access
lpValueName - Supplies the name of the value.
lpString - Supplies the string to be appended to the REG_MULTI_SZ value
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD ValueLength = 512;
DWORD ReturnedLength;
LPWSTR ValueData;
DWORD StringLength;
DWORD Status;
DWORD cbValueData;
PWSTR s;
DWORD Type;
StringLength = (lstrlenW(lpString)+1)*sizeof(WCHAR);
retry:
ValueData = LocalAlloc(LMEM_FIXED, ValueLength + StringLength);
if (ValueData == NULL) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
cbValueData = ValueLength;
Status = DmQueryValue(hKey,
lpValueName,
&Type,
(LPBYTE)ValueData,
&cbValueData);
if (Status == ERROR_MORE_DATA) {
//
// The existing value is too large for our buffer.
// Retry with a larger buffer.
//
ValueLength = cbValueData;
LocalFree(ValueData);
goto retry;
}
if (Status == ERROR_FILE_NOT_FOUND) {
//
// The value does not currently exist. Create the
// value with our data.
//
s = ValueData;
} else if (Status == ERROR_SUCCESS) {
//
// A value already exists. Append our string to the
// MULTI_SZ.
//
s = (PWSTR)((PCHAR)ValueData + cbValueData) - 1;
} else {
LocalFree(ValueData);
return(Status);
}
CopyMemory(s, lpString, StringLength);
s += (StringLength / sizeof(WCHAR));
*s++ = L'\0';
Status = DmSetValue(hKey,
lpValueName,
REG_MULTI_SZ,
(CONST BYTE *)ValueData,
(DWORD)((s-ValueData)*sizeof(WCHAR)));
LocalFree(ValueData);
return(Status);
}
DWORD
DmRemoveFromMultiSz(
IN HDMKEY hKey,
IN LPCWSTR lpValueName,
IN LPCWSTR lpString
)
/*++
Routine Description:
Removes a string from a REG_MULTI_SZ value.
Arguments:
hKey - Supplies the key where the value exists. This key must
have been opened with READ | KEY_SET_VALUE access
lpValueName - Supplies the name of the value.
lpString - Supplies the string to be removed from the REG_MULTI_SZ value
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
LPWSTR Buffer=NULL;
DWORD BufferSize;
DWORD DataSize;
LPWSTR Current;
DWORD CurrentLength;
DWORD i;
LPWSTR Next;
PCHAR Src, Dest;
DWORD NextLength;
DWORD MultiLength;
BufferSize = 0;
Status = DmQueryString(hKey,
lpValueName,
REG_MULTI_SZ,
&Buffer,
&BufferSize,
&DataSize);
if (Status != ERROR_SUCCESS) {
goto FnExit;
}
MultiLength = DataSize/sizeof(WCHAR);
Status = ClRtlMultiSzRemove(Buffer,
&MultiLength,
lpString);
if (Status == ERROR_SUCCESS) {
//
// Set the new value back.
//
Status = DmSetValue(hKey,
lpValueName,
REG_MULTI_SZ,
(CONST BYTE *)Buffer,
MultiLength * sizeof(WCHAR));
} else if (Status == ERROR_FILE_NOT_FOUND) {
Status = ERROR_SUCCESS;
}
FnExit:
if (Buffer) LocalFree(Buffer);
return(Status);
}
DWORD
DmGetKeySecurity(
IN HDMKEY hKey,
IN SECURITY_INFORMATION RequestedInformation,
OUT PSECURITY_DESCRIPTOR pSecurityDescriptor,
IN LPDWORD lpcbSecurityDescriptor
)
/*++
Routine Description:
Retrieves a copy of the security descriptor protecting
the specified cluster registry key.
Arguments:
hKey - Supplies the handle of the key
RequestedInformation - Specifies a SECURITY_INFORMATION structure that
indicates the requested security information.
pSecurityDescriptor - Points to a buffer that receives a copy of the
requested security descriptor.
lpcbSecurityDescriptor - Points to a variable that specifies the size,
in bytes, of the buffer pointed to by the pSecurityDescriptor parameter.
When the function returns, the variable contains the number of bytes
written to the buffer.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
PDMKEY Key = (PDMKEY)hKey;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
{
Status = ERROR_KEY_DELETED;
goto FnExit;
}
Status = RegGetKeySecurity(Key->hKey,
RequestedInformation,
pSecurityDescriptor,
lpcbSecurityDescriptor);
FnExit:
RELEASE_LOCK(gLockDmpRoot);
return(Status);
}
DWORD
DmSetKeySecurity(
IN HDMKEY hKey,
IN SECURITY_INFORMATION SecurityInformation,
IN PSECURITY_DESCRIPTOR pSecurityDescriptor
)
/*++
Routine Description:
Sets the security on the specified registry key.
Arguments:
hKey - Supplies a handle to a currently open key.
SecurityInformation - Supplies the type of security information to
be set.
pRpcSecurityDescriptor - Supplies the security information
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
PDMKEY Key = (PDMKEY)hKey;
//make sure the key wasnt deleted/invalidated/reopened while we had a
//handle open to it
if (ISKEYDELETED(Key))
return(ERROR_KEY_DELETED);
Status = GumSendUpdateEx(GumUpdateRegistry,
DmUpdateSetSecurity,
4,
sizeof(SecurityInformation),
&SecurityInformation,
(lstrlenW(Key->Name)+1)*sizeof(WCHAR),
Key->Name,
GetSecurityDescriptorLength(pSecurityDescriptor),
pSecurityDescriptor,
sizeof(Key->GrantedAccess),
&Key->GrantedAccess);
return(Status);
}
DWORD
DmCommitRegistry(
VOID
)
/*++
Routine Description:
Flushes the registry to disk, producing a new persistent cluster registry state.
Arguments:
None
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
ACQUIRE_SHARED_LOCK(gLockDmpRoot);
Status = RegFlushKey(DmpRoot);
RELEASE_LOCK(gLockDmpRoot);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmCommitRegistry failed to flush dirty data %1!d!\n",
Status);
}
return(Status);
}
DWORD
DmRollbackRegistry(
VOID
)
/*++
Routine Description:
Rolls the registry back to the last previously committed state.
Arguments:
None
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD Status;
BOOLEAN WasEnabled;
ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
//hold the key lock as well
EnterCriticalSection(&KeyLock);
Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
&WasEnabled);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmRollbackRegistry failed to restore privilege %1!d!\n",
Status);
goto FnExit;
}
//
// Restart the registry watcher thread so it is not trying to use
// DmpRoot while we are messing with things.
//
DmpRestartFlusher();
//
// Close any open handles
//
DmpInvalidateKeys();
Status = NtRestoreKey(DmpRoot,
NULL,
REG_REFRESH_HIVE);
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
WasEnabled);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmRollbackRegistry: NtRestoreKey failed %1!d!\n",
Status);
goto FnExit;
}
//
// Reopen handles
//
RegCloseKey(DmpRoot);
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
DmpClusterParametersKeyName,
&DmpRoot);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmRollbackRegistry failed to reopen DmpRoot %1!d!\n",
Status);
goto FnExit;
}
DmpReopenKeys();
FnExit:
//release the locks
LeaveCriticalSection(&KeyLock);
RELEASE_LOCK(gLockDmpRoot);
if (Status != ERROR_SUCCESS)
{
ClRtlLogPrint(LOG_CRITICAL,
"[DM] DmRollbackRegistry failed to flush dirty data %1!d!\n",
Status);
}
return(Status);
}
DWORD
DmRtlCreateKey(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey,
IN DWORD dwOptions,
IN DWORD samDesired,
IN OPTIONAL LPVOID lpSecurityDescriptor,
OUT HDMKEY * phkResult,
OUT LPDWORD lpDisposition
)
/*++
Routine Description:
Wrapper function for DmCreateKey. Its definition corresponds to
ClusterRegCreateKey. This should be used instead of DmCreateKey
when passing to ClRtl* funtions.
--*/
{
DWORD status;
*phkResult = DmCreateKey(
hKey,
lpSubKey,
dwOptions,
samDesired,
lpSecurityDescriptor,
lpDisposition
);
if (*phkResult == NULL)
status=GetLastError();
else
status = ERROR_SUCCESS;
return status;
}
DWORD
DmRtlOpenKey(
IN HDMKEY hKey,
IN LPCWSTR lpSubKey,
IN DWORD samDesired,
OUT HDMKEY * phkResult
)
/*++
Routine Description:
Wrapper function for DmOpenKey. Its definition corresponds to
ClusterRegOpenKey. This should be used instead of DmOpenKey when
passing to ClRtl* funtions. See DmOpenKey for argument description
--*/
{
DWORD status;
*phkResult = DmOpenKey(
hKey,
lpSubKey,
samDesired
);
if (*phkResult == NULL)
status=GetLastError();
else
status=ERROR_SUCCESS;
return status;
}