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.
1419 lines
39 KiB
1419 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dmsync.c
|
|
|
|
Abstract:
|
|
|
|
Contains the registry synchronization code for the Cluster Database
|
|
Manager.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 5/23/1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "dmp.h"
|
|
|
|
|
|
#if NO_SHARED_LOCKS
|
|
extern CRITICAL_SECTION gLockDmpRoot;
|
|
#else
|
|
extern RTL_RESOURCE gLockDmpRoot;
|
|
#endif
|
|
|
|
const WCHAR DmpClusterParametersKeyName[] = L"Cluster";
|
|
extern const UNICODE_STRING RegistryMachineClusterString = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Cluster");
|
|
extern const OBJECT_ATTRIBUTES RegistryMachineClusterObja = RTL_CONSTANT_OBJECT_ATTRIBUTES(&RegistryMachineClusterString, OBJ_CASE_INSENSITIVE);
|
|
|
|
//
|
|
// Private Constants
|
|
//
|
|
#define CHUNK_SIZE 4096
|
|
|
|
//
|
|
// Private macro
|
|
//
|
|
#define ClosePipe( _pipe ) \
|
|
(_pipe.push)(_pipe.state, \
|
|
NULL, \
|
|
0 ) \
|
|
|
|
|
|
|
|
//
|
|
// Client-Side Utility Routines
|
|
//
|
|
void
|
|
FilePipePush(
|
|
FILE_PIPE_STATE *state,
|
|
unsigned char *pBuffer,
|
|
unsigned long BufferSize
|
|
)
|
|
{
|
|
DWORD dwBytesWritten;
|
|
DWORD dwStatus;
|
|
|
|
dwStatus = NmCryptor_Decrypt(&state->Cryptor, pBuffer, BufferSize);
|
|
if (dwStatus != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"FilePipePush :: Decryption failed with error %1!u!\n",
|
|
dwStatus);
|
|
RpcRaiseException(dwStatus);
|
|
}
|
|
|
|
if (BufferSize != 0) {
|
|
if (!QfsWriteFile (state->hFile,
|
|
state->Cryptor.PayloadBuffer,
|
|
state->Cryptor.PayloadSize,
|
|
&dwBytesWritten,
|
|
NULL))
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"FilePipePush :: Write file failed with error %1!u!\n",
|
|
dwStatus);
|
|
RpcRaiseException(dwStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
FilePipePull(
|
|
FILE_PIPE_STATE *state,
|
|
unsigned char *pBuffer,
|
|
unsigned long BufferSize,
|
|
unsigned long __RPC_FAR *Written
|
|
)
|
|
{
|
|
DWORD dwBytesRead;
|
|
BOOL Success;
|
|
DWORD dwStatus;
|
|
|
|
if (BufferSize != 0) {
|
|
NmCryptor_PrepareEncryptionBuffer(
|
|
&state->Cryptor, pBuffer, BufferSize);
|
|
|
|
Success = QfsReadFile (state->hFile,
|
|
state->Cryptor.PayloadBuffer,
|
|
state->Cryptor.PayloadSize,
|
|
&dwBytesRead,
|
|
NULL);
|
|
|
|
if (!Success)
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"FilePipePush :: Read file failed with error %1!u!\n",
|
|
dwStatus);
|
|
RpcRaiseException(dwStatus);
|
|
}
|
|
|
|
dwStatus = NmCryptor_Encrypt(&state->Cryptor, dwBytesRead);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"FilePipePush :: Encryption failed with error %1!u!\n",
|
|
dwStatus);
|
|
RpcRaiseException(dwStatus);
|
|
}
|
|
|
|
*Written = state->Cryptor.EncryptedSize;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
PipeAlloc (
|
|
FILE_PIPE_STATE *state,
|
|
unsigned long RequestedSize,
|
|
unsigned char **buf,
|
|
unsigned long *ActualSize
|
|
)
|
|
{
|
|
*buf = state->pBuffer;
|
|
|
|
*ActualSize = (RequestedSize < state->BufferSize ?
|
|
RequestedSize :
|
|
state->BufferSize);
|
|
}
|
|
|
|
|
|
VOID
|
|
DmInitFilePipe(
|
|
IN PFILE_PIPE FilePipe,
|
|
IN QfsHANDLE hFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a file pipe.
|
|
|
|
Arguments:
|
|
|
|
FilePipe - Supplies a pointer to the file pipe to be initialized
|
|
|
|
hFile - Supplies a handle to the file to be transmitted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
FilePipe->State.hFile = hFile;
|
|
FilePipe->State.BufferSize = CHUNK_SIZE;
|
|
FilePipe->State.pBuffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
|
|
if (FilePipe->State.pBuffer == NULL) {
|
|
CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
FilePipe->Pipe.state = (char __RPC_FAR *)&FilePipe->State;
|
|
FilePipe->Pipe.alloc = (void __RPC_FAR *)PipeAlloc;
|
|
FilePipe->Pipe.push = (void __RPC_FAR *)FilePipePush;
|
|
FilePipe->Pipe.pull = (void __RPC_FAR *)FilePipePull;
|
|
|
|
NmCryptor_Init(&FilePipe->State.Cryptor, TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
DmFreeFilePipe(
|
|
IN PFILE_PIPE FilePipe
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees a file pipe initialized by DmInitFilePipe
|
|
|
|
Arguments:
|
|
|
|
FilePipe - Supplies the file pipe to be freed.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NmCryptor_Destroy(&FilePipe->State.Cryptor);
|
|
LocalFree(FilePipe->State.pBuffer);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmPullFile(
|
|
IN LPCWSTR FileName,
|
|
IN BYTE_PIPE Pipe
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new file and pulls the data down the RPC pipe
|
|
|
|
Arguments:
|
|
|
|
FileName - Supplies the name of the file.
|
|
|
|
Pipe - Supplies the RPC pipe to pull the data from.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
QfsHANDLE File;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
PUCHAR Buffer;
|
|
DWORD BytesRead;
|
|
NM_CRYPTOR Decryptor;
|
|
|
|
NmCryptor_Init(&Decryptor, TRUE);
|
|
|
|
//
|
|
// Create a new file to hold the bits from the client.
|
|
//
|
|
File = QfsCreateFile(FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
|
|
if (!QfsIsHandleValid(File)) {
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmPullFile failed to create file %1!ws! error %2!d!\n",
|
|
FileName,
|
|
Status);
|
|
return(Status);
|
|
}
|
|
|
|
Buffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
|
|
|
|
if (Buffer == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
QfsCloseHandle(File);
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
return (Status);
|
|
}
|
|
|
|
try {
|
|
do {
|
|
(Pipe.pull)(Pipe.state,
|
|
Buffer,
|
|
CHUNK_SIZE,
|
|
&BytesRead);
|
|
if (BytesRead == 0) {
|
|
break;
|
|
}
|
|
|
|
Status = NmCryptor_Decrypt(&Decryptor, Buffer, BytesRead);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmPullFile :: Failed to decrypt buffer for '%1!ws!' error %2!d!\n",
|
|
FileName, Status);
|
|
break;
|
|
}
|
|
|
|
if (!QfsWriteFile(File,
|
|
Decryptor.PayloadBuffer,
|
|
Decryptor.PayloadSize,
|
|
&BytesRead,
|
|
NULL))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmPullFile :: WriteFile to file failed with error %1!ws! error %2!d!\n",
|
|
FileName, Status);
|
|
break;
|
|
}
|
|
} while ( TRUE );
|
|
} except (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
Status = GetExceptionCode();
|
|
ClRtlLogPrint(LOG_ERROR,
|
|
"[DM] DmPullFile :: Exception code 0x%1!08lx! raised for file %2!ws!\n",
|
|
Status, FileName);
|
|
}
|
|
|
|
LocalFree(Buffer);
|
|
|
|
QfsFlushFileBuffers(File);
|
|
|
|
QfsCloseHandle(File);
|
|
|
|
NmCryptor_Destroy(&Decryptor);
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmPushFile(
|
|
IN LPCWSTR FileName,
|
|
IN BYTE_PIPE Pipe,
|
|
IN BOOL EncryptData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a file and pushes it down the RPC pipe
|
|
|
|
Arguments:
|
|
|
|
FileName - Supplies the name of the file.
|
|
|
|
Pipe - Supplies the RPC pipe to push it down.
|
|
|
|
EncryptData - If TRUE, data passed over the Rpc pipe will be encrypted
|
|
(If NT5 node is in the cluster, data won't be encrypted)
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
QfsHANDLE File;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
PUCHAR Buffer;
|
|
DWORD BytesRead;
|
|
NM_CRYPTOR Encryptor;
|
|
|
|
NmCryptor_Init(&Encryptor, EncryptData);
|
|
|
|
//
|
|
// Got a file with the right bits in it. Push it down
|
|
// to the client.
|
|
//
|
|
File = QfsCreateFile(FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (!QfsIsHandleValid(File)) {
|
|
Status = GetLastError();
|
|
ClosePipe( Pipe );
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmPushFile failed to open file %1!ws! error %2!d!\n",
|
|
FileName,
|
|
Status);
|
|
return(Status);
|
|
}
|
|
|
|
Buffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
|
|
|
|
if (Buffer == NULL) {
|
|
ClosePipe( Pipe );
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
QfsCloseHandle(File);
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
return(Status);
|
|
}
|
|
|
|
try {
|
|
do {
|
|
NmCryptor_PrepareEncryptionBuffer(
|
|
&Encryptor, Buffer, CHUNK_SIZE);
|
|
|
|
if (!QfsReadFile(File,
|
|
Encryptor.PayloadBuffer,
|
|
Encryptor.PayloadSize,
|
|
&BytesRead,
|
|
NULL))
|
|
{
|
|
Status = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmPushFile failed to read file %1!ws! error %2!d!\n",
|
|
FileName, Status);
|
|
break;
|
|
}
|
|
|
|
Status = NmCryptor_Encrypt(&Encryptor, BytesRead);
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmPushFile failed to encrypt file %1!ws! error %2!d!\n",
|
|
FileName, Status);
|
|
break;
|
|
}
|
|
|
|
(Pipe.push)(Pipe.state,
|
|
Buffer,
|
|
Encryptor.EncryptedSize);
|
|
|
|
} while ( BytesRead != 0 );
|
|
} except (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
Status = GetExceptionCode();
|
|
ClRtlLogPrint(LOG_ERROR,
|
|
"[DM] DmPushFile :: Exception code 0x%1!08lx! raised for file %2!ws!\n",
|
|
Status, FileName);
|
|
}
|
|
|
|
LocalFree(Buffer);
|
|
|
|
QfsCloseHandle(File);
|
|
NmCryptor_Destroy(&Encryptor);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmpSyncDatabase(
|
|
IN RPC_BINDING_HANDLE RpcBinding,
|
|
IN OPTIONAL LPCWSTR Directory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connects to a remote node and attempts to sync with its
|
|
cluster database.
|
|
|
|
Arguments:
|
|
|
|
RpcBinding - The RPC binding handle to use to sync the database.
|
|
|
|
Directory - if present, supplies the directory where CLUSDB should
|
|
be created.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the database was successfully updated.
|
|
|
|
Win32 error otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
WCHAR FileName[MAX_PATH+1];
|
|
FILE_PIPE FilePipe;
|
|
QfsHANDLE hFile;
|
|
|
|
//
|
|
// Issue conditional synchronization
|
|
//
|
|
Status = DmCreateTempFileName(FileName);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
hFile = QfsCreateFile(FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
|
|
if (!QfsIsHandleValid(hFile)) {
|
|
Status = GetLastError();
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
} else {
|
|
DmInitFilePipe(&FilePipe, hFile);
|
|
Status = DmSyncDatabase(RpcBinding,
|
|
FilePipe.Pipe);
|
|
|
|
DmFreeFilePipe(&FilePipe);
|
|
//
|
|
// Flush the file buffers to avoid corrupting CLUSDB on a power failure.
|
|
//
|
|
QfsFlushFileBuffers(hFile);
|
|
QfsCloseHandle(hFile);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// A new registry file was successfully downloaded.
|
|
// Install it into the current registry.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,"[DM] Obtained new database.\n");
|
|
|
|
//acquire the exclusive locks so that no new keys are opened while
|
|
// the registry is being reinstated
|
|
ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
|
|
// hold the key lock as well
|
|
EnterCriticalSection(&KeyLock);
|
|
|
|
// Invalidate any open keys
|
|
DmpInvalidateKeys();
|
|
|
|
Status = DmInstallDatabase(FileName, Directory, TRUE);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSyncDatabase failed, error %1!u!.\n",
|
|
Status);
|
|
}
|
|
// Reopen the keys for read/write access
|
|
DmpReopenKeys();
|
|
// release the locks
|
|
LeaveCriticalSection(&KeyLock);
|
|
RELEASE_LOCK(gLockDmpRoot);
|
|
|
|
} else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] Failed to get a new database, status %1!u!\n",
|
|
Status
|
|
);
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
}
|
|
|
|
QfsDeleteFile(FileName);
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmInstallDatabase(
|
|
IN LPWSTR FileName,
|
|
IN OPTIONAL LPCWSTR Directory,
|
|
IN BOOL bDeleteSrcFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Installs a new cluster registry database from the specified file
|
|
|
|
Arguments:
|
|
|
|
FileName - The name of the file from which to read the registry database
|
|
to install.
|
|
|
|
Directory - if present, supplies the directory where the CLUSDB file should
|
|
be created.
|
|
if not present, the current directory is used.
|
|
|
|
bDeleteSrcFile - Delete the Source file represented by FileName.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the installation completed successfully
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
BOOLEAN WasEnabled;
|
|
WCHAR Path[MAX_PATH];
|
|
WCHAR *p;
|
|
WCHAR BkpPath[MAX_PATH];
|
|
|
|
Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
&WasEnabled);
|
|
if (Status != ERROR_SUCCESS) {
|
|
if (Status == STATUS_PRIVILEGE_NOT_HELD) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] Restore privilege not held by cluster service\n");
|
|
} else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] Attempt to enable restore privilege failed %1!lx!\n",Status);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Restart the registry watcher thread so it is not trying to use
|
|
// DmpRoot while we are messing with things.
|
|
//
|
|
ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
|
|
DmpRestartFlusher();
|
|
|
|
//
|
|
// Close DmpRoot (it should be the only thing open) so that we can
|
|
// unload the current cluster database.
|
|
//
|
|
RegCloseKey(DmpRoot);
|
|
RegCloseKey(DmpRootCopy);
|
|
DmpRoot = DmpRootCopy = NULL;
|
|
|
|
Status = RegUnLoadKey(HKEY_LOCAL_MACHINE, DmpClusterParametersKeyName);
|
|
|
|
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
WasEnabled);
|
|
if (Status == ERROR_SUCCESS) {
|
|
//
|
|
// Get the CLUSDB full pathname.
|
|
//
|
|
if (Directory == NULL) {
|
|
Status = GetModuleFileName(NULL, Path, MAX_PATH);
|
|
|
|
//
|
|
// GetModuleFileName may not NULL terminate the Path.
|
|
//
|
|
Path [ RTL_NUMBER_OF ( Path ) - 1 ] = UNICODE_NULL;
|
|
|
|
if (Status == 0) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] Couldn't find cluster database\n");
|
|
Status = GetLastError();
|
|
} else {
|
|
Status = ERROR_SUCCESS;
|
|
p=wcsrchr(Path, L'\\');
|
|
if (p != NULL) {
|
|
*p = L'\0';
|
|
wcscpy(BkpPath, Path);
|
|
#ifdef OLD_WAY
|
|
wcscat(Path, L"\\CLUSDB");
|
|
#else // OLD_WAY
|
|
wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
|
|
#endif // OLD_WAY
|
|
wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
|
|
} else {
|
|
CL_UNEXPECTED_ERROR(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
} else {
|
|
lstrcpyW(Path, Directory);
|
|
lstrcpyW(BkpPath, Path);
|
|
#ifdef OLD_WAY
|
|
wcscat(Path, L"\\CLUSDB");
|
|
#else // OLD_WAY
|
|
wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
|
|
#endif // OLD_WAY
|
|
wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
|
|
}
|
|
if (Status == ERROR_SUCCESS) {
|
|
//
|
|
// Now copy the supplied file to CLUSDB
|
|
//
|
|
Status = DmpSafeDatabaseCopy(FileName, Path, BkpPath, bDeleteSrcFile);
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmInstallDatabase :: DmpSafeDatabaseCopy() failed %1!d!\n",
|
|
Status);
|
|
|
|
// SS: BUG BUG - we should not reload the old hive
|
|
//on a join, that would be catastrophic to continue
|
|
//on a form, while uploading from a checkpoint file
|
|
// it would be the same
|
|
//
|
|
// Try and reload the old hive
|
|
//
|
|
// Status = DmpLoadHive(Path);
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
} else {
|
|
//
|
|
// Finally, reload the hive.
|
|
//
|
|
Status = DmpLoadHive(Path);
|
|
}
|
|
}
|
|
} else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] RegUnloadKey of existing database failed %1!d!\n",
|
|
Status);
|
|
goto FnExit;
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmInstallDatabase :: failed to load hive %1!d!\n",
|
|
Status);
|
|
goto FnExit;
|
|
}
|
|
//
|
|
// Reopen DmpRoot and DmpRootCopy
|
|
//
|
|
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|
DmpClusterParametersKeyName,
|
|
&DmpRoot);
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
goto FnExit;
|
|
}
|
|
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|
DmpClusterParametersKeyName,
|
|
&DmpRootCopy);
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// HACKHACK John Vert (jvert) 6/3/1997
|
|
// There is a bug in the registry with refresh
|
|
// where the Parent field in the root cell doesn't
|
|
// get flushed to disk, so it gets blasted if we
|
|
// do a refresh. Then we crash in unload. So flush
|
|
// out the registry to disk here to make sure the
|
|
// right Parent field gets written to disk.
|
|
//
|
|
if (Status == ERROR_SUCCESS) {
|
|
DWORD Dummy=0;
|
|
//
|
|
// Make something dirty in the root
|
|
//
|
|
RegSetValueEx(DmpRoot,
|
|
L"Valid",
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE)&Dummy,
|
|
sizeof(Dummy));
|
|
RegDeleteValue(DmpRoot, L"Valid");
|
|
Status = RegFlushKey(DmpRoot);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmInstallDatabase : RegFlushKey failed with error %1!d!\n",
|
|
Status);
|
|
}
|
|
|
|
}
|
|
|
|
FnExit:
|
|
RELEASE_LOCK(gLockDmpRoot);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmGetDatabase(
|
|
IN HKEY hKey,
|
|
IN LPWSTR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes the registry database to a specified file.
|
|
|
|
Arguments:
|
|
|
|
hKey - Supplies the root of the registry tree to get.
|
|
|
|
FileName - The name of the file into which to write the current
|
|
registry database.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the update completed successfully
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN WasEnabled;
|
|
DWORD Status;
|
|
NTSTATUS Error;
|
|
|
|
//
|
|
// Make sure this file does not exist already.
|
|
//
|
|
QfsDeleteFile(FileName);
|
|
|
|
Status = ClRtlEnableThreadPrivilege(SE_BACKUP_PRIVILEGE,
|
|
&WasEnabled);
|
|
if ( Status != STATUS_SUCCESS ) {
|
|
CL_LOGFAILURE( Status );
|
|
goto FnExit;
|
|
}
|
|
Status = QfsRegSaveKey(hKey,
|
|
FileName,
|
|
NULL);
|
|
// this is used for checkpointing and shouldnt fail, but if it does we
|
|
// will log an event and delete the file
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
CL_LOGFAILURE( Status );
|
|
CsLogEventData1( LOG_CRITICAL,
|
|
CS_DISKWRITE_FAILURE,
|
|
sizeof(Status),
|
|
&Status,
|
|
FileName );
|
|
QfsDeleteFile(FileName);
|
|
}
|
|
|
|
Error = ClRtlRestoreThreadPrivilege(SE_BACKUP_PRIVILEGE,
|
|
WasEnabled);
|
|
|
|
if (Error != ERROR_SUCCESS)
|
|
{
|
|
CL_UNEXPECTED_ERROR(Error);
|
|
}
|
|
FnExit:
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Server-side join routines.
|
|
//
|
|
//
|
|
error_status_t
|
|
s_DmSyncDatabase(
|
|
IN handle_t IDL_handle,
|
|
OUT BYTE_PIPE Regdata
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes a new configuration database to a joining node.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used.
|
|
|
|
Regdata - The RPC data pipe to use to transfer the data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the update completed successfully
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
{
|
|
HANDLE File;
|
|
DWORD Status;
|
|
WCHAR FileName[MAX_PATH+1];
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL, "[DM] Supplying database to joining node.\n");
|
|
|
|
Status = DmCreateTempFileName(FileName);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
DmCommitRegistry(); // Ensure up-to-date snapshot
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 01/19/2001
|
|
//
|
|
// Hold the root lock before trying to save the hive. This is necessary so that
|
|
// an NtRestoreKey/RegCloseKey on the root key is not in progress at the time
|
|
// the save is attempted.
|
|
//
|
|
ACQUIRE_EXCLUSIVE_LOCK( gLockDmpRoot );
|
|
|
|
Status = DmGetDatabase(DmpRoot,FileName);
|
|
|
|
RELEASE_LOCK ( gLockDmpRoot );
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClosePipe(Regdata);
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
} else {
|
|
Status = DmPushFile(FileName, Regdata, FALSE); // FALSE == don't encrypt
|
|
QfsDeleteFile(FileName);
|
|
}
|
|
} else {
|
|
RpcRaiseException( Status );
|
|
ClosePipe(Regdata);
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
}
|
|
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] Finished supplying database to joining node.\n"
|
|
);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmCreateTempFileName(
|
|
OUT LPWSTR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a temporary filename for use by the cluster service.
|
|
|
|
Arguments:
|
|
|
|
FileName - Returns the name of the temporary file. The buffer
|
|
pointed to must be big enough for at least MAX_PATH
|
|
characters.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR TempPath[MAX_PATH];
|
|
DWORD Status;
|
|
|
|
GetTempPath(sizeof(TempPath)/sizeof(WCHAR),TempPath);
|
|
|
|
Status = QfsGetTempFileName(TempPath,L"CLS",0,FileName);
|
|
if (Status == 0) {
|
|
//
|
|
// Somebody has probably set the TMP variable incorrectly.
|
|
// Just use the current directory.
|
|
//
|
|
Status = QfsGetTempFileName(L".", L"CLS",0,FileName);
|
|
if (Status == 0) {
|
|
Status = GetLastError();
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set DACL on the file handle object granting full rights only to admin and owner.
|
|
//
|
|
Status = QfsSetFileSecurityInfo( FileName,
|
|
GENERIC_ALL, // for Admins
|
|
GENERIC_ALL, // for Owner
|
|
0 ); // for Everyone
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmCreateTempFile: ClRtlSetObjSecurityInfo failed for file %1!ws!, Status=%2!u!\r\n",
|
|
FileName,
|
|
Status);
|
|
return ( Status );
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
DWORD
|
|
DmpLoadHive(
|
|
IN LPCWSTR Path
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the cluster database into HKLM\Cluster
|
|
|
|
Arguments:
|
|
|
|
Path - Supplies the fully qualified filename of the cluster database.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN WasEnabled;
|
|
RTL_RELATIVE_NAME_U RelativeName;
|
|
OBJECT_ATTRIBUTES SourceFile;
|
|
UNICODE_STRING FileName;
|
|
NTSTATUS Status;
|
|
BOOLEAN ErrorFlag;
|
|
LPWSTR FreeBuffer;
|
|
|
|
//
|
|
// If the cluster database is not loaded, load it now.
|
|
//
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[DM] Loading cluster database from %1!ws!\n", Path);
|
|
|
|
ErrorFlag = RtlDosPathNameToRelativeNtPathName_U(Path,
|
|
&FileName,
|
|
NULL,
|
|
&RelativeName);
|
|
if (!ErrorFlag) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] RtlDosPathNameToRelativeNtPathName_U failed\n");
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
FreeBuffer = FileName.Buffer;
|
|
if (RelativeName.RelativeName.Length) {
|
|
FileName = RelativeName.RelativeName;
|
|
} else {
|
|
RelativeName.ContainingDirectory = NULL;
|
|
}
|
|
InitializeObjectAttributes(&SourceFile,
|
|
&FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RelativeName.ContainingDirectory,
|
|
NULL);
|
|
|
|
Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
&WasEnabled);
|
|
if (Status != ERROR_SUCCESS) {
|
|
if (Status == STATUS_PRIVILEGE_NOT_HELD) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] Restore privilege not held by cluster service\n");
|
|
} else {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] Attempt to enable restore privilege failed %1!lx!\n",Status);
|
|
}
|
|
} else {
|
|
//
|
|
// Note : Sunitas
|
|
// There used to be a registry bug where if we set REG_NO_LAZY_FLUSH and the hive
|
|
// is corrupt, the system crashes. So we used to first try loading it without the
|
|
// REG_NO_LAZY_FLUSH. If that works, unload it and do it again with
|
|
// REG_NO_LAZY_FLUSH. The registry folks claim that is fixed..so I am
|
|
// removing that hack
|
|
//
|
|
Status = NtLoadKey2((POBJECT_ATTRIBUTES)&RegistryMachineClusterObja,
|
|
&SourceFile,
|
|
REG_NO_LAZY_FLUSH);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpLoadHive: NtLoadKey2 failed with error, %1!u!\n",
|
|
Status);
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
}
|
|
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
WasEnabled);
|
|
}
|
|
|
|
RtlReleaseRelativeName(&RelativeName);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
|
|
return(Status);
|
|
|
|
}
|
|
|
|
DWORD DmpUnloadHive()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unloads the cluster database from HKLM\Cluster. This is called at initialization
|
|
to make sure that the database is loaded with the correct flags.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN WasEnabled;
|
|
NTSTATUS Status;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[DM] DmpUnloadHive: unloading the hive\r\n");
|
|
|
|
Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
&WasEnabled);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
if (Status == STATUS_PRIVILEGE_NOT_HELD)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpUnloadHive:: Restore privilege not held by cluster service\n");
|
|
}
|
|
else
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpUnloadHive: Attempt to enable restore privilege failed %1!lx!\n",Status);
|
|
}
|
|
goto FnExit;
|
|
}
|
|
|
|
Status = NtUnloadKey((POBJECT_ATTRIBUTES)&RegistryMachineClusterObja);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpUnloadHive: NtUnloadKey failed with error, %1!u!\n",
|
|
Status);
|
|
CL_UNEXPECTED_ERROR(Status);
|
|
}
|
|
|
|
ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
|
|
WasEnabled);
|
|
|
|
FnExit:
|
|
return(Status);
|
|
|
|
}
|
|
|
|
DWORD
|
|
DmpSafeDatabaseCopy(
|
|
IN LPCWSTR FileName,
|
|
IN LPCWSTR Path,
|
|
IN LPCWSTR BkpPath,
|
|
IN BOOL bDeleteSrcFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the cluster database into HKLM\Cluster
|
|
|
|
Arguments:
|
|
FileName - Supplies the fully qualified filename of the new cluster database
|
|
Path - Supplies the fully qualified filename of the cluster database.
|
|
BkpPath - Supplies the fully qualified filename of the cluster database temporary
|
|
backup
|
|
bDeleteSrcFile - Specifies whether the source file may be deleted
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
//set the file attributes of the bkp file to be normal so that we can
|
|
//overwrite it if it exists
|
|
if (!QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
|
|
BkpPath, GetLastError());
|
|
//this may fail because the file doesnt exist but that is not fatal so we ignore the error
|
|
}
|
|
|
|
//Save the database to a temp database that can be used for recovery
|
|
//ClRtlCopyFileAndFlushBuffers preserves attributes of the old file
|
|
if (!QfsClRtlCopyFileAndFlushBuffers(Path, BkpPath))
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to create a backup copy of database, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
//hide the file since users are not supposed to know about it
|
|
if (!QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
|
|
BkpPath, dwStatus);
|
|
goto FnExit;
|
|
|
|
}
|
|
|
|
//set DatabaseCopyInProgress key to be TRUE
|
|
dwStatus = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress",1);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to set ClusterDatabaseCopyInProgress, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
//delete clusdb
|
|
if (!QfsDeleteFile(Path))
|
|
{
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[DM] DmpSafeDatabaseCopy:: Couldnt delete the database file, Error=%1!u!\n",
|
|
GetLastError());
|
|
//this is not fatal, we will still try the move file
|
|
}
|
|
//copy the new database to clusdb
|
|
if (bDeleteSrcFile)
|
|
{
|
|
//the source file may be deleted, this is true at join sync time
|
|
//the source file is a temporary file
|
|
if (!QfsMoveFileEx(FileName, Path, MOVEFILE_REPLACE_EXISTING |
|
|
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH))
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to move %1!ws! to %2!ws!, Status=%3!u!\n",
|
|
FileName, Path, dwStatus);
|
|
goto FnExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//the source file must not be deleted use copy..this is true
|
|
//when the logs are being rolled at form and we are uploading
|
|
//the database from a checkpoint file
|
|
if (!QfsClRtlCopyFileAndFlushBuffers(FileName, Path))
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to copy %1!ws! to %2!ws!, Status=%3!u!\n",
|
|
FileName, Path, dwStatus);
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
//set databaseCopyInProgress key to FALSE
|
|
dwStatus = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress", 0);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to set ClusterDatabaseCopyInProgress, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
//now that clusdb is safely copied, we can delete the backup
|
|
//for that we need to set the file attribute to normal
|
|
if (!QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
|
|
BkpPath, GetLastError());
|
|
|
|
}
|
|
|
|
//delete the backup
|
|
if (!QfsDeleteFile(BkpPath))
|
|
{
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[DM] DmpSafeDatabaseCopy:: Failed to delete bkp database file %1!ws!, Status=%2!u!\n",
|
|
BkpPath, GetLastError());
|
|
//this is not fatal so ignore the error
|
|
}
|
|
|
|
FnExit:
|
|
return(dwStatus);
|
|
|
|
}
|
|
|
|
DWORD
|
|
DmpSetDwordInClusterServer(
|
|
LPCWSTR lpszValueName,
|
|
DWORD dwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the value specified under
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
|
|
to the value specified by dwValue. It flushes the change.
|
|
|
|
Arguments:
|
|
|
|
lpszValueName : Sets the value for the name specified by lpszValueName
|
|
dwValue : The value to set to.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if everything worked ok
|
|
|
|
--*/
|
|
{
|
|
|
|
HKEY hKey;
|
|
DWORD dwStatus = ERROR_SUCCESS; // returned by registry API functions
|
|
|
|
// Attempt to open an existing key in the registry.
|
|
|
|
dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
|
|
0, // reserved
|
|
KEY_WRITE,
|
|
&hKey );
|
|
|
|
// Was the registry key opened successfully ?
|
|
|
|
if ( dwStatus == ERROR_SUCCESS )
|
|
{
|
|
|
|
DWORD dwValueType = REG_DWORD;
|
|
DWORD dwDataBufferSize = sizeof( DWORD );
|
|
|
|
dwStatus = RegSetValueExW( hKey,
|
|
lpszValueName,
|
|
0, // reserved
|
|
dwValueType,
|
|
(LPBYTE) &dwValue,
|
|
dwDataBufferSize );
|
|
|
|
//Flush the key
|
|
RegFlushKey(hKey);
|
|
|
|
// Close the registry key.
|
|
|
|
RegCloseKey( hKey );
|
|
|
|
// Was the value set successfully?
|
|
}
|
|
|
|
return(dwStatus);
|
|
|
|
} // DmpSetDwordInClusterServer
|
|
|
|
|
|
DWORD DmpGetDwordFromClusterServer(
|
|
IN LPCWSTR lpszValueName,
|
|
OUT LPDWORD pdwValue,
|
|
IN DWORD dwDefaultValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the DWORD value specified in lpszValueName.
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server".
|
|
If the value doesnt exist, the default is returned.
|
|
|
|
Arguments:
|
|
|
|
lpszValueName : The Value to read.
|
|
pdwValue : Returns the value of the key specified by lpszValueName
|
|
dwDefaultValue: The value to be returned if the specified key doesnt exist
|
|
or in case of error.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if everything worked ok or if the key wasnt present.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD dwStatus; // returned by registry API functions
|
|
DWORD dwClusterInstallState;
|
|
DWORD dwValueType;
|
|
DWORD dwDataBufferSize = sizeof( DWORD );
|
|
|
|
*pdwValue = dwDefaultValue;
|
|
// Read the registry key that indicates whether cluster files are installed.
|
|
|
|
dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
|
|
0, // reserved
|
|
KEY_READ,
|
|
&hKey );
|
|
|
|
// Was the registry key opened successfully ?
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
if ( dwStatus == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
*pdwValue = dwDefaultValue;
|
|
dwStatus = ERROR_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
// Read the entry.
|
|
dwStatus = RegQueryValueExW( hKey,
|
|
lpszValueName,
|
|
0, // reserved
|
|
&dwValueType,
|
|
(LPBYTE) pdwValue,
|
|
&dwDataBufferSize );
|
|
|
|
// Was the value read successfully ?
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
if ( dwStatus == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
*pdwValue = dwDefaultValue;
|
|
dwStatus = ERROR_SUCCESS;
|
|
goto FnExit;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
// Close the registry key.
|
|
if ( hKey )
|
|
{
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
return ( dwStatus );
|
|
|
|
} //*** DmpGetDwordFromClusterServer
|
|
|