Leaked source code of windows server 2003
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

/*++
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