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