Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1604 lines
51 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"
//
// 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; 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] );
} // 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; 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.
//
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;
}