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