|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
pnp
Abstract:
This module processes disk related PnP notifications and tries to adjust partitions and drive letter information accordingly.
Author:
Gor Nishanov (gorn) 21-Dec-1998
Environment:
User Mode
Revision History:
--*/
#define UNICODE 1
#define INITGUID 1
#include <nt.h>
#include <ntdef.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <dbt.h>
#include <devioctl.h>
#include <devguid.h>
#include <ioevent.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <mountmgr.h>
#include <pnpmgr.h>
#include <setupapi.h>
//#include <windows.h>
#include "disksp.h"
#include "newmount.h"
#include "newdisks.h"
#include <strsafe.h> // Should be included last.
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
static HWND DummyWindow = 0; static BOOL PnPInitialized = FALSE; static HANDLE NotificationWatcherThreadHandle;
static HANDLE PnpInterfacesRegistered;
static LONG VolumeListUpdateInProcess = 0;
RTL_RESOURCE PnpVolumeLock; RTL_RESOURCE PnpWaitingListLock;
PWCHAR g_DiskResource = L"rtPhysical Disk"; #define RESOURCE_TYPE ((RESOURCE_HANDLE)g_DiskResource)
LIST_ENTRY WaitingDisks;
typedef struct _WAITING_DISK { LIST_ENTRY ListEntry; PDISK_RESOURCE ResourceEntry; HANDLE Event; DWORD Signature; ULONG PartitionCount; } WAITING_DISK, *PWAITING_DISK;
#define AcquireShared( _res_lock ) \
RtlAcquireResourceShared( _res_lock, TRUE );
#define ReleaseShared( _res_lock ) \
RtlReleaseResource( _res_lock );
#define AcquireExclusive( _res_lock ) \
RtlAcquireResourceExclusive( _res_lock, TRUE );
#define ReleaseExclusive( _res_lock ) \
RtlReleaseResource( _res_lock );
DWORD NotificationWatcherThread( IN LPVOID );
VOID ProcessMountPointChange( HDEVNOTIFY devNotify, DWORD Signature );
PWAITING_DISK FindWaitingDisk( DWORD Signature );
DWORD GetVolName( PWCHAR Name, PWCHAR *VolGuid );
DWORD StartNotificationWatcherThread( VOID) { DWORD status = ERROR_SUCCESS; HANDLE thread;
if ( InterlockedCompareExchange(&PnPInitialized, TRUE, FALSE) ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] PnP was already initialized.\n", status ); return ERROR_SUCCESS; }
PnpInterfacesRegistered = NULL; PnpInterfacesRegistered = CreateEvent( NULL, // security attributes
TRUE, // manual reset
FALSE, // initial state nonsignaled
NULL ); // event name
if ( NULL == PnpInterfacesRegistered ) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] Unable to create event for PnP interface registration. \n", status ); status = ERROR_SUCCESS; }
thread = CreateThread( NULL, // security attributes
0, // stack_size = default
NotificationWatcherThread, (LPVOID)0, // no parameters
0, // runs immediately
0 ); // don't need thread id
if(thread == NULL) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] StartNotificationWatcherThread failed, error: %1!u!. \n", status ); } else {
if ( NULL != PnpInterfacesRegistered ) {
//
// Before returning to caller, make sure all PnP interfaces
// are registered.
//
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Waiting for PnP interface registration to complete.\n" );
status = WaitForSingleObject( PnpInterfacesRegistered, 30 * 1000 );
if ( WAIT_TIMEOUT == status ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] PnP interface registration failed to complete in time, error: %1!u! \n", status ); }
CloseHandle( PnpInterfacesRegistered ); PnpInterfacesRegistered = NULL;
status = ERROR_SUCCESS; }
} NotificationWatcherThreadHandle = thread;
return status; }
VOID StopNotificationWatcher( VOID ) /*++
Routine Description:
Handler for console control events
Arguments:
dwCtrlType - Indicates the console event to handle.
Return Value:
TRUE if the event was handled, FALSE otherwise.
--*/
{ HANDLE localHandle = NotificationWatcherThreadHandle; if (DummyWindow) { PostMessage(DummyWindow, WM_QUIT, 0, 0); if (localHandle) { WaitForSingleObject(localHandle, 10 * 1000); CloseHandle(localHandle); } } }
#define WM_WatchDisk (WM_USER + 1)
#define WM_StopWatchingDisk (WM_USER + 2)
VOID WatchDisk( IN PDISK_RESOURCE ResourceEntry ) { if (DummyWindow) { PostMessage(DummyWindow, WM_WatchDisk, 0, (LPARAM)ResourceEntry); } }
VOID StopWatchingDisk( IN PDISK_RESOURCE ResourceEntry ) { if (DummyWindow) { SendMessage(DummyWindow, WM_StopWatchingDisk, 0, (LPARAM)ResourceEntry); } }
///////////////////////////////////////////////////////////////////////////
VOID MyUnregisterDeviceNotification(HDEVNOTIFY hNotify) { #if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Unregistering device notification - HDEVNOTIFY %1!x! \n", hNotify ); #endif
UnregisterDeviceNotification( hNotify ); }
HDEVNOTIFY MyRegisterDeviceNotification( IN HANDLE hRecipient, IN LPVOID NotificationFilter, IN DWORD Flags ) {
#if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Registering device notification - Recipient %1!x! Flags %2!x! \n", hRecipient, Flags ); #endif
return RegisterDeviceNotification( hRecipient, NotificationFilter, Flags ); }
DWORD RegisterDeviceHandle( IN HANDLE wnd, IN HANDLE device, OUT HDEVNOTIFY *devNotify) { DEV_BROADCAST_HANDLE DbtHandle; DWORD status = ERROR_SUCCESS; *devNotify = 0;
ZeroMemory(&DbtHandle,sizeof(DEV_BROADCAST_HANDLE));
DbtHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE); DbtHandle.dbch_devicetype = DBT_DEVTYP_HANDLE; DbtHandle.dbch_handle = device;
*devNotify = MyRegisterDeviceNotification( (HANDLE)wnd, &DbtHandle, DEVICE_NOTIFY_WINDOW_HANDLE ); if (!*devNotify) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] DBT_DEVTYP_HANDLE failed, error %1!u!\n", status ); } return status; }
DWORD RegisterDeviceInterface( IN HANDLE wnd, IN const GUID * guid, OUT HDEVNOTIFY *devNotify) { DEV_BROADCAST_DEVICEINTERFACE filter; DWORD status = ERROR_SUCCESS; *devNotify = 0;
ZeroMemory(&filter, sizeof(filter)); filter.dbcc_size = sizeof(filter); filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; CopyMemory(&filter.dbcc_classguid, guid, sizeof(filter.dbcc_classguid));
*devNotify = MyRegisterDeviceNotification( (HANDLE)wnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE ); if (!*devNotify) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] DBT_DEVTYP_DEVICEINTERFACE failed, error %1!u!\n", status ); } return status; }
///////////// Forward Declarations /////////////////
typedef struct _VOLUME *PVOLUME;
#define LOG_GUID_START( _Broadcast_, _Vol_ ) LogPnpGuid( _Broadcast_, _Vol_, L"Received" );
#define LOG_GUID_END( _Broadcast_, _Vol_ ) LogPnpGuid( _Broadcast_, _Vol_, L"Processed" );
VOID LogPnpGuid( PDEV_BROADCAST_HANDLE Broadcast, PVOLUME Vol, PWSTR BeginEndStr );
VOID PokeDiskResource( PVOLUME vol ); /*++
Routine Description:
Updates ClusterRegistry info if necessary
Arguments:
Volume of interest (used only to get the disk signature) Updates a per disk basis
Return Value:
None
--*/
DWORD GetVolumeInfo( PVOLUME Vol, PHANDLE FileHandle );
///////////// End Forward Declarations /////////////
////////////// Notification List Management //////////////////////////////
//
// We maintain a list of all volumes we are getting PnP notifications for
//
// PVOLUME FindVolume(HDEVNOTIFY Key);
// VOID DestroyVolume(PVOLUME vol);
// VOID RemoveVolume(HDEVNOTIFY devNotify);
// VOID AddVolume(PWCHAR Name)
//
LIST_ENTRY VolumeList;
typedef struct _VOLUME { LIST_ENTRY ListEntry; HDEVNOTIFY DevNotify; DWORD Signature; LONG UpdateActive; LARGE_INTEGER PartOffset; LARGE_INTEGER PartLength; ULONG PartNo; BYTE PartitionType; CHAR DriveLetter; WCHAR Name[1]; } VOLUME;
PVOLUME FindVolume(HDEVNOTIFY Key) { PLIST_ENTRY entry; for ( entry = VolumeList.Flink; entry != &VolumeList; entry = entry->Flink ) { PVOLUME vol = CONTAINING_RECORD( entry, VOLUME, ListEntry );
if (vol->DevNotify == Key) { return(vol); } } return 0; }
VOID DestroyVolume( PVOLUME vol) { // (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION,
// L"Destroying entry for %1!s!\n", vol->Name);
MyUnregisterDeviceNotification(vol->DevNotify); LocalFree(vol); }
VOID RemoveVolume(HDEVNOTIFY devNotify) { PVOLUME vol = NULL;
// Use a lock here as the online thread might be parsing the volume list.
AcquireExclusive( &PnpVolumeLock );
vol = FindVolume( devNotify ); if (!vol) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] RemoveVolume: devNotify %1!d! is not in the list\n", devNotify); ReleaseExclusive( &PnpVolumeLock ); return; }
PokeDiskResource(vol);
RemoveEntryList(&vol->ListEntry); ReleaseExclusive( &PnpVolumeLock ); DestroyVolume(vol); }
VOID AddVolume( PWCHAR Name ) { PWAITING_DISK waitDisk; PLIST_ENTRY entry; PVOLUME volList; PVOLUME vol = NULL; PWCHAR volGuid = NULL;
DWORD status; DWORD signature;
size_t len;
HANDLE fileHandle;
BOOL duplicateEntry; BOOL keepVolume = FALSE;
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Adding Name %1!s! \n", Name );
//
// Convert name to VolGuid name. If name is already a VolGuid
// name, the correct name will be returned. GetVolName will
// always return a name with a trailing backslash.
//
status = GetVolName( Name, &volGuid );
if ( ERROR_SUCCESS != status || !volGuid ) { goto FnExit; }
len = wcslen(volGuid);
// VOLUME structure includes 1 char for Name, so just add the
// length of the VolGuid (not length + 1).
vol = LocalAlloc(LPTR, sizeof(VOLUME) + len * sizeof(WCHAR));
if ( NULL == vol ) {
status = GetLastError(); (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR, L"[PnP] AddVolume: can't alloc VOL+%1!d!, error %2!u!\n", len, status );
goto FnExit; }
wcsncpy( vol->Name, volGuid, len ); vol->Name[len] = L'\0'; // Allocation included space for NULL.
//
// Skip CDROM devices. This requires a trailing backslash and
// prefix \\?\.
//
if ( DRIVE_CDROM == GetDriveType( vol->Name ) ) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Skipping CDROM volume %1!s!\n", vol->Name ); goto FnExit; }
//
// Skip floppy devices. This requires a trailing backslash and
// prefix \\?\.
//
if ( DRIVE_REMOVABLE == GetDriveType( vol->Name ) ) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Skipping floppy volume %1!s!\n", vol->Name ); goto FnExit; }
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Attempting to add volume %1!s!\n", volGuid );
if (len > 0 && vol->Name[len-1] == L'\\') { // remove trailing backslash
vol->Name[len-1] = 0; }
if (len > 2 && vol->Name[0] == L'\\' && vol->Name[1] == L'\\') { // Convert to NT file name
vol->Name[1] = L'?'; }
//
// Make sure the volume isn't already in the list. If so,
// skip it.
//
duplicateEntry = FALSE; AcquireShared( &PnpVolumeLock );
len = wcslen( vol->Name ); for ( entry = VolumeList.Flink; entry != &VolumeList; entry = entry->Flink ) { volList = CONTAINING_RECORD( entry, VOLUME, ListEntry );
if ( ( len == wcslen( volList->Name) ) && ( 0 == ClRtlStrNICmp( vol->Name, volList->Name, len ) ) ) {
duplicateEntry = TRUE; break; } }
ReleaseShared( &PnpVolumeLock );
if ( duplicateEntry ) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Skipping duplicate volume %1!s!\n", vol->Name ); goto FnExit; }
status = GetVolumeInfo( vol, &fileHandle );
//
// We might have a clustered disk now, but we can't read the
// partition info or drive layout because the disk is reserved
// by another node.
//
// If the disk is reserved by another node, we typically see
// this returned:
// 170 ERROR_BUSY
// If the disk is offline, we can see this:
// 2 ERROR_FILE_NOT_FOUND
//
// About all we know for sure is that if this is a non-fixed device,
// ERROR_INVALID_FUNCTION will be returned. For now, skip these
// devices and track any other volumes coming through.
//
if ( ERROR_INVALID_FUNCTION == status ) {
if ( INVALID_HANDLE_VALUE != fileHandle) { DevfileClose( fileHandle ); }
// Change this from LOG_ERROR to LOG_INFORMATION. This thread gets
// notified when non-fixed disks arrive (i.e. floppy), so logging
// an error for a floppy disk is misleading.
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Skipping volume %1!ws! \n", vol->Name);
goto FnExit; }
if ( INVALID_HANDLE_VALUE == fileHandle ) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_WARNING, L"[PnP] AddVolume: Unable to get volume handle (%1!ws!), error %2!u!\n", vol->Name, status);
goto FnExit; }
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: adding volume %1!s!\n", vol->Name );
status = RegisterDeviceHandle(DummyWindow, fileHandle, &vol->DevNotify); DevfileClose( fileHandle );
if (status != ERROR_SUCCESS) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR, L"[PnP] AddVolume: RDN(%1!ws!), error %2!u!\n", vol->Name, status); goto FnExit; }
GetAssignedLetter(vol->Name, &vol->DriveLetter);
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: %1!s! '%2!c!', %3!d! (%4!u!)\n", Name, (vol->DriveLetter)?vol->DriveLetter:' ', vol->PartitionType, vol->DevNotify);
// Use a lock here as the online thread might be parsing the volume list.
// As soon as the volume is added to the list, another thread could come
// through and remove it. Save the signature to a local variable so
// we can check the waiting list.
signature = vol->Signature; keepVolume = TRUE; AcquireExclusive( &PnpVolumeLock ); InsertTailList(&VolumeList, &vol->ListEntry); ReleaseExclusive( &PnpVolumeLock );
AcquireShared( &PnpWaitingListLock ); waitDisk = FindWaitingDisk( signature ); if ( waitDisk ) {
//
// We have a waiting disk that matches this volume signature.
// Now see if all the volumes are in the volume list.
//
if ( IsDiskInPnpVolumeList( waitDisk->ResourceEntry, FALSE ) ) {
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: All volumes present, signal event for signature %1!x!\n", signature );
//
// All volumes present, signal the event.
//
SetEvent( waitDisk->Event );
} else {
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: All volumes not ready for signature %1!x!\n", signature ); } } else {
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Online request not queued for signature %1!x!\n", signature );
}
ReleaseShared( &PnpWaitingListLock );
FnExit:
if ( volGuid ) { LocalFree( volGuid ); }
if ( !keepVolume && vol ) { LocalFree( vol ); }
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolume: Adding Name %1!s! - processed \n", Name );
}
DWORD GetVolName( PWCHAR Name, PWCHAR *VolGuid ) { PWCHAR volName = NULL; PWCHAR tempName = NULL;
DWORD volNameLenBytes; DWORD tempNameLenBytes; DWORD nameLen;
DWORD dwError = ERROR_SUCCESS;
if ( VolGuid ) { *VolGuid = NULL; }
#if DBG
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] GetVolName: Name %1!s!\n", Name ); #endif
nameLen = wcslen( Name );
//
// Create a buffer with room for a backslash.
//
tempNameLenBytes = ( nameLen * sizeof(WCHAR) ) + sizeof(UNICODE_NULL) + sizeof(WCHAR);
tempName = LocalAlloc( LPTR, tempNameLenBytes );
if ( !tempName ) { dwError = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolName: LocalAlloc for tempName failed, error %1!d! \n", dwError ); goto FnExit; }
wcsncpy( tempName, Name, nameLen ); // Room for NULL and '\' in buffer.
//
// Add trailing backslash.
//
if ( nameLen > 0 && tempName[nameLen-1] != L'\\' ) { //
// This is safe because temporary buffer is larger than
// original buffer.
//
tempName[nameLen] = L'\\'; }
#if DBG
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] GetVolName: tempName %1!s!\n", tempName ); #endif
volNameLenBytes = MAX_PATH * sizeof(WCHAR); volName = LocalAlloc( LPTR, volNameLenBytes );
if ( !volName ) { dwError = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolName: LocalAlloc for volName failed, error %1!d! \n", dwError ); goto FnExit; }
if ( !GetVolumeNameForVolumeMountPointW( tempName, volName, volNameLenBytes / sizeof(WCHAR) ) ) { dwError = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolName: GetVolumeNameForVolumeMountPoint failed, error %1!d! \n", dwError ); goto FnExit; }
if ( VolGuid ) { *VolGuid = volName; }
FnExit:
if ( dwError != ERROR_SUCCESS && volName ) { LocalFree( volName ); }
if ( tempName ) { LocalFree( tempName ); }
#if DBG
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] GetVolName: returns error %1!d!\n", dwError ); #endif
return dwError;
} // GetVolName
///////////////////// VolumeManagement code ends //////////////////////////////////////////
//////////////////// WatchedList //////////////////////
//
// We maintain a list of disks that are currently online
// and under cluster control. Any PnP notification
// coming for the volumes belonging to these disks,
// need to be processed and cluster registry might need
// to be updated
//
LIST_ENTRY WatchedList;
PDISK_RESOURCE FindDisk(DWORD Signature) { PLIST_ENTRY entry; PDISK_RESOURCE watchedDisk = NULL;
if ( !Signature ) { goto FnExit; }
if ( IsListEmpty( &WatchedList ) ) { goto FnExit; }
for ( entry = WatchedList.Flink; entry != &WatchedList; entry = entry->Flink ) {
watchedDisk = CONTAINING_RECORD( entry, DISK_RESOURCE, PnpWatchedListEntry );
if ( watchedDisk->DiskInfo.Params.Signature == Signature ) { goto FnExit; }
watchedDisk = NULL; }
FnExit:
return watchedDisk; }
VOID RemoveDisk( PDISK_RESOURCE ResourceEntry ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[PnP] Stop watching PnP events for disk %1!x!\n", ResourceEntry->DiskInfo.Params.Signature );
if ( IsListEmpty( &WatchedList ) ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] RemoveDisk: WatchedList is empty \n"); goto FnExit; }
if ( ResourceEntry->PnpWatchedListEntry.Flink == 0 || ResourceEntry->PnpWatchedListEntry.Blink == 0 ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] RemoveDisk: disk %1!x! not found or previously removed \n", ResourceEntry->DiskInfo.Params.Signature); goto FnExit; }
RemoveEntryList( &ResourceEntry->PnpWatchedListEntry ); ResourceEntry->PnpWatchedListEntry.Flink = 0; ResourceEntry->PnpWatchedListEntry.Blink = 0;
FnExit:
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[PnP] Stop watching disk %1!x! - processed \n", ResourceEntry->DiskInfo.Params.Signature );
return; }
VOID MarkMatchingPartition( PVOLUME Volume, PDRIVE_LAYOUT_INFORMATION driveLayout) /*++
Routine Description:
Finds a partition in DRIVE_LAYOUT_INFORMATION corresponding to the Volume in question and marks it. This routine is used in the code that verifies that there is a volume in the VolumeList for every recognized partition on the disk.
Arguments:
Return Value:
none
--*/ { PPARTITION_INFORMATION p = driveLayout->PartitionEntry; PPARTITION_INFORMATION end = p + driveLayout->PartitionCount;
for(;p < end; ++p) { if(p->RecognizedPartition && p->StartingOffset.QuadPart == Volume->PartOffset.QuadPart && p->PartitionLength.QuadPart == Volume->PartLength.QuadPart) { p->PartitionType = 1; } } }
VOID AddDisk( PDISK_RESOURCE ResourceEntry ) { DWORD idx; PDRIVE_LAYOUT_INFORMATION driveLayout = NULL; HANDLE fileHandle; WCHAR deviceName[MAX_PATH]; PLIST_ENTRY entry; BOOL success;
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[PnP] Start watching PnP events for disk %1!x!\n", ResourceEntry->DiskInfo.Params.Signature );
if ( ResourceEntry->PnpWatchedListEntry.Flink != NULL && ResourceEntry->PnpWatchedListEntry.Blink != NULL ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] AddDisk: disk %1!x! is already being watched\n", ResourceEntry->DiskInfo.Params.Signature); goto FnExit; }
InsertHeadList( &WatchedList, &ResourceEntry->PnpWatchedListEntry );
// Now we need to verify that we are watching for changes on every //
// recognized partition on this drive //
if ( FAILED( StringCchPrintf( deviceName, RTL_NUMBER_OF(deviceName), TEXT("\\\\.\\PhysicalDrive%d"), ResourceEntry->DiskInfo.PhysicalDrive ) ) ) { goto FnExit; }
fileHandle = CreateFile(deviceName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] AddDisk: Can't open %1!s!, error %2!d! \n", deviceName, GetLastError() ); goto FnExit; }
UpdateCachedDriveLayout( fileHandle ); success = ClRtlGetDriveLayoutTable(fileHandle, &driveLayout, 0); CloseHandle( fileHandle );
if ( !success ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"[PnP] AddDisk: Error performing GetDriveLayout; error %1!d!\n", GetLastError() ); goto FnExit; }
// Clear PartitionType field. We will be using it to mark partions
// which are in the our list of watched volumes
for ( idx = 0; idx < (INT)driveLayout->PartitionCount; ++idx ) { driveLayout->PartitionEntry[idx].PartitionType = 0; }
// Walk the list of all volumes and mark if this volume is in the partition table //
for ( entry = VolumeList.Flink; entry != &VolumeList; entry = entry->Flink ) { PVOLUME vol = CONTAINING_RECORD( entry, VOLUME, ListEntry );
if (vol->Signature == driveLayout->Signature) { MarkMatchingPartition(vol, driveLayout); } }
// Now all partitions that are in our list is marked
// We need to add all unmarked partitions to the list
for ( idx = 0; idx < (INT)driveLayout->PartitionCount; ++idx ) { if (driveLayout->PartitionEntry[idx].PartitionType == 0 && driveLayout->PartitionEntry[idx].RecognizedPartition ) { if ( FAILED( StringCchPrintf( deviceName, RTL_NUMBER_OF( deviceName ), GLOBALROOT_HARDDISK_PARTITION_FMT, ResourceEntry->DiskInfo.PhysicalDrive, driveLayout->PartitionEntry[idx].PartitionNumber ) ) ) { continue; }
AddVolume( deviceName ); } }
FnExit:
if ( driveLayout ) { LocalFree( driveLayout ); }
(DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[PnP] Start watching PnP events for disk %1!x! - processed \n", ResourceEntry->DiskInfo.Params.Signature );
} //////////////////// WatchedList management end //////////////////////
void PokeDiskResource( PVOLUME vol) /*++
Routine Description:
Updates ClusterRegistry info if necessary
Arguments:
Volume of interest (used only to get the disk signature) Updates a per disk basis
Return Value:
None
--*/ { PDISK_RESOURCE ResourceEntry; MOUNTIE_INFO Info; HANDLE fileHandle; DWORD status; PVOID OldMountieVolume; WCHAR deviceName[MAX_PATH];
ResourceEntry = FindDisk( vol->Signature );
if ( !ResourceEntry ) { return; }
if( ResourceEntry->MountieInfo.UpdateThreadIsActive ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] PokeDiskResource: ClusApi is read only. PnP request ignored\n"); return; }
ZeroMemory( &Info, sizeof(Info) );
(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) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] PokeDiskResource: Can't open %1!s!\n", deviceName); return; }
status = MountieRecreateVolumeInfoFromHandle( fileHandle, ResourceEntry->MountieInfo.HarddiskNo, 0, &Info); CloseHandle(fileHandle);
if (status != ERROR_SUCCESS) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] PokeDiskResource: Can't read partition table, error %1!d!\n", status); return; }
MountiePrint(&Info, ResourceEntry->ResourceHandle);
status = VolumesReady(&Info, ResourceEntry);
if ( status != ERROR_SUCCESS ) { (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"[PnP] PokeDiskResource: Volumes not ready, error %1!d!\n", status); MountieCleanup(&Info); return; }
MountieVerify(&Info, ResourceEntry, TRUE);
ResourceEntry->MountieInfo.DriveLetters = Info.DriveLetters; OldMountieVolume = InterlockedExchangePointer(&ResourceEntry->MountieInfo.Volume, Info.Volume); Info.Volume = OldMountieVolume; ResourceEntry->MountieInfo.NeedsUpdate = Info.NeedsUpdate; ResourceEntry->MountieInfo.VolumeStructSize = Info.VolumeStructSize;
MountiePrint(&ResourceEntry->MountieInfo, ResourceEntry->ResourceHandle); MountieUpdate(&ResourceEntry->MountieInfo, ResourceEntry);
MountieCleanup(&Info); }
//
// [HACKHACK] Currently, there is not polically correct way
// for the resource to learn whether it is a quorum resource or not
//
DWORD GetQuorumSignature( OUT PDWORD QuorumSignature) { WCHAR buf[MAX_PATH]; WCHAR guid[ sizeof(GUID) * 3 + 1]; // 2 character per byte + 1, in case somebody will put a dash //
// between every byte //
DWORD BufSize; DWORD Status; DWORD Type; HKEY Key;
Status = RegOpenKey( HKEY_LOCAL_MACHINE, DISKS_REG_CLUSTER_QUORUM, &Key ); if (Status != ERROR_SUCCESS) { return Status; }
BufSize = sizeof(guid); Status = RegQueryValueExW(Key, CLUSREG_NAME_QUORUM_RESOURCE, 0, &Type, (LPBYTE)guid, &BufSize ); RegCloseKey( Key ); if (Status != ERROR_SUCCESS) { return Status; }
//
// Now, we got a quorum resource guid.
// Let's try to open this resource and read its parameters.
//
(VOID) StringCchPrintf( buf, RTL_NUMBER_OF(buf), TEXT("Cluster\\Resources\\%ws\\Parameters"), guid );
Status = RegOpenKey( HKEY_LOCAL_MACHINE, buf, &Key ); if (Status != ERROR_SUCCESS) { return Status; } BufSize = sizeof(DWORD); Status = RegQueryValueExW(Key, CLUSREG_NAME_PHYSDISK_SIGNATURE, 0, &Type, (LPBYTE)QuorumSignature, &BufSize ); if (Status != ERROR_SUCCESS) {
// During cluster install, the registry key doesn't yet exist.
(DiskpLogEvent)(RESOURCE_TYPE, LOG_WARNING, L"[PnP] DriveLetterChange: failed to open Path = %1!ws!\n", buf); }
RegCloseKey(Key); return Status; }
DWORD CheckQuorumLetterChange( HDEVNOTIFY devNotify, UCHAR Old, UCHAR New, DWORD Signature) { static HDEVNOTIFY QuorumDevNotify = 0; static UCHAR StoredDriveLetter = 0; DWORD status; UCHAR QuorumDriveLetter; LPWSTR QuorumPath; DWORD QuorumSignature;
//
// If we are not watching the disk this volume is on, do nothing
//
if ( FindDisk(Signature) == NULL ) { return ERROR_SUCCESS; }
status = GetQuorumSignature(&QuorumSignature); if (status != ERROR_SUCCESS) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR, L"[PnP] DriveLetterChange: Unable to query quorum drive signature, status %1!u!\n", status); QuorumDevNotify = 0; StoredDriveLetter = 0; return status; }
//
// Not a quorum disk. Ignore this notification
//
if ( QuorumSignature != Signature ) { return ERROR_SUCCESS; }
status = DiskspGetQuorumPath(&QuorumPath); if (status != ERROR_SUCCESS) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR, L"[PnP] DriveLetterChange: Unable to query quorum drive letter, status %1!u!\n", status); QuorumDevNotify = 0; StoredDriveLetter = 0; return status; } QuorumDriveLetter = (UCHAR) QuorumPath[0];
if (QuorumDriveLetter == Old) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] DriveLetterChange: Quorum drive letter %1!c! is being changed\n", QuorumDriveLetter); QuorumDevNotify = devNotify; StoredDriveLetter = QuorumDriveLetter; }
if (New && QuorumDevNotify == devNotify && QuorumDriveLetter != New && QuorumDriveLetter == StoredDriveLetter) { WCHAR szOld[2] = {QuorumDriveLetter, 0}; WCHAR szNew[2] = {New, 0};
ClusterLogEvent2( LOG_UNUSUAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RES_DISK_PNP_CHANGING_QUORUM, 0, NULL, szOld, szNew);
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] DriveLetterChange: Quorum drive letter changed from %1!c! to %2!c!\n", QuorumDriveLetter, New); QuorumPath[0] = New; status = DiskspSetQuorumPath(QuorumPath); if (status != ERROR_SUCCESS) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_SEVERE, L"[PnP] DriveLetterChange: Unable to update QuorumPath (%1!c!: => %2!c!:), status %3!u!\n", QuorumDriveLetter, New, status); } }
LocalFree(QuorumPath); return status; }
VOID ProcessDriveLetterChange( HDEVNOTIFY devNotify ) { PVOLUME vol = FindVolume(devNotify); CHAR ch; if (!vol) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] DriveLetterChange: devNotify %1!d! is not in the list\n", devNotify); return; } GetAssignedLetter(vol->Name, &ch); (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] DriveLetterChange: %1!c! => %2!c!\n", NICE_DRIVE_LETTER(vol->DriveLetter), NICE_DRIVE_LETTER(ch) ); if (vol->PartitionType == PARTITION_IFS && vol->DriveLetter != ch) { CheckQuorumLetterChange(devNotify, vol->DriveLetter, ch, vol->Signature); PokeDiskResource(vol); } vol->DriveLetter = ch; }
VOID ProcessVolumeInfoChange( HDEVNOTIFY devNotify ) { PVOLUME vol = FindVolume(devNotify); BOOL success; HANDLE fileHandle = NULL; PARTITION_INFORMATION partInfo; DWORD bytesReturned; NTSTATUS ntStatus;
if (!vol) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] VolumeInfoChange: devNotify %1!d! is not in the list\n", devNotify); return; } ntStatus = DevfileOpen(&fileHandle, vol->Name); if ( !NT_SUCCESS(ntStatus) || !fileHandle ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] VolumeInfoChange: Can't open %1!ws!, error %2!X!.\n", vol->Name, ntStatus); return; }
success = DeviceIoControl( fileHandle, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(PARTITION_INFORMATION), &bytesReturned, FALSE );
DevfileClose( fileHandle ); if (!success) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR, L"[PnP] VolumeInfoChange: Error performing GetPartitionInfo; error %1!d!\n", GetLastError()); return; } (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] VolumeInfoChange: partType %1!d! => %2!d!\n", vol->PartitionType, partInfo.PartitionType ); if (vol->PartitionType != partInfo.PartitionType && (partInfo.PartitionType == PARTITION_IFS || vol->PartitionType == PARTITION_IFS) ) { PokeDiskResource(vol); } vol->PartitionType = partInfo.PartitionType; }
//////////////////////////// WindowProc /////////////////////////////////////
#ifndef PDEV_BROADCAST_HEADER
typedef struct _DEV_BROADCAST_HEADER * PDEV_BROADCAST_HEADER; #endif
LRESULT CALLBACK TestWndProc( HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
) { if (uMsg == WM_WatchDisk) { PDISK_RESOURCE p = (PDISK_RESOURCE)lParam; if (p) { AddDisk(p); } return TRUE; } if (uMsg == WM_StopWatchingDisk) { PDISK_RESOURCE p = (PDISK_RESOURCE)lParam; if (p) { RemoveDisk(p); } return TRUE; } if (uMsg != WM_DEVICECHANGE) { LRESULT result;
#if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] WM_DEVICECHANGE - calling DefWindowProc \n" ); #endif
result = DefWindowProc(hwnd, uMsg, wParam, lParam); #if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] WM_DEVICECHANGE - DefWindowProc returns %1!x! \n", result ); #endif
return result; }
if (!lParam) { return TRUE; } #if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event %1!x! received\n", wParam ); #endif
switch( ((PDEV_BROADCAST_HEADER)lParam)->dbcd_devicetype ) { case DBT_DEVTYP_DEVICEINTERFACE: { PDEV_BROADCAST_DEVICEINTERFACE p = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
if (wParam == DBT_DEVICEARRIVAL && IsEqualGUID(&p->dbcc_classguid, &GUID_IO_VOLUME_DEVICE_INTERFACE) ) { AddVolume( p->dbcc_name ); } break; } case DBT_DEVTYP_HANDLE: { PDEV_BROADCAST_HANDLE p = (PDEV_BROADCAST_HANDLE)lParam;
if (wParam == DBT_DEVICEREMOVECOMPLETE) {
PVOLUME vol = 0; DWORD signature = 0;
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event DBT_DEVICEREMOVECOMPLETE received \n" );
// First, save the signature because we are going
// to remove the volume.
vol = FindVolume( p->dbch_hdevnotify ); if ( vol ) { signature = vol->Signature; }
RemoveVolume(p->dbch_hdevnotify);
if ( signature ) { ProcessMountPointChange( p->dbch_hdevnotify, signature ); }
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event DBT_DEVICEREMOVECOMPLETE processed \n" );
} else if (wParam == DBT_CUSTOMEVENT) { PVOLUME Vol = 0;
Vol = FindVolume( p->dbch_hdevnotify );
LOG_GUID_START( p, Vol );
//
// If we are watching this volume and it has a valid disk
// resource structure, find out if we are re-creating the
// drive letter. If so, we can safely ignore the following
// pnp events.
//
if ( Vol && Vol->Signature ) {
PDISK_RESOURCE resourceEntry = NULL;
resourceEntry = FindDisk( Vol->Signature );
if ( resourceEntry && resourceEntry->IgnoreMPNotifications ) {
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Skipping event processing for signature %x \n", Vol->Signature );
LOG_GUID_END( p, Vol ); break; } }
if ( IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_NAME_CHANGE) ) { // Update disk info.
GetVolumeInfo( Vol, NULL ); ProcessDriveLetterChange( p->dbch_hdevnotify ); ProcessMountPointChange( p->dbch_hdevnotify, 0 ); } else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_CHANGE) ) { // Update disk info.
GetVolumeInfo( Vol, NULL ); ProcessVolumeInfoChange( p->dbch_hdevnotify );
if ( Vol ) { ProcessMountPointChange( p->dbch_hdevnotify, Vol->Signature ); } } else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE) ) { // Update disk info.
GetVolumeInfo( Vol, NULL ); ProcessVolumeInfoChange( p->dbch_hdevnotify );
if ( Vol ) { ProcessMountPointChange( p->dbch_hdevnotify, 0 ); }
} else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_MOUNT) ) { // ProcessDriveLetterChange( p->dbch_hdevnotify );
}
LOG_GUID_END( p, Vol );
}
break; } } return TRUE; }
VOID LogPnpGuid( PDEV_BROADCAST_HANDLE Broadcast, PVOLUME Vol, PWSTR BeginEndStr ) { PWCHAR guidName = 0; LPDWORD dw = (LPDWORD)&Broadcast->dbch_eventguid;
if ( IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_NAME_CHANGE) ) { guidName = L"GUID_IO_VOLUME_NAME_CHANGE"; } else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_CHANGE) ) { guidName = L"GUID_IO_VOLUME_CHANGE"; } else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE) ) { guidName = L"GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE"; } else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_LOCK) ) { #if DBG
guidName = L"GUID_IO_VOLUME_LOCK"; #else
return; #endif
} else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_UNLOCK) ) { #if DBG
guidName = L"GUID_IO_VOLUME_UNLOCK"; #else
return; #endif
} else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_MOUNT) ) { #if DBG
guidName = L"GUID_IO_VOLUME_MOUNT"; #else
return; #endif
} else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_DISMOUNT) ) { guidName = L"GUID_IO_VOLUME_DISMOUNT"; } else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_LOCK_FAILED) ) { guidName = L"GUID_IO_VOLUME_LOCK_FAILED"; } else if (IsEqualGUID(&Broadcast->dbch_eventguid, &GUID_IO_VOLUME_DISMOUNT_FAILED) ) { guidName = L"GUID_IO_VOLUME_DISMOUNT_FAILED"; }
if (guidName) { if (Vol) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event %1!s! for %2!c! (Partition%3!d!) - %4!s! \n", guidName, NICE_DRIVE_LETTER(Vol->DriveLetter), Vol->PartNo, BeginEndStr ); } else { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event %1!s! for %2!d! - %3!s! \n", guidName, Broadcast->dbch_hdevnotify, BeginEndStr ); } } else { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Event %2!x! %3!x! %4!x! %5!x! for %1!d! - %6!s! \n", Broadcast->dbch_hdevnotify, dw[0], dw[1], dw[2], dw[3], BeginEndStr ); }
} // LogPnpGuid
VOID AddVolumes() /*++
Routine Description:
Enumerate all known volumes and register for the notifications on these volumes
Arguments:
None
Return Value:
None
--*/ { PSP_DEVICE_INTERFACE_DETAIL_DATA pDiDetail = NULL;
DWORD dwError = ERROR_SUCCESS; DWORD count; DWORD sizeDiDetail;
LONG oldValue;
BOOL result;
HDEVINFO hdevInfo = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DATA devInterfaceData; SP_DEVINFO_DATA devInfoData;
//
// If this routine is currently running, the old value will be 1. If so,
// we don't need to run again. This call will set the flag to 1 if it is 0.
//
oldValue = InterlockedCompareExchange( &VolumeListUpdateInProcess, 1, 0 );
if ( 1 == oldValue ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolumes: Volume list update in process, skipping update \n" ); goto FnExit; }
//
// Get a device interface set which includes all volume devices
// present on the machine. VolumeClassGuid is a predefined GUID that
// will return all volume-type device interfaces
//
hdevInfo = SetupDiGetClassDevs( &VolumeClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
if ( INVALID_HANDLE_VALUE == hdevInfo ) { dwError = GetLastError(); goto FnExit; }
ZeroMemory( &devInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA) );
//
// Iterate over all devices interfaces in the set
//
for ( count = 0; ; count++ ) {
// must set size first
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
//
// Retrieve the device interface data for each device interface
//
result = SetupDiEnumDeviceInterfaces( hdevInfo, NULL, &VolumeClassGuid, count, &devInterfaceData );
if ( !result ) {
//
// If we retrieved the last item, break
//
dwError = GetLastError();
if ( ERROR_NO_MORE_ITEMS == dwError ) { dwError = ERROR_SUCCESS; break;
}
//
// Some other error occurred. Stop processing.
//
goto FnExit; }
//
// Get the required buffer-size for the device path. Note that
// this call is expected to fail with insufficient buffer error.
//
result = SetupDiGetDeviceInterfaceDetail( hdevInfo, &devInterfaceData, NULL, 0, &sizeDiDetail, NULL );
if ( !result ) {
dwError = GetLastError();
//
// If a value other than "insufficient buffer" is returned,
// we have to skip this device.
//
if ( ERROR_INSUFFICIENT_BUFFER != dwError ) { continue; }
} else {
//
// The call should have failed since we're getting the
// required buffer size. If it doesn't fail, something bad
// happened.
//
continue; }
//
// Allocate memory for the device interface detail.
//
pDiDetail = LocalAlloc( LPTR, sizeDiDetail );
if ( !pDiDetail ) { dwError = GetLastError(); goto FnExit; }
// must set the struct's size member
pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
//
// Finally, retrieve the device interface detail info.
//
result = SetupDiGetDeviceInterfaceDetail( hdevInfo, &devInterfaceData, pDiDetail, sizeDiDetail, NULL, &devInfoData );
if ( !result ) {
dwError = GetLastError();
LocalFree( pDiDetail ); pDiDetail = NULL;
//
// Shouldn't fail, if it does, try the next device.
//
continue; }
#if DBG
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] AddVolumes: Found volume %1!ws! \n", pDiDetail->DevicePath ); #endif
AddVolume( pDiDetail->DevicePath );
LocalFree( pDiDetail ); pDiDetail = NULL;
}
FnExit:
//
// If old update value was zero, then it is now 1. Reset it to
// zero so another update can take place if needed.
//
if ( 0 == oldValue ) { InterlockedExchange( &VolumeListUpdateInProcess, 0 ); }
if ( INVALID_HANDLE_VALUE != hdevInfo ) { SetupDiDestroyDeviceInfoList( hdevInfo ); }
if ( pDiDetail ) { LocalFree( pDiDetail ); }
#if DBG
if ( ERROR_SUCCESS != dwError ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] AddVolumes: returns error %1!d! \n", dwError ); } #endif
} // AddVolumes
DWORD GetVolumeInfo( PVOLUME Vol, PHANDLE FileHandle ) /*++
Routine Description:
Get drive layout info and partition info for the specified volume.
Arguments:
Vol - Pointer to PVOLUME structure. Caller is responsible for allocating and freeing.
FileHandle - Returned handle to volume. Caller is responsible for closing. This parameter is optional. If not specified by the user, the volume handle will be closed by this routine.
Return Value:
Win32 error value.
--*/ { PARTITION_INFORMATION partInfo; PDRIVE_LAYOUT_INFORMATION driveLayout;
DWORD status = ERROR_SUCCESS; DWORD bytesReturned; DWORD oldValue; NTSTATUS ntStatus;
HANDLE hFile = NULL;
BOOL success;
//
// If no VOL parameter specified or the signature is already set,
// we don't need to update the volume information.
//
if ( !Vol || Vol->Signature ) { return ERROR_INVALID_PARAMETER; }
if ( FileHandle ) { *FileHandle = INVALID_HANDLE_VALUE; }
#if DBG
(DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] GetVolumeInfo: Updating info: %1!s!\n", Vol->Name ); #endif
ntStatus = DevfileOpen(&hFile, Vol->Name); if ( !NT_SUCCESS(ntStatus) ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] GetVolumeInfo: error opening: %1!ws!, error %2!X!.\n", Vol->Name, ntStatus);
return RtlNtStatusToDosError(ntStatus); }
//
// If update already in progress for this disk, skip the update.
//
oldValue = InterlockedCompareExchange( &Vol->UpdateActive, 1, 0 );
if ( 1 == oldValue ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolumeInfo: Skip update: %1!ws! \n", Vol->Name); goto FnExit; }
success = DeviceIoControl( hFile, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(PARTITION_INFORMATION), &bytesReturned, FALSE );
if (!success) {
status = GetLastError();
// Change this from LOG_ERROR to LOG_WARNING. This thread gets
// notified when non-fixed disks arrive (i.e. floppy), so logging
// an error for a floppy disk is misleading.
(DiskpLogEvent)(RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolumeInfo: GetPartitionInfo (%1!ws!), error %2!u!\n", Vol->Name, status);
goto FnExit; }
Vol->PartOffset = partInfo.StartingOffset; Vol->PartLength = partInfo.PartitionLength; Vol->PartNo = partInfo.PartitionNumber; Vol->PartitionType = partInfo.PartitionType;
success = ClRtlGetDriveLayoutTable(hFile, &driveLayout, 0);
if ( !success ) {
status = GetLastError();
if ( ERROR_NOT_READY != status ) { (DiskpLogEvent)(RESOURCE_TYPE, LOG_WARNING, L"[PnP] GetVolumeInfo: GetDriveLayout (%1!ws!), error %2!u!\n", Vol->Name, status ); }
goto FnExit; }
Vol->Signature = driveLayout->Signature;
LocalFree(driveLayout);
GetAssignedLetter(Vol->Name, &Vol->DriveLetter);
FnExit:
if ( FileHandle ) { *FileHandle = hFile; } else { DevfileClose( hFile ); }
//
// If old update value was zero, then it is now 1. Reset it to
// zero so another update can take place if needed.
//
if ( 0 == oldValue ) { InterlockedExchange( &Vol->UpdateActive, 0 ); }
return status;
} // GetVolumeInfo
DWORD NotificationWatcherThread( IN LPVOID unused )
/*++
Routine Description:
Creates window. Process messages until WM_QUIT is received
Arguments:
unused
Return Value:
status
--*/
{ WNDCLASSEX cl; ATOM classAtom; DWORD status = ERROR_SUCCESS; static WCHAR* clsname = L"RESDLL!DISKS!MESSAGEWND"; HDEVNOTIFY devNotify = 0; MSG msg;
try {
SetErrorMode(SEM_FAILCRITICALERRORS); InitializeListHead( &VolumeList ); InitializeListHead( &WaitingDisks ); InitializeListHead( &WatchedList );
ZeroMemory( &cl, sizeof(cl) );
cl.cbSize = sizeof(cl); cl.lpfnWndProc = TestWndProc; cl.lpszClassName = clsname;
classAtom = RegisterClassEx( &cl ); if (classAtom == 0) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] Failed to register window class, error %1!u!.\n", status ); return status; }
DummyWindow = CreateWindowEx( 0, // extended window style
clsname, // pointer to registered class name
L"ClusterDiskPnPWatcher",// pointer to window name
0, // window style
0, // horizontal position of window
0, // vertical position of window
0, // window width
0, // window height
HWND_MESSAGE, // handle to parent or owner window
0, // handle to menu, or child-window identifier
0, // handle to application instance (ignored on NT)
NULL // pointer to window-creation data
); if (DummyWindow == 0) { status = GetLastError(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] Failed to create message window, error %u.\n", status ); UnregisterClass( clsname , 0); return status; }
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] PnP window created successfully.\n");
//
// Call AddVolumes after registering for device arrival notification.
//
status = RegisterDeviceInterface(DummyWindow, &MOUNTDEV_MOUNTED_DEVICE_GUID, &devNotify); AddVolumes();
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] PnP interface registration complete.\n");
if ( NULL != PnpInterfacesRegistered ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] Setting PnP interface registration event.\n"); SetEvent( PnpInterfacesRegistered ); }
if (status == ERROR_SUCCESS) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] NotifierThread is waiting for messages.\n"); while(GetMessage(&msg, 0, 0, 0)) { if (msg.message == WM_QUIT) { break; } DispatchMessage(&msg); } MyUnregisterDeviceNotification( devNotify ); #if 0
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] NotifierThread is shutting down.\n"); #endif
} else { (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] Unable to register for MOUNTDEV_MOUNTED_DEVICE_GUID, error %1!u!.\n", status ); }
DestroyWindow( DummyWindow ); DummyWindow = 0; UnregisterClass( clsname , 0 );
// Use a lock here as the online thread might be parsing the volume list.
AcquireExclusive( &PnpVolumeLock ); while ( !IsListEmpty(&VolumeList) ) { PLIST_ENTRY listEntry; PVOLUME vol; listEntry = RemoveHeadList(&VolumeList); vol = CONTAINING_RECORD( listEntry, VOLUME, ListEntry ); DestroyVolume(vol); } ReleaseExclusive( &PnpVolumeLock ); #if 0
(DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] PnpThread: Volumes destroyed.\n"); #endif
} except (EXCEPTION_EXECUTE_HANDLER) { //
// We can leave without this thread
//
status = GetExceptionCode(); (DiskpLogEvent)( RESOURCE_TYPE, LOG_ERROR, L"[PnP] PnpThread: Exception caught, error %1!u!.\n", status ); }
InterlockedCompareExchange(&PnPInitialized, FALSE, TRUE); return status; }
VOID ProcessMountPointChange( HDEVNOTIFY devNotify, DWORD Signature ) /*++
Routine Description:
Updates mount point info in the cluster registry.
Arguments:
devNotify - Handle to the device notification.
Signature - Disk signature when a volume is being added or removed from the system. If zero, then volume is not added or removed.
Return Value:
None
--*/ { PDISK_RESOURCE resourceEntry; PVOLUME vol = 0; DWORD diskSig = 0;
if ( !Signature ) {
//
// Get the volume for the device notification.
//
vol = FindVolume( devNotify );
if ( !vol ) {
(DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] ProcessMountPointChange: devNotify %1!d! is not in the list \n", devNotify ); return; }
diskSig = vol->Signature;
} else {
diskSig = Signature; }
//
// Search the WatchedList to find a disk that matches the signature.
// If no entry found, we are not watching this disk.
//
resourceEntry = FindDisk( diskSig );
if ( !resourceEntry ) { (DiskpLogEvent)( RESOURCE_TYPE, LOG_INFORMATION, L"[PnP] ProcessMountPointChange: Unable to get ResourceEntry for signature %1!x! \n", diskSig ); return; }
//
// If disk not online, don't do anything.
//
if ( !resourceEntry->Valid ) { return; }
//
// If a new volume is being added to or removed from the system, then
// rebuild the mountpoint list. Otherwise, just validate mountpoints.
//
if ( Signature ) { DisksProcessMountPointInfo( resourceEntry ); } else { DisksUpdateMPList( resourceEntry ); }
} // ProcessMountPointChange
//////////////////////////////////////////////////////////////////////////////////
DWORD QueueWaitForVolumeEvent( HANDLE Event, PDISK_RESOURCE ResourceEntry ) /*++
Routine Description:
Queues a request to watch for particular volume arrivals. The event will be signaled only when all volumes are available on the system.
Arguments:
Event - event to be signaled when all volumes for the specified disk are available.
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
Return Value:
ERROR_SUCCESS - request queued.
Win32 error on failure.
--*/ { PWAITING_DISK waitDisk; PMOUNTIE_VOLUME mountVol = ResourceEntry->MountieInfo.Volume;
DWORD dwError = ERROR_SUCCESS;
waitDisk = LocalAlloc( LPTR, sizeof(WAITING_DISK) );
if ( !waitDisk ) { dwError = GetLastError(); (DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[PnP] QueueWaitForVolumeEvent: can't allocate storage for disk entry. Error %1!u! \n", dwError ); goto FnExit; }
waitDisk->ResourceEntry = ResourceEntry; waitDisk->Event = Event; waitDisk->Signature = mountVol->Signature; waitDisk->PartitionCount = mountVol->PartitionCount;
AcquireExclusive( &PnpWaitingListLock ); InsertHeadList( &WaitingDisks, &waitDisk->ListEntry ); ReleaseExclusive( &PnpWaitingListLock );
FnExit:
return dwError;
} // QueueWaitForVolumeEvent
BOOL IsDiskInPnpVolumeList( PDISK_RESOURCE ResourceEntry, BOOL UpdateVolumeList ) /*++
Routine Description:
Checks all the volumes currently known by the PnP thread and see if all volumes for the specified disk are recognized.
Arguments:
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
UpdateVolumeList - TRUE means call AddVolumes to make sure all volumes are in the volume list.
Return Value:
TRUE - If all volumes for the specified disk are available on the system.
--*/ { PLIST_ENTRY entry; PVOLUME vol = 0; PMOUNTIE_VOLUME mountVol = ResourceEntry->MountieInfo.Volume;
DWORD partitionCount = 0;
BOOL retVal = FALSE;
partitionCount = 0; AcquireShared( &PnpVolumeLock );
for ( entry = VolumeList.Flink; entry != &VolumeList; entry = entry->Flink ) { vol = CONTAINING_RECORD( entry, VOLUME, ListEntry );
GetVolumeInfo( vol, NULL );
if ( vol->Signature && vol->Signature == mountVol->Signature ) { partitionCount++; } }
ReleaseShared( &PnpVolumeLock );
//
// Might be some non-NTFS partitions on the disk, so if there
// are more volumes than partitions, we are good.
//
retVal = ( partitionCount >= mountVol->PartitionCount ) ? TRUE : FALSE;
//
// If we didn't find all the volumes, ask system to walk through the
// volumes again.
//
if ( !retVal && UpdateVolumeList ) {
//
// This call shouldn't be required. However, sometimes we can't
// find volumes that should be available. So we need to walk through
// the pnp list again.
//
AddVolumes();
//
// Walk the list one more time...
//
partitionCount = 0; AcquireShared( &PnpVolumeLock );
for ( entry = VolumeList.Flink; entry != &VolumeList; entry = entry->Flink ) { vol = CONTAINING_RECORD( entry, VOLUME, ListEntry );
GetVolumeInfo( vol, NULL );
if ( vol->Signature && vol->Signature == mountVol->Signature ) { partitionCount++; } }
ReleaseShared( &PnpVolumeLock );
//
// Might be some non-NTFS partitions on the disk, so if there
// are more volumes than partitions, we are good.
//
retVal = ( partitionCount >= mountVol->PartitionCount ) ? TRUE : FALSE; }
return retVal;
} // IsDiskInPnpVolumeList
PWAITING_DISK FindWaitingDisk( DWORD Signature ) /*++
Routine Description:
Find the entry in the waiting list for the specified disk signature.
The caller will hold the critical section.
Arguments:
Signature - Disk signature of the entry to be removed.
Return Value:
Pointer to a WAITING_DISK entry for the disk.
NULL if entry not found.
--*/ { PLIST_ENTRY entry; PWAITING_DISK waitDisk = NULL;
if ( !Signature ) { goto FnExit; }
for ( entry = WaitingDisks.Flink; entry != &WaitingDisks; entry = entry->Flink ) { waitDisk = CONTAINING_RECORD( entry, WAITING_DISK, ListEntry );
if ( waitDisk->Signature == Signature ) { goto FnExit; }
waitDisk = 0; }
FnExit:
return waitDisk;
} // FindWaitingDisk
DWORD RemoveWaitForVolumeEvent( PDISK_RESOURCE ResourceEntry ) /*++
Routine Description:
Remove from the disk waiting list the entry for the specified disk.
Arguments:
ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
Return Value:
--*/ { PWAITING_DISK waitDisk = NULL; PMOUNTIE_VOLUME mountVol = ResourceEntry->MountieInfo.Volume;
AcquireExclusive( &PnpWaitingListLock );
waitDisk = FindWaitingDisk( mountVol->Signature );
if ( !waitDisk ) { #if DBG
(DiskpLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[PnP] RemoveWaitForVolumeEvent: can't locate waiting volume in list \n" ); #endif
ReleaseExclusive( &PnpWaitingListLock ); return ERROR_INVALID_PARAMETER; }
RemoveEntryList( &waitDisk->ListEntry ); ReleaseExclusive( &PnpWaitingListLock );
LocalFree( waitDisk );
return ERROR_SUCCESS;
} // RemoveWaitForVolumeEvent
|