/*++

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"

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

WCHAR g_DiskResource[] = L"rtPhysical Disk";
#define RESOURCE_TYPE ((RESOURCE_HANDLE)g_DiskResource)

extern PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT;    // L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\";

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

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;

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;
    LARGE_INTEGER PartOffset;
    LARGE_INTEGER PartLength;
    ULONG         PartNo;
    BYTE PartitionType;
    UCHAR 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;
        
    INT     len;
    
    HANDLE  fileHandle;
    
    BOOL    duplicateEntry;
    BOOL    keepVolume = FALSE;

    //
    // 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;
    }

    (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION,
        L"[PnP] AddVolume: Attempting to add volume %1!s!\n", volGuid );
    
    len = wcslen(volGuid);
    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;
    }

    ZeroMemory( vol, sizeof(VOLUME) + len * sizeof(WCHAR) );
    wcscpy(vol->Name, volGuid);
    
    //
    // 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;
    }

    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 == _wcsnicmp( 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 );
    }
}


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 );
    
    //
    // 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 //////////////////////////////////////////

//////////////////// WatchedDiskTable //////////////////////
//
// We maintain a table 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
//
PDISK_RESOURCE WatchedDiskTable[MAX_DISKS] = {0};
INT WatchedDiskCount = 0;

INT
FindDisk(DWORD Signature)
{
    INT i;
    
    if ( !Signature ) {
        return -1;
    }
    
    for(i = 0; i < WatchedDiskCount; ++i) {
        if (WatchedDiskTable[i]->DiskInfo.Params.Signature == Signature) {
            return i;
        }
    }
    return -1;
}

VOID
RemoveDisk(
    PDISK_RESOURCE ResourceEntry
    )
{
    INT i = FindDisk(ResourceEntry->DiskInfo.Params.Signature);
    (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION,
        L"[PnP] Stop watching disk %1!x!\n",
        ResourceEntry->DiskInfo.Params.Signature );

    if (i < 0) {
       (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING,
           L"[PnP] RemoveDisk: disk %1!x! not found\n", 
           ResourceEntry->DiskInfo.Params.Signature);
       return;
    }
    --WatchedDiskCount;
    if (WatchedDiskCount > 0) {
        WatchedDiskTable[i] = WatchedDiskTable[WatchedDiskCount];
    }
}

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
    )
{
    INT i = FindDisk(ResourceEntry->DiskInfo.Params.Signature);
    PDRIVE_LAYOUT_INFORMATION driveLayout;
    HANDLE fileHandle;
    WCHAR deviceName[MAX_PATH];
    PLIST_ENTRY entry;
    BOOL success;

    (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION,
        L"[PnP] Start watching disk %1!x!\n",
        ResourceEntry->DiskInfo.Params.Signature );

    if (i >= 0) {
        (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
            L"[PnP] AddDisk: disk %1!x! is already being watched\n", 
            ResourceEntry->DiskInfo.Params.Signature);
        return;
    }
    if (WatchedDiskCount >= MAX_DISKS) {
        (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
            L"[PnP] AddDisk: Disk limit is reached\n");
        return;
    }
    WatchedDiskTable[WatchedDiskCount++] = ResourceEntry;

    // Now we need to verify that we are watching for changes on every //
    // recognized partition on this drive                              //

    wsprintf( deviceName, L"\\\\.\\PhysicalDrive%d", ResourceEntry->DiskInfo.PhysicalDrive );
    fileHandle = CreateFile(deviceName+0,
                     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!\n", deviceName);
        return;
    }

    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() );
        return;
    }

    // Clear PartitionType field. We will be using it to mark partions
    // which are in the our list of watched volumes

    for (i = 0; i < (INT)driveLayout->PartitionCount; ++i) {
        driveLayout->PartitionEntry[i].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 (i = 0; i < (INT)driveLayout->PartitionCount; ++i) {
        if (driveLayout->PartitionEntry[i].PartitionType == 0
            && driveLayout->PartitionEntry[i].RecognizedPartition
            ) 
        {
            swprintf( deviceName, GLOBALROOT_HARDDISK_PARTITION_FMT, 
                      ResourceEntry->DiskInfo.PhysicalDrive, 
                      driveLayout->PartitionEntry[i].PartitionNumber);
            AddVolume( deviceName );
        }
    }

    LocalFree( driveLayout );
}
//////////////////// WatchedDiskTable 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

--*/
{
    INT i = FindDisk(vol->Signature);
    PDISK_RESOURCE ResourceEntry;
    MOUNTIE_INFO Info;
    HANDLE fileHandle;
    DWORD status;
    PVOID OldMountieVolume;
    WCHAR deviceName[MAX_PATH];

    if(i == -1) {
        return;
    }

    ResourceEntry = WatchedDiskTable[i];
    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) );
    
    wsprintf( deviceName, L"\\\\.\\PhysicalDrive%d", ResourceEntry->DiskInfo.PhysicalDrive );
    fileHandle = CreateFile(deviceName+0,
                     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;

    lstrcpy(buf, CLUSREG_KEYNAME_CLUSTER);
    lstrcat(buf, L"\\");
    lstrcat(buf, CLUSREG_KEYNAME_QUORUM);

    Status = RegOpenKey( HKEY_LOCAL_MACHINE, buf, &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.
    //

    lstrcpy(buf, CLUSREG_KEYNAME_CLUSTER);
    lstrcat(buf, L"\\");
    lstrcat(buf, CLUSREG_KEYNAME_RESOURCES);
    lstrcat(buf, L"\\");
    lstrcat(buf, guid);
    lstrcat(buf, L"\\");
    lstrcat(buf, CLUSREG_KEYNAME_PARAMETERS);
    
    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) {
        (DiskpLogEvent)(RESOURCE_TYPE, LOG_ERROR,
            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) == -1) {
        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);
    UCHAR 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) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    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) {
                RemoveVolume(p->dbch_hdevnotify);
            } else if (wParam == DBT_CUSTOMEVENT) {
                PVOLUME Vol = 0;
                PWCHAR guidName = 0;
                LPDWORD dw = (LPDWORD)&p->dbch_eventguid;
                
                Vol = FindVolume( p->dbch_hdevnotify );

                if ( IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_NAME_CHANGE) ) 
                {
                    // Update disk info.
                    GetVolumeInfo( Vol, NULL );
    
                    ProcessDriveLetterChange( p->dbch_hdevnotify );
                    ProcessMountPointChange( p->dbch_hdevnotify );
                    guidName = L"GUID_IO_VOLUME_NAME_CHANGE";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_CHANGE) ) 
                {
                    // Update disk info.
                    GetVolumeInfo( Vol, NULL );
    
                    ProcessVolumeInfoChange( p->dbch_hdevnotify );
                    ProcessMountPointChange( p->dbch_hdevnotify );
                    guidName = L"GUID_IO_VOLUME_CHANGE";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_LOCK) ) 
                {
                    guidName = L"GUID_IO_VOLUME_LOCK";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_UNLOCK) ) 
                {
                    guidName = L"GUID_IO_VOLUME_UNLOCK";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_MOUNT) ) 
                {
//                        ProcessDriveLetterChange( p->dbch_hdevnotify );
                    guidName = L"GUID_IO_VOLUME_MOUNT";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_DISMOUNT) ) 
                {
                    guidName = L"GUID_IO_VOLUME_DISMOUNT";
                } 
                else if (IsEqualGUID(&p->dbch_eventguid, &GUID_IO_VOLUME_LOCK_FAILED) ) 
                {
                    guidName = L"GUID_IO_VOLUME_LOCK_FAILED";
                } 
                else if (IsEqualGUID(&p->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!) received.\n", 
                             guidName, NICE_DRIVE_LETTER(Vol->DriveLetter), Vol->PartNo );
                    } else {
                        (DiskpLogEvent)(
                             RESOURCE_TYPE,
                             LOG_INFORMATION,
                             L"[PnP] Event %1!s! for %2!d! received\n", 
                             guidName, p->dbch_hdevnotify );
                    }
                } else {
                    (DiskpLogEvent)(
                         RESOURCE_TYPE,
                         LOG_INFORMATION,
                         L"[PnP] Event %2!x! %3!x! %4!x! %5!x! for %1!d! received\n", 
                         p->dbch_hdevnotify, dw[0], dw[1], dw[2], dw[3] );
                }
            }
//#endif
            break;
        }
    }
    return TRUE;
}


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;

    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 ( !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();

            //
            // 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 ( 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;
    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;
    }

    (DiskpLogEvent)(RESOURCE_TYPE, LOG_INFORMATION,
        L"[PnP] GetVolumeInfo: Updating info for %1!s!\n", Vol->Name );
    
    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);
    }
    
    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();
        (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 );
    }
    
    return status;
    
}   // UpdateVolumeInfo


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

Routine Description:

    Updates mount point info in the cluster registry.

Arguments:

    devNotify - Handle to the device notification.  

Return Value:

    None

--*/
{
    PDISK_RESOURCE resourceEntry;
    PVOLUME vol;
    INT idx;
    
    //
    // 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;
    }
    
    //
    // Search the WatchedDiskTable to find a disk that matches the signature.  
    // The return value is the index into the WatchedDiskTable.
    //
    
    idx = FindDisk( vol->Signature );
    if ( idx == -1 ) {
        (DiskpLogEvent)(
            RESOURCE_TYPE, 
            LOG_INFORMATION,
            L"[PnP] ProcessMountPointChange: Unable to find disk for signature %1!x! \n", 
            vol->Signature );
        return;
    }

    //
    // Get the ResourceEntry for the disk.
    //
    
    resourceEntry = WatchedDiskTable[idx];

    if ( !resourceEntry ) {
        
        (DiskpLogEvent)(
            RESOURCE_TYPE,
            LOG_ERROR,
            L"[PnP] ProcessMountPointChange: Unable to get ResourceEntry for signature %1!x! \n",
            vol->Signature );
        return;
    }

    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;

    if ( UpdateVolumeList ) {
        
        //
        // This call shouldn't be required.  However, sometimes we can't find 
        // find volumes that should be available.  So we need to walk through
        // the pnp list again.
        //
        AddVolumes();
    }

    AcquireExclusive( &PnpVolumeLock );
        
    for ( entry = VolumeList.Flink;
          entry != &VolumeList;
          entry = entry->Flink
        )
    {
        vol = CONTAINING_RECORD(
                   entry,
                   VOLUME,
                   ListEntry
                   );

        GetVolumeInfo( vol, NULL );
        
        if ( vol->Signature == mountVol->Signature ) {
            partitionCount++;
        }
    }

    ReleaseExclusive( &PnpVolumeLock );

    //    
    // Might be some non-NTFS partitions on the disk, so if there
    // are more volumes than partitions, we are good.
    //
    
    return ( ( partitionCount >= mountVol->PartitionCount ) ? TRUE : FALSE ) ;
    
}   // 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 ) {
        (DiskpLogEvent)(
             ResourceEntry->ResourceHandle,
             LOG_INFORMATION,
             L"[PnP] RemoveWaitForVolumeEvent: can't locate waiting volume in list \n" );
        ReleaseExclusive( &PnpWaitingListLock );
        return ERROR_INVALID_PARAMETER;
    }
    
    RemoveEntryList( &waitDisk->ListEntry );
    ReleaseExclusive( &PnpWaitingListLock );
    
    LocalFree( waitDisk );

    return ERROR_SUCCESS;
    
}   // RemoveWaitForVolumeEvent