Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2624 lines
70 KiB

/*++
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