/*++ Copyright (c) 2001 Microsoft Corporation Module Name: DllUpgd.c Abstract: Routines for supporting resource DLL upgrade. Author: Chittur Subbaraman (chitturs) 18-March-2001 Revision History: 18-March-2001 Created --*/ #include "fmp.h" // // Defines used locally within this module // #define CLUSTER_RESDLL_BACKUP_FILE_EXTENSION L".~WFPKDLLBKP$" #define CLUSTER_RESDLL_RENAMED_FILE_EXTENSION L".~WFPKDLLOLD$" #define CLUSTER_RESDLL_BACKUP_FILES L".~WFPKDLL*$" DWORD FmpUpgradeResourceDLL( IN PFM_RESOURCE pResource, IN LPWSTR lpszInstallationPath ) /*++ Routine Description: Upgrades a resource DLL currently loaded in one or more monitors. Arguments: pResource - A resource of the type implemented by the DLL. lpszInstallationPath - The full installation path of the DLL (including the full DLL name with extension) Return Value: ERROR_SUCCESS on success. Win32 error code otherwise. --*/ { DWORD dwStatus = ERROR_SUCCESS; LPWSTR lpszNewDllName; WCHAR szCurrentDllPath[MAX_PATH]; // // Get the DLL file name from the installation path. Also, get rid of any trailing '\' in // the supplied path. // // IMPORTANT: Note that lpszNewDLLName points into lpszInstallationPath buffer and so // we should not modify the lpszInstallation path buffer (there is really no reason to // do that) while we use lpszNewDllName. // dwStatus = FmpParsePathForFileName( lpszInstallationPath, TRUE, // Check for path existence &lpszNewDllName ); // // If the parse fails or if the supplied "path" is a filename, bail. // if ( ( dwStatus != ERROR_SUCCESS ) || ( lpszNewDllName == NULL ) ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpgradeResourceDLL: Unable to parse supplied path %1!ws! for file name, Status=%2!u!\n", (lpszInstallationPath == NULL) ? L"NULL":lpszInstallationPath, dwStatus); goto FnExit; } ClRtlLogPrint(LOG_NOISE, "[FM] FmpUpgradeResourceDLL: Installation path %1!ws!, resource [%2!ws!] %3!ws!\n", lpszInstallationPath, OmObjectName(pResource), OmObjectId(pResource)); // // Validate the supplied parameters. If validation is successful, get the full path of the // currently loaded DLL. // dwStatus = FmpValidateResourceDLLReplacement( pResource, lpszNewDllName, szCurrentDllPath ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpgradeResourceDLL: Validation for resource DLL replacement failed, Status=%1!u!\n", dwStatus); goto FnExit; } // // Acquire the monitor lock so as to serialize one resource DLL upgrade process with // others as well as with monitor restarts. // FmpAcquireMonitorLock(); // // Now, replace the DLL with the supplied DLL in a recoverable fashion. // dwStatus = FmpReplaceResourceDLL( lpszNewDllName, szCurrentDllPath, lpszInstallationPath ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpgradeResourceDLL: Replacement of resource DLL failed, Status=%1!u!\n", dwStatus); goto FnReleaseLockAndExit; } // // Shutdown and restart the monitors that have the resource DLL loaded. // dwStatus = FmpRecycleMonitors( lpszNewDllName ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpUpgradeResourceDLL: Recycling of resource DLL failed, Status=%1!u!\n", dwStatus); goto FnReleaseLockAndExit; } // // Attempt deletion of backup files in case all the steps are successful so far. Note that // this attempt is necessary here since it is not possible to delete the .old file // before we recycle the monitors since the monitors hold references to the DLL. // FmpDeleteBackupFiles ( szCurrentDllPath ); // Delete backup files FnReleaseLockAndExit: FmpReleaseMonitorLock(); FnExit: ClRtlLogPrint(LOG_NOISE, "[FM] FmpUpgradeResourceDLL: Exit with status %1!u!...\n", dwStatus); return ( dwStatus ); } // FmpUpgradeResourceDLL DWORD FmpParsePathForFileName( IN LPWSTR lpszPath, IN BOOL fCheckPathExists, OUT LPWSTR *ppszFileName ) /*++ Routine Description: Get the name of the file at the end of a supplied path. Arguments: lpszPath - A path including the file name. fCheckPathExists - Check if the path exists. ppszFileName - The name of the file parsed from the path. Return Value: ERROR_SUCCESS on success. Win32 error code otherwise. Note: This function will get rid of any trailing '\' in the supplied path. Also, this function will return a file name only if the input supplied is a valid path, else NULL file name will be returned. --*/ { DWORD dwStatus = ERROR_SUCCESS; LPWSTR s; *ppszFileName = NULL; // // Check for invalid parameter. // if ( lpszPath == NULL ) { dwStatus = ERROR_INVALID_PARAMETER; ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpParsePathForFileName: Input param is NULL, Status=%1!u!\n", dwStatus); goto FnExit; } // // Make sure the last character is NULL if it is a '\'. This is to avoid getting confused // with paths such as C:\windows\cluster\clusres.dll\ // if ( lpszPath[lstrlen ( lpszPath ) - 1] == L'\\' ) lpszPath[lstrlen ( lpszPath ) - 1] = L'\0'; // // Parse the supplied path and look for the last occurrence of '\'. If there is no '\' at all, // may be the caller supplied a file name, bail with NULL out param but with success status. // s = wcsrchr( lpszPath, L'\\' ); if ( s == NULL ) { goto FnExit; } // // If the supplied parameter is a path (as opposed to a plain file name) and the caller // requested to check for validity, do so. // if ( fCheckPathExists && !ClRtlPathFileExists( lpszPath ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpParsePathForFileName: Path %1!ws! does not exist, Status=%2!u!\n", lpszPath, dwStatus); goto FnExit; } // // Return the pointer to the char after the last '\'. // s++; *ppszFileName = s; FnExit: return ( dwStatus ); }// FmpParsePathForFileName DWORD FmpValidateResourceDLLReplacement( IN PFM_RESOURCE pResource, IN LPWSTR lpszNewDllName, OUT LPWSTR lpszCurrentDllPath ) /*++ Routine Description: Validate the resource DLL replacement request. Arguments: pResource - The resource which is implemeted by the DLL. lpszNewDllName - The name of the DLL. lpszCurrentDllPath - The full path of the currently loaded DLL. Return Value: ERROR_SUCCESS on success. Win32 error code otherwise. --*/ { DWORD dwStatus = ERROR_SUCCESS; LPWSTR lpszFileName; LPWSTR lpszDllName = NULL; WCHAR szDLLNameOfRes[MAX_PATH]; BOOL fDllPathFound = TRUE; // // Get the plain file name from the DLL name stored in the restype structure. Since the // parse function can potentially get rid of the trailing '\', make a copy of the DLL // name. // // // IMPORTANT: Do not write stuff into szDllNameOfRes while lpszDllName is being used // since lpszDllName points inside szDllNameOfRes. // lstrcpy( szDLLNameOfRes, pResource->Type->DllName ); dwStatus = FmpParsePathForFileName ( szDLLNameOfRes, TRUE, // Check for path existence &lpszDllName ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpValidateResourceDLLReplacement: Unable to parse path %1!ws! for filename, Status %2!u!\n", szDLLNameOfRes, dwStatus); goto FnExit; } // // If the dll information in the resource type structure is a file name, then you need to // search the path to find the full path of the DLL. Otherwise, you can merely copy the // information from the resource type structure and expand any environment strings in it. // if ( lpszDllName == NULL ) { lpszDllName = pResource->Type->DllName; fDllPathFound = FALSE; } else { LPWSTR pszDllName; // // Expand any environment variables included in the DLL path name. // pszDllName = ClRtlExpandEnvironmentStrings( pResource->Type->DllName ); if ( pszDllName == NULL ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpValidateResourceDLLReplacement: Resource's DLL name %1!ws! cannot be expanded, Status=%2!u!\n", pResource->Type->DllName, dwStatus); goto FnExit; } lstrcpy( lpszCurrentDllPath, pszDllName ); LocalFree( pszDllName ); } if ( lstrcmp( lpszDllName, lpszNewDllName ) != 0 ) { dwStatus = ERROR_INVALID_PARAMETER; ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpValidateResourceDLLReplacement: Resource's DLL name %1!ws! does not match supplied name, Status=%2!u!\n", lpszDllName, dwStatus); goto FnExit; } // // Search all the paths specified in the environment variable and get the full current // path of the DLL that is loaded into the monitor. // if ( ( fDllPathFound == FALSE ) && ( !SearchPath ( NULL, // Search all paths as LoadLibrary does lpszNewDllName, // File name to search for NULL, // No extension required MAX_PATH, // Size of out buffer lpszCurrentDllPath, // Buffer to receive full Dll path with file name &lpszFileName ) ) ) // Filename at the end of the path { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpValidateResourceDLLReplacement: SearchPath API for file %1!ws! failed, Status=%2!u!\n", lpszNewDllName, dwStatus); goto FnExit; } ClRtlLogPrint(LOG_NOISE, "[FM] FmpValidateResourceDLLReplacement: Current resource DLL full path %1!ws!\n", lpszCurrentDllPath); FnExit: return ( dwStatus ); }// FmpValidateResourceDLLReplacement DWORD FmpReplaceResourceDLL( IN LPWSTR lpszNewDllName, IN LPWSTR lpszCurrentDllPath, IN LPWSTR lpszInstallationPath ) /*++ Routine Description: Replace the resource DLL with the one from the install path. Arguments: lpszNewDllName - The name of the DLL. lpszCurrentDllPath - The full path of the currently loaded DLL. lpszInstallationPath - The installation path of the DLL. Return Value: ERROR_SUCCESS on success. Win32 error code otherwise. --*/ { DWORD dwStatus = ERROR_SUCCESS; HKEY hClussvcParamsKey = NULL; DWORD cbListSize = 0, cchLen = 0; LPWSTR lpmszUpgradeList = NULL; WCHAR szBakFile[MAX_PATH], szOldFile[MAX_PATH]; WCHAR szClusterDirectory[MAX_PATH]; DWORD dwType, dwLen; // // // This function works as follows. First we make a copy of the existing resource DLL file // to a file with CLUSTER_RESDLL_BACKUP_FILE_EXTENSION extension. Then we set the registry // value under the clussvc parameters key to indicate that an upgrade is starting. After // this, the existing DLL file is renamed. If all steps are successful so far, we copy // new DLL file from the supplied path. Once this is successful, the registry value set // above is deleted. // // This algorithm gives us the following guarantees: // // 1. If the registry value is set, then a good backup file with CLUSTER_RESDLL_BACKUP_FILE_EXTENSION // must exist. // // 2. If the registry value is not set, then the existing DLL file was not touched by // the upgrade process or the DLL upgrade was completely successful. // // Thus, only if the registry value is set at the time FmpCreateMonitor is invoked, it // will go through the elaborate recovery process implemented in FmpRecoverResourceDLLFiles. // At recovery time, we can be sure that the backup file with CLUSTER_RESDLL_BACKUP_FILE_EXTENSION // is a perfectly good backup. Also, at recovery time we cannot be sure of the state (good/bad) // of the existing DLL file (if it exists at all) or the renamed file with // CLUSTER_RESDLL_RENAMED_FILE_EXTENSION. So, the recovery process is pessimistic and just // copies the backup file wit CLUSTER_RESDLL_BACKUP_FILE_EXTENSION over any existing DLL // file. // // Sideeffect: Even if the registry value is not set, there could be a stale backup file // left. Thus wheneever FmpCreateMonitor is invoked, it has to cleanup those files. // This is done by invoking FmpDeleteBackupFiles(NULL) from FmpRecoverResourceDLLFiles. // // // Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters // dwStatus = RegOpenKeyW( HKEY_LOCAL_MACHINE, CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, &hClussvcParamsKey ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: RegOpenKeyEx on %1!ws! failed, Status=%2!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, dwStatus); goto FnExit; } // // See whether a past failed upgrade has left any values in the upgrade progress list // dwStatus = RegQueryValueExW( hClussvcParamsKey, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, 0, &dwType, NULL, &cbListSize ); if ( ( dwStatus != ERROR_SUCCESS ) && ( dwStatus != ERROR_FILE_NOT_FOUND ) ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: RegQueryValueEx (1st time) on %1!ws! key, value %2!ws! failed, Status=%3!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } if ( cbListSize != 0 ) { // // Found some values left out from past upgrade. Read those values. // lpmszUpgradeList = LocalAlloc ( LPTR, cbListSize ); if ( lpmszUpgradeList == NULL ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: Mem alloc failure, Status=%1!u!\n", dwStatus); goto FnExit; } dwStatus = RegQueryValueExW( hClussvcParamsKey, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, 0, &dwType, ( LPBYTE ) lpmszUpgradeList, &cbListSize ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: RegQueryValueEx (2nd time) on %1!ws! key, value %2!ws! failed, Status=%3!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } } // // Check whether a failed upgrade of the same DLL has occurred in the past. // if ( ClRtlMultiSzScan( lpmszUpgradeList, lpszCurrentDllPath ) != NULL ) { ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmpReplaceResourceDLL: ClRtlMultiSzScan detected %1!ws! in the multi-sz, skip append...\n", lpszCurrentDllPath); goto skip_multisz_append; } // // Append the current DLL path to the REG_MULTI_SZ // cchLen = cbListSize/sizeof( WCHAR ); dwStatus = ClRtlMultiSzAppend( &lpmszUpgradeList, &cchLen, lpszCurrentDllPath ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: ClRtlMultiSzAppend failed for %1!ws!, Status=%2!u!\n", lpszCurrentDllPath, dwStatus); goto FnExit; } // // Get the cluster bits installed directory // dwStatus = ClRtlGetClusterDirectory( szClusterDirectory, MAX_PATH ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: Could not get cluster dir, Status=%1!u!\n", dwStatus); goto FnExit; } lstrcpy( szBakFile, szClusterDirectory ); dwLen = lstrlenW( szBakFile ); if ( szBakFile[dwLen-1] != L'\\' ) { szBakFile[dwLen++] = L'\\'; szBakFile[dwLen] = L'\0'; } lstrcat( szBakFile, lpszNewDllName ); lstrcat( szBakFile, CLUSTER_RESDLL_BACKUP_FILE_EXTENSION ); // // Copy the current DLL to a bak file and save it into the cluster installation directory. // This needs to be done BEFORE the registry value is set so that you can be sure that once you // perform a recovery, the file that you use from the backup is good. // if ( !CopyFileEx( lpszCurrentDllPath, // Source file szBakFile, // Destination file NULL, // No progress routine NULL, // No data to progress routine NULL, // No cancel variable 0 ) ) // No flags { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: CopyFileEx of %1!ws! to %2!ws! failed, Status=%3!u!\n", lpszCurrentDllPath, szBakFile, dwStatus); goto FnExit; } // // Set the file attributes to RO and hidden. Continue even if an error occurs since it is // not fatal. // if ( !SetFileAttributes( szBakFile, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpReplaceResourceDLL: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szBakFile, dwStatus); } // // Set the new upgrade list // dwStatus = RegSetValueExW( hClussvcParamsKey, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, 0, REG_MULTI_SZ, ( LPBYTE ) lpmszUpgradeList, cchLen * sizeof ( WCHAR ) ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: RegSetValueExW (1st time) on %1!ws! key, value %2!ws! failed, Status=%3!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } skip_multisz_append: lstrcpy( szOldFile, szClusterDirectory ); dwLen = lstrlenW( szOldFile ); if ( szOldFile[dwLen-1] != L'\\' ) { szOldFile[dwLen++] = L'\\'; szOldFile[dwLen] = L'\0'; } lstrcat( szOldFile, lpszNewDllName ); lstrcat( szOldFile, CLUSTER_RESDLL_RENAMED_FILE_EXTENSION ); // // Rename the currently loaded DLL to the a .old file in the cluster installation directory // if ( !MoveFileEx( lpszCurrentDllPath, // Source file szOldFile, // Destination file MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: MoveFileEx of %1!ws! to %2!ws! failed, Status=%3!u!\n", lpszCurrentDllPath, szOldFile, dwStatus); goto FnExit; } // // Set the file attributes to RO and hidden. Continue even if an error occurs since it is // not fatal. // if ( !SetFileAttributes( szOldFile, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpReplaceResourceDLL: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szOldFile, dwStatus); } // // Copy the new DLL from the installation path to the current DLL path. This should succeed // since the current DLL has been renamed. // if ( !CopyFileEx( lpszInstallationPath, // Source file lpszCurrentDllPath, // Destination file NULL, // No progress routine NULL, // No data to progress routine NULL, // No cancel variable 0 ) ) // No flags { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: CopyFileEx of %1!ws! to %2!ws! failed, Status=%3!u!\n", lpszInstallationPath, lpszCurrentDllPath, dwStatus); goto FnExit; } // // Now get rid of the value we set in the registry. The BAK and OLD files are deleted later. // dwStatus = FmpResetMultiSzValue ( hClussvcParamsKey, lpmszUpgradeList, &cchLen, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, lpszCurrentDllPath ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: Unable to remove %1!ws! from value %2!ws!, Status=%3!u!\n", lpszCurrentDllPath, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } FnExit: LocalFree( lpmszUpgradeList ); if ( hClussvcParamsKey != NULL ) { RegCloseKey( hClussvcParamsKey ); } return ( dwStatus ); }// FmpReplaceResourceDLL DWORD FmpRecycleMonitors( IN LPCWSTR lpszDllName ) /*++ Routine Description: Recycle all the monitors that have the specified resource DLL loaded. Arguments: lpszDllName - The name of the loaded resource DLL. Return Value: ERROR_SUCCESS on success. Win32 error code otherwise. --*/ { DWORD i, dwStatus = ERROR_SUCCESS; FM_MONITOR_ENUM_HEADER MonitorEnumHeader; ClRtlLogPrint(LOG_NOISE, "[FM] FmpRecycleMonitors: Attempting to recycle all monitors that have loaded the DLL %1!ws!\n", lpszDllName); MonitorEnumHeader.ppMonitorList = NULL; MonitorEnumHeader.fDefaultMonitorAdded = FALSE; // // Create a list of monitors that have the resource DLL loaded. // dwStatus = FmpCreateMonitorList( lpszDllName, &MonitorEnumHeader ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRecycleMonitors: FmpCreateMonitorList failed with status %1!u!\n", dwStatus); goto FnExit; } // // Now, shutdown and restart the monitors identified above. Shutdown and restart of each // monitor is done one by one so that the long shutdown time of some monitors will not affect // the restart of others. The FmpRestartMonitor function invokes a shutdown on the monitor, // waits until the monitor is fully shutdown and then restarts all the resources in the // old monitor in the new monitor. // for ( i=0; iRefCount ); FmpRestartMonitor( MonitorEnumHeader.ppMonitorList[i] ); } // for FnExit: LocalFree( MonitorEnumHeader.ppMonitorList ); ClRtlLogPrint(LOG_NOISE, "[FM] FmpRecycleMonitors: Return with status %1!u!\n", dwStatus); return ( dwStatus ); }// FmpRecycleMonitors DWORD FmpCreateMonitorList( IN LPCWSTR lpszDllName, OUT PFM_MONITOR_ENUM_HEADER pMonitorHeader ) /*++ Routine Description: Create a list of monitors that have the resource DLL implementing the resource loaded. Arguments: lpszDllName - The resource DLL that is being upgraded. pMonitorHeader - The enumeration list header which points to a list of monitors that have the DLL loaded. Return Value: ERROR_SUCCESS if successful. Win32 error code on error. --*/ { DWORD dwStatus = ERROR_SUCCESS; pMonitorHeader->cEntries = 0; pMonitorHeader->cAllocated = ENUM_GROW_SIZE; pMonitorHeader->ppMonitorList = LocalAlloc( LPTR, ENUM_GROW_SIZE * sizeof ( PRESMON ) ); if ( pMonitorHeader->ppMonitorList == NULL ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpCreateMonitorList: Mem alloc failed with status %1!u!\n", dwStatus); goto FnExit; } // // Go through all the cluster resources to identify the monitors that have loaded the // specified DLL. // OmEnumObjects( ObjectTypeResource, FmpFindHostMonitors, ( PVOID ) lpszDllName, ( PVOID ) pMonitorHeader ); FnExit: return ( dwStatus ); }// FmpCreateMonitorList BOOL FmpFindHostMonitors( IN LPCWSTR lpszDllName, IN OUT PFM_MONITOR_ENUM_HEADER pMonitorEnumHeader, IN PFM_RESOURCE pResource, IN LPCWSTR lpszResourceId ) /*++ Routine Description: Callback routine for enumerating all resources in the cluster. This routine will build a list of monitors that have loaded the specified DLL. Arguments: lpszDllName - The DLL whose host processes have to be determined. pMonitorEnumHeader - The monitor list enumeration header pResource - The resource being enumerated. lpszResourceId - The Id of the resource object being enumerated. Returns: TRUE - The enumeration should continue. FALSE - The enumeration must stop --*/ { BOOL fStatus = TRUE; PRESMON *ppMonitorList; DWORD i; LPWSTR lpszDllNameOfRes = NULL; WCHAR szDLLNameOfRes[MAX_PATH]; DWORD dwStatus; // // Check whether the currently allocated monitor list has reached capacity. If so, // create a new bigger list, copy the contents of the old list to the new one and // free the old list. // if ( pMonitorEnumHeader->cEntries == pMonitorEnumHeader->cAllocated ) { ppMonitorList = LocalAlloc( LPTR, pMonitorEnumHeader->cAllocated * 2 * sizeof ( PRESMON ) ); if ( ppMonitorList == NULL ) { fStatus = FALSE; ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpFindHostMonitors: Mem alloc failed with status %1!u!\n", GetLastError()); goto FnExit; } for ( i=0; icEntries; i++ ) { ppMonitorList[i] = pMonitorEnumHeader->ppMonitorList[i]; } pMonitorEnumHeader->cAllocated *= 2; LocalFree( pMonitorEnumHeader->ppMonitorList ); pMonitorEnumHeader->ppMonitorList = ppMonitorList; } // // Get the plain file name from the DLL name stored in the restype structure. Since the // parse function can potentially get rid of the trailing '\', make a copy of the DLL // name. // // // IMPORTANT: Do not write stuff into szDllNameOfRes while lpszDllName is being used // since lpszDllName points inside szDllNameOfRes. // lstrcpy( szDLLNameOfRes, pResource->Type->DllName ); dwStatus = FmpParsePathForFileName ( szDLLNameOfRes, TRUE, // Check for path existence &lpszDllNameOfRes ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpFindHostMonitors: Unable to parse path %1!ws! for filename, Status %2!u!\n", szDLLNameOfRes, dwStatus); fStatus = FALSE; goto FnExit; } if ( lpszDllNameOfRes == NULL ) lpszDllNameOfRes = pResource->Type->DllName; // // If this resource is not implemented in the specified DLL, you are done. // if ( lstrcmp( lpszDllNameOfRes, lpszDllName ) != 0 ) { fStatus = TRUE; goto FnExit; } ClRtlLogPrint(LOG_NOISE, "[FM] FmpFindHostMonitors: Resource DLL %1!ws! for resource %2!ws! [%3!ws!] is loaded currently in %4!ws! monitor...\n", lpszDllName, OmObjectId(pResource), OmObjectName(pResource), (pResource->Monitor == FmpDefaultMonitor) ? L"default":L"separate"); // // Since multiple resources can be loaded in the default monitor, you don't want to add // the default monitor multiple times in the list. Use a global static variable to indicate // that the default monitor has been added in the list. Also, note that only one resource can // be loaded in a separate monitor process and so there is no question of adding the separate // monitor multiple times in the list. // if ( pResource->Monitor == FmpDefaultMonitor ) { if ( pMonitorEnumHeader->fDefaultMonitorAdded == TRUE ) { fStatus = TRUE; goto FnExit; } pMonitorEnumHeader->fDefaultMonitorAdded = TRUE; } pMonitorEnumHeader->ppMonitorList[pMonitorEnumHeader->cEntries] = pResource->Monitor; pMonitorEnumHeader->cEntries ++; FnExit: return ( fStatus ); } // FmpFindHostMonitors DWORD FmpRecoverResourceDLLFiles( VOID ) /*++ Routine Description: Check whether any resource DLLs need to be recovered due to a crash during an upgrade. Arguments: None. Returns: ERROR_SUCCESS on success Win32 error code otherwise --*/ { DWORD dwStatus = ERROR_SUCCESS; LPWSTR lpszDllPath = NULL; LPCWSTR lpmszUpgradeList = NULL; LPWSTR lpmszBegin = NULL; DWORD cbListSize = 0, cchLen = 0; DWORD dwType, dwIndex; HKEY hClussvcParamsKey = NULL; // // Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters // dwStatus = RegOpenKeyW( HKEY_LOCAL_MACHINE, CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, &hClussvcParamsKey ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRecoverResourceDLLFiles: RegOpenKeyEx on %1!ws! failed, Status=%2!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, dwStatus); goto FnExit; } // // See whether a past failed upgrade has left any values in the upgrade progress list // dwStatus = RegQueryValueExW( hClussvcParamsKey, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, 0, &dwType, NULL, &cbListSize ); if ( dwStatus != ERROR_SUCCESS ) { if ( dwStatus == ERROR_FILE_NOT_FOUND ) { dwStatus = ERROR_SUCCESS; // // Delete any backup files left over from past failed upgrades. // FmpDeleteBackupFiles( NULL ); } else ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRecoverResourceDLLFiles: RegQueryValueEx (1st time) on %1!ws! key, value %2!ws! failed, Status=%3!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } if ( cbListSize == 0 ) { ClRtlLogPrint(LOG_NOISE, "[FM] FmpRecoverResourceDLLFiles: Value size is 0 for %1!ws! key, value %2!ws!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST); goto FnExit; } // // Found some values left out from past upgrade. Read those values. Also, copy // those values into a temp buffer for allowing easy MULTI_SZ removal. // lpmszUpgradeList = LocalAlloc ( LPTR, 2 * cbListSize ); // Twice size needed for temp buffer below if ( lpmszUpgradeList == NULL ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRecoverResourceDLLFiles: Mem alloc failure, Status=%1!u!\n", dwStatus); goto FnExit; } lpmszBegin = ( LPWSTR ) lpmszUpgradeList; dwStatus = RegQueryValueExW( hClussvcParamsKey, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, 0, &dwType, ( LPBYTE ) lpmszUpgradeList, &cbListSize ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRecoverResourceDLLFiles: RegQueryValueEx (2nd time) on %1!ws! key, value %2!ws! failed, Status=%3!u!\n", CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, dwStatus); goto FnExit; } CopyMemory( lpmszBegin + cbListSize/sizeof(WCHAR), lpmszUpgradeList, cbListSize ); cchLen = cbListSize/sizeof(WCHAR); // // This loop walks through the multi strings read from the registry and tries to // see if the file exists in the path. If not, it tries to copy the file from // a backup. Once it succeeds in copying a file from the backup, it tries to // delete the value from the MULTI_SZ and the appropriate backup files from the // cluster directory. // for ( dwIndex = 0; ; dwIndex++ ) { lpszDllPath = ( LPWSTR ) ClRtlMultiSzEnum( lpmszUpgradeList, cbListSize/sizeof(WCHAR), dwIndex ); // // If you reached the end of the multi-string, bail. // if ( lpszDllPath == NULL ) { break; } // // Assume the worst and copy the DLL file from the good backup. // ClRtlLogPrint(LOG_NOISE, "[FM] FmpRecoverResourceDLLFiles: Resource DLL binary %1!ws! cannot be trusted due to a failure during upgrade...\n", lpszDllPath); ClRtlLogPrint(LOG_NOISE, "[FM] FmpRecoverResourceDLLFiles: Attempting to use a copy from backup...\n", lpszDllPath); dwStatus = FmpCopyBackupFile( lpszDllPath ); if ( dwStatus == ERROR_SUCCESS ) { // // The copy went fine. So, reset the registry value set during the upgrade. // dwStatus = FmpResetMultiSzValue ( hClussvcParamsKey, lpmszBegin + cbListSize/sizeof(WCHAR), &cchLen, CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST, lpszDllPath ); if ( dwStatus == ERROR_SUCCESS ) // // The registry value reset went fine, so get rid of the backup files // FmpDeleteBackupFiles( lpszDllPath ); } } // for FnExit: LocalFree( lpmszBegin ); if ( hClussvcParamsKey != NULL ) { RegCloseKey( hClussvcParamsKey ); } return ( dwStatus ); }// FmpRecoverResourceDLLFiles DWORD FmpResetMultiSzValue( IN HKEY hKey, IN LPWSTR lpmszList, IN OUT LPDWORD pcchLen, IN LPCWSTR lpszValueName, IN LPCWSTR lpszString ) /*++ Routine Description: Gets rid of a specified string from a multi-string and sets the string to the given value name in the registry. The value is deleted if on the string removal the multi-string becomes empty. Arguments: hKey - An open registry handle. lpmszList - A multi-string. pcchLen - A pointer to the length of the multi string. On return, it will be set to the new length of the multi-string. lpszValueName - The value name to be modified. lpszString - The string to be removed from the multi-string. Returns: ERROR_SUCCESS on success Win32 error code otherwise --*/ { DWORD dwStatus = ERROR_SUCCESS; // // Remove the supplied string from the multi-sz // dwStatus = ClRtlMultiSzRemove( lpmszList, pcchLen, lpszString ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpReplaceResourceDLL: ClRtlMultiSzRemove failed for %1!ws!, Status=%2!u!\n", lpszString, dwStatus); goto FnExit; } // // ClRtlMultiSzRemove will return a size of 1 character if the string is empty // if ( *pcchLen <= 2 ) { // // After removal from the multi-sz, there is nothing left, so delete the value // dwStatus = RegDeleteValue( hKey, lpszValueName ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpResetMultiSzValue: RegDeleteValue on %1!ws! value failed, Status=%2!u!\n", lpszValueName, dwStatus); goto FnExit; } } else { // // Put the rest of the values back into the registry. // dwStatus = RegSetValueExW( hKey, lpszValueName, 0, REG_MULTI_SZ, ( LPBYTE ) lpmszList, ( *pcchLen ) * sizeof ( WCHAR ) ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpResetMultiSzValue: RegSetValueEx on %1!ws! value failed, Status=%2!u!\n", lpszValueName, dwStatus); goto FnExit; } } FnExit: return ( dwStatus ); }// FmpResetMultiSzValue DWORD FmpCopyBackupFile( IN LPCWSTR lpszPath ) /*++ Routine Description: Parse the path for the DLL file name and copy the backup version of the file. Arguments: lpszPath - Path including the DLL file name. Returns: ERROR_SUCCESS on success Win32 error code otherwise Note: We can only trust CLUSTER_RESDLL_BACKUP_FILE_EXTENSION file as the good backup since that backup was made prior to setting the CLUSREG_NAME_SVC_PARAM_RESDLL_UPGD_PROGRESS_LIST value. So, we don't look at the CLUSTER_RESDLL_RENAMED_FILE_EXTENSION file in this function. --*/ { WCHAR szSourceFile[MAX_PATH]; WCHAR szTempFile[MAX_PATH]; WCHAR szClusterDir[MAX_PATH]; LPWSTR lpszFileName; DWORD dwStatus = ERROR_SUCCESS, i, dwLen; // // Get the plain file name from the DLL name stored in the restype structure. Since the // parse function can potentially get rid of the trailing '\', make a copy of the DLL // name. // // IMPORTANT: Dont write into szTempFile after you parse the file name since lpszFileName // points into szTempFile. // lstrcpy( szTempFile, lpszPath ); dwStatus = FmpParsePathForFileName ( szTempFile, FALSE, // Don't check for existence &lpszFileName ); if ( ( dwStatus != ERROR_SUCCESS ) || ( lpszFileName == NULL ) ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpCopyBackupFile: Unable to parse path %1!ws! for filename, Status %2!u!\n", szTempFile, dwStatus); goto FnExit; } // // Get the cluster bits installed directory // dwStatus = ClRtlGetClusterDirectory( szClusterDir, MAX_PATH ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpCopyBackupFile: Could not get cluster dir, Status=%1!u!\n", dwStatus); goto FnExit; } lstrcpy( szSourceFile, szClusterDir ); dwLen = lstrlenW( szSourceFile ); if ( szSourceFile[dwLen-1] != L'\\' ) { szSourceFile[dwLen++] = L'\\'; szSourceFile[dwLen] = L'\0'; } lstrcat( szSourceFile, lpszFileName ); lstrcat( szSourceFile, CLUSTER_RESDLL_BACKUP_FILE_EXTENSION ); // // Change the file attributes to normal // if ( !SetFileAttributes( szSourceFile, FILE_ATTRIBUTE_NORMAL ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpCopyBackupFile: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } // // Copy the backup file to the DLL path. // if ( !CopyFileEx( szSourceFile, // Source file lpszPath, // Destination file NULL, // No progress routine NULL, // No data to progress routine NULL, // No cancel variable 0 ) ) // No flags { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpCopyBackupFile: CopyFileEx of %1!ws! to %2!ws! failed, Status=%3!u!\n", szSourceFile, lpszPath, dwStatus); } else { dwStatus = ERROR_SUCCESS; ClRtlLogPrint(LOG_NOISE, "[FM] FmpCopyBackupFile: CopyFileEx of %1!ws! to %2!ws! successful...\n", szSourceFile, lpszPath, dwStatus); goto FnExit; } FnExit: return ( dwStatus ); }// FmpCopyBackupFile VOID FmpDeleteBackupFiles( IN LPCWSTR lpszPath OPTIONAL ) /*++ Routine Description: Parse the path for the DLL file name and delete the backup files corresponding to it OR delete all files with the known backup extension in the %windir%\cluster directory. Arguments: lpszPath - Path including the DLL file name. OPTIONAL Returns: ERROR_SUCCESS on success Win32 error code otherwise --*/ { WCHAR szSourceFile[MAX_PATH]; WCHAR szTempFile[MAX_PATH]; WCHAR szClusterDir[MAX_PATH]; LPWSTR lpszFileName = L"*"; // Use in case IN param is NULL DWORD dwStatus = ERROR_SUCCESS, i, dwLen; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA FindData; if ( lpszPath == NULL ) goto skip_path_parse; // // Get the plain file name from the DLL name stored in the restype structure. Since the // parse function can potentially get rid of the trailing '\', make a copy of the DLL // name. // // IMPORTANT: Dont write into szTempFile after you parse the file name since lpszFileName // points into szTempFile. // lstrcpy( szTempFile, lpszPath ); dwStatus = FmpParsePathForFileName ( szTempFile, FALSE, // Don't check for existence &lpszFileName ); if ( ( dwStatus != ERROR_SUCCESS ) || ( lpszFileName == NULL ) ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpDeleteBackupFiles: Unable to parse path %1!ws! for filename, Status %2!u!\n", szTempFile, dwStatus); goto FnExit; } skip_path_parse: // // Get the cluster bits installed directory // dwStatus = ClRtlGetClusterDirectory( szClusterDir, MAX_PATH ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpDeleteBackupFiles: Could not get cluster dir, Status=%1!u!\n", dwStatus); goto FnExit; } if ( lpszPath == NULL ) { // // Delete all files that match the backup file pattern. // lstrcpy( szSourceFile, szClusterDir ); dwLen = lstrlenW( szSourceFile ); if ( szSourceFile[dwLen-1] != L'\\' ) { szSourceFile[dwLen++] = L'\\'; szSourceFile[dwLen] = L'\0'; } lstrcat( szSourceFile, lpszFileName ); lstrcat( szSourceFile, CLUSTER_RESDLL_BACKUP_FILES ); if ( ( hFind = FindFirstFile( szSourceFile, &FindData ) ) == INVALID_HANDLE_VALUE ) { dwStatus = GetLastError(); if ( dwStatus != ERROR_FILE_NOT_FOUND ) ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in FindFirstFile for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); goto FnExit; } do { // // Get the file name matching the pattern above and get the full path including // the file name. Then change the file attributes to normal for allowing deletion. // lstrcpy( szSourceFile, szClusterDir ); dwLen = lstrlenW( szSourceFile ); if ( szSourceFile[dwLen-1] != L'\\' ) { szSourceFile[dwLen++] = L'\\'; szSourceFile[dwLen] = L'\0'; } lstrcat( szSourceFile, FindData.cFileName ); if ( !SetFileAttributes( szSourceFile, FILE_ATTRIBUTE_NORMAL ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } if ( !DeleteFile( szSourceFile ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in DeleteFile for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } } while ( FindNextFile( hFind, &FindData ) ); FindClose ( hFind ); goto FnExit; } lstrcpy( szSourceFile, szClusterDir ); dwLen = lstrlenW( szSourceFile ); if ( szSourceFile[dwLen-1] != L'\\' ) { szSourceFile[dwLen++] = L'\\'; szSourceFile[dwLen] = L'\0'; } lstrcat( szSourceFile, lpszFileName ); lstrcat( szSourceFile, CLUSTER_RESDLL_BACKUP_FILE_EXTENSION ); if ( !SetFileAttributes( szSourceFile, FILE_ATTRIBUTE_NORMAL ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } if ( !DeleteFile( szSourceFile ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in DeleteFile for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } lstrcpy( szSourceFile, szClusterDir ); dwLen = lstrlenW( szSourceFile ); if ( szSourceFile[dwLen-1] != L'\\' ) { szSourceFile[dwLen++] = L'\\'; szSourceFile[dwLen] = L'\0'; } lstrcat( szSourceFile, lpszFileName ); lstrcat( szSourceFile, CLUSTER_RESDLL_RENAMED_FILE_EXTENSION ); if ( !SetFileAttributes( szSourceFile, FILE_ATTRIBUTE_NORMAL ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in SetFileAttributes for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } if ( !DeleteFile( szSourceFile ) ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_NOISE, "[FM] FmpDeleteBackupFiles: Failed in DeleteFile for %1!ws!, Status=%2!u!\n", szSourceFile, dwStatus); } FnExit: return; }