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.
3804 lines
106 KiB
3804 lines
106 KiB
/*++
|
|
|
|
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 );
|