|
|
/*++
Copyright (c) 1995-2001 Microsoft Corporation
Module Name:
disks.c
Abstract:
Resource DLL for disks.
Author:
John Vert (jvert) 5-Dec-1995 Rod Gamache (rodga) 18-Dec-1995
Revision History:
--*/
#include "disksp.h"
#include "lm.h"
#include "arbitrat.h"
#include "newdisks.h"
#include "newmount.h"
#include <strsafe.h> // Should be included last.
#define UNICODE 1
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
#define MAX_HANDLES 10
extern PWCHAR g_DiskResource; // L"rtPhysical Disk"
#define RESOURCE_TYPE ((RESOURCE_HANDLE)g_DiskResource)
#define ONLINE_CHK_FILE_NAME L"zClusterOnlineChk.tmp"
LONG DiskCount = 0;
CRITICAL_SECTION DisksLock;
LIST_ENTRY DisksListHead;
HANDLE DisksRegistryThread = NULL; HANDLE DisksTerminateEvent = NULL; HANDLE DiskspClusDiskZero = NULL;
extern CLRES_FUNCTION_TABLE DisksFunctionTable;
extern RTL_RESOURCE PnpVolumeLock; extern RTL_RESOURCE PnpWaitingListLock; // Disks waiting for pnp volume arrival (online)
//
// Disk resource property names
//
#define DISKS_SIGNATURE CLUSREG_NAME_PHYSDISK_SIGNATURE
#define DISKS_DRIVE CLUSREG_NAME_PHYSDISK_DRIVE // pseudonym for signature
#define DISKS_SKIPCHKDSK CLUSREG_NAME_PHYSDISK_SKIPCHKDSK
#define DISKS_CONDITIONAL_MOUNT CLUSREG_NAME_PHYSDISK_CONDITIONAL_MOUNT
#define DISKS_MPVOLGUIDS CLUSREG_NAME_PHYSDISK_MPVOLGUIDS
#define DISKS_VOLGUID CLUSREG_NAME_PHYSDISK_VOLGUID // Not saved in cluster DB
#define DISKS_SERIALNUMBER CLUSREG_NAME_PHYSDISK_SERIALNUMBER
//
// Disk resource private read-write properties.
// Allow for a pseudonym for Signature (Drive), but don't allow both
// drive and signature to be passed.
//
RESUTIL_PROPERTY_ITEM DiskResourcePrivateProperties[] = { { DISKS_SIGNATURE, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0xFFFFFFFF, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Signature) }, { DISKS_SKIPCHKDSK, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,SkipChkdsk) }, { DISKS_CONDITIONAL_MOUNT, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,ConditionalMount) }, { DISKS_MPVOLGUIDS, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, MPVolGuids) }, { DISKS_SERIALNUMBER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS,SerialNumber) }, { 0 } };
RESUTIL_PROPERTY_ITEM DiskResourcePrivatePropertiesAlt[] = { { DISKS_SIGNATURE, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0xFFFFFFFF, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Signature) }, { DISKS_SKIPCHKDSK, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,SkipChkdsk) }, { DISKS_DRIVE, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Drive) }, { DISKS_CONDITIONAL_MOUNT, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,ConditionalMount) }, { DISKS_MPVOLGUIDS, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, MPVolGuids) }, { DISKS_VOLGUID, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, VolGuid) }, { DISKS_SERIALNUMBER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS,SerialNumber) }, { 0 } };
#define CLUSTERLOG_ENV_VARIABLE L"ClusterLog"
//
// Local functions.
//
DWORD DisksValidatePrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PDISK_PARAMS Params );
DWORD DisksSetPrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize );
DWORD DisksGetPrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned );
DWORD ProcessDllExtension( IN PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned );
DWORD DisksOpenChkdskLogFile( IN PDISK_RESOURCE ResourceEntry, IN OUT PHANDLE ChkdskLogFile, IN OUT LPWSTR *ChkdskLogFileName );
BOOL DiskIsDynamic( IN DWORD DiskNumber );
//
// Error callbacks from disk library
//
VOID DiskErrorFatal( INT MessageId, DWORD Error, LPSTR File, DWORD Line ) { DWORD Data[3];
Data[0] = MessageId; Data[1] = Error; Data[2] = Line; ClusResLogSystemEventData(LOG_CRITICAL, RES_FTSET_DISK_ERROR, sizeof(Data), Data); }
VOID DiskErrorLogInfo( LPSTR Format, ... ) { }
BOOLEAN WINAPI DisksDllEntryPoint( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) { switch ( Reason ) {
case DLL_PROCESS_ATTACH: InitializeCriticalSection( &DisksLock ); InitializeListHead( &DisksListHead ); RtlInitializeResource( &PnpVolumeLock ); RtlInitializeResource( &PnpWaitingListLock ); ArbitrationInitialize();
break;
case DLL_PROCESS_DETACH: //
// only do clean up if we're not exiting the process.
// ClRtlDestroyWorkQueue waits on an event to be set and it is
// possible at this point that there are no threads to do that.
// This causes resmon to linger and generally be a pest.
//
if (DiskspClusDiskZero) { DevfileClose(DiskspClusDiskZero); } ArbitrationCleanup(); DeleteCriticalSection( &DisksLock ); RtlDeleteResource( &PnpVolumeLock ); RtlDeleteResource( &PnpWaitingListLock ); break;
default: break; }
return(TRUE);
} // DisksDllEntryPoint
VOID WINAPI DisksTerminate( IN RESID Resource ) { PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource;
if ( resourceEntry == NULL ) { DISKS_PRINT("Terminate, bad resource value \n"); return; } // Wait for offline thread to complete, if there is one //
ClusWorkerTerminate(&(resourceEntry->OfflineThread)); DisksOfflineOrTerminate(resourceEntry, TERMINATE); }
DWORD WINAPI DisksArbitrate( IN RESID Resource, IN PQUORUM_RESOURCE_LOST LostQuorumResource )
/*++
Routine Description:
Arbitrate for a device by performing a reservation on the device.
Arguments:
Resource - supplies resource id to be brought online.
LostQuorumResource - routine to call when quorum resource is lost.
Return Value:
ERROR_SUCCESS if successful. A Win32 error code if other failure.
--*/
{ PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource; DWORD status;
//
// Make sure the RESID is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("Arbitrate, bad resource value \n"); return(ERROR_RESOURCE_NOT_FOUND); }
// [HACKHACK]
// [GorN] 10/28/1999. If Offline thread detects that
// it is being terminated, it will not set the resource status to
// offline. ArbitrateCount != 0 will give it a hint on whether
// to set the resource status or not
InterlockedIncrement(&resourceEntry->ArbitrationInfo.ArbitrateCount);
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"[DiskArb] Wait for offline thread to complete...\n" ); ClusWorkerTerminate(&(resourceEntry->OfflineThread));
//
// Perform DoAttach only. Do not open.
//
status = DisksOpenResourceFileHandle(resourceEntry, L"Arbitrate",0); if (status != ERROR_SUCCESS) { goto error_exit; }
status = DiskArbitration( resourceEntry, DiskspClusDiskZero );
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"[DiskArb] Arbitrate returned status %1!u!.\n", status );
if (status == ERROR_SUCCESS) { resourceEntry->LostQuorum = LostQuorumResource; } error_exit: InterlockedDecrement(&resourceEntry->ArbitrationInfo.ArbitrateCount);
return status; } // DisksArbitrate //
RESID WINAPI DisksOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle )
/*++
Routine Description:
Open routine for Disk resource.
Arguments:
ResourceName - supplies the resource name
ResourceKey - supplies handle to this resource's cluster registry key
ResourceHandle - the resource handle to be supplied with SetResourceStatus is called.
Return Value:
RESID of created resource Zero on failure
--*/
{ DWORD status; HKEY clusDiskParametersKey = NULL; HKEY resourceParametersKey = NULL; HKEY resKey = NULL; PDISK_RESOURCE resourceEntry; LPWSTR nameOfPropInError; DWORD previousDiskCount;
//
// Open registry parameters key for ClusDisk.
//
status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, CLUSDISK_REGISTRY_SIGNATURES, 0, KEY_READ, &clusDiskParametersKey );
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open ClusDisk parameters key. Error: %1!u!.\n", status ); return(0); }
//
// Open the resource's parameters key.
//
status = ClusterRegOpenKey( ResourceKey, CLUSREG_KEYNAME_PARAMETERS, KEY_READ | KEY_WRITE, &resourceParametersKey );
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open resource parameters key. Error: %1!u!.\n", status ); goto error_exit; }
//
// Get a handle to our resource key so that we can get our name later
// if we need to log an event.
//
status = ClusterRegOpenKey( ResourceKey, L"", KEY_READ, &resKey); if (status != ERROR_SUCCESS) { (DiskpLogEvent)(ResourceHandle, LOG_ERROR, L"Unable to open resource key. Error: %1!u!.\n", status ); goto error_exit; }
//
// Allocate and zero disk info structure.
//
resourceEntry = LocalAlloc(LMEM_FIXED, sizeof(DISK_RESOURCE)); if (!resourceEntry) { status = GetLastError(); (DiskpLogEvent)(ResourceHandle, LOG_ERROR, L"Unable to allocate disk resource data. Error: %1!u!.\n", status ); goto error_exit; } ZeroMemory( resourceEntry, sizeof(DISK_RESOURCE)); resourceEntry->ResourceParametersKey = resourceParametersKey; resourceEntry->ClusDiskParametersKey = clusDiskParametersKey; resourceEntry->ResourceKey = resKey; resourceEntry->ResourceHandle = ResourceHandle; //resourceEntry->Inserted = FALSE;
//resourceEntry->Attached = FALSE;
//resourceEntry->DiskInfo.Params.Signature = 0;
status = ArbitrationInfoInit(resourceEntry); if ( status != ERROR_SUCCESS ) { LocalFree( resourceEntry ); goto error_exit; } status = CreateArbWorkQueue(ResourceHandle); if ( status != ERROR_SUCCESS ) { LocalFree( resourceEntry ); goto error_exit; }
#if 0
//
// GN: It seems that there is no point doing this here
// If we are on the join path, we cannot get
// any information about the disk and the call will fail
//
// If we are forming the cluster we will update the information
// when we bring the disk online
//
status = DiskspSsyncDiskInfo( L"Open", resourceEntry , 0 ); if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)(ResourceHandle, LOG_ERROR, L"Unable to ssync DiskInfo. Error: %1!u!.\n", status ); } #endif
//
// Save disk info structure.
//
EnterCriticalSection( &DisksLock );
if (DiskspClusDiskZero == NULL) { status = DevfileOpen(&DiskspClusDiskZero, DEVICE_CLUSDISK0); if (!NT_SUCCESS(status) ) { MountieCleanup ( &resourceEntry -> MountieInfo ); ArbitrationInfoCleanup( resourceEntry ); LocalFree( resourceEntry ); LeaveCriticalSection( &DisksLock ); (DiskpLogEvent)( ResourceHandle, LOG_ERROR, L"Cannot open a handle to clusdisk driver, %1!x!.\n", status); goto error_exit; } }
previousDiskCount = InterlockedExchangeAdd(&DiskCount, 1);
LeaveCriticalSection( &DisksLock );
if (previousDiskCount == 0) { StartNotificationWatcherThread(); }
DisksMountPointInitialize( resourceEntry );
//
// Read our disk signature from the resource parameters.
//
status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ResourceParametersKey, DiskResourcePrivateProperties, (LPBYTE) &resourceEntry->DiskInfo.Params, FALSE, //CheckForRequiredProperties
&nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Open: Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); }
return( (RESID)( resourceEntry ) );
error_exit:
if ( clusDiskParametersKey != NULL ) { RegCloseKey( clusDiskParametersKey ); }
if ( resourceParametersKey != NULL ) { ClusterRegCloseKey( resourceParametersKey ); }
if ( resKey != NULL ) { ClusterRegCloseKey( resKey ); }
SetLastError( status ); return((RESID)0);
} // DisksOpen
DWORD WINAPI DisksRelease( IN RESID Resource )
/*++
Routine Description:
Release arbitration for a device by stopping the reservation thread.
Arguments:
Resource - supplies resource id to be brought online
Return Value:
ERROR_SUCCESS if successful. ERROR_HOST_NODE_NOT_OWNER if the resource is not owned. A Win32 error code if other failure.
--*/
{ PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("Release, bad resource value \n"); return(ERROR_RESOURCE_NOT_FOUND); }
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksRelease started, Inserted = %1!u! \n", resourceEntry->Inserted );
if (resourceEntry->Inserted) { // [GN] #209018 //
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Cannot release, Disk is online.\n"); } else { StopPersistentReservations(resourceEntry); } resourceEntry->LostQuorum = NULL;
return(ERROR_SUCCESS);
} // DisksRelease
DWORD WINAPI DisksOnline( IN RESID Resource, IN OUT PHANDLE EventHandle )
/*++
Routine Description:
Online routine for Disk resource.
Arguments:
Resource - supplies resource id to be brought online
EventHandle - supplies a pointer to a handle to signal on error.
Return Value:
ERROR_SUCCESS if successful. ERROR_RESOURCE_NOT_FOUND if RESID is not valid. ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to acquire 'ownership'. Win32 error code if other failure.
--*/
{ PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource; DWORD Status;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("Online, bad resource value \n"); return(ERROR_RESOURCE_NOT_FOUND); }
//
// Shutdown the online thread if it's running.
//
ClusWorkerTerminate(&resourceEntry->OnlineThread);
Status = ClusWorkerCreate(&resourceEntry->OnlineThread, DisksOnlineThread, resourceEntry); if (Status == ERROR_SUCCESS) { Status = ERROR_IO_PENDING; } return(Status);
} // DisksOnline
DWORD DisksOfflineThread( IN PCLUS_WORKER Worker, IN PDISK_RESOURCE ResourceEntry ) { RESOURCE_STATUS resourceStatus; DWORD status;
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed; //resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1; ClusWorkerTerminate( &ResourceEntry->OnlineThread ); status = DisksOfflineOrTerminate(ResourceEntry, OFFLINE);
if (status == ERROR_SUCCESS) { resourceStatus.ResourceState = ClusterResourceOffline; }
//
// [HACKHACK] [GorN 10/04/1999]
// If Terminate is called when the offline is in progress,
// the terminate blocks waiting for OfflineThread to complete.
// However, offline thread is stuck trying
// to set ResourceStatus, since event list lock in the resmon
// is taken out by Terminate thread.
//
// The following code doesn't fix this deadlock completely.
// It just reduces the window during which the problem can occur.
// [Resmon times out SetResourceStatus in 3 minutes, this breaks the deadlock]
//
// [HACKHACK] [GorN 10/28/1999]
// Arbitrate is also trying to terminate the offline thread
// We need some way to distinguish between these two cases
//
// The order of setting is
// ArbitrateCount
// ClusWorkerTerminate
//
// Order of checking is ClusWorkerTerminate then ArbitrateCount.
// (Won't work with aggressive memory access reordering, but who cares <grin>)
//
if ( !ClusWorkerCheckTerminate( Worker ) || ResourceEntry->ArbitrationInfo.ArbitrateCount) { (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle, &resourceStatus ); } return status; }
DWORD WINAPI DisksOffline( IN RESID ResourceId ) { PDISK_RESOURCE ResourceEntry = (PDISK_RESOURCE)ResourceId; DWORD status = ERROR_SUCCESS;
//
// Make sure the Resource is okay.
//
if ( ResourceEntry == NULL ) { DISKS_PRINT("Offline, bad resource value \n"); return(ERROR_RESOURCE_NOT_FOUND); }
status = ClusWorkerCreate( &ResourceEntry->OfflineThread, (PWORKER_START_ROUTINE)DisksOfflineThread, ResourceEntry ); if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Offline: Unable to start thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; }
return status;
} // DisksOffline
BOOL WINAPI DisksIsAlive( IN RESID Resource )
/*++
Routine Description:
IsAlive routine for Disk resource.
Arguments:
Resource - supplies the resource id to be polled.
Return Value:
TRUE - Resource is alive and well
FALSE - Resource is toast.
--*/
{ DWORD status; PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("IsAlive, bad resource value \n"); return(FALSE); }
if ( resourceEntry->DiskInfo.FailStatus != 0 ) { (DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"IsAlive, error checking device, error %1!u!.\n", resourceEntry->DiskInfo.FailStatus ); ClusResLogSystemEventByKey(resourceEntry->ResourceKey, LOG_CRITICAL, RES_DISK_FAILED_SCSI_CHECK); resourceEntry->DiskInfo.FailStatus = 0; return(FALSE); }
//
// Check out the interesting partitions.
//
#if 0
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"About to call DriveIsAlive!\n" ); #endif
status = DisksDriveIsAlive( resourceEntry, FALSE);
if (status == ERROR_SUCCESS) { return(TRUE); } else { ClusResLogSystemEventByKeyData(resourceEntry->ResourceKey, LOG_CRITICAL, RES_DISK_FILESYSTEM_FAILED, sizeof(status), &status); return(FALSE); }
} // DisksIsAlive
BOOL WINAPI DisksLooksAlive( IN RESID Resource )
/*++
Routine Description:
LooksAlive routine for Disk resource.
Arguments:
Resource - supplies the resource id to be polled.
Return Value:
TRUE - Resource looks like it is alive and well
FALSE - Resource looks like it is toast.
--*/
{ PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("Online, bad resource value \n"); return(FALSE); }
if ( resourceEntry->DiskInfo.FailStatus != 0 ) { (DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"LooksAlive, error checking device, error %1!u!.\n", resourceEntry->DiskInfo.FailStatus ); return FALSE; }
return(TRUE);
} // DisksLooksAlive
VOID WINAPI DisksClose( IN RESID Resource )
/*++
Routine Description:
Close routine for Disk resource.
Arguments:
Resource - supplies resource id to be closed.
Return Value:
None.
--*/
{ PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("Close, bad resource value \n"); return; }
//
// Wait for the online thread to finish.
//
DisksTerminate( Resource );
DisksMountPointCleanup( resourceEntry );
if ( resourceEntry->DiskInfo.Params.SerialNumber ) { LocalFree( resourceEntry->DiskInfo.Params.SerialNumber ); resourceEntry->DiskInfo.Params.SerialNumber = NULL; }
if ( InterlockedExchangeAdd(&DiskCount, -1) == 1 ) { // This is the last disk //
StopNotificationWatcher(); DestroyArbWorkQueue(); }
ClusterRegCloseKey( resourceEntry->ResourceParametersKey); ClusterRegCloseKey( resourceEntry->ResourceKey); RegCloseKey(resourceEntry->ClusDiskParametersKey); ArbitrationInfoCleanup(resourceEntry); MountieCleanup( &resourceEntry->MountieInfo ); LocalFree(resourceEntry);
return;
} // DisksClose
DWORD DisksCheckCorruption( IN PWCHAR DeviceName, IN PWCHAR VolumeName, IN PDISK_RESOURCE ResourceEntry )
/*++
Routine Description:
Checks for disk corruption problems.
Arguments:
DeviceName - Supplies name of the form: \Device\HarddiskX\PartitionY [Note: no trailing backslash]
VolumeName - Supplies the device name of the form: \\?\Volume{GUID}\ [Note trailing backslash!]
ResourceEntry - Supplies a pointer to the resource structure
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{ BOOL dirty; DWORD status = ERROR_SUCCESS;
if ( ResourceEntry->DiskInfo.Params.SkipChkdsk ) { status = ERROR_SUCCESS; goto FnExit; }
status = DisksIsVolumeDirty( DeviceName, ResourceEntry, &dirty ); if (status == ERROR_SUCCESS && dirty) {
status = ERROR_DISK_CORRUPT; }
FnExit:
return status;
} // DisksCheckCorruption
DWORD DisksFixCorruption( IN PWCHAR VolumeName, IN PDISK_RESOURCE ResourceEntry, IN DWORD CorruptStatus )
/*++
Routine Description:
Fix file or disk corrupt problems.
Arguments:
VolumeName - Supplies the device name of the form: \\?\Volume{GUID}\ [Note trailing backslash!]
ResourceEntry - Supplies a pointer to the disk resource entry
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
Notes:
We'll need to lock the volume exclusive while we do this... So threads that call this routine should ensure there are no open files!
--*/
{ LPWSTR chkdskLogFileName = NULL;
PWCHAR sysDir = NULL; PWCHAR checkDiskInfo = NULL;
const DWORD sysDirChars = MAX_PATH * 2; const DWORD checkDiskInfoChars = MAX_PATH * 2;
DWORD status; DWORD len;
HANDLE chkdskLogFile = INVALID_HANDLE_VALUE;
STARTUPINFOW startupInfo; PROCESS_INFORMATION processInfo; RESOURCE_STATUS resourceStatus; RESOURCE_EXIT_STATE exit;
BOOL replaceBackslash;
BOOL bInheritHandles; UINT previousMode;
previousMode = SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX );
sysDir = LocalAlloc( LPTR, ( sysDirChars * sizeof(WCHAR) ) );
if ( !sysDir ) { status = GetLastError(); goto FnExit; }
//
// Get system directory path for CreateProcess.
//
len = GetSystemDirectoryW( sysDir, sysDirChars - 1 );
if ( !len ) { status = GetLastError(); goto FnExit; }
if ( len > sysDirChars - 1 ) { status = ERROR_BAD_PATHNAME; goto FnExit; }
if ( FAILED( StringCchCat( sysDir, sysDirChars, TEXT("\\chkdsk.exe") ) ) ) { status = ERROR_INSUFFICIENT_BUFFER; goto FnExit; }
//
// We need to strip the trailing backslash off so chkdsk will work.
//
len = wcslen( VolumeName );
if ( len > MAX_PATH ) { status = ERROR_ALLOTTED_SPACE_EXCEEDED; goto FnExit; }
if ( VolumeName[len-1] == L'\\') { VolumeName[len-1] = UNICODE_NULL; replaceBackslash = TRUE; } else { replaceBackslash = FALSE; }
checkDiskInfo = LocalAlloc( LPTR, checkDiskInfoChars * sizeof(WCHAR) );
if ( !checkDiskInfo ) { status = GetLastError(); goto FnExit; }
//
// Now handle the corruption problem by running CHKDSK.
//
if ( FAILED( StringCchPrintf( checkDiskInfo, checkDiskInfoChars, TEXT("chkdsk.exe /x /f %ws"), VolumeName ) ) ) { status = ERROR_INSUFFICIENT_BUFFER; goto FnExit; }
//
// Restore the backslash.
//
if ( replaceBackslash ) { VolumeName[len-1] = L'\\'; }
ZeroMemory( &startupInfo, sizeof(STARTUPINFOW) ); startupInfo.cb = sizeof(STARTUPINFO); startupInfo.lpDesktop = L"WinSta0\\Default";
bInheritHandles = FALSE;
status = DisksOpenChkdskLogFile( ResourceEntry, &chkdskLogFile, &chkdskLogFileName );
if ( NO_ERROR == status && INVALID_HANDLE_VALUE != chkdskLogFile ) {
//
// When the output is redirected, we don't want to show the console window because it
// will be blank with simply a title in it. The event log message will let the user
// know to look in the chkdsk file.
//
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; startupInfo.wShowWindow = SW_HIDE;
// Someone watching the console won't know what is happening, so show
// the window anyway...
// StartupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
//
// [169631] Chkdsk now verifies that hStdInput is not NULL.
// Since the resmon process was started with InheritHandles set to FALSE,
// GetStdHandle(STD_INPUT_HANDLE) will return NULL. When we run chkdsk with
// the options "/f /x", chkdsk should not be prompting the user and the
// input handle wouldn't be used. However, ulibs.dll was changed to always
// insure a nonzero input handle was supplied. So we have to supply some type
// of input handle. We could put INVALID_HANDLE_VALUE here, but the checks
// may change and it will fail later. For now, point input to the temporary
// output file we created.
//
if ( NULL == startupInfo.hStdInput ) { startupInfo.hStdInput = chkdskLogFile; }
startupInfo.hStdOutput = chkdskLogFile; startupInfo.hStdError = chkdskLogFile;
bInheritHandles = TRUE; }
//
// Log an event
//
if ( CorruptStatus == ERROR_DISK_CORRUPT ) { // Must be corrupt disk
ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_DISK_CORRUPT_DISK, VolumeName, chkdskLogFileName); } else { // Must be corrupt file.
ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_DISK_CORRUPT_FILE, VolumeName, chkdskLogFileName); }
if ( chkdskLogFileName ) { LocalFree( chkdskLogFileName ); chkdskLogFileName = NULL; }
if ( !CreateProcessW( sysDir, checkDiskInfo, NULL, NULL, bInheritHandles, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInfo ) ) { status = GetLastError();
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"DisksFixCorruption: CreateProcessW for chkdsk.exe failed %1!u! \n", status );
goto FnExit; }
CloseHandle( processInfo.hThread );
//
// Wait for CHKDSK to finish.
//
//
// Don't wait "forever"... things could get ugly if we dismount the file
// system while ChkDsk is running! But KeithKa says its okay to kill
// ChkDsk while it is running - it has to handle powerfails, crashes, etc.
//
resourceStatus.ResourceState = ClusterResourceOnlinePending; while ( !ResourceEntry->OnlineThread.Terminate ) { (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle, &resourceStatus ); status = WaitForSingleObject( processInfo.hProcess, 2000 ); if ( status != WAIT_TIMEOUT ) { break; } }
if ( ResourceEntry->OnlineThread.Terminate ) { // If we were asked to terminate, make sure ChkNtfs is killed
TerminateProcess( processInfo.hProcess, 999 ); CloseHandle( processInfo.hProcess );
status = ERROR_SHUTDOWN_CLUSTER; goto FnExit; }
//
// Update our checkpoint state.
//
++resourceStatus.CheckPoint; exit = (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle, &resourceStatus ); if ( exit == ResourceExitStateTerminate ) { TerminateProcess( processInfo.hProcess, 998 ); CloseHandle( processInfo.hProcess );
status = ERROR_SHUTDOWN_CLUSTER; goto FnExit; }
if ( (status == 0) && GetExitCodeProcess( processInfo.hProcess, &status ) ) {
// [From supera.hxx]
//
// These symbols are used by Chkdsk functions to return an appropriate
// exit status to the chkdsk program.
// In order of most important first, the error level order are as follows:
// 3 > 1 > 2 > 0
// An error level of 3 will overwrite an error level of 1, 2, or 0.
// #define CHKDSK_EXIT_SUCCESS 0
// #define CHKDSK_EXIT_ERRS_FIXED 1
// #define CHKDSK_EXIT_MINOR_ERRS 2 // whether or not "/f"
// #define CHKDSK_EXIT_CLEANUP_WORK 2 // whether or not "/f"
// #define CHKDSK_EXIT_COULD_NOT_CHK 3
// #define CHKDSK_EXIT_ERRS_NOT_FIXED 3
// #define CHKDSK_EXIT_COULD_NOT_FIX 3
if ( status >= 3 ) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"FixCorruption: chkdsk.exe returned status of %1!u! - Could not fix errors.\n", status );
status = ERROR_DISK_CORRUPT; } else {
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"FixCorruption: chkdsk.exe returned status of %1!u! - No errors or minor errors fixed.\n", status );
status = ERROR_SUCCESS; } }
CloseHandle( processInfo.hProcess );
FnExit:
if ( sysDir ) { LocalFree( sysDir ); }
if ( checkDiskInfo ) { LocalFree( checkDiskInfo ); }
if ( chkdskLogFileName ) { LocalFree( chkdskLogFileName ); }
if ( INVALID_HANDLE_VALUE != chkdskLogFile ) { CloseHandle( chkdskLogFile ); }
SetErrorMode( previousMode );
return status;
} // DisksFixCorruption
DWORD DiskspGetQuorumPath( OUT LPWSTR* lpQuorumLogPath ) /*++
Routine Description:
Reads QuorumPath value from the registry.
Arguments:
lpQuorumLogPath - receives the poiner to a buffer containing QuorumLogPath the buffer needs to be deallocated later via LocalFree
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/ { DWORD Status; LPWSTR QuorumLogPath = NULL; DWORD QuorumLogSize = 0; DWORD Type; HKEY QuorumKey;
Status = RegOpenKey( HKEY_LOCAL_MACHINE, DISKS_REG_CLUSTER_QUORUM, &QuorumKey ); if ( Status == ERROR_SUCCESS ) { Status = RegQueryValueExW(QuorumKey, DISKS_REG_QUORUM_PATH, 0, &Type, NULL, &QuorumLogSize ); if ( Status != ERROR_SUCCESS ) { RegCloseKey(QuorumKey); return( Status ); }
if ( (Type != REG_SZ) || (QuorumLogSize > (MAX_PATH - 2)) ) { RegCloseKey(QuorumKey); return(ERROR_INVALID_DATA); } if ( (Status == ERROR_SUCCESS) || (Status == ERROR_MORE_DATA) ) { QuorumLogPath = LocalAlloc( LMEM_FIXED, (QuorumLogSize + 1) * sizeof(WCHAR) ); if ( QuorumLogPath == NULL ) { RegCloseKey( QuorumKey ); return(ERROR_NOT_ENOUGH_MEMORY); // Mostly catastrophic
}
Status = RegQueryValueExW(QuorumKey, DISKS_REG_QUORUM_PATH, 0, &Type, (LPBYTE)QuorumLogPath, &QuorumLogSize ); if (Status == ERROR_SUCCESS) { *lpQuorumLogPath = QuorumLogPath; } else { LocalFree(QuorumLogPath); *lpQuorumLogPath = 0; } } RegCloseKey( QuorumKey ); } return Status; }
DWORD DiskspSetQuorumPath( IN LPWSTR QuorumLogPath ) /*++
Routine Description:
Reads QuorumPath value from the registry.
Arguments:
lpQuorumLogPath - receives the poiner to a buffer containing QuorumLogPath the buffer needs to be deallocated later via LocalFree
ResourceEntry - receives a drive letter of the quorum
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/ { DWORD status; HKEY QuorumKey; HKEY ClusterKey;
HCLUSTER hCluster;
hCluster = OpenCluster(NULL); if (!hCluster) { status = GetLastError(); return status; }
ClusterKey = GetClusterKey(hCluster, KEY_READ | KEY_WRITE); if (!ClusterKey) { status = GetLastError(); CloseCluster(hCluster); return status; }
status = ClusterRegOpenKey( ClusterKey, CLUSREG_KEYNAME_QUORUM, KEY_READ | KEY_WRITE, &QuorumKey ); if (status != ERROR_SUCCESS) { ClusterRegCloseKey(ClusterKey); CloseCluster(hCluster); return status; }
status = ResUtilSetSzValue( QuorumKey, CLUSREG_NAME_QUORUM_PATH, QuorumLogPath, 0);
ClusterRegCloseKey(QuorumKey); ClusterRegCloseKey(ClusterKey); CloseCluster(hCluster); return status; }
DWORD DisksDriveIsAlive( IN PDISK_RESOURCE ResourceEntry, IN BOOL Online )
/*++
Routine Description:
Checks out a drive partition to see if the filesystem has mounted it and it's working. We will also run CHKDSK if the partition/certain files are Corrupt and the Online flag is TRUE.
Arguments:
ResourceEntry - Supplies a pointer to the resource entry for this disk
Online - TRUE if the disk was just brought online.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{ PMOUNTIE_PARTITION entry;
DWORD Status = ERROR_SUCCESS; DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo ); DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive; DWORD i;
LPWSTR QuorumLogPath = NULL; BOOL QuorumResource = FALSE;
WCHAR szDiskPartName[MAX_PATH]; WCHAR szGlobalDiskPartName[MAX_PATH]; WCHAR szVolumeName[MAX_PATH]; WCHAR szQuorumVolumeName[MAX_PATH]; WCHAR szQuorumDriveLetter[16];
szQuorumVolumeName[0] = L'\0';
//
// Find the quorum path... this is a hack!
//
if ( Online ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DriveIsAlive called for Online check\n" );
{ DWORD QuorumSignature; Status = GetQuorumSignature( &QuorumSignature ); if (Status == ERROR_SUCCESS) { QuorumResource = (QuorumSignature == ResourceEntry->DiskInfo.Params.Signature);
//
// Only get the quorum path if this is quorum disk.
//
Status = DiskspGetQuorumPath( &QuorumLogPath ); if (Status != ERROR_SUCCESS) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DiskspGetQuorumPath returned %1!u!\n", Status );
} else {
//
// For now, quorum path will have a drive letter. Get the corresponding volume name.
//
(VOID) StringCchPrintf( szQuorumDriveLetter, RTL_NUMBER_OF( szQuorumDriveLetter ), TEXT("%wc:\\"), QuorumLogPath[0] );
if ( !GetVolumeNameForVolumeMountPointW( szQuorumDriveLetter, szQuorumVolumeName, RTL_NUMBER_OF(szQuorumVolumeName) )) {
Status = GetLastError();
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DriveIsAlive: GetVolumeNameForVolumeMountPoint (quorum) returned %1!u!\n", Status );
}
}
} else { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"GetQuorumSignature returned %1!u!\n", Status ); } }
}
#if 0
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DriveIsAlive is now checking each partition\n" ); #endif
//
// Now check out each interesting partition. Since only "valid" partitions are
// saved in the MountieInfo structure, we will only look at those (ignoring those
// partitions that are not NTFS).
//
for ( i = 0; i < nPartitions; ++i ) {
entry = MountiePartition( &ResourceEntry->MountieInfo, i );
if ( !entry ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"DriveIsAlive: No partition entry for partition %1!u! \n", i );
//
// Something bad happened to our data structures. We have to indicate that the
// drive is not alive.
//
Status = ERROR_INVALID_DATA;
goto FnExit; }
//
// Create device name of form \Device\HarddiskX\PartitionY (no trailing backslash).
//
(VOID) StringCchPrintf( szDiskPartName, RTL_NUMBER_OF( szDiskPartName ), DEVICE_HARDDISK_PARTITION_FMT, physicalDrive, entry->PartitionNumber ); //
// Given the DiskPartName, get the VolGuid name. This name must have a trailing
// backslash to work correctly.
//
(VOID) StringCchPrintf( szGlobalDiskPartName, RTL_NUMBER_OF( szGlobalDiskPartName ), GLOBALROOT_HARDDISK_PARTITION_FMT, physicalDrive, entry->PartitionNumber );
szVolumeName[0] = L'\0';
if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName, szVolumeName, RTL_NUMBER_OF(szVolumeName) )) {
Status = GetLastError();
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"DriveIsAlive: GetVolumeNameForVolumeMountPoint for %1!ws! returned %2!u!\n", szGlobalDiskPartName, Status );
//
// If disk is not corrupt, exit. If disk is corrupt, fall through so chkdsk runs.
//
if ( ERROR_DISK_CORRUPT != Status && ERROR_FILE_CORRUPT != Status ) {
//
// Something bad happened. We have to stop checking this disk. Return the
// error status we received.
//
goto FnExit; }
}
//
// Simple algorithm used here is to do a FindFirstFile on X:\* and see
// if it works. Then we open each file for read access. This is the
// cluster directory, and all files in it are subject to our opening.
//
Status = DiskspCheckPath( szVolumeName, ResourceEntry, FALSE, Online );
//
// [HACKHACK] Ignore error 21 during periodic IsAlive/LooksAlive
//
if ( !Online && (Status == ERROR_NOT_READY) ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"DiskpCheckPath for %1!ws!: returned status = %2!u! (chkdsk.exe running?)\n", szVolumeName, Status ); Status = ERROR_SUCCESS; }
// if we haven't chkdsk'd yet, keep looking.
if ( Status != ERROR_SUCCESS ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"DiskpCheckPath for %1!ws!: returned status = %2!u!\n", szVolumeName, Status ); }
if ( (Status == ERROR_SUCCESS) && Online && QuorumLogPath && QuorumResource && ( wcslen( szVolumeName ) == wcslen( szQuorumVolumeName ) ) && ( !wcsncmp( szVolumeName, szQuorumVolumeName, wcslen( szQuorumVolumeName ) ))) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DriveIsAlive checking quorum drive to insure cluster directory accessible. \n" );
//
// Everything looks fine... if this is the quorum device, then
// we should check the quorum log path if given
//
Status = DiskspCheckPath( QuorumLogPath, ResourceEntry, TRUE, Online ); }
if ( (Status == ERROR_SUCCESS) && Online ) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DriveIsAlive checking that file system is not corrupt. If so, chkdsk may run. \n" );
//
// Check if the volume dirty bit is on
//
Status = DisksCheckCorruption( szDiskPartName, szVolumeName, ResourceEntry );
#if CLUSRES_FORCE_CHKDSK // For debugging...
if ( Online ) { Status = ERROR_DISK_CORRUPT; } #endif
//
// If we're requested to shutdown, then do that immediately
if ( ResourceEntry->OnlineThread.Terminate ) { Status = ERROR_SHUTDOWN_CLUSTER; goto FnExit; } }
if ( (Status != ERROR_SUCCESS) && Online) { if ( ResourceEntry->DiskInfo.Params.ConditionalMount ) {
Status = DisksFixCorruption( szVolumeName, ResourceEntry, ERROR_DISK_CORRUPT );
//
// Since ConditionalMount is set, if we couldn't fix the corruption
// on the disk, we don't want to continue checking the other
// partitions - we want to return an error. So we fall through
// and check the status. If status wasn't successful, we break out
// of the loop to return the error.
//
} else {
//
// Disk is corrupt, but we didn't run chkdsk. Return error
// to caller.
//
goto FnExit; } } if ( Status != ERROR_SUCCESS ) { goto FnExit; }
}
//
// Now check that the drive letters are accessible. The previous code
// ran chkdsk on volumes as necessary, so we just need to make sure
// drive letters are accessible.
//
Status = WaitForDriveLetters( DisksGetLettersForSignature( ResourceEntry ), ResourceEntry, 0 ); // Don't wait for the drive letter.
if ( ERROR_SUCCESS != Status ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"DriveIsAlive failed checking drive letters \n" ); }
FnExit:
if ( QuorumLogPath ) { LocalFree( QuorumLogPath ); }
return(Status);
} // DisksDriveIsAlive
DWORD DisksMountDrives( IN PDISK_INFO DiskInfo, IN PDISK_RESOURCE ResourceEntry, IN DWORD Signature ) /*++
Routine Description:
For each drive letter on the supplied disk, this mounts the filesystem and checks it out.
Arguments:
DiskInfo - Supplies the disk information
ResourceEntry - Supplies a pointer to the disk resource
Signature - the signature for the disk.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{ DWORD Status; WCHAR wDeviceName[4]; WCHAR wShareName[4]; DWORD letterMask; UCHAR index; UCHAR driveLetter; SHARE_INFO_2 shareInfo;
DWORD autoShareServer;
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksMountDrives: calling IsAlive function.\n" );
//
// Call the IsAlive to see if the filesystem checks out ok.
//
Status = DisksDriveIsAlive( ResourceEntry, TRUE);
if ( (Status != ERROR_SUCCESS) || (ResourceEntry->OnlineThread.Terminate) ) { return(Status); }
//
// Now create the drive$ share name for each drive letter;
//
letterMask = DisksGetLettersForSignature( ResourceEntry);
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksMountDrives: letter mask is %1!08x!.\n", letterMask);
//
// Check if admin shares should be created.
// Assume we have to create shares, so default value is "1".
//
// If the value entry exists and the value is zero, admin shares should not be created.
// If the value entry does not exist, or the value is non-zero, admin shares should be created.
//
autoShareServer = 1; Status = GetRegDwordValue( TEXT("SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters"), TEXT("AutoShareServer"), &autoShareServer );
//
// If value is now zero, we don't create admin shares.
//
if ( !autoShareServer ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksMountDrives: Not creating admin shares based on LanmanServer AutoShareServer setting \n" ); return ERROR_SUCCESS; }
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksMountDrives: creating admin share names.\n" );
index = 0; while ( letterMask ) { while ( !(letterMask & 1) ) { letterMask = letterMask >> 1; index++; } driveLetter = 'A' + index; letterMask = letterMask >> 1; index++; if ( isalpha(driveLetter) ) { (VOID) StringCchPrintf( wDeviceName, RTL_NUMBER_OF(wDeviceName), TEXT("%c:\\"), driveLetter ); (VOID) StringCchPrintf( wShareName, RTL_NUMBER_OF(wShareName), TEXT("%c$"), driveLetter ); shareInfo.shi2_netname = wShareName; shareInfo.shi2_type = STYPE_DISKTREE; shareInfo.shi2_remark = NULL; shareInfo.shi2_permissions = 0; shareInfo.shi2_max_uses = (DWORD)-1; shareInfo.shi2_current_uses = 0; shareInfo.shi2_path = wDeviceName; shareInfo.shi2_passwd = NULL;
Status = NetShareAdd( NULL, 2, (PBYTE)&shareInfo, NULL ); if ( Status != ERROR_SUCCESS && Status != NERR_DuplicateShare ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"DisksMountDrives: error creating default share %1!ws!. Error: %2!u!.\n", wShareName, Status); } } }
return(ERROR_SUCCESS);
} // DisksMountDrives
DWORD DisksDismountDrive( IN PDISK_RESOURCE ResourceEntry, IN DWORD Signature )
/*++
Routine Desccription:
Delete the default device share names for a given disk.
Arguments:
ResourceHandle - the resource handle for logging events
Signature - the disk's signature
Return Value:
WIN32 error code.
--*/
{ WCHAR shareName[8]; DWORD letterMask; UCHAR index; UCHAR driveLetter;
letterMask = DisksGetLettersForSignature( ResourceEntry);
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksDismountDrives: letter mask is %1!08x!.\n", letterMask);
index = 0; while ( letterMask ) { while ( !(letterMask & 1) ) { letterMask = letterMask >> 1; index++; } driveLetter = 'A' + index; letterMask = letterMask >> 1; index++; if ( isalpha(driveLetter) ) { shareName[0] = (WCHAR)driveLetter; shareName[1] = (WCHAR)'$'; shareName[2] = (WCHAR)0;
NetShareDel( NULL, shareName, 0 );
} }
return (ERROR_SUCCESS);
} // DisksDismountDrive
LPWSTR GetRegParameter( IN HKEY RegKey, IN LPCWSTR ValueName )
/*++
Routine Description:
Queries a parameter out of the registry and allocates the necessary storage for it.
Arguments:
RegKey - Supplies the cluster key where the parameter is stored
ValueName - Supplies the name of the value.
Return Value:
A pointer to a buffer containing the parameter if successful.
NULL if unsuccessful.
--*/
{ LPWSTR Value; LPWSTR expValue; DWORD ValueLength; DWORD expLength; DWORD ValueType; DWORD Status;
ValueLength = 0; Status = RegQueryValueEx(RegKey, ValueName, NULL, &ValueType, NULL, &ValueLength); if ( (Status != ERROR_SUCCESS) && (Status != ERROR_MORE_DATA) ) { return(NULL); }
// Make the buffer slightly larger in case the string
// was not stored with proper null-termination.
if ( REG_SZ == ValueType || REG_MULTI_SZ == ValueType || REG_EXPAND_SZ == ValueType ) { ValueLength++; }
Value = LocalAlloc(LPTR, ValueLength ); if (Value == NULL) { return(NULL); } Status = RegQueryValueEx(RegKey, ValueName, NULL, &ValueType, (LPBYTE)Value, &ValueLength); if (Status != ERROR_SUCCESS) { LocalFree(Value); Value = NULL; goto FnExit; }
//
// Expand strings if required.
//
if ( REG_EXPAND_SZ == ValueType ) {
//
// Find length of expanded buffer.
//
expLength = 0; expLength = ExpandEnvironmentStrings( Value, NULL, 0 );
//
// If we can't get length of the required expanded buffer,
// don't return the unexpanded string.
//
if ( !expLength ) { LocalFree( Value ); Value = NULL; goto FnExit; }
ValueLength = expLength; expValue = LocalAlloc( LPTR, ValueLength * sizeof(WCHAR) );
if ( !expValue ) { LocalFree( Value ); Value = NULL; goto FnExit; }
expLength = ExpandEnvironmentStrings( Value, expValue, ValueLength );
//
// If string couldn't be expanded, free both buffers and
// return NULL. If string was expanded, free unexpanded
// buffer and return expanded buffer.
//
if ( !expLength || expLength > ValueLength ) { LocalFree( Value ); Value = NULL; LocalFree( expValue ); } else { LocalFree( Value ); Value = expValue; } }
FnExit:
return(Value);
} // GetRegParameter
DWORD DisksResourceControl( IN RESID Resource, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ DWORD status = ERROR_SUCCESS; PDISK_RESOURCE resourceEntry = (PDISK_RESOURCE)Resource; DWORD required;
*BytesReturned = 0;
//
// Make sure the Resource is okay.
//
if ( resourceEntry == NULL ) { DISKS_PRINT("ResourceControl, bad resource value \n"); return(ERROR_RESOURCE_NOT_FOUND); }
switch ( ControlCode ) {
case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; break;
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( DiskResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break;
case CLUSCTL_RESOURCE_GET_CLASS_INFO: *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { status = ERROR_MORE_DATA; } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE; ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED; } break;
case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO: status = GetDiskInfo( resourceEntry->DiskInfo.Params.Signature, &OutBuffer, OutBufferSize, BytesReturned );
// Add the endmark.
if ( OutBufferSize > *BytesReturned ) { OutBufferSize -= *BytesReturned; } else { OutBufferSize = 0; } *BytesReturned += sizeof(CLUSPROP_SYNTAX); if ( OutBufferSize >= sizeof(CLUSPROP_SYNTAX) ) { PCLUSPROP_SYNTAX ptrSyntax = OutBuffer; ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK; } break;
case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( DiskResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break;
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = DisksGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break;
case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = DisksValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break;
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = DisksSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize );
DiskspSsyncDiskInfo(L"ResourceControl", resourceEntry, 0);
break;
case CLUSCTL_RESOURCE_DELETE: if ( resourceEntry->DiskInfo.Params.Signature ) { (DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Delete disk resource %1!lx!\n", resourceEntry->DiskInfo.Params.Signature ); status = DoDetach( resourceEntry->DiskInfo.Params.Signature, resourceEntry->ResourceHandle ); } break;
case CLUSCTL_RESOURCE_GET_CHARACTERISTICS: *BytesReturned = sizeof(DWORD); if ( OutBufferSize < sizeof(DWORD) ) { status = ERROR_MORE_DATA; } else { LPDWORD ptrDword = OutBuffer; *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_DELETE_REQUIRES_ALL_NODES; } break;
case CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION:
status = ProcessDllExtension( resourceEntry, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned ); break;
case CLUSCTL_RESOURCE_REMOVE_DEPENDENCY: case CLUSCTL_RESOURCE_ADD_DEPENDENCY:
(DiskpLogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Add/Remove dependency: source signature %1!lx! target name (%2!ws!) \n", resourceEntry->DiskInfo.Params.Signature, InBuffer );
status = DisksProcessMPControlCode( resourceEntry, ControlCode );
break;
default: status = ERROR_INVALID_FUNCTION; break; }
return(status);
} // DisksResourceControl
DWORD DisksResourceTypeControl( IN LPCWSTR ResourceTypeName, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned )
/*++
Routine Description:
Process control requests for this resource type.
Arguments:
ResourceTypeName - the name of the resource type - not very useful!
ControlCode - the control request
InBuffer - pointer to the input buffer
InBufferSize - the size of the input buffer
OutBuffer - pointer to the output buffer
OutBufferSize - the size of the output buffer
BytesReturned - the number of bytes returned (or needed if larger than OutBufferSize and ERROR_MORE_DATA is returned
Return Value:
ERROR_SUCCESS if successful
A WIN32 error on failure
--*/
{ DWORD status = ERROR_SUCCESS; DWORD required;
*BytesReturned = 0;
switch ( ControlCode ) {
case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break;
case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( DiskResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break;
case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( DiskResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break;
case CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO: *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { status = ERROR_MORE_DATA; } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE; ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED; status = ERROR_SUCCESS; } break;
case CLUSCTL_RESOURCE_TYPE_STORAGE_GET_AVAILABLE_DISKS: status = ClusDiskGetAvailableDisks( OutBuffer, OutBufferSize, BytesReturned ); break;
case CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS: *BytesReturned = sizeof(DWORD); if ( OutBufferSize < sizeof(DWORD) ) { status = ERROR_MORE_DATA; } else { LPDWORD ptrDword = OutBuffer; *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_DELETE_REQUIRES_ALL_NODES; } break;
default: status = ERROR_INVALID_FUNCTION; break; }
return(status);
} // DisksResourceTypeControl
DWORD DisksGetPrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned )
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function for resources of type Physical Disk.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
OutBuffer - Returns the output data.
OutBufferSize - Supplies the size, in bytes, of the data pointed to by OutBuffer.
BytesReturned - The number of bytes returned in OutBuffer.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{ DWORD status; DWORD required;
status = ResUtilGetAllProperties( ResourceEntry->ResourceParametersKey, DiskResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; }
return(status);
} // DisksGetPrivateResProperties
DWORD DisksConvertDriveToSignature( IN OUT PDISK_PARAMS Params, IN RESOURCE_HANDLE ResourceHandle )
/*++
Routine Description:
Change the drive letter or VolGuid to the associated disk signature.
Arguments:
Params - Supplies the parameter block that contains the drive letter (or VolGuid) to convert to a signature.
VolGuid syntax is returned from mountvol.exe and in the form: \\?\Volume{e6de97f1-6f97-11d3-bb7f-806d6172696f}\
ResourceHandle -
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly or is invalid.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{ PWCHAR deviceName = NULL; DWORD deviceNameBytes; DWORD lenChar; DWORD status = ERROR_SUCCESS; LPWSTR drive = Params->Drive; LPWSTR volGuid = Params->VolGuid; HANDLE fileHandle; BOOL success; PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
//
// Build device string for CreateFile
//
if ( drive ) {
deviceNameBytes = (( wcslen( drive ) + wcslen( L"\\\\.\\" ) ) * sizeof(WCHAR) ) + sizeof( UNICODE_NULL );
deviceName = LocalAlloc( LPTR, deviceNameBytes );
if ( !deviceName ) { status = GetLastError(); goto FnExit; }
if ( FAILED( StringCchPrintf( deviceName, deviceNameBytes/sizeof(WCHAR), TEXT("\\\\.\\%ws"), drive ) ) ) { status = ERROR_INSUFFICIENT_BUFFER; goto FnExit; }
} else if ( volGuid ) {
deviceNameBytes = ( wcslen( volGuid ) * sizeof( WCHAR ) ) + sizeof( UNICODE_NULL );
deviceName = LocalAlloc( LPTR, deviceNameBytes );
if ( !deviceName ) { status = GetLastError(); goto FnExit; }
CopyMemory( deviceName, volGuid, deviceNameBytes );
//
// If user specified \\?\Volume{guid}\ with trailing backslash, we need to get
// rid of the backslash.
//
lenChar = wcslen( deviceName );
if ( lenChar > 1 && L'\\' == deviceName[lenChar-1] ) { deviceName[lenChar-1] = L'\0'; }
} else { status = ERROR_INVALID_PARAMETER; goto FnExit; }
fileHandle = CreateFileW( deviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if ( (fileHandle == INVALID_HANDLE_VALUE) || (fileHandle == NULL) ) { status = GetLastError(); (DiskpLogEvent)( ResourceHandle, LOG_ERROR, L"ConvertDriveToSignature, error opening device '%1!ws!'. Error: %2!u!\n", deviceName, status ); goto FnExit; }
//
// Get drive layout - in order to get disk signature.
//
success = ClRtlGetDriveLayoutTable( fileHandle, &driveLayout, NULL );
if ( success && driveLayout->Signature ) { Params->Signature = driveLayout->Signature;
if ( Params->Drive ) { LocalFree( Params->Drive ); Params->Drive = NULL; }
if ( Params->VolGuid ) { LocalFree( Params->VolGuid ); Params->VolGuid = NULL; }
} else { status = ERROR_FILE_NOT_FOUND; }
if ( driveLayout ) { LocalFree( driveLayout ); } CloseHandle( fileHandle );
FnExit:
if ( deviceName ) { LocalFree( deviceName ); }
return(status);
} // DisksConvertDriveToSignature
DWORD DisksValidatePrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PDISK_PARAMS Params )
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control function for resources of type Physical Disk.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer.
Params - Supplies the parameter block to fill in.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly or is invalid.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{ DWORD status; DWORD enableSanBoot; DWORD diskNumber; DWORD oldSerNumLen = 0; DWORD newSerNumLen = 0; DISK_PARAMS params; PDISK_PARAMS pParams; PDISK_PARAMS currentParams = &ResourceEntry->DiskInfo.Params; PSCSI_ADDRESS_ENTRY criticalDiskList = NULL;
SCSI_ADDRESS scsiAddress; CLUSPROP_SCSI_ADDRESS clusScsiAddress;
//
// Check if there is input data.
//
if ( (InBuffer == NULL) || (InBufferSize < sizeof(DWORD)) ) { return(ERROR_INVALID_DATA); }
//
// Duplicate the resource parameter block.
//
if ( Params == NULL ) { pParams = ¶ms; } else { pParams = Params; } ZeroMemory( pParams, sizeof(DISK_PARAMS) ); status = ResUtilDupParameterBlock( (LPBYTE) pParams, (LPBYTE) currentParams, DiskResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { return(status); }
//
// Parse and validate the properties. Accept the alternate name here.
//
status = ResUtilVerifyPropertyTable( DiskResourcePrivatePropertiesAlt, NULL, TRUE, // Allow unknowns
InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status != ERROR_SUCCESS ) { goto FnExit; }
//
// First make sure there are no bogus properties - i.e. we don't allow
// specifying both the signature and the drive in the same request.
// We also don't allow specifying the VolGuid and signature in the same
// request.
//
if ( (pParams->Drive || pParams->VolGuid) && pParams->Signature ) { status = ERROR_INVALID_PARAMETER; (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Can't specify Drive and Signature (or VolGuid and Signature) \n" ); goto FnExit; }
if ( pParams->Drive || pParams->VolGuid ) { //
// Convert from drive to signature.
//
status = DisksConvertDriveToSignature( pParams, ResourceEntry->ResourceHandle ); }
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Can't convert Drive (or VolGuid) to Signature \n" ); goto FnExit; }
//
// Verify the new serial number is valid and not overwriting an existing
// serial number.
//
if ( pParams->SerialNumber ) { newSerNumLen = wcslen( pParams->SerialNumber ); }
if ( currentParams->SerialNumber ) { oldSerNumLen = wcslen( currentParams->SerialNumber ); }
//
// If there was an old serial number, make sure the new serial number is
// the same.
//
if ( oldSerNumLen && ( oldSerNumLen != newSerNumLen || 0 != wcsncmp( currentParams->SerialNumber, pParams->SerialNumber, newSerNumLen ) ) ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: New serial number does not match existing serial number \n" ); status = ERROR_INVALID_PARAMETER; goto FnExit; }
//
// Check serial number and signature.
//
if ( 0 == oldSerNumLen && pParams->SerialNumber && 0 == currentParams->Signature ) {
//
// New serial number specified and no current signature.
//
if ( 0 == pParams->Signature ) {
//
// No new signature specified, use the new serial number to
// find the new signature.
//
status = GetSignatureFromSerialNumber( pParams->SerialNumber, &pParams->Signature );
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Unable to get signature for serial number \n" );
goto FnExit; }
} else {
//
// New signature and new serial number specified. Fail the request.
//
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Can't specify both signature and serial number \n" );
status = ERROR_INVALID_PARAMETER; goto FnExit; } }
//
// Validate the parameter values.
//
// Make sure the disk signature is not zero.
//
if ( 0 == pParams->Signature ) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Signature not set or zero specified \n" );
status = ERROR_INVALID_PARAMETER; goto FnExit; }
// At this point, we have a valid disk signature.
//
// If we have no serial number, get it. If we can't get the serial number,
// continue processing. Some disks don't return serial numbers.
//
if ( !pParams->SerialNumber || newSerNumLen <= 1 ) {
GetSerialNumber( pParams->Signature, &pParams->SerialNumber ); }
//
// Check if disk is on system bus. If we can't get the SCSI address,
// assume the disk is not on system bus.
//
ZeroMemory( &clusScsiAddress, sizeof(clusScsiAddress) ); status = GetScsiAddress( pParams->Signature, &clusScsiAddress.dw, &diskNumber );
if ( ERROR_SUCCESS != status ) {
// Reset return value to success.
status = ERROR_SUCCESS;
// Don't fail with error here - fall through...
} else {
//
// Make sure we don't have a dynamic disk.
//
if ( DiskIsDynamic( diskNumber ) ) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Disk signature %1!08x! is dynamic \n", pParams->Signature ); status = ERROR_INVALID_PARAMETER; goto FnExit; }
scsiAddress.Length = sizeof(SCSI_ADDRESS); scsiAddress.PortNumber = clusScsiAddress.PortNumber; scsiAddress.PathId = clusScsiAddress.PathId; scsiAddress.TargetId = clusScsiAddress.TargetId; scsiAddress.Lun = clusScsiAddress.Lun;
GetCriticalDisks( &criticalDiskList );
//
// Make sure the SCSI address is not system disk.
//
enableSanBoot = 0; GetRegDwordValue( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS, CLUSREG_VALUENAME_MANAGEDISKSONSYSTEMBUSES, &enableSanBoot );
if ( !enableSanBoot ) {
//
// Signature is valid if:
// - the signature is for a disk not on system bus
// - the signature is for a disk not on same bus as paging disk
//
if ( IsBusInList( &scsiAddress, criticalDiskList ) ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Disk signature %1!08x! is on critical bus \n", pParams->Signature ); status = ERROR_INVALID_PARAMETER; goto FnExit; }
} else {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksValidatePrivateResProperties: Enable SAN boot key set \n" );
// Allow disks on system bus to be added to cluster.
//
// Signature is valid if:
// - the signature is not for the system disk
// - the signature is not a pagefile disk
if ( IsDiskInList( &scsiAddress, criticalDiskList ) ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Disk signature %1!08x! is critical disk \n", pParams->Signature ); status = ERROR_INVALID_PARAMETER; goto FnExit; }
}
}
//
// For now, we don't allow setting mount point volume GUIDs this way.
// This is a multi SZ string, so use memcmp to skip past each string's
// terminating NULL.
//
if ( ( currentParams->MPVolGuidsSize != pParams->MPVolGuidsSize ) || ( 0 != memcmp( currentParams->MPVolGuids, pParams->MPVolGuids, currentParams->MPVolGuidsSize ) ) ) {
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_WARNING, L"DisksValidatePrivateResProperties: Can't set MP Volume GUIDs \n" );
status = ERROR_INVALID_PARAMETER; goto FnExit; }
FnExit:
//
// Cleanup our parameter block.
//
if ( pParams == ¶ms ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->DiskInfo.Params, DiskResourcePrivateProperties ); }
if ( criticalDiskList ) { CleanupScsiAddressList( criticalDiskList ); }
return(status);
} // DisksValidatePrivateResProperties
DWORD DisksSetPrivateResProperties( IN OUT PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize )
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function for resources of type Physical Disk.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{ DWORD status; DISK_PARAMS params;
ZeroMemory( ¶ms, sizeof(DISK_PARAMS) );
//
// Parse and validate the properties.
//
status = DisksValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->DiskInfo.Params, DiskResourcePrivateProperties ); return(status); }
//
// We cannot allow changing the Signature 'on the fly'...
//
if ( (ResourceEntry->DiskInfo.Params.Signature != 0) && (params.Signature != ResourceEntry->DiskInfo.Params.Signature) ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->DiskInfo.Params, DiskResourcePrivateProperties ); (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"SetPrivateResProperties doesn't allow changing signature, old %1!lx!, new %2!lx!\n", ResourceEntry->DiskInfo.Params.Signature, params.Signature ); return(ERROR_INVALID_STATE); }
//
// Save the parameter values.
//
// NB: Unknown, or non-property table values are dealt with farther below. That is why InBuffer and
// InBufferSize are not used in this call. Only the propertys in the parameter block are handled here.
//
status = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey, DiskResourcePrivateProperties, NULL, (LPBYTE) ¶ms, NULL, 0, (LPBYTE) &ResourceEntry->DiskInfo.Params );
ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->DiskInfo.Params, DiskResourcePrivateProperties );
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"SetPrivateResProperties: Error %1!d! saving properties\n", status ); return(status); }
//
// Save any unknown properties.
//
status = ResUtilSetUnknownProperties( ResourceEntry->ResourceParametersKey, DiskResourcePrivatePropertiesAlt, InBuffer, InBufferSize );
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"SetPrivateResProperties: Error %1!d! saving unknown properties\n", status ); }
//
// Try to attach to this device if we have a signature.
//
if ( ResourceEntry->DiskInfo.Params.Signature ) { #if 0
DiskspVerifyState( ResourceEntry ); #endif
DoAttach( ResourceEntry->DiskInfo.Params.Signature, ResourceEntry->ResourceHandle, TRUE ); // Dismount, then offline
// ignore status return
}
//
// If the resource is online, return a non-success status.
//
if (status == ERROR_SUCCESS) { if ( ResourceEntry->Valid ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } }
return status;
} // DisksSetPrivateResProperties
#define ASRP_GET_LOCAL_DISK_INFO "AsrpGetLocalDiskInfo"
#define ASRP_GET_LOCAL_VOLUME_INFO "AsrpGetLocalVolumeInfo"
DWORD ProcessDllExtension( IN PDISK_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++
Routine Description:
Processes the CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION control function for resources of type Physical Disk. This routine calls a specific DLL and DLL entry point, as required by ASR. This routine will call into that entry point with the disk device name represented by the Signature parameter.
Arguments:
ResourceEntry -
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer.
OutBuffer - Supplies a pointer to a buffer containing output data.
OutBufferSize - Supplies the size, in bytes, of the data pointed to by OutBuffer.
BytesReturned - the number of bytes returned (or needed if larger than OutBufferSize and ERROR_MORE_DATA is returned
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The input data is formatted incorrectly.
ERROR_REVISION_MISMATCH - The input buffer did not have the correct revision information.
ERROR_MORE_DATA - The output buffer is not large enough to hold all the requested data.
Win32 error code - The function failed.
--*/ { lpPassThruFunc passThruFunc = NULL;
PCHAR contextStr; PCHAR deviceName = NULL; PCHAR dllProcName;
PDISK_DLL_EXTENSION_INFO passThru = InBuffer;
HINSTANCE dllModule = NULL; DWORD scsiAddress; DWORD diskNumber; DWORD dwStatus; DWORD signature = ResourceEntry->DiskInfo.Params.Signature; const DWORD deviceNameChars = MAX_PATH;
if ( !InBuffer || !OutBuffer || !OutBufferSize ) { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; }
if ( InBufferSize < sizeof(DISK_DLL_EXTENSION_INFO) ) { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; }
try {
if ( passThru->MajorVersion != NT5_MAJOR_VERSION || passThru->MinorVersion != 0 ) {
dwStatus = ERROR_REVISION_MISMATCH; leave; }
//
// Get the DLL entry point name from the Input buffer.
//
dllProcName = passThru->DllProcName;
if ( CSTR_EQUAL != CompareStringA( LOCALE_INVARIANT, NORM_IGNORECASE, dllProcName, strlen(dllProcName), ASRP_GET_LOCAL_DISK_INFO, strlen(ASRP_GET_LOCAL_DISK_INFO) ) && CSTR_EQUAL != CompareStringA( LOCALE_INVARIANT, NORM_IGNORECASE, dllProcName, strlen(dllProcName), ASRP_GET_LOCAL_VOLUME_INFO, strlen(ASRP_GET_LOCAL_VOLUME_INFO) ) ) {
dwStatus = ERROR_INVALID_PARAMETER; leave; } //
// No check on the ContextStr.
//
contextStr = passThru->ContextStr;
//
// Get the SCSI address to build the device name.
//
dwStatus = GetScsiAddress( signature, &scsiAddress, &diskNumber );
if ( NO_ERROR != dwStatus ) { leave; }
deviceName = LocalAlloc( LPTR, deviceNameChars );
if ( !deviceName ) { dwStatus = GetLastError(); leave; }
//
// Called routine is expecting device name to be an ANSI string.
//
(VOID) StringCchPrintfA( deviceName, deviceNameChars, "\\\\.\\PhysicalDrive%d", diskNumber );
//
// Assume the DLL has not yet been loaded into the address space.
//
// Security team says not to use the full path name for DLLs shipped with
// the OS in the system32 directory. This DLL is shipped with the OS.
// This call expects a wide string.
//
dllModule = LoadLibrary( L"syssetup.dll" );
if ( NULL == dllModule ) { dwStatus = GetLastError(); leave; }
//
// The function name MUST be as defined (i.e. with exactly the same type
// and number of parameters) or we will have stack problems.
// This call expects an ANSI string.
//
passThruFunc = (lpPassThruFunc)GetProcAddress( dllModule, dllProcName );
if ( NULL == passThruFunc ) {
//
// The specified function is not available in the DLL, exit now.
//
dwStatus = GetLastError(); leave; }
//
// Call into the specified DLL.
//
dwStatus = (passThruFunc)( deviceName, contextStr, OutBuffer, OutBufferSize, BytesReturned );
} except (EXCEPTION_EXECUTE_HANDLER) {
dwStatus = GetExceptionCode();
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ProcessDllExtension: Exception occurred %1!u! \n", dwStatus ); }
FnExit:
if ( dllModule ) { FreeLibrary( dllModule ); }
if ( deviceName ) { LocalFree( deviceName ); }
return dwStatus;
} // ProcessDllExtension
DWORD DisksOpenChkdskLogFile( IN PDISK_RESOURCE ResourceEntry, IN OUT PHANDLE ChkdskLogFile, IN OUT LPWSTR *ChkdskLogFileName ) /*++
Routine Description:
Creates a file to log chkdsk output and returns the handle to the caller. The file will not be deleted on close.
Arguments:
ResourceEntry - Supplies a pointer to the resource structure
ChkdskLogFile - Returned handled of newly opened log file.
ChkdskLogFileName - String pointer to the name of the newly opened log file. Caller is responsible for freeing storage.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{ DWORD status = NO_ERROR; HANDLE retHandle = INVALID_HANDLE_VALUE;
PWCHAR last; PWCHAR current;
LPWSTR clusterDir = NULL; LPWSTR fileName = NULL; LPWSTR finalFileName = NULL;
DWORD clusterDirLength; // This value can change.
DWORD fileNameLength = MAX_PATH; DWORD finalFileNameLength;
SECURITY_ATTRIBUTES sa;
if ( !ChkdskLogFile || !ChkdskLogFileName ) { return ERROR_INVALID_PARAMETER; }
*ChkdskLogFile = INVALID_HANDLE_VALUE; *ChkdskLogFileName = NULL;
_try {
//
// Get the environment variable "ClusterLog". Embedded in this string
// is the cluster directory.
//
clusterDir = LocalAlloc( LMEM_FIXED, MAX_PATH * sizeof(WCHAR) ); if ( !clusterDir ) { status = GetLastError(); _leave; }
clusterDirLength = GetEnvironmentVariableW( CLUSTERLOG_ENV_VARIABLE, clusterDir, MAX_PATH );
if ( !clusterDirLength ) { status = GetLastError(); _leave; }
if ( clusterDirLength > MAX_PATH ) {
LocalFree( clusterDir ); clusterDir = LocalAlloc( LMEM_FIXED, clusterDirLength * sizeof( WCHAR ) ); if ( NULL == clusterDir ) { status = GetLastError(); _leave; }
clusterDirLength = GetEnvironmentVariableW( CLUSTERLOG_ENV_VARIABLE, clusterDir, clusterDirLength );
if ( !clusterDirLength ) { status = GetLastError(); LocalFree( clusterDir ); clusterDir = NULL; _leave; } }
//
// We have the log file path and name. Find the last backslash and strip off the
// log file name. This will be used as our temporary file path.
//
last = NULL; current = (PWCHAR) clusterDir;
while ( *current != L'\0' ) {
if ( L'\\' == *current ) { last = current; } current++; }
if ( !last ) { status = ERROR_BAD_FORMAT; _leave; }
//
// Change the last backslash to the end of string mark.
//
*last = L'\0';
//
// Now create a file name based on the disk signature.
//
fileName = LocalAlloc( LPTR, fileNameLength * sizeof(WCHAR) ); if ( !fileName ) { status = GetLastError(); _leave; }
if ( FAILED( StringCchPrintf( fileName, fileNameLength, TEXT("\\ChkDsk_Disk%d_Sig%08X.log"), ResourceEntry->DiskInfo.PhysicalDrive, ResourceEntry->DiskInfo.Params.Signature ) ) ) { status = ERROR_INSUFFICIENT_BUFFER; _leave; }
//
// Put it all together for the final name.
//
finalFileNameLength = fileNameLength + clusterDirLength + MAX_PATH;
finalFileName = LocalAlloc( LPTR, finalFileNameLength * sizeof(WCHAR)); if ( !finalFileName ) { status = GetLastError(); _leave; }
(VOID) StringCchCopy( finalFileName, finalFileNameLength, clusterDir ); (VOID) StringCchCat( finalFileName, finalFileNameLength, fileName );
//
// Now open up the file name to log the chkdsk info.
//
ZeroMemory( &sa, sizeof(sa) ); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE;
retHandle = CreateFileW( finalFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, // Create a new file or overwrite existing file
FILE_ATTRIBUTE_NORMAL, NULL );
if ( INVALID_HANDLE_VALUE == retHandle ) { status = GetLastError(); (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"DisksOpenChkdskLogFile: CreateFile returned status of %1!u! \n", status ); _leave; }
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"DisksOpenChkdskLogFile: chkdsk.exe output is in file: %1!s! \n", finalFileName );
} _finally {
*ChkdskLogFile = retHandle;
if ( clusterDir ) { LocalFree( clusterDir ); }
if ( fileName ) { LocalFree( fileName ); }
if ( finalFileName ) { *ChkdskLogFileName = finalFileName; } }
return status;
} // DisksOpenChkdskLogFile
DWORD GetRegDwordValue( IN LPWSTR RegKeyName, IN LPWSTR ValueName, OUT LPDWORD ValueBuffer ) { DWORD dwValue; DWORD dwValueType; DWORD dwDataBufferSize = sizeof( DWORD ); DWORD dwError;
HKEY hKey = NULL;
if ( !ValueBuffer ) { dwError = ERROR_INVALID_PARAMETER; goto FnExit; }
// Don't set the default value in case the caller has already done so.
// *ValueBuffer = 0;
//
// Open the specified registry key.
//
dwError = RegOpenKeyExW( HKEY_LOCAL_MACHINE, RegKeyName, 0, KEY_READ, &hKey );
if ( ERROR_SUCCESS != dwError ) { goto FnExit; }
//
// Get the DWORD value.
//
dwError = RegQueryValueExW( hKey, ValueName, NULL, &dwValueType, (LPBYTE) &dwValue, &dwDataBufferSize );
if ( ERROR_SUCCESS == dwError ) {
// Insure that a DWORD value was returned. If not, return an error.
if ( REG_DWORD == dwValueType && sizeof(DWORD) == dwDataBufferSize ) {
*ValueBuffer = dwValue;
} else {
dwError = ERROR_BAD_FORMAT;
} }
FnExit:
if ( hKey ) { RegCloseKey( hKey ); }
return dwError;
} // GetRegDwordValue
BOOL DiskIsDynamic( IN DWORD DiskNumber ) /*
Routine Description:
Determine if the disk has any dynamic partitions.
Arguments:
DiskNumber - physical disk number
Return Value:
TRUE - if the disk has dynamic partitions or any other type of error occurred (can't open disk, can't read drive layout)
FALSE - disk has no dynamic partitions
*/ { PDRIVE_LAYOUT_INFORMATION driveLayout = NULL; PPARTITION_INFORMATION partitionInfo; PWCHAR deviceName = NULL;
HANDLE deviceHandle = INVALID_HANDLE_VALUE;
DWORD deviceNameChars = MAX_PATH; DWORD idx;
BOOL success; BOOL retVal = FALSE;
deviceName = LocalAlloc( LPTR, deviceNameChars * sizeof(WCHAR) );
if ( !deviceName ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"DiskIsDynamic: allocating buffer for disk %1!u! failed %2!u! \n", DiskNumber, GetLastError() ); retVal = TRUE; goto FnExit; }
(VOID) StringCchPrintf( deviceName, deviceNameChars, TEXT("\\\\.\\\\PhysicalDrive%d"), DiskNumber );
deviceHandle = CreateFile( deviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( INVALID_HANDLE_VALUE == deviceHandle ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"DiskIsDynamic: opening disk %1!u! failed %2!u! \n", DiskNumber, GetLastError() ); retVal = TRUE; goto FnExit; }
UpdateCachedDriveLayout( deviceHandle ); success = ClRtlGetDriveLayoutTable( deviceHandle, &driveLayout, NULL );
if ( !success || !driveLayout || 0 == driveLayout->Signature ) { retVal = TRUE; goto FnExit; }
//
// Walk through partitions and make sure none are dynamic. If any
// partition is dynamic, ignore the disk.
//
for ( idx = 0; idx < driveLayout->PartitionCount; idx++ ) { partitionInfo = &driveLayout->PartitionEntry[idx];
if ( 0 == partitionInfo->PartitionNumber ) { continue; }
//
// If any partition on the disk is dynamic, skip the disk.
//
if ( PARTITION_LDM == partitionInfo->PartitionType ) {
(DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"DiskIsDynamic: skipping dynamic disk with signature %1!08x! \n", driveLayout->Signature );
retVal = TRUE;
goto FnExit; } }
FnExit:
if ( deviceName ) { LocalFree( deviceName ); }
if ( driveLayout ) { LocalFree( driveLayout); }
if ( INVALID_HANDLE_VALUE != deviceHandle ) { CloseHandle( deviceHandle ); }
return retVal;
} // DiskIsDynamic
//***********************************************************
//
// Define Function Table
//
//***********************************************************
CLRES_V1_FUNCTION_TABLE( DisksFunctionTable, CLRES_VERSION_V1_00, Disks, DisksArbitrate, DisksRelease, DisksResourceControl, DisksResourceTypeControl );
|