/*++ 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 #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; IndexName, 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); RegCloseKey(DmpRootCopy); DmpRoot = DmpRootCopy = NULL; Status = RegOpenKeyW(HKEY_LOCAL_MACHINE, DmpClusterParametersKeyName, &DmpRoot); if ( Status == ERROR_SUCCESS ) { Status = RegOpenKeyW(HKEY_LOCAL_MACHINE, DmpClusterParametersKeyName, &DmpRootCopy); } 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; }