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
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
|
|
|
|
|