/*++ Copyright (c) 1996 Microsoft Corporation Module Name: dmlocal.c Abstract: Contains the routines for local transactions that can be called within gum handlers. Author: Sunita Shrivastava (sunitas) 24-Apr-1996 Revision History: --*/ #include "dmp.h" #include "clusudef.h" extern BOOL gbIsQuoLoggingOn; extern PFM_RESOURCE gpQuoResource; extern DWORD gbIsQuoResOnline; extern HLOG ghQuoLog; #if NO_SHARED_LOCKS extern CRITICAL_SECTION gLockDmpRoot; #else extern RTL_RESOURCE gLockDmpRoot; #endif /**** @doc EXTERNAL INTERFACES CLUSSVC DM ****/ /**** @func HXSACTION | DmBeginLocalUpdate| Called by gum handlers to make consistent changes to the local registry. The log is reset and a start transaction record is written to the log, if the log is active. @comm When GumHandlers need to update the registry consitently they must use the LocalApis provided by DM. @rdesc Returns a transaction handle. NULL on failure. Call GetLastError() for error code. @xref ****/ HLOCALXSACTION DmBeginLocalUpdate() { DWORD dwError=ERROR_SUCCESS; LSN StartXsactionLsn; DWORD dwSequence; HXSACTION hXsaction = NULL; PLOCALXSACTION pLocalXsaction = NULL; ClRtlLogPrint(LOG_NOISE, "[DM] DmBeginLocalUpdate Entry\r\n"); //lock the data base, so that a check point is not taken in this duration //this lock is released in DmCommitLocalUpdate() or DmAbortLocalUpdate() //this lock also prevents the the registry from being flushed. ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot); //Commit the registry so that it can be restored on abort if ((dwError = DmCommitRegistry()) != ERROR_SUCCESS) { goto FnExit; } //allocate memory for local transaction pLocalXsaction = LocalAlloc(LMEM_FIXED, sizeof(LOCALXSACTION)); if (!pLocalXsaction) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } pLocalXsaction->dwSig = LOCALXSAC_SIG; dwSequence = GumGetCurrentSequence(GumUpdateRegistry); pLocalXsaction->dwSequence = dwSequence; pLocalXsaction->hLogXsaction = NULL; InitializeListHead(&pLocalXsaction->PendingNotifyListHead); //log the start checkpoint record if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog) { hXsaction = LogStartXsaction(ghQuoLog, dwSequence ,RMRegistryMgr, 0); if (!hXsaction) { dwError = GetLastError(); } pLocalXsaction->hLogXsaction = hXsaction; } FnExit: if (dwError != ERROR_SUCCESS) { if (pLocalXsaction) LocalFree(pLocalXsaction); pLocalXsaction = NULL; RELEASE_LOCK(gLockDmpRoot); ClRtlLogPrint(LOG_NOISE, "[DM] DmBeginLocalUpdate Exit, pLocalXsaction=0x%1!08lx! Error=0x%2!08lx!\r\n", pLocalXsaction, dwError); SetLastError(dwError); } else { ClRtlLogPrint(LOG_NOISE, "[DM] DmBeginLocalUpdate Exit, pLocalXsaction=0x%1!08lx!\r\n", pLocalXsaction); } return((HLOCALXSACTION)pLocalXsaction); } /**** @func DWORD | DmCommitLocalUpdate| This api must be called to commit the changes to the local registry. @parm IN HXSACTION | hXsaction | The handle to the transaction to be committed. @comm A commit record is written the quorum log if logging is active. @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD DmCommitLocalUpdate(IN HLOCALXSACTION hLocalXsaction) { DWORD dwError=ERROR_SUCCESS; PLOCALXSACTION pLocalXsaction; ClRtlLogPrint(LOG_NOISE, "[DM] DmCommitLocalUpdate Entry\r\n"); GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); //update the gum sequence DmpUpdateSequence(); DmpReportPendingNotifications(pLocalXsaction, TRUE ); //write a commit record to the quorum log if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { CL_ASSERT(pLocalXsaction->hLogXsaction); dwError = LogCommitXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, 0); // // Chittur Subbaraman (chitturs) - 1/19/99 // pLocalXsaction->hLogXsaction = NULL; } // // Chittur Subbaraman (chitturs) - 1/19/99 // // Make sure that the hLogXsaction memory is freed (even in the case // in which you started a local xsaction and didn't get a chance to // commit it or abort it to the log because quorum logging got turned // off in the middle, for example. This turning off of the logging // in the middle of a transaction could be considered as a bug ?) // LocalFree( pLocalXsaction->hLogXsaction ); //invalidate the signature and free the transaction structure pLocalXsaction->dwSig = 0; LocalFree(pLocalXsaction); //release the database RELEASE_LOCK(gLockDmpRoot); ClRtlLogPrint(LOG_NOISE, "[DM] DmCommitLocalUpdate Exit, returning 0x%1!08lx!\r\n", dwError); return(dwError); } /**** @func DWORD | DmAbortLocalUpdate| DmAbortLocalUpdate aborts all the changes to the local registry associated with this transaction. @parm IN HXSACTION | hXsaction | The handle to the transaction to be committed. @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD DmAbortLocalUpdate(IN HLOCALXSACTION hLocalXsaction) { DWORD dwError=ERROR_SUCCESS; PLOCALXSACTION pLocalXsaction; ClRtlLogPrint(LOG_NOISE, "[DM] DmAbortLocalUpdate Entry\r\n"); GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); //write the abort chkpoint record //if the locker node is logging this is valid, //if the nonlocker node is logging, and it aborts // some other node will inherit the quorum log and //checkpoint and hence commit this update. if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { CL_ASSERT(pLocalXsaction->hLogXsaction); LogAbortXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, 0); // // Chittur Subbaraman (chitturs) - 1/19/99 // pLocalXsaction->hLogXsaction = NULL; } //SS: if the rollback fails, then we kill ourselves?? //restore the old registry if ((dwError = DmRollbackRegistry()) != ERROR_SUCCESS) { CL_UNEXPECTED_ERROR(dwError); } //free any pending notifications that were built up for //this transaction DmpReportPendingNotifications(pLocalXsaction, FALSE ); // // Chittur Subbaraman (chitturs) - 1/19/99 // // Make sure that the hLogXsaction memory is freed (even in the case // in which you started a local xsaction and didn't get a chance to // commit it or abort it to the log because quorum logging got turned // off in the middle, for example. This turning off of the logging // in the middle of a transaction could be considered as a bug ?) // LocalFree( pLocalXsaction->hLogXsaction ); //free the transaction structure, it cannot be used any more pLocalXsaction->dwSig = 0; LocalFree(pLocalXsaction); //release the database RELEASE_LOCK(gLockDmpRoot); ClRtlLogPrint(LOG_NOISE, "[DM] DmAbortLocalUpdate Exit, returning 0x%1!08lx!\r\n", dwError); return(dwError); } DWORD DmLocalSetValue( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpValueName, IN DWORD dwType, IN CONST BYTE *lpData, IN DWORD cbData ) /*++ Routine Description: This routine sets the named value for the specified cluster registry key on the local machine Arguments: hKey - Supplies the cluster registry subkey whose value is to be set lpValueName - Supplies the name of the value to be set. dwType - Supplies the value data type lpData - Supplies a pointer to the value data cbData - Supplies the length of the value data. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Status = ERROR_SUCCESS; PDMKEY Key; PUCHAR Dest; DWORD NameLength; DWORD ValueNameLength; DWORD UpdateLength; PDM_SET_VALUE_UPDATE Update = NULL; PLOCALXSACTION pLocalXsaction; Key = (PDMKEY)hKey; GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); Status = RegSetValueExW(Key->hKey, lpValueName, 0, dwType, lpData, cbData); if (Status != ERROR_SUCCESS) { goto FnExit; } DmpAddToPendingNotifications(pLocalXsaction, Key->Name, CLUSTER_CHANGE_REGISTRY_VALUE); //write it to the quorum log if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { Key = (PDMKEY)hKey; NameLength = (lstrlenW(Key->Name)+1)*sizeof(WCHAR); ValueNameLength = (lstrlenW(lpValueName)+1)*sizeof(WCHAR); UpdateLength = sizeof(DM_SET_VALUE_UPDATE) + NameLength + ValueNameLength + cbData; Update = (PDM_SET_VALUE_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength); if (Update == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } Update->lpStatus = NULL; Update->NameOffset = FIELD_OFFSET(DM_SET_VALUE_UPDATE, KeyName)+NameLength; Update->DataOffset = Update->NameOffset + ValueNameLength; Update->DataLength = cbData; Update->Type = dwType; CopyMemory(Update->KeyName, Key->Name, NameLength); Dest = (PUCHAR)Update + Update->NameOffset; CopyMemory(Dest, lpValueName, ValueNameLength); Dest = (PUCHAR)Update + Update->DataOffset; CopyMemory(Dest, lpData, cbData); if (LogWriteXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, DmUpdateSetValue, Update, UpdateLength) == NULL_LSN) { Status = GetLastError(); } } FnExit: if (Update) LocalFree(Update); return(Status); } HDMKEY DmLocalCreateKey( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpSubKey, IN DWORD dwOptions, IN DWORD samDesired, IN OPTIONAL LPVOID lpSecurityDescriptor, OUT LPDWORD lpDisposition ) /*++ Routine Description: Creates a key in the local registry. If the key exists, it is opened. If it does not exist, it is created. Arguments: hKey - Supplies the key that the create is relative to. lpSubKey - Supplies the key name relative to hKey dwOptions - Supplies any registry option flags. samDesired - Supplies desired security access mask lpSecurityDescriptor - Supplies security for the newly created key. Disposition - Returns whether the key was opened (REG_OPENED_EXISTING_KEY) or created (REG_CREATED_NEW_KEY) Return Value: A handle to the specified key if successful NULL otherwise. LastError will be set to the specific error code. --*/ { PDMKEY Parent; PDMKEY Key = NULL; DWORD NameLength; DWORD Status; PDM_CREATE_KEY_UPDATE CreateUpdate = NULL; PVOID pBuffer = NULL; DWORD dwBufLength; DWORD dwSecurityLength; PLOCALXSACTION pLocalXsaction; GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); if (dwOptions == REG_OPTION_VOLATILE) { Status = ERROR_INVALID_PARAMETER; goto FnExit; } Parent = (PDMKEY)hKey; // // Allocate the DMKEY structure. // NameLength = (lstrlenW(Parent->Name) + 1 + lstrlenW(lpSubKey) + 1)*sizeof(WCHAR); Key = LocalAlloc(LMEM_FIXED, sizeof(DMKEY)+NameLength); if (Key == NULL) { CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY); Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } // // Create the key on the local machine. // Status = RegCreateKeyExW(Parent->hKey, lpSubKey, 0, NULL, 0, samDesired, lpSecurityDescriptor, &Key->hKey, lpDisposition); if (Status != ERROR_SUCCESS) { goto FnExit; } // // Create the key name // lstrcpyW(Key->Name, Parent->Name); if (Key->Name[0] != UNICODE_NULL) { lstrcatW(Key->Name, L"\\"); } lstrcatW(Key->Name, lpSubKey); Key->GrantedAccess = samDesired; EnterCriticalSection(&KeyLock); InsertHeadList(&KeyList, &Key->ListEntry); InitializeListHead(&Key->NotifyList); LeaveCriticalSection(&KeyLock); //add the pending notification to be delivered on commit DmpAddToPendingNotifications(pLocalXsaction, Key->Name, CLUSTER_CHANGE_REGISTRY_NAME); //successfully created key, write to the log if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { //get the length of the security structure if (ARGUMENT_PRESENT(lpSecurityDescriptor)) { dwSecurityLength = GetSecurityDescriptorLength(lpSecurityDescriptor); } else { dwSecurityLength = 0; } CreateUpdate = (PDM_CREATE_KEY_UPDATE)LocalAlloc(LMEM_FIXED, sizeof(DM_CREATE_KEY_UPDATE)); if (CreateUpdate == NULL) { CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY); Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } // // Issue the update. // CreateUpdate->lpDisposition = lpDisposition; CreateUpdate->phKey = &Key->hKey; CreateUpdate->samDesired = samDesired; CreateUpdate->dwOptions = dwOptions; if (ARGUMENT_PRESENT(lpSecurityDescriptor)) { CreateUpdate->SecurityPresent = TRUE; } else { CreateUpdate->SecurityPresent = FALSE; } //marshall the data, pBuffer = GumMarshallArgs(&dwBufLength, 3, sizeof(DM_CREATE_KEY_UPDATE), CreateUpdate, (lstrlenW(Key->Name)+1)*sizeof(WCHAR), Key->Name, dwSecurityLength, lpSecurityDescriptor); if (pBuffer) { CL_ASSERT(pLocalXsaction->hLogXsaction); //write it to the logger if (LogWriteXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, DmUpdateCreateKey, pBuffer, dwBufLength) == NULL_LSN) { Status = GetLastError(); goto FnExit; } } } FnExit: if (Status != ERROR_SUCCESS) { if (Key) LocalFree(Key); Key = NULL; SetLastError(Status); } if (CreateUpdate) LocalFree(CreateUpdate); if (pBuffer) LocalFree(pBuffer); return((HDMKEY)Key); } DWORD DmLocalRemoveFromMultiSz( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpValueName, IN LPCWSTR lpString ) /*++ Routine Description: Removes a string from a REG_MULTI_SZ value. Arguments: hKey - Supplies the key where the value exists. This key must have been opened with READ | KEY_SET_VALUE access lpValueName - Supplies the name of the value. lpString - Supplies the string to be removed from the REG_MULTI_SZ value Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Status; LPWSTR Buffer=NULL; DWORD BufferSize; DWORD DataSize; LPWSTR Current; DWORD CurrentLength; DWORD i; LPWSTR Next; PCHAR Src, Dest; DWORD NextLength; DWORD MultiLength; BufferSize = 0; Status = DmQueryString(hKey, lpValueName, REG_MULTI_SZ, &Buffer, &BufferSize, &DataSize); if (Status != ERROR_SUCCESS) { return(Status); } MultiLength = DataSize/sizeof(WCHAR); Status = ClRtlMultiSzRemove(Buffer, &MultiLength, lpString); if (Status == ERROR_SUCCESS) { // // Set the new value back. // Status = DmLocalSetValue(hLocalXsaction, hKey, lpValueName, REG_MULTI_SZ, (CONST BYTE *)Buffer, MultiLength * sizeof(WCHAR)); } else if (Status == ERROR_FILE_NOT_FOUND) { Status = ERROR_SUCCESS; } if (Buffer) LocalFree(Buffer); return(Status); } DWORD DmLocalAppendToMultiSz( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpValueName, IN LPCWSTR lpString ) /*++ Routine Description: Adds another string to a REG_MULTI_SZ value. If the value does not exist, it will be created. Arguments: hLocalXsaction - A handle to a local transaction. hKey - Supplies the key where the value exists. This key must have been opened with KEY_READ | KEY_SET_VALUE access lpValueName - Supplies the name of the value. lpString - Supplies the string to be appended to the REG_MULTI_SZ value Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD ValueLength = 512; DWORD ReturnedLength; LPWSTR ValueData; DWORD StringLength; DWORD Status; DWORD cbValueData; PWSTR s; DWORD Type; StringLength = (lstrlenW(lpString)+1)*sizeof(WCHAR); retry: ValueData = LocalAlloc(LMEM_FIXED, ValueLength + StringLength); if (ValueData == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); } cbValueData = ValueLength; Status = DmQueryValue(hKey, lpValueName, &Type, (LPBYTE)ValueData, &cbValueData); if (Status == ERROR_MORE_DATA) { // // The existing value is too large for our buffer. // Retry with a larger buffer. // ValueLength = cbValueData; LocalFree(ValueData); goto retry; } if (Status == ERROR_FILE_NOT_FOUND) { // // The value does not currently exist. Create the // value with our data. // s = ValueData; } else if (Status == ERROR_SUCCESS) { // // A value already exists. Append our string to the // MULTI_SZ. // s = (PWSTR)((PCHAR)ValueData + cbValueData) - 1; } else { LocalFree(ValueData); return(Status); } CopyMemory(s, lpString, StringLength); s += (StringLength / sizeof(WCHAR)); *s++ = L'\0'; Status = DmLocalSetValue( hLocalXsaction, hKey, lpValueName, REG_MULTI_SZ, (CONST BYTE *)ValueData, (DWORD)((s-ValueData)*sizeof(WCHAR))); LocalFree(ValueData); return(Status); } DWORD DmLocalDeleteKey( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpSubKey ) /*++ Routine Description: Deletes the specified key from the local registry. A key that has subkeys cannot be deleted. Arguments: hKey - Supplies a handle to a currently open key. lpSubKey - Points to a null-terminated string specifying the name of the key to delete. This parameter cannot be NULL, and the specified key must not have subkeys. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { PDMKEY Key; DWORD NameLength; DWORD UpdateLength; PDM_DELETE_KEY_UPDATE Update=NULL; DWORD Status; PLOCALXSACTION pLocalXsaction; GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); Key = (PDMKEY)hKey; NameLength = (lstrlenW(Key->Name) + 1 + lstrlenW(lpSubKey) + 1)*sizeof(WCHAR); UpdateLength = NameLength + sizeof(DM_DELETE_KEY_UPDATE); Update = (PDM_DELETE_KEY_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength); if (Update == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } //dont need an update on status thru marshalled data Update->lpStatus = NULL; CopyMemory(Update->Name, Key->Name, (lstrlenW(Key->Name) + 1) * sizeof(WCHAR)); if (Update->Name[0] != L'\0') { lstrcatW(Update->Name, L"\\"); } lstrcatW(Update->Name, lpSubKey); Status = RegDeleteKeyW(DmpRoot, Update->Name); if (Status != ERROR_SUCCESS) goto FnExit; //add the pending notification to be delivered on commit DmpAddToPendingNotifications(pLocalXsaction, Update->Name, CLUSTER_CHANGE_REGISTRY_NAME); //successfully deleted key, write to the log if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { if (LogWriteXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, DmUpdateDeleteKey, Update, UpdateLength) == NULL_LSN) { Status = GetLastError(); goto FnExit; } } FnExit: if (Update) LocalFree(Update); return(Status); } DWORD DmLocalDeleteTree( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpSubKey ) /*++ Routine Description: Deletes the specified registry subtree in the local registry. All subkeys are deleted. Arguments: hKey - Supplies a handle to a currently open key. lpSubKey - Points to a null-terminated string specifying the name of the key to delete. This parameter cannot be NULL. Any subkeys of the specified key will also be deleted. Return Value: If the function succeeds, the return value is ERROR_SUCCESS. If the function fails, the return value is an error value. --*/ { HDMKEY Subkey; DWORD i; DWORD Status; LPWSTR KeyBuffer=NULL; DWORD MaxKeyLen; DWORD NeededSize; Subkey = DmOpenKey(hKey, lpSubKey, MAXIMUM_ALLOWED); if (Subkey == NULL) { Status = GetLastError(); return(Status); } // // Get the size of name buffer we will need. // Status = DmQueryInfoKey(Subkey, NULL, &MaxKeyLen, NULL, NULL, NULL, NULL, NULL); if (Status != ERROR_SUCCESS) { CL_UNEXPECTED_ERROR( Status ); DmCloseKey(Subkey); return(Status); } KeyBuffer = LocalAlloc(LMEM_FIXED, (MaxKeyLen+1)*sizeof(WCHAR)); if (KeyBuffer == NULL) { CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY ); DmCloseKey(Subkey); return(ERROR_NOT_ENOUGH_MEMORY); } // // Enumerate the subkeys and apply ourselves recursively to each one. // i=0; do { NeededSize = MaxKeyLen+1; Status = DmEnumKey(Subkey, i, KeyBuffer, &NeededSize, NULL); if (Status == ERROR_SUCCESS) { // // Call ourselves recursively on this keyname. // DmLocalDeleteTree(hLocalXsaction, Subkey, KeyBuffer); } else { // // Some odd error, keep going with the next key. // ++i; } } while ( Status != ERROR_NO_MORE_ITEMS ); DmCloseKey(Subkey); Status = DmLocalDeleteKey(hLocalXsaction, hKey, lpSubKey); if (KeyBuffer != NULL) { LocalFree(KeyBuffer); } return(Status); } DWORD DmLocalDeleteValue( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpValueName ) { PDMKEY Key; DWORD NameLength; DWORD ValueNameLength; DWORD UpdateLength; PDM_DELETE_VALUE_UPDATE Update=NULL; PUCHAR Dest; DWORD Status; HKEY hRegKey; PLOCALXSACTION pLocalXsaction; GETLOCALXSACTION(pLocalXsaction, hLocalXsaction); Key = (PDMKEY)hKey; Status = RegOpenKeyExW(DmpRoot, Key->Name, 0, KEY_SET_VALUE, &hRegKey); if (Status != ERROR_SUCCESS) { goto FnExit; } Status = RegDeleteValueW(hRegKey, lpValueName); RegCloseKey(hRegKey); if (Status!=ERROR_SUCCESS) goto FnExit; //add the pending notification to be delivered on commit DmpAddToPendingNotifications(pLocalXsaction, Key->Name, CLUSTER_CHANGE_REGISTRY_VALUE); //successfully created key, write to the log if (gbIsQuoLoggingOn && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource) && ghQuoLog && pLocalXsaction->hLogXsaction) { //if successful and this is the logging node, then log // the transaction NameLength = (lstrlenW(Key->Name)+1)*sizeof(WCHAR); ValueNameLength = (lstrlenW(lpValueName)+1)*sizeof(WCHAR); UpdateLength = sizeof(DM_DELETE_VALUE_UPDATE) + NameLength + ValueNameLength; Update = (PDM_DELETE_VALUE_UPDATE)LocalAlloc(LMEM_FIXED, UpdateLength); if (Update == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } Update->lpStatus = NULL; Update->NameOffset = FIELD_OFFSET(DM_DELETE_VALUE_UPDATE, KeyName)+NameLength; CopyMemory(Update->KeyName, Key->Name, NameLength); Dest = (PUCHAR)Update + Update->NameOffset; CopyMemory(Dest, lpValueName, ValueNameLength); if (LogWriteXsaction(ghQuoLog, pLocalXsaction->hLogXsaction, DmUpdateDeleteValue, Update, UpdateLength) == NULL_LSN) { Status = GetLastError(); goto FnExit; } } FnExit: if (Update) LocalFree(Update); return(Status); } /**** @func VOID | DmpReportPendingNotifications| This is called on commit or abort of a local transactions. On a commit, notifications related to changes within a transaction are delivered. @parm IN PLOCALXSACTION | pLocalXsaction| A pointer to the local transation context. @parm IN BOOL | bCommit| Set to TRUE when the transaction is commited. @comm The pending notification structure associated with the transaction is cleaned up. @xref ****/ VOID DmpReportPendingNotifications( IN PLOCALXSACTION pLocalXsaction, IN BOOL bCommit ) { PLIST_ENTRY pListEntry; PDM_PENDING_NOTIFY pDmPendingNotify; pListEntry = pLocalXsaction->PendingNotifyListHead.Flink; //remove the entries and proces them one by one //free them when done while (pListEntry != &pLocalXsaction->PendingNotifyListHead) { pDmPendingNotify = CONTAINING_RECORD(pListEntry, DM_PENDING_NOTIFY, ListEntry); // if transaction is commited if (bCommit) DmpReportNotify(pDmPendingNotify->pszKeyName, pDmPendingNotify->dwFilter); pListEntry = pListEntry->Flink; RemoveEntryList( &pDmPendingNotify->ListEntry ); LocalFree(pDmPendingNotify->pszKeyName); LocalFree(pDmPendingNotify); } return; } /**** @func DWORD | DmpAddToPendingNotifications| This is called by the DmLocal Api's to queue registry notifications on success. The notifications are delivered or thrown away depending on whether the transaction commits or aborts. @parm IN PLOCALXSACTION | pLocalXsaction| A pointer to the local transation context. @parm IN LPCWSTR | pszName| A pointer to the registry key name. @parm IN DWORD | dwFilter | The filters associated with the notification. @comm A new pending notification structure is created and associated with the transaction. @xref ****/ DWORD DmpAddToPendingNotifications( IN PLOCALXSACTION pLocalXsaction, IN LPCWSTR pszName, IN DWORD dwFilter ) { DWORD dwError = ERROR_SUCCESS; PDM_PENDING_NOTIFY pDmPendingNotify; pDmPendingNotify = LocalAlloc(LPTR, sizeof(DM_PENDING_NOTIFY)); if (!pDmPendingNotify) { dwError = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(dwError); goto FnExit; } pDmPendingNotify->pszKeyName = LocalAlloc(LMEM_FIXED, ((lstrlenW(pszName) + 1 ) * sizeof(WCHAR))); if (!pDmPendingNotify->pszKeyName) { dwError = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(dwError); goto FnExit; } //initialize the structure lstrcpyW(pDmPendingNotify->pszKeyName, pszName); pDmPendingNotify->dwFilter = dwFilter; InitializeListHead(&pDmPendingNotify->ListEntry); //add to the list InsertTailList(&pLocalXsaction->PendingNotifyListHead, &pDmPendingNotify->ListEntry); FnExit: return(dwError); } /**** @func DWORD | DmAmITheOwnerOfTheQuorumResource| This harmless function is used by regroup module to determine whether a node thinks that it is the owner of the quorum resource or not. ****/ DWORD DmAmITheOwnerOfTheQuorumResource() { return gpQuoResource && gpQuoResource->Group && gbIsQuoResOnline && AMIOWNEROFQUORES(gpQuoResource); } DWORD DmRtlLocalCreateKey( IN HLOCALXSACTION hLocalXsaction, IN HDMKEY hKey, IN LPCWSTR lpSubKey, IN DWORD dwOptions, IN DWORD samDesired, IN OPTIONAL LPVOID lpSecurityDescriptor, OUT HDMKEY * phkResult, OUT LPDWORD lpDisposition ) /*++ Routine Description: Wrapper function for DmLocalCreateKey to be used with CLRtl* functions. --*/ { DWORD status; *phkResult= DmLocalCreateKey( hLocalXsaction, hKey, lpSubKey, dwOptions, samDesired, lpSecurityDescriptor, lpDisposition ); if(* phkResult == NULL) status=GetLastError(); else status=ERROR_SUCCESS; return status; }