You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1691 lines
58 KiB
1691 lines
58 KiB
/*++
|
|
|
|
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"
|
|
#include "strsafe.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 = NULL;
|
|
LPWSTR lpszCurrentDllPath = NULL;
|
|
|
|
//
|
|
// 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,
|
|
&lpszCurrentDllPath );
|
|
|
|
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,
|
|
lpszCurrentDllPath,
|
|
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 ( lpszCurrentDllPath ); // Delete backup files
|
|
|
|
FnReleaseLockAndExit:
|
|
FmpReleaseMonitorLock();
|
|
|
|
FnExit:
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[FM] FmpUpgradeResourceDLL: Exit with status %1!u!...\n",
|
|
dwStatus);
|
|
LocalFree ( lpszCurrentDllPath );
|
|
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 *ppszCurrentDllPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validate the resource DLL replacement request.
|
|
|
|
Arguments:
|
|
|
|
pResource - The resource which is implemeted by the DLL.
|
|
|
|
lpszNewDllName - The name of the DLL.
|
|
|
|
ppszCurrentDllPath - The full path of the currently loaded DLL.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success.
|
|
|
|
Win32 error code otherwise.
|
|
--*/
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
LPWSTR lpszDllName = NULL;
|
|
LPWSTR lpszDLLNameOfRes = NULL;
|
|
BOOL fDllPathFound = TRUE;
|
|
DWORD cchDllName;
|
|
|
|
//
|
|
// Initialize return value
|
|
//
|
|
*ppszCurrentDllPath = NULL;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
cchDllName = lstrlen ( pResource->Type->DllName ) + 1;
|
|
|
|
lpszDLLNameOfRes = LocalAlloc ( LPTR, cchDllName * sizeof ( WCHAR ) );
|
|
|
|
if ( lpszDLLNameOfRes == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpValidateResourceDLLReplacement: Memory alloc for dll name %1!ws! failed, Status %2!u!\n",
|
|
pResource->Type->DllName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
( void ) StringCchCopy( lpszDLLNameOfRes, cchDllName, pResource->Type->DllName );
|
|
|
|
dwStatus = FmpParsePathForFileName ( lpszDLLNameOfRes,
|
|
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",
|
|
lpszDLLNameOfRes,
|
|
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
|
|
{
|
|
//
|
|
// Expand any environment variables included in the DLL path name.
|
|
//
|
|
*ppszCurrentDllPath = ClRtlExpandEnvironmentStrings( pResource->Type->DllName );
|
|
|
|
if ( *ppszCurrentDllPath == 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;
|
|
}
|
|
}
|
|
|
|
if ( lstrcmpi( 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 )
|
|
{
|
|
DWORD cchPathLen;
|
|
|
|
//
|
|
// First find the size of the buffer needed to hold the full path
|
|
//
|
|
if ( ( cchPathLen = SearchPath ( NULL, // Search all paths as LoadLibrary does
|
|
lpszNewDllName, // File name to search for
|
|
NULL, // No extension required
|
|
0, // Size of out buffer
|
|
NULL, // Buffer to receive full Dll path with file name
|
|
NULL ) ) == 0 ) // Filename at the end of the path
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpValidateResourceDLLReplacement: SearchPath API (1st time) for file %1!ws! failed, Status=%2!u!\n",
|
|
lpszNewDllName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
*ppszCurrentDllPath = LocalAlloc ( LPTR, cchPathLen * sizeof ( WCHAR ) );
|
|
|
|
if ( *ppszCurrentDllPath == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpValidateResourceDLLReplacement: Memory alloc for path failed, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
//
|
|
// Now find the full path
|
|
//
|
|
if ( !SearchPath ( NULL, // Search all paths as LoadLibrary does
|
|
lpszNewDllName, // File name to search for
|
|
NULL, // No extension required
|
|
cchPathLen, // Size of out buffer
|
|
*ppszCurrentDllPath, // Buffer to receive full Dll path with file name
|
|
NULL ) ) // Filename at the end of the path
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpValidateResourceDLLReplacement: SearchPath API (2nd time) 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",
|
|
*ppszCurrentDllPath);
|
|
|
|
FnExit:
|
|
LocalFree ( lpszDLLNameOfRes );
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
LocalFree ( *ppszCurrentDllPath );
|
|
*ppszCurrentDllPath = NULL;
|
|
}
|
|
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, RTL_NUMBER_OF ( szClusterDirectory ) );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpReplaceResourceDLL: Could not get cluster dir, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
( void ) StringCchCopy( szBakFile, RTL_NUMBER_OF ( szBakFile ), szClusterDirectory );
|
|
|
|
dwLen = lstrlenW( szBakFile );
|
|
|
|
if ( szBakFile[dwLen-1] != L'\\' )
|
|
{
|
|
szBakFile[dwLen++] = L'\\';
|
|
szBakFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szBakFile, RTL_NUMBER_OF ( szBakFile ), lpszNewDllName );
|
|
( void ) StringCchCat( szBakFile, RTL_NUMBER_OF ( 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:
|
|
( void ) StringCchCopy( szOldFile, RTL_NUMBER_OF ( szOldFile ), szClusterDirectory );
|
|
|
|
dwLen = lstrlenW( szOldFile );
|
|
|
|
if ( szOldFile[dwLen-1] != L'\\' )
|
|
{
|
|
szOldFile[dwLen++] = L'\\';
|
|
szOldFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szOldFile, RTL_NUMBER_OF ( szOldFile ), lpszNewDllName );
|
|
( void ) StringCchCat( szOldFile, RTL_NUMBER_OF ( 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; i<MonitorEnumHeader.cEntries; i++ )
|
|
{
|
|
//
|
|
// Increment the ref count. It will be decremented by the restart function.
|
|
//
|
|
InterlockedIncrement( &MonitorEnumHeader.ppMonitorList[i]->RefCount );
|
|
FmpRestartMonitor( MonitorEnumHeader.ppMonitorList[i], FALSE, NULL );
|
|
} // 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;
|
|
LPWSTR lpszDLLPathOfRes = NULL;
|
|
DWORD dwStatus;
|
|
DWORD cchDllName;
|
|
|
|
//
|
|
// 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; i<pMonitorEnumHeader->cEntries; 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.
|
|
//
|
|
cchDllName = lstrlen ( pResource->Type->DllName ) + 1;
|
|
|
|
lpszDLLPathOfRes = LocalAlloc ( LPTR, cchDllName * sizeof ( WCHAR ) );
|
|
|
|
if ( lpszDLLPathOfRes == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpFindHostMonitors: Memory alloc for dll name %1!ws! failed, Status %2!u!\n",
|
|
pResource->Type->DllName,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
( void ) StringCchCopy( lpszDLLPathOfRes, cchDllName, pResource->Type->DllName );
|
|
|
|
dwStatus = FmpParsePathForFileName ( lpszDLLPathOfRes,
|
|
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",
|
|
lpszDLLPathOfRes,
|
|
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 ( lstrcmpi( 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:
|
|
LocalFree ( lpszDLLPathOfRes );
|
|
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.
|
|
//
|
|
( void ) StringCchCopy( szTempFile, RTL_NUMBER_OF ( 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, RTL_NUMBER_OF ( szClusterDir ) );
|
|
|
|
if ( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpCopyBackupFile: Could not get cluster dir, Status=%1!u!\n",
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
( void ) StringCchCopy( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), szClusterDir );
|
|
|
|
dwLen = lstrlenW( szSourceFile );
|
|
|
|
if ( szSourceFile[dwLen-1] != L'\\' )
|
|
{
|
|
szSourceFile[dwLen++] = L'\\';
|
|
szSourceFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), lpszFileName );
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( 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 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;
|
|
DWORD cchPath;
|
|
LPWSTR lpszTempFile = NULL;
|
|
|
|
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.
|
|
//
|
|
cchPath = lstrlen ( lpszPath ) + 1;
|
|
|
|
lpszTempFile = LocalAlloc ( LPTR, cchPath * sizeof ( WCHAR ) );
|
|
|
|
if ( lpszTempFile == NULL )
|
|
{
|
|
dwStatus = GetLastError();
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[FM] FmpDeleteBackupFiles: Memory allocation for path %1!ws! failed, Status %2!u!\n",
|
|
lpszPath,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
( void ) StringCchCopy( lpszTempFile, cchPath, lpszPath );
|
|
|
|
dwStatus = FmpParsePathForFileName ( lpszTempFile,
|
|
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",
|
|
lpszTempFile,
|
|
dwStatus);
|
|
goto FnExit;
|
|
}
|
|
|
|
skip_path_parse:
|
|
//
|
|
// Get the cluster bits installed directory
|
|
//
|
|
dwStatus = ClRtlGetClusterDirectory( szClusterDir, RTL_NUMBER_OF ( szClusterDir ) );
|
|
|
|
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.
|
|
//
|
|
( void ) StringCchCopy( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), szClusterDir );
|
|
|
|
dwLen = lstrlenW( szSourceFile );
|
|
|
|
if ( szSourceFile[dwLen-1] != L'\\' )
|
|
{
|
|
szSourceFile[dwLen++] = L'\\';
|
|
szSourceFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), lpszFileName );
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( 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.
|
|
//
|
|
( void ) StringCchCopy( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), szClusterDir );
|
|
|
|
dwLen = lstrlenW( szSourceFile );
|
|
|
|
if ( szSourceFile[dwLen-1] != L'\\' )
|
|
{
|
|
szSourceFile[dwLen++] = L'\\';
|
|
szSourceFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( 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;
|
|
}
|
|
|
|
( void ) StringCchCopy( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), szClusterDir );
|
|
|
|
dwLen = lstrlenW( szSourceFile );
|
|
|
|
if ( szSourceFile[dwLen-1] != L'\\' )
|
|
{
|
|
szSourceFile[dwLen++] = L'\\';
|
|
szSourceFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), lpszFileName );
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( 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);
|
|
}
|
|
|
|
( void ) StringCchCopy( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), szClusterDir );
|
|
|
|
dwLen = lstrlenW( szSourceFile );
|
|
|
|
if ( szSourceFile[dwLen-1] != L'\\' )
|
|
{
|
|
szSourceFile[dwLen++] = L'\\';
|
|
szSourceFile[dwLen] = L'\0';
|
|
}
|
|
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( szSourceFile ), lpszFileName );
|
|
( void ) StringCchCat( szSourceFile, RTL_NUMBER_OF ( 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;
|
|
}
|