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.
2661 lines
82 KiB
2661 lines
82 KiB
/*++
|
|
|
|
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"
|
|
#include <strsafe.h> // Should be included last.
|
|
|
|
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
|
|
|
|
|
|
DWORD
|
|
WaitForVolumes(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN DWORD timeOutInSeconds
|
|
);
|
|
|
|
DWORD
|
|
DiskspCheckPathLite(
|
|
IN LPWSTR VolumeName,
|
|
IN PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
NTSTATUS
|
|
ForcePnpVolChangeEvent(
|
|
PWSTR RootPath
|
|
);
|
|
|
|
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;
|
|
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 idx;
|
|
DWORD PartitionCount;
|
|
DWORD handleArraySize;
|
|
|
|
HANDLE fileHandle;
|
|
DWORD bytesReturned;
|
|
WCHAR szDiskPartName[MAX_PATH];
|
|
NTSTATUS ntStatus;
|
|
|
|
ACCESS_MASK access = SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA;
|
|
|
|
(DiskpLogEvent)(
|
|
resourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"%1!ws!, ResourceEntry @ %2!p! Valid %3!x! \n",
|
|
WideName,
|
|
resourceEntry,
|
|
resourceEntry->Valid );
|
|
|
|
if (Terminate) {
|
|
access = SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
|
|
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);
|
|
|
|
(VOID) StringCchPrintf( szDiskPartName,
|
|
RTL_NUMBER_OF( 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 = DevfileOpenEx( &fileHandle, szDiskPartName, access );
|
|
if ( !NT_SUCCESS(ntStatus) ) {
|
|
(DiskpLogEvent)(
|
|
resourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"%1!ws!, error opening %2!ws!, error %3!X!.\n",
|
|
WideName, szDiskPartName, ntStatus );
|
|
|
|
//
|
|
// For terminate, we can't do anything else but try the next
|
|
// partition.
|
|
//
|
|
|
|
if ( Terminate ) {
|
|
continue;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
resourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"%1!ws!, Opening %2!ws! with restricted access.\n",
|
|
WideName, szDiskPartName );
|
|
|
|
//
|
|
// If creating the device handle failed for offline, try opening
|
|
// the device once more with the restricted attributes.
|
|
//
|
|
|
|
access = SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
|
|
|
|
ntStatus = DevfileOpenEx( &fileHandle, szDiskPartName, access );
|
|
|
|
//
|
|
// If this still fails, then try the next partition.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(ntStatus) ) {
|
|
(DiskpLogEvent)(
|
|
resourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"%1!ws!, error opening %2!ws! (restricted access), error %3!X!.\n",
|
|
WideName, szDiskPartName, ntStatus );
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Offline: created handle with restricted attributes.
|
|
// Fall through and try to flush everything...
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// 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_WARNING,
|
|
L"%1!ws!, error flushing file buffers on device %2!ws!. Error: %3!u!.\n",
|
|
WideName, szDiskPartName, GetLastError() );
|
|
|
|
//
|
|
// If FlushFileBuffers fails, we should still try locking
|
|
// and dismounting the volume.
|
|
//
|
|
// Fall through...
|
|
}
|
|
|
|
(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_WARNING,
|
|
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 );
|
|
|
|
DevfileClose( fileHandle );
|
|
fileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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] ) {
|
|
DevfileClose( 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;
|
|
RESOURCE_STATUS resourceStatus;
|
|
BOOL success;
|
|
DWORD bytesReturned;
|
|
UINT i, nRetries;
|
|
HANDLE MountManager = 0;
|
|
DWORD ntStatus;
|
|
BOOL autoMountDisabled;
|
|
MOUNTMGR_QUERY_AUTO_MOUNT queryAutoMount;
|
|
|
|
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! Skip this online request.\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 //
|
|
#if DBG
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"DiskArbitration must be called before DisksOnline.\n");
|
|
#endif
|
|
// [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;
|
|
}
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Wait for async cleanup worker thread in ClusDisk to complete. \n");
|
|
|
|
//
|
|
// 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 );
|
|
status = RtlNtStatusToDosError( status );
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Send Offline IOCTL to all existing volumes, then Online IOCTL. \n");
|
|
|
|
//
|
|
// Volume snapshots require us to make sure everything is offline first.
|
|
// Ignore the returned status.
|
|
//
|
|
|
|
GoOffline( ResourceEntry );
|
|
|
|
//
|
|
// Bring the device online.
|
|
// we have to do this here for PartMgr poke to succeed.
|
|
//
|
|
status = GoOnline( ResourceEntry );
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Online, error bringing device online. Error: %1!u!.\n",
|
|
status );
|
|
goto exit;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Recreate volume information from cluster database. \n");
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Check that the cached signature is the same as the signature on the disk.
|
|
// They can be different if the disk fails and the user replaces the failed
|
|
// disk with a new disk having a different disk signature. If the user doesn't
|
|
// change the signature of the new disk to be the same as the original disk,
|
|
// and doesn't reboot or reinstall the disk (uninstall via devmgmt and rescan
|
|
// via diskmgmt), then the disk will try to come online using the old signature.
|
|
// Either online will succeed, or pnp will say the volume doesn't come online.
|
|
// We don't want online to succeed because if the disk fails to another node,
|
|
// it will likely fail when DoAttach is called (if disk wasn't ever online to
|
|
// the other node). If the disk was previously online, then we didn't previously
|
|
// verify the cached signature in DiskInfo.Params matches the disk, so now we
|
|
// make that check.
|
|
//
|
|
|
|
if ( ResourceEntry->DiskInfo.Params.Signature &&
|
|
ResourceEntry->DiskInfo.Params.Signature != ResourceEntry->MountieInfo.Volume->Signature ) {
|
|
|
|
WCHAR sigStr[10];
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Online, Cluster DB signature %1!lX! does not match PhysicalDrive%2!u! signature %3!lX!\n",
|
|
ResourceEntry->DiskInfo.Params.Signature,
|
|
ResourceEntry->DiskInfo.PhysicalDrive,
|
|
ResourceEntry->MountieInfo.Volume->Signature );
|
|
|
|
if ( SUCCEEDED( StringCchPrintf( sigStr,
|
|
RTL_NUMBER_OF(sigStr),
|
|
TEXT("%08lX"),
|
|
ResourceEntry->DiskInfo.Params.Signature ) ) ) {
|
|
ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
|
|
LOG_CRITICAL,
|
|
RES_DISK_MISSING,
|
|
sigStr);
|
|
}
|
|
|
|
status = ERROR_FILE_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Now poke partition manager
|
|
//
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Ask partition manager to create volumes for any new partitions. \n");
|
|
|
|
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;
|
|
|
|
//
|
|
// Find out if volume auto mount is enabled or disabled.
|
|
//
|
|
|
|
if ( !DeviceIoControl( MountManager,
|
|
IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT,
|
|
NULL,
|
|
0,
|
|
&queryAutoMount,
|
|
sizeof (queryAutoMount),
|
|
&bytesReturned,
|
|
NULL )) {
|
|
|
|
status = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Online, MountMgr unable to return automount status - assume automount disabled. Error: %1!u!.\n",
|
|
status );
|
|
|
|
autoMountDisabled = TRUE;
|
|
|
|
} else {
|
|
autoMountDisabled = (Disabled == queryAutoMount.CurrentState);
|
|
}
|
|
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, volume auto-mount is %1!ws! \n",
|
|
autoMountDisabled ? L"disabled" : L"enabled" );
|
|
|
|
// 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 = VolumesReadyLoop(&ResourceEntry->MountieInfo, ResourceEntry);
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, VolumesReady returns: %1!u!. PnP informs us that all volumes exist. \n",
|
|
status );
|
|
|
|
__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, call MountMgr to check for new, 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_WARNING,
|
|
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 notification from PnP thread that all volumes exist...\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 = VolumesReadyLoop(&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 - all volumes exist. \n");
|
|
|
|
//
|
|
// VolumesReady should work now. If not, there is some other problem.
|
|
//
|
|
|
|
status = VolumesReadyLoop(&ResourceEntry->MountieInfo, ResourceEntry);
|
|
__leave;
|
|
}
|
|
|
|
|
|
resourceStatus.CheckPoint += 1;
|
|
(DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
|
|
&resourceStatus );
|
|
|
|
//
|
|
// If auto mount is disabled in mountmgr, the volumes will
|
|
// arrive from pnp in offline state. In order for our pnp
|
|
// thread to read the drive layout, we have to make sure
|
|
// they are online. If we online the physical disk, the
|
|
// volumes associated with this disk will also be brought
|
|
// online.
|
|
//
|
|
// Only do the clusdisk online after waiting for a "reasonable"
|
|
// period of time. If we send online to clusdisk, then there is
|
|
// a chance the volume may be just arriving via pnp and then
|
|
// the volume might be marked as requiring a reboot. We are
|
|
// going to wait for at least 6 seconds, and every 6 seconds
|
|
// we will try another clusdisk online request.
|
|
//
|
|
|
|
//
|
|
// Remove the check for autoMountDisabled. Now send the volume
|
|
// offline and volume online after a "reasonable" time has passed.
|
|
//
|
|
|
|
if ( i && ( i % 3 == 0 ) ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, send IOCTL to mark all volumes online.\n" );
|
|
|
|
GoOffline( ResourceEntry );
|
|
status = GoOnline( ResourceEntry );
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"Online, error bringing device online. Error: %1!u!.\n",
|
|
status );
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} __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, all volumes for this disk exist.\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 );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Wait for all volume GUIDs and drive letters to be accessible and mounted. \n");
|
|
|
|
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;
|
|
WCHAR deviceName[64];
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Insure mount point information is correct.\n" );
|
|
|
|
DisksProcessMountPointInfo( ResourceEntry );
|
|
|
|
(VOID) StringCchPrintf( deviceName,
|
|
RTL_NUMBER_OF( deviceName ),
|
|
TEXT("\\\\.\\PhysicalDrive%d"),
|
|
ResourceEntry->DiskInfo.PhysicalDrive );
|
|
|
|
fileHandle = CreateFile( deviceName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL );
|
|
if ( INVALID_HANDLE_VALUE == fileHandle ||
|
|
NULL == fileHandle ) {
|
|
status = GetLastError();
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Online, error opening disk to retrieve serial number. Error: %1!u!.\n",
|
|
status );
|
|
|
|
} else {
|
|
|
|
// Retrieve and validate serial number.
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online, Retrieve and validate the disk serial number.\n" );
|
|
|
|
status = RetrieveSerialNumber( fileHandle,
|
|
&newSerialNumber );
|
|
|
|
CloseHandle( fileHandle );
|
|
fileHandle = NULL;
|
|
|
|
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;
|
|
WCHAR deviceName[MAX_PATH];
|
|
HKEY signatureKey = NULL;
|
|
PWCHAR diskName;
|
|
DWORD count;
|
|
LPWSTR nameOfPropInError;
|
|
WCHAR 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 from cluster database. Error: %2!u!.\n",
|
|
(nameOfPropInError == NULL ? L"" : nameOfPropInError),
|
|
status, InfoString );
|
|
return(status);
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] DisksOpenResourceFileHandle: Attaching to disk with signature %1!x! \n",
|
|
ResourceEntry->DiskInfo.Params.Signature
|
|
);
|
|
|
|
//
|
|
// Try to attach to this device.
|
|
//
|
|
status = DoAttach( ResourceEntry->DiskInfo.Params.Signature,
|
|
ResourceEntry->ResourceHandle,
|
|
FALSE ); // Offline, then dismount
|
|
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).
|
|
|
|
if ( SUCCEEDED( StringCchPrintf( Signature,
|
|
RTL_NUMBER_OF(Signature),
|
|
TEXT("%08lX"),
|
|
ResourceEntry->DiskInfo.Params.Signature ) ) ) {
|
|
ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
|
|
LOG_CRITICAL,
|
|
RES_DISK_MISSING,
|
|
Signature);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Now open the signature key under ClusDisk.
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] DisksOpenResourceFileHandle: Retrieving disk number from ClusDisk registry key \n" );
|
|
|
|
if ( FAILED( StringCchPrintf( resourceString,
|
|
RTL_NUMBER_OF(resourceString),
|
|
TEXT("%08lX"),
|
|
ResourceEntry->DiskInfo.Params.Signature ) ) ) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
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, L"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 = swscanf( diskName, DEVICE_HARDDISK, &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
|
|
//
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] DisksOpenResourceFileHandle: Retrieving handle to PhysicalDrive%1!u! \n",
|
|
ResourceEntry->DiskInfo.PhysicalDrive );
|
|
|
|
if ( fileHandle ) {
|
|
(VOID) StringCchPrintf( deviceName,
|
|
RTL_NUMBER_OF( deviceName ),
|
|
TEXT("\\\\.\\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();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"%3!ws!: error opening device '%1!ws!'. Error: %2!u!\n",
|
|
deviceName,
|
|
status, InfoString );
|
|
return(status);
|
|
}
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] DisksOpenResourceFileHandle: Returns success.\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!: Update disk registry information. \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
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
NTSTATUS ntStatus;
|
|
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 IS_REPARSE_POINT(x) ( (x) & FILE_ATTRIBUTE_REPARSE_POINT )
|
|
|
|
#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:
|
|
|
|
Checks out a disk partition to see if the filesystem has mounted
|
|
it and it's working.
|
|
|
|
Arguments:
|
|
|
|
data - Filled in structure for checking the volume.
|
|
|
|
FileNameLength - Number of characters (not including trailing NULL) in
|
|
data->FileName.
|
|
VolumeGUID names always end in a trailing backslash.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE FindHandle;
|
|
DWORD Status;
|
|
DWORD len;
|
|
DWORD adjust;
|
|
|
|
if ( FileNameLength < 1 ) {
|
|
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_WARNING,
|
|
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)
|
|
|| IS_REPARSE_POINT( 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 | FILE_SHARE_WRITE,
|
|
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 >= RTL_NUMBER_OF(data.FileName) - 1 ) {
|
|
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"DiskspCheckPath: VolumeName length > buffer size \n" );
|
|
return ERROR_ALLOTTED_SPACE_EXCEEDED;
|
|
}
|
|
if ( FAILED( StringCchCopy( data.FileName,
|
|
RTL_NUMBER_OF(data.FileName),
|
|
VolumeName ) ) ) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// 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_WARNING,
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks out all volumes on a disk resource. In addition, will check
|
|
all drive letters on the volumes. If no drive letters on the disk,
|
|
volumes are always checked.
|
|
|
|
If a volume or drive letter is not ready, wait the specified number
|
|
of seconds before retrying.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - Supplies a pointer to the disk resource entry.
|
|
|
|
TimeOutInSeconds - Number of seconds to wait for drive letter to come active.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - all drive letters and volumes on disk are accessible.
|
|
|
|
ERROR_DISK_CORRUPT - drive letters or volumes not accessible.
|
|
Chkdsk.exe should be run on all volumes.
|
|
|
|
Win32 error code - one or more drive letters is not accessible.
|
|
|
|
--*/
|
|
{
|
|
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,
|
|
TimeOutInSeconds // seconds timeout
|
|
);
|
|
|
|
if ( NO_ERROR != status ) {
|
|
|
|
//
|
|
// For any error checking drive letters, always return
|
|
// either disk or file corrupt so chkdsk will run.
|
|
//
|
|
|
|
return ERROR_DISK_CORRUPT;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
(VOID)StringCchPrintf( szGlobalDiskPartName,
|
|
RTL_NUMBER_OF( szGlobalDiskPartName ),
|
|
GLOBALROOT_HARDDISK_PARTITION_FMT,
|
|
physicalDrive,
|
|
entry->PartitionNumber );
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"WaitForVolumes: Insure volume GUID name exists and accessible for %1!ws! \n",
|
|
szGlobalDiskPartName );
|
|
|
|
szVolumeName[0] = L'\0';
|
|
|
|
if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
|
|
szVolumeName,
|
|
RTL_NUMBER_OF(szVolumeName) )) {
|
|
|
|
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;
|
|
}
|
|
|
|
ForcePnpVolChangeEvent( szVolumeName );
|
|
|
|
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 >= RTL_NUMBER_OF(data.FileName) - 1 ) {
|
|
(DiskpLogEvent)(ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"DiskspCheckPathLite: VolumeName length > buffer size \n" );
|
|
return ERROR_ALLOTTED_SPACE_EXCEEDED;
|
|
}
|
|
if ( FAILED( StringCchCopy( data.FileName,
|
|
RTL_NUMBER_OF(data.FileName),
|
|
VolumeName ) ) ) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Send the path with trailing backslash to DiskspCheckPathInternal.
|
|
//
|
|
|
|
status = DiskspCheckPathInternal( &data, len );
|
|
|
|
return status;
|
|
|
|
} // DiskspCheckPathLite
|
|
|
|
|
|
DWORD
|
|
DiskspCheckDriveLetter(
|
|
IN WCHAR DriveLetter,
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN BOOL Online
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
Online - Indicates whether this is Online or IsAlive processing.
|
|
ERROR_FILE_NOT_FOUND error is logged only during Online.
|
|
|
|
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 );
|
|
|
|
if ( NO_ERROR != status ) {
|
|
|
|
//
|
|
// Log all errors during Online, but don't log errors indicating
|
|
// the volume is empty (no files) during IsAlive (too noisy).
|
|
//
|
|
|
|
if ( ( ERROR_FILE_NOT_FOUND != status &&
|
|
ERROR_EMPTY != status ) || Online ) {
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks out all drive letters on a disk resource. If a drive letter
|
|
is not ready, wait the specified number of seconds before retrying.
|
|
|
|
Arguments:
|
|
|
|
DriveLetter - Bit map of the drive letters for the disk
|
|
|
|
ResourceEntry - Supplies a pointer to the disk resource entry.
|
|
|
|
TimeOutInSeconds - Number of seconds to wait for drive letter to come active.
|
|
If zero, this routine is called during IsAlive.
|
|
If nonzero, this routine is called during Online.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - all drive letters on disk are accessible.
|
|
|
|
Win32 error code - one or more drive letters is not accessible.
|
|
|
|
--*/
|
|
{
|
|
DWORD retryInterval = 2000;
|
|
DWORD retries = TimeOutInSeconds / (retryInterval / 1000);
|
|
|
|
BOOL online = ( TimeOutInSeconds ? TRUE : FALSE );
|
|
|
|
//
|
|
// If device has no drive letters, then we are done. Only log
|
|
// this fact during online, not IsAlive (TimeOutInSeconds
|
|
// is zero for IsAlive).
|
|
//
|
|
|
|
if ( !DriveLetters && online ) {
|
|
(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, online );
|
|
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 user requested no wait time (ie. IsAlive running),
|
|
// retries will be zero. In this case, return status
|
|
// immediately.
|
|
//
|
|
|
|
if (retries-- == 0) {
|
|
return status;
|
|
}
|
|
Sleep(retryInterval);
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ForcePnpVolChangeEvent(
|
|
PWSTR RootPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the current volume label, then write it back. This causes a
|
|
PnP event for GUID_IO_VOLUME_CHANGE to take place. We do this so
|
|
the shell can see the new online disk. The problem is that the
|
|
shell sees a GUID_IO_VOLUME_MOUNT, but the disk is not yet online
|
|
so the shell doesn't correctly display the file system type or
|
|
volume label.
|
|
|
|
Arguments:
|
|
|
|
RootPath - Supplies the device name of the form:
|
|
\\?\Volume{GUID}\ [Note trailing backslash!]
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
{
|
|
LPWSTR volumeLabel = NULL;
|
|
LPWSTR fileSystemName = NULL;
|
|
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD maxComponentLength;
|
|
DWORD fileSystemFlags;
|
|
|
|
if ( !RootPath ) {
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto FnExit;
|
|
}
|
|
|
|
volumeLabel = LocalAlloc( LPTR, MAX_PATH * sizeof(WCHAR) );
|
|
if ( !volumeLabel ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
fileSystemName = LocalAlloc( LPTR, MAX_PATH * sizeof(WCHAR) );
|
|
if ( !fileSystemName ) {
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// RootPath must end in a trailing backslash.
|
|
//
|
|
|
|
if ( !GetVolumeInformationW( RootPath,
|
|
volumeLabel,
|
|
MAX_PATH, // Number of chars
|
|
NULL,
|
|
&maxComponentLength,
|
|
&fileSystemFlags,
|
|
fileSystemName,
|
|
MAX_PATH ) ) { // Number of chars
|
|
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
//
|
|
// Set the volume label to the same as the current. This will force
|
|
// PnP event for GUID_IO_VOLUME_CHANGE to take place.
|
|
//
|
|
|
|
if ( !SetVolumeLabelW( RootPath,
|
|
volumeLabel )) {
|
|
|
|
dwError = GetLastError();
|
|
goto FnExit;
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if ( volumeLabel ) {
|
|
LocalFree( volumeLabel );
|
|
}
|
|
|
|
if ( fileSystemName ) {
|
|
LocalFree( fileSystemName );
|
|
}
|
|
|
|
return dwError;
|
|
|
|
} // ForcePnpVolChangeEvent
|
|
|