windows-nt/Source/XPSP1/NT/base/cluster/resdll/disks/newdisks.c
2020-09-26 16:20:57 +08:00

2220 lines
66 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
newdisks.c
Abstract:
Some of the functions that used to be
in disks.c reside now here
Author:
Gor Nishanov (GorN) 31-July-1998
Revision History:
--*/
#include "disksp.h"
#include "partmgrp.h"
#include "arbitrat.h"
#include "newdisks.h"
#include "newmount.h"
#include "mountmgr.h"
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
PSTR DiskName = "\\Device\\Harddisk%u";
PWCHAR DEVICE_HARDDISK_PARTITION_FMT = L"\\Device\\Harddisk%u\\Partition%u";
//
// This string is needed to convert the \Device\HarddiskX\PartitionY name to
// the Vol{GUID} name. Note that the trailing backslash is required!
//
PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT = L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\";
DWORD
WaitForDriveLetters(
IN DWORD DriveLetters,
IN PDISK_RESOURCE ResourceEntry,
IN DWORD timeOutInSeconds
);
DWORD
WaitForVolumes(
IN PDISK_RESOURCE ResourceEntry,
IN DWORD timeOutInSeconds
);
DWORD
DiskspCheckPathLite(
IN LPWSTR VolumeName,
IN PDISK_RESOURCE ResourceEntry
);
DWORD
DiskCleanup(
PDISK_RESOURCE ResourceEntry
)
/*++
Routine Description:
Stops the reservations, dismount drive and frees DiskCpInfo
Arguments:
ResourceEntry - the disk info structure for the disk.
Return Value:
ERROR_SUCCESS if successful.
A Win32 error code on failure.
--*/
{
DWORD status = ERROR_SUCCESS;
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DiskCleanup started.\n");
StopPersistentReservations(ResourceEntry);
//
// If the remaining data in the resource entry is not valid, then leave
// now.
//
if ( !ResourceEntry->Valid ) {
goto FnExit;
}
//
// Delete the DiskCpInfo.
//
if ( ResourceEntry->DiskCpInfo ) {
LocalFree(ResourceEntry->DiskCpInfo);
ResourceEntry->DiskCpInfo = NULL;
ResourceEntry->DiskCpSize = 0;
}
ResourceEntry->Attached = FALSE;
ResourceEntry->Valid = FALSE;
//
// Remove the Dos Drive Letters, this is better done here rather than
// in ClusDisk.
//
DisksDismountDrive( ResourceEntry,
ResourceEntry->DiskInfo.Params.Signature );
FnExit:
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DiskCleanup returning final error %1!u! \n",
status );
return(status);
} // DiskCleanup
static
DWORD
DisksSetDiskInfoThread(
LPVOID lpThreadParameter
)
/*++
Routine Description:
Registry update thread.
Arguments:
lpThreadParameter - stores ResourceEntry.
Return Value:
None
--*/
{
DWORD Status;
BOOL TryAgain = TRUE;
PDISK_RESOURCE ResourceEntry = lpThreadParameter;
DWORD i;
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Checkpoint thread started.\n");
//
// Will die in 10 minutes if unsuccessful
//
for(i = 0; i < 300; ++i) {
//
// Wait for either the terminate event or the timeout
//
Status = WaitForSingleObject( DisksTerminateEvent, 2000 );
if (Status == WAIT_TIMEOUT ) {
Status = MountieUpdate(&ResourceEntry->MountieInfo, ResourceEntry);
if ( Status == ERROR_SUCCESS ) {
// We're done
break;
} else if ( Status != ERROR_SHARING_PAUSED ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Watcher Failed to checkpoint disk info, status = %1!u!.\n", Status );
break;
}
} else {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"CheckpointThread: WaitForSingleObject returned status = %1!u!.\n", Status );
break;
}
}
InterlockedExchange(
&ResourceEntry->MountieInfo.UpdateThreadIsActive, 0);
return(ERROR_SUCCESS);
} // DisksSetDiskInfoThread
DWORD
DisksOfflineOrTerminate(
IN PDISK_RESOURCE resourceEntry,
IN BOOL Terminate
)
/*++
Routine Description:
Used by DisksOffline and DisksTerminate.
Routine performs the following steps:
1. ClusWorkerTerminate (Terminate only)
2. Then for all of the partitions on the drive...
a. Flush the file buffers. (Offline only)
b. Lock the volume to purge all in memory contents of the volume. (Offline only)
c. Dismount the volume
3. Removes default network shares (C$, F$, etc)
Arguments:
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
Terminate - Set it to TRUE to cause Termination Behavior
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
PWCHAR WideName = (Terminate)?L"Terminate":L"Offline";
PHANDLE handleArray = NULL;
BOOL Offline = !Terminate;
DWORD status;
DWORD length;
DWORD idx;
DWORD PartitionCount;
DWORD handleArraySize;
HANDLE fileHandle;
DWORD bytesReturned;
WCHAR szDiskPartName[MAX_PATH];
NTSTATUS ntStatus;
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, ResourceEntry @ %2!x! Valid %3!x! \n",
WideName,
resourceEntry,
resourceEntry->Valid );
if (Terminate) {
ClusWorkerTerminate(&resourceEntry->OnlineThread);
}
StopWatchingDisk(resourceEntry);
//
// If the disk info is not valid, then don't use it!
//
if ( !resourceEntry->Valid ) {
DiskCleanup( resourceEntry );
return(ERROR_SUCCESS);
}
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Processing disk number %2!u!.\n",
WideName,
resourceEntry->DiskInfo.PhysicalDrive );
#if 0
if( Offline ) {
//
// Checkpoint our registry state
//
}
#endif
PartitionCount = MountiePartitionCount(&resourceEntry->MountieInfo);
//
// Allocate a buffer to hold handle for each partition. Since the
// lock is released as soon as we call CloseHandle, we need to save all
// the handles and close them after the disk is marked offline by
// DiskCleanup. If we cannot allocate the storage for the handle
// array, we will fall back to the previous behavior.
//
handleArraySize = PartitionCount * sizeof(HANDLE);
handleArray = LocalAlloc( LPTR, handleArraySize );
if ( !handleArray ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_WARNING,
L"%1!ws!, Unable to allocate storage for handle array, error %2!u!.\n",
WideName,
GetLastError() );
} else {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Using handle array. \n",
WideName );
}
//
// For all of the partitions on the drive...
//
// 1. Flush the file buffers. (Offline only)
// 2. Lock the volume to purge all in memory contents of the volume. (Offline only)
// 3. Dismount the volume
//
for ( idx = 0; idx < PartitionCount; ++idx ) {
PMOUNTIE_PARTITION partition = MountiePartition(&resourceEntry->MountieInfo, idx);
swprintf( szDiskPartName, DEVICE_HARDDISK_PARTITION_FMT,
resourceEntry->DiskInfo.PhysicalDrive,
partition->PartitionNumber);
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Opening device %2!ws!.\n",
WideName,
szDiskPartName );
ntStatus = DevfileOpen(&fileHandle, szDiskPartName);
if ( !NT_SUCCESS(ntStatus) ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_ERROR,
L"%2!ws!, error opening %3!ws!, error %1!X!.\n",
ntStatus, WideName, szDiskPartName );
continue;
}
//
// Save the current partition handle and close it after the device has been
// marked offline.
//
if ( handleArray ) {
handleArray[idx] = fileHandle;
}
if (Offline) {
DWORD retryCount;
//
// Flush file buffers
//
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, FlushFileBuffers for %2!ws!.\n",
WideName,
szDiskPartName );
if ( !FlushFileBuffers( fileHandle ) ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_ERROR,
L"Offline, error flushing file buffers on device %2!ws!. Error: %1!u!.\n",
GetLastError(), szDiskPartName );
CloseHandle( fileHandle );
continue;
}
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Locking volume for %2!ws!.\n",
WideName,
szDiskPartName );
//
// Lock the volume, try this twice
//
retryCount = 0;
while ( (retryCount < 2) &&
!DeviceIoControl( fileHandle,
FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,
&bytesReturned,
NULL ) ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_WARNING,
L"%1!ws!, Locking volume failed, error %2!u!.\n",
WideName,
GetLastError() );
retryCount++;
Sleep(600);
}
}
//
// Now Dismount the volume.
//
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Dismounting volume %2!ws!.\n", WideName, szDiskPartName);
if ( !DeviceIoControl( fileHandle,
FSCTL_DISMOUNT_VOLUME,
NULL,
0,
NULL,
0,
&bytesReturned,
NULL ) ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!, error dismounting volume %2!ws!. Error %3!u!.\n",
WideName, szDiskPartName, GetLastError() );
}
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Dismount complete, volume %2!ws!.\n", WideName, szDiskPartName);
//
// Close the handle only if we couldn't allocate the handle array.
//
if ( !handleArray ) {
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, No handle array, closing device %2!ws!.\n",
WideName,
szDiskPartName );
CloseHandle( fileHandle );
}
}
//
// Take this resource off the 'online' list.
//
EnterCriticalSection( &DisksLock );
if ( resourceEntry->Inserted ) {
RemoveEntryList( &resourceEntry->ListEntry );
resourceEntry->Inserted = FALSE;
if ( IsListEmpty( &DisksListHead ) ) {
//
// Fire Disk Terminate Event
//
SetEvent( DisksTerminateEvent ) ;
CloseHandle( DisksTerminateEvent );
DisksTerminateEvent = NULL;
}
}
LeaveCriticalSection( &DisksLock );
status = ERROR_SUCCESS;
DiskCleanup( resourceEntry );
//
// If we have the handle array, close the handles to all the partitions. This
// is done after DiskCleanup sets the disk state to offline. Issuing the lock
// and keeping the handle open will prevent new mounts on the partition.
//
if ( handleArray ) {
for ( idx = 0; idx < PartitionCount; idx++ ) {
if ( handleArray[idx] ) {
CloseHandle( handleArray[idx] );
}
}
LocalFree( handleArray );
}
resourceEntry->Valid = FALSE;
(DiskpLogEvent)(
resourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!, Returning final error %2!u!.\n",
WideName,
status );
return(status);
} // DisksOfflineOrTerminate
DWORD
DisksOnlineThread(
IN PCLUS_WORKER Worker,
IN PDISK_RESOURCE ResourceEntry
)
/*++
Routine Description:
Brings a disk resource online.
Arguments:
Worker - Supplies the cluster worker context
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
DWORD status = ERROR_INVALID_PARAMETER;
HANDLE fileHandle = NULL;
HANDLE event = NULL;
DWORD index;
RESOURCE_STATUS resourceStatus;
DWORD threadId;
BOOL success;
HANDLE thread;
DWORD bytesReturned;
UINT i, nRetries;
HANDLE MountManager = 0;
DWORD ntStatus;
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed;
//resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
//
// Check if we've been here before... without offline/terminate
//
if ( ResourceEntry->Inserted ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, resource is already in online list!\n");
goto exit;
}
ResourceEntry->DiskInfo.FailStatus = 0;
status = DisksOpenResourceFileHandle(ResourceEntry, L"Online", &fileHandle);
if (status != ERROR_SUCCESS) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, DisksOpenResourceFileHandle failed. Error: %1!u!\n", status);
goto exit;
}
if( !ReservationInProgress(ResourceEntry) ) { // [GN] 209018 //
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_WARNING,
L"DiskArbitration must be called before DisksOnline.\n");
// [GN] We can steal the disk while regular arbitration is in progress in another thread //
// We have to do a regular arbitration, no shortcuts as the one below
// status = StartPersistentReservations(ResourceEntry, fileHandle);
// if ( status != ERROR_SUCCESS ) {
status = DiskArbitration( ResourceEntry, DiskspClusDiskZero );
// }
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, arbitration failed. Error: %1!u!.\n",
status );
status = ERROR_RESOURCE_NOT_AVAILABLE;
goto exit;
}
}
//
// Synchronize with async cleanup worker
//
{
ULONG waitTimeInSeconds = 10;
status = DevfileIoctl( fileHandle,
IOCTL_DISK_CLUSTER_WAIT_FOR_CLEANUP,
&waitTimeInSeconds, sizeof(waitTimeInSeconds),
NULL, 0,
&bytesReturned );
if ( !NT_SUCCESS(status) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, WaitForCleanup returned. Status: 0x%1!x!.\n",
status );
goto exit;
}
}
//
// Bring the device online.
// we have to do this here for PartMgr poke to succeed.
//
status = GoOnline( fileHandle, ResourceEntry->ResourceHandle );
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, error bringing device online. Error: %1!u!.\n",
status );
goto exit;
}
//
// Create volume information
//
status = MountieRecreateVolumeInfoFromHandle(fileHandle,
ResourceEntry->DiskInfo.PhysicalDrive,
ResourceEntry->ResourceHandle,
&ResourceEntry->MountieInfo);
if (status != ERROR_SUCCESS) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, MountieCreateFromHandle failed. Error: %1!u!\n", status);
goto exit;
}
//
// Now poke partition manager
//
success = DeviceIoControl( fileHandle,
IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS,
NULL,
0,
NULL,
0,
&bytesReturned,
FALSE );
if (!success) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, Partmgr failed to claim all partitions. Error: %1!u!.\n",
status );
goto exit;
}
//
// Before attempting to access the device, close our open handles.
//
CloseHandle( fileHandle );
fileHandle = NULL;
ntStatus = DevfileOpen(&MountManager, MOUNTMGR_DEVICE_NAME);
if (!NT_SUCCESS(ntStatus)) {
status = RtlNtStatusToDosError(ntStatus);
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, MountMgr open failed. Error: %1!u!.\n", status);
goto exit;
}
resourceStatus.ResourceState = ClusterResourceOnlinePending;
// we will wait not more than 48 seconds per partition
nRetries = 24 * ResourceEntry->MountieInfo.Volume->PartitionCount;
// Don't wait for longer than 10 minutes total time //
if (nRetries > 300) {
nRetries = 300;
}
__try {
// We cannot call VolumesReady because pnp might be adding this device
// the same time we are trying to get the volume name. We must wait
// for the volume to show up in the pnp list.
//
// Check the PnP thread's volume list to see if the volume arrived.
// If not in the list, fall through and wait for the event to be
// signaled.
//
// First time through the list, don't update the volume list as this
// can be time consuming.
//
if ( IsDiskInPnpVolumeList( ResourceEntry, FALSE ) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, disk found in PnP volume list on first attempt \n");
//
// VolumesReady should work now. If not, there is some other problem.
//
status = VolumesReady(&ResourceEntry->MountieInfo, ResourceEntry);
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, VolumesReady returns: %1!u!. \n",
status );
//
// If disk corrupt or file corrupt error returned, disk is ready and
// we need to run chkdsk. Change status to success and fall through.
//
if ( ERROR_DISK_CORRUPT == status || ERROR_FILE_CORRUPT == status ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_WARNING,
L"Online, Allowing corrupt disk online for chkdsk processing \n" );
status = STATUS_SUCCESS;
}
__leave;
}
//
// Create event for waiting.
//
event = CreateEvent( NULL, // security attributes
TRUE, // manual reset
FALSE, // initial state: nonsignaled
NULL ); // object name
if ( !event ) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, Failed to create event for PnP notification. Error: %1!u!.\n",
status );
__leave;
}
//
// Tell our PnP code we are waiting for this disk.
//
status = QueueWaitForVolumeEvent( event,
ResourceEntry );
if ( ERROR_SUCCESS != status ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, Failed to create event for PnP notification. Error: %1!u!.\n",
status );
__leave;
}
resourceStatus.CheckPoint += 1;
(DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
&resourceStatus );
for (i = 0; i < nRetries; ++i) {
//
// Make sure we aren't terminated. We don't need to wait for pnp
// in this case.
//
if ( ResourceEntry->OnlineThread.Terminate ) {
status = ERROR_SHUTDOWN_CLUSTER;
__leave;
}
//
// Ask mountmgr to process volumes.
//
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, about to call check unprocessed volumes...\n");
success = DeviceIoControl( MountManager,
IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES,
NULL,
0,
NULL,
0,
&bytesReturned,
FALSE );
if ( !success ) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, MountMgr failed to check unprocessed volumes. Error: %1!u!.\n",
status );
// The call to the mountmgr can return an error if there are
// disks reserved by another node. Fall through...
}
//
// Wait for event signal or timeout. We wait only 2 seconds
// at a time so we can update the checkpoint.
//
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, waiting for PnP notification...\n");
status = WaitForSingleObject( event,
2000 );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, wait for PnP notification returns %1!u! \n", status );
if ( WAIT_OBJECT_0 == status ) {
//
// Event was signaled, try VolumesReady one more time.
// This should work or there is a more serious problem.
status = VolumesReady(&ResourceEntry->MountieInfo, ResourceEntry);
__leave;
}
if ( WAIT_TIMEOUT != status ) {
status = GetLastError();
__leave;
}
//
// Force a check in the PnP thread's volume list in
// case we somehow missed the volume arrival. If all
// volumes are available for this disk, we are done
// waiting.
//
if ( IsDiskInPnpVolumeList( ResourceEntry, TRUE ) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, disk found in PnP volume list\n");
//
// VolumesReady should work now. If not, there is some other problem.
//
status = VolumesReady(&ResourceEntry->MountieInfo, ResourceEntry);
__leave;
}
resourceStatus.CheckPoint += 1;
(DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
&resourceStatus );
}
} __finally {
//
// Tell our PnP code that we are no longer waiting. Not a
// problem if we never queued the request previously.
//
RemoveWaitForVolumeEvent( ResourceEntry );
if ( event ) {
CloseHandle( event );
}
}
DevfileClose(MountManager);
resourceStatus.ResourceState = ClusterResourceFailed;
if (status != ERROR_SUCCESS) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, volumes not ready. Error: %1!u!.\n",
status );
goto exit;
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, volumes ready.\n");
//
// Need to synchronize with ClusDisk by issuing
// IOCTL_CLUSTER_VOLUME_TEST on all partitions
//
// Need to read volume type. If it is RAW, we need to
// dismount and mount it again as a workaround for 389861
//
resourceStatus.ResourceState = ClusterResourceOnline;
ResourceEntry->Valid = TRUE;
//
// Start the registry notification thread, if needed.
//
EnterCriticalSection( &DisksLock );
if ( IsListEmpty( &DisksListHead ) ) {
DisksTerminateEvent = CreateEventW( NULL,
TRUE,
FALSE,
NULL );
if ( DisksTerminateEvent == NULL ) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, error %1!d! creating registry thread terminate event\n",
status);
LeaveCriticalSection( &DisksLock );
goto exit;
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, created registry thread terminate event \n");
}
ResourceEntry->Inserted = TRUE;
InsertTailList( &DisksListHead, &ResourceEntry->ListEntry );
LeaveCriticalSection( &DisksLock );
DiskspSsyncDiskInfo(L"Online", ResourceEntry, MOUNTIE_VALID | MOUNTIE_THREAD );
status = WaitForVolumes( ResourceEntry,
30 // seconds timeout //
);
//
// We cannot just check for NO_ERROR status. The problem is that WaitForVolumes
// calls GetFileAttributes, which can return ERROR_DISK_CORRUPT (1393). If the
// disk is corrupt, we should fall through and let chkdsk try to clean it up.
// We should also do this if ERROR_FILE_CORRUPT (1392) is returned (haven't
// yet seen file corrupt error returned from GetFileAttributes, but one never knows).
//
if ( NO_ERROR != status && ERROR_DISK_CORRUPT != status && ERROR_FILE_CORRUPT != status ) {
ClusResLogSystemEventByKey( ResourceEntry->ResourceKey,
LOG_CRITICAL,
RES_DISK_MOUNT_FAILED );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, error waiting for file system to mount. Error: %1!u!.\n",
status );
resourceStatus.ResourceState = ClusterResourceFailed;
ResourceEntry->Valid = FALSE;
EnterCriticalSection( &DisksLock );
CL_ASSERT ( ResourceEntry->Inserted );
ResourceEntry->Inserted = FALSE;
RemoveEntryList( &ResourceEntry->ListEntry );
if ( IsListEmpty( &DisksListHead ) ) {
//
// Fire Disk Terminate Event
//
SetEvent( DisksTerminateEvent ) ;
CloseHandle( DisksTerminateEvent );
DisksTerminateEvent = NULL;
}
LeaveCriticalSection( &DisksLock );
goto exit;
}
status = DisksMountDrives( &ResourceEntry->DiskInfo,
ResourceEntry,
ResourceEntry->DiskInfo.Params.Signature );
if ( status != ERROR_SUCCESS ) {
ClusResLogSystemEventByKey( ResourceEntry->ResourceKey,
LOG_CRITICAL,
RES_DISK_MOUNT_FAILED );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online, error mounting disk or creating share names for disk. Error: %1!u!.\n",
status );
resourceStatus.ResourceState = ClusterResourceFailed;
ResourceEntry->Valid = FALSE;
EnterCriticalSection( &DisksLock );
CL_ASSERT ( ResourceEntry->Inserted );
ResourceEntry->Inserted = FALSE;
RemoveEntryList( &ResourceEntry->ListEntry );
if ( IsListEmpty( &DisksListHead ) ) {
//
// Fire Disk Terminate Event
//
SetEvent( DisksTerminateEvent ) ;
CloseHandle( DisksTerminateEvent );
DisksTerminateEvent = NULL;
}
LeaveCriticalSection( &DisksLock );
}
if ( ERROR_SUCCESS == status ) {
LPWSTR newSerialNumber = NULL;
DWORD newSerNumLen = 0;
DWORD oldSerNumLen = 0;
DisksProcessMountPointInfo( ResourceEntry );
// Validate serial number.
status = GetSerialNumber( ResourceEntry->DiskInfo.Params.Signature,
&newSerialNumber );
if ( NO_ERROR == status && newSerialNumber ) {
newSerNumLen = wcslen( newSerialNumber );
if ( ResourceEntry->DiskInfo.Params.SerialNumber ) {
oldSerNumLen = wcslen( ResourceEntry->DiskInfo.Params.SerialNumber );
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, Old SerNum (%1!ws!) Old SerNumLen (%2!d!) \n",
ResourceEntry->DiskInfo.Params.SerialNumber,
oldSerNumLen
);
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, New SerNum (%1!ws!) New SerNumLen (%2!d!) \n",
newSerialNumber,
newSerNumLen
);
if ( 0 == oldSerNumLen ||
NULL == ResourceEntry->DiskInfo.Params.SerialNumber ||
newSerNumLen != oldSerNumLen ||
( 0 != wcsncmp( ResourceEntry->DiskInfo.Params.SerialNumber,
newSerialNumber,
newSerNumLen ) ) ) {
// Need to log an error?
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_WARNING,
L"Online, disk serial number has changed. Saving new serial number.\n" );
// Free existing serial number and update serial number.
if ( ResourceEntry->DiskInfo.Params.SerialNumber ) {
LocalFree( ResourceEntry->DiskInfo.Params.SerialNumber );
}
ResourceEntry->DiskInfo.Params.SerialNumber = newSerialNumber;
newSerialNumber = NULL;
// User MP thread to post info into registry.
PostMPInfoIntoRegistry( ResourceEntry );
}
}
if ( newSerialNumber ) {
LocalFree( newSerialNumber );
}
// Reset status to success so online completes.
status = ERROR_SUCCESS;
}
exit:
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, setting ResourceState %1!u! .\n",
resourceStatus.ResourceState );
(DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
&resourceStatus );
if ( fileHandle != NULL) {
CloseHandle( fileHandle );
}
if (status == ERROR_SUCCESS) {
WatchDisk(ResourceEntry);
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online, returning final error %1!u! ResourceState %2!u! Valid %3!x! \n",
status,
resourceStatus.ResourceState,
ResourceEntry->Valid );
return(status);
} // DisksOnlineThread
DWORD
DisksOpenResourceFileHandle(
IN PDISK_RESOURCE ResourceEntry,
IN PWCHAR InfoString,
OUT PHANDLE fileHandle OPTIONAL
)
/*++
Routine Description:
Open a file handle for the resource.
It performs the following steps:
1. Read Disk signature from cluster registry
2. Attaches clusdisk driver to a disk with this signature
3. Gets Harddrive no from ClusDisk driver
4. Opens \\.\PhysicalDrive%d device and returns the handle
Arguments:
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
InfoString - Supplies a label to be printed with error messages
fileHandle - receives file handle
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
DWORD status;
UCHAR deviceName[MAX_PATH];
HKEY signatureKey = NULL;
PCHAR diskName;
PWCHAR signature;
DWORD count;
WCHAR wDeviceName[MAX_PATH];
LPWSTR nameOfPropInError;
UCHAR resourceString[MAX_PATH];
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"[DiskArb]------- Disks%1!ws! -------.\n", InfoString);
//
// Read our disk signature from the resource parameters.
//
status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ResourceParametersKey,
DiskResourcePrivateProperties,
(LPBYTE) &ResourceEntry->DiskInfo.Params,
TRUE, // CheckForRequiredProperties
&nameOfPropInError );
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%3!ws!: Unable to read the '%1' property. Error: %2!u!.\n",
(nameOfPropInError == NULL ? L"" : nameOfPropInError),
status, InfoString );
return(status);
}
//
// Try to attach to this device.
//
status = DoAttach( ResourceEntry->DiskInfo.Params.Signature,
ResourceEntry->ResourceHandle );
if ( status != ERROR_SUCCESS ) {
WCHAR Signature[9];
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%3!ws!: Unable to attach to signature %1!lx!. Error: %2!u!.\n",
ResourceEntry->DiskInfo.Params.Signature,
status, InfoString );
// Added this event message back in as replication has been changed (regroup
// shouldn't be blocked now).
wsprintfW( Signature, L"%08lX", ResourceEntry->DiskInfo.Params.Signature );
ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
LOG_CRITICAL,
RES_DISK_MISSING,
Signature);
return(status);
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"[DiskArb]DisksOpenResourceFileHandle: Attach successful.\n");
//
// Now open the signature key under ClusDisk.
//
sprintf( resourceString, "%08lX", ResourceEntry->DiskInfo.Params.Signature );
status = RegOpenKeyEx( ResourceEntry->ClusDiskParametersKey,
resourceString,
0,
KEY_READ,
&signatureKey );
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%3!ws!: Unable to open ClusDisk signature key %1!lx!. Error: %2!u!.\n",
ResourceEntry->DiskInfo.Params.Signature,
status, InfoString );
return(status);
}
//
// Read our disk name from ClusDisk.
//
diskName = GetRegParameter(signatureKey, "DiskName");
RegCloseKey( signatureKey );
if ( diskName == NULL ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: Unable to read disk name.\n", InfoString );
return(ERROR_INVALID_PARAMETER);
}
//
// Parse disk name to find disk drive number.
//
count = sscanf( diskName, DiskName, &ResourceEntry->DiskInfo.PhysicalDrive );
// count is zero if failed! Otherwise count of parsed values.
LocalFree(diskName);
if (count == 0) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: Unable to parse disk name.\n", InfoString );
return ERROR_INVALID_PARAMETER;
}
//
// Build device string for CreateFile
//
if ( fileHandle ) {
sprintf( deviceName, "\\\\.\\PhysicalDrive%d", ResourceEntry->DiskInfo.PhysicalDrive );
*fileHandle = CreateFile( 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();
mbstowcs( wDeviceName, deviceName, strlen(deviceName) );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%3!ws!: error opening device '%1!ws!'. Error: %2!u!\n",
wDeviceName,
status, InfoString );
return(status);
}
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"[DiskArb]DisksOpenResourceFileHandle: CreateFile successful.\n");
return(ERROR_SUCCESS);
} // DisksOpenResourceFileHandle
DWORD
DiskspSsyncDiskInfo(
IN PWCHAR InfoLabel,
IN PDISK_RESOURCE ResourceEntry,
IN DWORD Options
)
/*++
Routine Description:
Restores the disk registry information
if necessary.
Arguments:
InfoLabel - Supplies a label to be printed with error messages
ResourceEntry - Supplies the disk resource structure.
Options - 0 or combination of the following:
MOUNTIE_VALID: ResourceEntry contains up to date MountieInfo.
If this flag is not set, MountieInfo will be recomputed
MOUNTIE_THREAD: If ERROR_SHARING_PAUSED prevents updating cluster registry,
launch a thread to do it later
MOUNTIE_QUIET: Quiet mode. Less noise in logs.
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise
--*/
{
DWORD status;
DWORD errorLevel;
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"%1!ws!: sync disk registry info \n",
InfoLabel );
if ( !(Options & MOUNTIE_VALID) ) {
HANDLE fileHandle;
status = DisksOpenResourceFileHandle(ResourceEntry, InfoLabel, &fileHandle);
if (status != ERROR_SUCCESS) {
return status;
}
status = MountieRecreateVolumeInfoFromHandle(fileHandle,
ResourceEntry->DiskInfo.PhysicalDrive,
ResourceEntry->ResourceHandle,
&ResourceEntry->MountieInfo);
CloseHandle(fileHandle);
if (status != ERROR_SUCCESS) {
if ( !(Options & MOUNTIE_QUIET) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: MountieCreateFromHandle failed, error: %2!u!\n",
InfoLabel, status );
}
return status;
}
}
status = MountieVerify(&ResourceEntry->MountieInfo, ResourceEntry, FALSE);
if (status != ERROR_SUCCESS) {
if ( !(Options & MOUNTIE_QUIET) ) {
if ( !DisksGetLettersForSignature( ResourceEntry ) ) {
// No drive letters, we are using mount points and this is not an error.
errorLevel = LOG_WARNING;
} else {
// Drive letters exist, this is likely an error.
errorLevel = LOG_ERROR;
}
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
errorLevel,
L"%1!ws!: MountieVerify failed, error: %2!u! \n",
InfoLabel, status );
}
}
status = MountieUpdate(&ResourceEntry->MountieInfo, ResourceEntry);
if (status != ERROR_SUCCESS) {
if (status != ERROR_SHARING_PAUSED) {
if ( !(Options & MOUNTIE_QUIET) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: MountieUpdate failed, error: %2!u!\n",
InfoLabel, status );
}
return status;
}
if ( Options & MOUNTIE_THREAD ) {
if ( InterlockedCompareExchange(
&ResourceEntry->MountieInfo.UpdateThreadIsActive,
1, 0)
)
{
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: update thread is already running.\n",
InfoLabel );
status = ERROR_ALREADY_EXISTS;
} else {
HANDLE thread;
DWORD threadId;
thread = CreateThread( NULL,
0,
DisksSetDiskInfoThread,
ResourceEntry,
0,
&threadId );
if ( thread == NULL ) {
status = GetLastError();
if ( !(Options & MOUNTIE_QUIET) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"%1!ws!: CreateThread failed, error: %2!u!\n",
InfoLabel, status );
}
InterlockedExchange(
&ResourceEntry->MountieInfo.UpdateThreadIsActive, 0);
} else {
CloseHandle( thread );
status = ERROR_SUCCESS;
}
}
}
}
return status;
} // DiskspSsyncDiskInfo
DWORD
DisksIsVolumeDirty(
IN PWCHAR DeviceName,
IN PDISK_RESOURCE ResourceEntry,
OUT PBOOL Dirty
)
/*++
Routine Description:
This routine opens the given nt drive and sends down
FSCTL_IS_VOLUME_DIRTY to determine the state of that volume's
dirty bit.
Arguments:
DeviceName -- name of the form:
\Device\HarddiskX\PartitionY [Note: no trailing backslash]
Dirty -- receives TRUE if the dirty bit is set
Return Value:
dos error code
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
DWORD status;
NTSTATUS ntStatus;
IO_STATUS_BLOCK iosb;
HANDLE fileHandle;
DWORD result = 0;
DWORD bytesReturned;
ntStatus = DevfileOpen( &fileHandle, DeviceName );
if ( !NT_SUCCESS(ntStatus) ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Error opening %1!ws!, error %2!x!.\n",
DeviceName, ntStatus );
return RtlNtStatusToDosError(ntStatus);
}
status = ERROR_SUCCESS;
if ( !DeviceIoControl( fileHandle,
FSCTL_IS_VOLUME_DIRTY,
NULL,
0,
&result,
sizeof(result),
&bytesReturned,
NULL ) ) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"FSCTL_IS_VOLUME_DIRTY for volume %1!ws! returned error %2!u!.\n",
DeviceName, status );
}
DevfileClose( fileHandle );
if ( status != ERROR_SUCCESS ) {
return status;
}
if (result & VOLUME_IS_DIRTY) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DisksIsVolumeDirty: Volume is dirty \n");
*Dirty = TRUE;
} else {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DisksIsVolumeDirty: Volume is clean \n");
*Dirty = FALSE;
}
return ERROR_SUCCESS;
} // DisksIsVolumeDirty
#define IS_SPECIAL_DIR(x) ( (x)[0]=='.' && ( (x)[1]==0 || ( (x)[1]=='.'&& (x)[2] == 0) ) )
#define FA_SUPER_HIDDEN (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
#define IS_SUPER_HIDDEN(x) ( ((x) & FA_SUPER_HIDDEN) == FA_SUPER_HIDDEN )
#define DISKP_CHECK_PATH_BUF_SIZE (MAX_PATH)
typedef struct _DISKP_CHECK_PATH_DATA {
WCHAR FileName[DISKP_CHECK_PATH_BUF_SIZE];
WIN32_FIND_DATAW FindData;
PDISK_RESOURCE ResourceEntry;
BOOL OpenFiles;
DWORD FileCount;
DWORD Level;
BOOL LogFileNotFound;
} DISKP_CHECK_PATH_DATA, *PDISKP_CHECK_PATH_DATA;
DWORD
DiskspCheckPathInternal(
IN OUT PDISKP_CHECK_PATH_DATA data,
IN DWORD FileNameLength
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
HANDLE FindHandle;
DWORD Status;
DWORD i, len;
DWORD adjust;
if ( !FileNameLength ) {
return ERROR_INVALID_DATA;
}
data->FileName[FileNameLength] = 0;
//
// GetFileAttributes must use a trailing slash on the Volume{GUID} name.
//
Status = GetFileAttributesW(data->FileName);
if (Status == 0xFFFFFFFF) {
Status = GetLastError();
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: GetFileAttrs(%1!s!) returned status of %2!d!.\n",
data->FileName,
Status);
return Status;
}
Status = ERROR_SUCCESS;
if ( data->FileName[FileNameLength - 1] == L'\\' ) {
//
// If path ends in backslash, simply add the asterisk.
//
if ( FileNameLength + 1 >= DISKP_CHECK_PATH_BUF_SIZE ) {
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: FileNameLength > buffer size (#1) \n" );
return(ERROR_ALLOTTED_SPACE_EXCEEDED);
}
data->FileName[FileNameLength + 0] = '*';
data->FileName[FileNameLength + 1] = 0;
adjust = 0;
} else {
if ( FileNameLength + 2 >= DISKP_CHECK_PATH_BUF_SIZE ) {
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: FileNameLength > buffer size (#2) \n" );
return(ERROR_ALLOTTED_SPACE_EXCEEDED);
}
data->FileName[FileNameLength + 0] = '\\';
data->FileName[FileNameLength + 1] = '*';
data->FileName[FileNameLength + 2] = 0;
adjust = 1;
}
FindHandle = FindFirstFileW(data->FileName, &data->FindData);
if (FindHandle == INVALID_HANDLE_VALUE) {
Status = GetLastError();
if (Status != ERROR_FILE_NOT_FOUND || data->LogFileNotFound) {
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: fff(%1!s!) returned status of %2!d!.\n",
data->FileName,
Status);
}
if (Status == ERROR_FILE_NOT_FOUND) {
Status = ERROR_EMPTY;
}
return Status;
}
++ data->Level;
++ data->FileCount;
if (data->OpenFiles) {
do {
if ( data->ResourceEntry->OnlineThread.Terminate ) {
// Returning SUCCESS means that we've closed all
// FindFile handles.
return(ERROR_SHUTDOWN_CLUSTER);
}
if ( IS_SPECIAL_DIR(data->FindData.cFileName )
|| IS_SUPER_HIDDEN(data->FindData.dwFileAttributes) )
{
continue;
}
len = wcslen(data->FindData.cFileName);
if (FileNameLength + len + 1 >= DISKP_CHECK_PATH_BUF_SIZE ) {
return(ERROR_ALLOTTED_SPACE_EXCEEDED);
}
MoveMemory(data->FileName + FileNameLength + adjust,
data->FindData.cFileName,
sizeof(WCHAR) * (len + 1) );
if ( data->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
Status = DiskspCheckPathInternal(data, FileNameLength + len + adjust);
if (Status != ERROR_SUCCESS) {
goto exit;
}
} else {
HANDLE FileHandle;
//
// Open with the same parameters that LogpCreate uses to try to catch quorum
// log corruption during online.
//
// We previously used OPEN_EXISTING parameter. Try OPEN_ALWAYS to match exactly what
// LogpCreate is using.
//
FileHandle = CreateFileW(data->FileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
NULL );
if ( FileHandle == INVALID_HANDLE_VALUE ) {
Status = GetLastError();
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: Open %1!ws! failed, status %2!d!.\n",
data->FileName,
Status
);
if (Status != ERROR_SHARING_VIOLATION) {
goto exit;
}
} else {
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DiskspCheckPath: Open %1!ws! succeeded. \n",
data->FileName
);
CloseHandle( FileHandle );
}
}
++(data->FileCount);
} while ( FindNextFileW( FindHandle, &data->FindData ) );
--(data->FileCount);
Status = GetLastError();
if (Status != ERROR_NO_MORE_FILES) {
(DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: fnf(%1!s!) returned status of %2!d!.\n",
data->FileName,
Status);
} else {
Status = ERROR_SUCCESS;
}
}
exit:
FindClose(FindHandle);
--(data->Level);
return Status;
} // DiskspCheckPathInternal
DWORD
DiskspCheckPath(
IN LPWSTR VolumeName,
IN PDISK_RESOURCE ResourceEntry,
IN BOOL OpenFiles,
IN BOOL LogFileNotFound
)
/*++
Routine Description:
Checks out a drive letter to see if the filesystem has mounted
it and it's working. We will also run CHKDSK if the partition/certain
files are Corrupt
Arguments:
VolumeName - Supplies the device name of the form:
\\?\Volume{GUID}\ [Note trailing backslash!]
ResourceEntry - Supplies a pointer to the disk resource entry.
OpenFiles - Span subdirectories and open files if TRUE.
Online - FILE_NOT_FOUND error is logged if TRUE
Return Value:
ERROR_SUCCESS if no corruption or corruption was found and corrected.
Win32 error code otherwise
--*/
{
DISKP_CHECK_PATH_DATA data;
DWORD len;
DWORD status;
ZeroMemory( &data, sizeof(data) );
data.OpenFiles = OpenFiles;
data.LogFileNotFound = LogFileNotFound;
data.ResourceEntry = ResourceEntry;
len = wcslen( VolumeName );
if (len >= DISKP_CHECK_PATH_BUF_SIZE) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: VolumeName length > buffer size \n" );
return ERROR_ALLOTTED_SPACE_EXCEEDED;
}
wcsncpy(data.FileName, VolumeName, DISKP_CHECK_PATH_BUF_SIZE);
//
// Send the path with trailing backslash to DiskspCheckPathInternal.
//
status = DiskspCheckPathInternal( &data, len );
data.FileName[len] = 0;
if (status != ERROR_SUCCESS || data.FileCount == 0) {
if (status != ERROR_EMPTY || data.LogFileNotFound) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: DCPI(%1!s!) returned status of %2!d!, files scanned %3!d!.\n",
data.FileName, status, data.FileCount);
}
if ( (status == ERROR_DISK_CORRUPT) ||
(status == ERROR_FILE_CORRUPT) )
{
if ( ResourceEntry->OnlineThread.Terminate ) {
return(ERROR_SHUTDOWN_CLUSTER);
}
// Should FixCorruption take forever? For now, "yes".
status = DisksFixCorruption( VolumeName,
ResourceEntry,
status );
if ( status != ERROR_SUCCESS ) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPath: FixCorruption for drive <%1!ws!:> returned status of %2!d!.\n",
VolumeName,
status);
}
} else {
// Some other error
// Assume that the error is benign if data.FileCount > 0
// ERROR_FILE_NOT_FOUND will be returned if there is no files on the volume
if (data.FileCount > 0 || status == ERROR_EMPTY) {
status = ERROR_SUCCESS;
}
}
}
return status;
} // DiskspCheckPath
DWORD
WaitForVolumes(
IN PDISK_RESOURCE ResourceEntry,
IN DWORD timeOutInSeconds
)
{
PMOUNTIE_PARTITION entry;
DWORD retryInterval = 2000;
DWORD retries = timeOutInSeconds / (retryInterval / 1000);
DWORD i;
DWORD partMap;
DWORD tempPartMap;
DWORD status;
DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
WCHAR szGlobalDiskPartName[MAX_PATH];
WCHAR szVolumeName[MAX_PATH];
//
// Check drive letters, if any. If no drive letters, check only volumes.
// If drive letters, we still fall through and check volumes.
//
status = WaitForDriveLetters( DisksGetLettersForSignature( ResourceEntry ),
ResourceEntry,
30 // seconds timeout
);
if ( NO_ERROR != status ) {
return status;
}
//
// Make sure the partition count is not too large for the bitmap.
//
if ( nPartitions > ( sizeof(partMap) * 8 ) ) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"WaitForVolumes: Partition count (%1!u!) greater than bitmap (%2!u!) \n",
nPartitions, sizeof(partMap) );
return ERROR_INVALID_DATA;
}
//
// Convert the partition count to a bitmap.
//
partMap = 0;
for (i = 0; i < nPartitions; i++) {
partMap |= (1 << i);
}
while ( TRUE ) {
tempPartMap = partMap;
for ( i = 0; tempPartMap; i++ ) {
if ( (1 << i) & tempPartMap ) {
tempPartMap &= ~(1 << i);
entry = MountiePartition( &ResourceEntry->MountieInfo, i );
if ( !entry ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"WaitForVolumes no partition entry for partition %1!u! \n", i );
//
// Something bad happened to our data structures. We want to keep checking
// each of the other partitions.
continue;
}
//
// Given the DiskPartName, get the VolGuid name. This name must have a trailing
// backslash to work correctly.
//
_snwprintf( szGlobalDiskPartName,
MAX_PATH,
GLOBALROOT_HARDDISK_PARTITION_FMT,
physicalDrive,
entry->PartitionNumber );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"WaitForVolumes: Calling GetVolumeNameForVolumeMountPoint for %1!ws! \n",
szGlobalDiskPartName );
if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
szVolumeName,
sizeof(szVolumeName)/sizeof(WCHAR) )) {
status = GetLastError();
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"WaitForVolumes: GetVolumeNameForVolumeMountPoint for %1!ws! returned %2!u!\n",
szGlobalDiskPartName,
status );
//
// Disk is corrupt. Immediately return an error so chkdsk can run during
// online processing.
//
if ( ERROR_DISK_CORRUPT == status || ERROR_FILE_CORRUPT == status ) {
return status;
}
//
// Something bad happened. Continue with the next partition.
continue;
}
status = DiskspCheckPathLite( szVolumeName, ResourceEntry );
switch (status) {
case ERROR_SUCCESS:
case ERROR_EMPTY:
// Not an error
// Clear this partition number from the check list
partMap &= ~(1 << i);
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_INVALID_PARAMETER:
// This is an error we expect to get when the volume
// wasn't mounted yet
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"WaitForVolumes: Volume (%1!ws!) file system not mounted (%2!u!) \n",
szVolumeName, status );
break;
default:
// This is not an error we expect.
// Probably something is very wrong with the system
// bail out
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"WaitForVolumes: Volume (%1!ws!) returns (%2!u!) \n",
szVolumeName, status );
return status;
}
}
}
if ( !partMap ) {
// All partitions are verified //
return ERROR_SUCCESS;
}
if ( retries-- == 0 ) {
return status;
}
Sleep(retryInterval);
}
return ERROR_SUCCESS;
} // WaitForVolumes
DWORD
DiskspCheckPathLite(
IN LPWSTR VolumeName,
IN PDISK_RESOURCE ResourceEntry
)
/*++
Routine Description:
Checks out a disk partition to see if the filesystem has mounted
it and it's working.
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 no corruption or corruption was found and corrected.
Win32 error code otherwise
--*/
{
DISKP_CHECK_PATH_DATA data;
DWORD len;
DWORD status;
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DiskspCheckPathLite: Volume name %1!ws! \n",
VolumeName );
ZeroMemory( &data, sizeof(data) );
data.OpenFiles = FALSE;
data.LogFileNotFound = FALSE;
data.ResourceEntry = ResourceEntry;
len = wcslen( VolumeName );
if (len >= DISKP_CHECK_PATH_BUF_SIZE) {
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
LOG_ERROR,
L"DiskspCheckPathLite: VolumeName length > buffer size \n" );
return ERROR_ALLOTTED_SPACE_EXCEEDED;
}
wcsncpy(data.FileName, VolumeName, DISKP_CHECK_PATH_BUF_SIZE);
//
// Send the path with trailing backslash to DiskspCheckPathInternal.
//
status = DiskspCheckPathInternal( &data, len );
return status;
} // DiskspCheckPathLite
DWORD
DiskspCheckDriveLetter(
IN WCHAR DriveLetter,
IN PDISK_RESOURCE ResourceEntry
)
/*++
Routine Description:
Checks out a drive letter to see if the filesystem has mounted
it and it's working. This is lightweight version of DiskspCheckPath
Arguments:
DriveLetter - Supplies find first file failed
ResourceEntry - Supplies a pointer to the disk resource entry.
Return Value:
ERROR_SUCCESS or
Win32 error code otherwise
--*/
{
DISKP_CHECK_PATH_DATA data;
DWORD len;
DWORD status;
ZeroMemory( &data, sizeof(data) );
data.OpenFiles = FALSE;
data.LogFileNotFound = FALSE;
data.ResourceEntry = ResourceEntry;
data.FileName[0] = DriveLetter;
data.FileName[1] = L':';
// data->FileName is zero initialized data->FileName[2] = 0 //
len = 2;
status = DiskspCheckPathInternal( &data, len );
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"DiskspCheckDriveLetter: Checking drive name (%1!ws!) returns %2!u! \n",
data.FileName,
status );
return status;
} // DiskspCheckDriveLetter
DWORD
WaitForDriveLetters(
IN DWORD DriveLetters,
IN PDISK_RESOURCE ResourceEntry,
IN DWORD timeOutInSeconds
)
{
DWORD retryInterval = 2000;
DWORD retries = timeOutInSeconds / (retryInterval / 1000);
if ( !DriveLetters ) {
(DiskpLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"WaitForDriveLetters: No drive letters for volume, skipping drive letter check \n" );
return ERROR_SUCCESS;
}
for(;;) {
DWORD tempDriveLetters = DriveLetters;
UINT i = 0;
DWORD status = ERROR_SUCCESS;
while (tempDriveLetters) {
if ( (1 << i) & tempDriveLetters ) {
tempDriveLetters &= ~(1 << i);
status = DiskspCheckDriveLetter( (WCHAR)(i + L'A'), ResourceEntry);
switch (status) {
case ERROR_SUCCESS:
case ERROR_EMPTY:
// Not an error
// Clear this drive letter from the check list
DriveLetters &= ~(1 << i);
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_INVALID_PARAMETER:
// This is an error we expect to get when the volume
// wasn't mounted yet
break;
default:
// This is not an error we expect.
// Probably something is very wrong with the system
// bail out
return status;
}
}
++i;
}
if (!DriveLetters) {
// All drive letters are verified //
return ERROR_SUCCESS;
}
if (retries-- == 0) {
return status;
}
Sleep(retryInterval);
}
return ERROR_SUCCESS;
}