/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    perfdisk.c

Abstract:


Author:

    Bob Watson (a-robw) Aug 95

Revision History:

--*/

// define the WMI Guids for this program
#ifndef INITGUID
#define INITGUID 1
#endif  

//
// Force everything to be UNICODE
//
#ifndef UNICODE
#define UNICODE
#endif

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntexapi.h>
#include <windows.h>
#include <ole2.h>
#pragma warning ( disable : 4201 ) 
#include <wmium.h>
#pragma warning ( default : 4201 )
#include <wmiguid.h>
#include <winperf.h>
#if DBG
#include <stdio.h>
#include <stdlib.h>
#endif
#include <ntprfctr.h>
#include <perfutil.h>
#include <assert.h>
#include "perfdisk.h"
#include "diskmsg.h"

// define this symbol to test if diskperf has installed itself
//  as an upper filter
// if this symbol is undefined, then check for diskperf before
//  returning any logical disk counters
#define _DONT_CHECK_FOR_VOLUME_FILTER

#ifndef _DONT_CHECK_FOR_VOLUME_FILTER
#include <regstr.h>     // for REGSTR_VAL_UPPERFILTERS
#endif

// bit field definitions for collect function flags

#define POS_COLLECT_PDISK_DATA      ((DWORD)0x00000001)
#define POS_COLLECT_LDISK_DATA      ((DWORD)0x00000003)
#define POS_COLLECT_IGNORE          ((DWORD)0x80000000)

#define POS_COLLECT_GLOBAL_DATA     ((DWORD)0x00000003)
#define POS_COLLECT_FOREIGN_DATA    ((DWORD)0)
#define POS_COLLECT_COSTLY_DATA     ((DWORD)0)

// global variables to this DLL

HANDLE  ThisDLLHandle = NULL;
HANDLE  hEventLog     = NULL;
HANDLE  hLibHeap      = NULL;

BOOL    bShownDiskPerfMessage = FALSE;
BOOL    bShownDiskVolumeMessage = FALSE;

LPWSTR  wszTotal = NULL;

const WCHAR cszNT4InstanceNames[] = {L"NT4 Instance Names"};
const WCHAR cszRegKeyPath[] = {L"System\\CurrentControlSet\\Services\\PerfDisk\\Performance"};

const WCHAR cszVolumeKey[] = {L"SYSTEM\\CurrentControlSet\\Control\\Class\\{71A27CDD-812A-11D0-BEC7-08002BE2092F}"};
const WCHAR cszRefreshInterval[] = {L"VolumeSpaceRefreshInterval"};

#define DISKPERF_SERVICE_NAME L"DiskPerf"
ULONG CheckVolumeFilter();

PDRIVE_VOLUME_ENTRY pPhysDiskList = NULL;
DWORD               dwNumPhysDiskListEntries = 0;
PDRIVE_VOLUME_ENTRY pVolumeList = NULL;
DWORD               dwNumVolumeListEntries = 0;
DWORD               dwWmiDriveCount = 0;
BOOL                bRemapDriveLetters = TRUE;
DWORD               dwMaxVolumeNumber = 0;

// start off with a big buffer then size according to return values
DWORD   WmiBufSize  = 0x10000;   // this can be smaller when the Diskperf.sys 
DWORD   WmiAllocSize = 0x10000;  // function is fixed to return the right status
LPBYTE  WmiBuffer   = NULL;

// variables local to this module

static POS_FUNCTION_INFO    posDataFuncInfo[] = {
    {LOGICAL_DISK_OBJECT_TITLE_INDEX,   POS_COLLECT_LDISK_DATA,     0, CollectLDiskObjectData},
    {PHYSICAL_DISK_OBJECT_TITLE_INDEX,  POS_COLLECT_PDISK_DATA,     0, CollectPDiskObjectData}
};

#define POS_NUM_FUNCS   (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))

static  bInitOk  = FALSE;
static  DWORD   dwOpenCount = 0;

WMIHANDLE   hWmiDiskPerf = NULL;

PM_OPEN_PROC    OpenDiskObject;
PM_COLLECT_PROC CollecDiskObjectData;
PM_CLOSE_PROC   CloseDiskObject;

DOUBLE      dSysTickTo100Ns;

#if DBG
const WCHAR cszDebugPrintLevel[] = {L"DebugPrintLevel"};

#define DEBUG_BUFFER_LENGTH MAX_PATH*2

ULONG_PTR HeapUsed = 0;
ULONG oldPLSize = 0;
ULONG oldVLSize = 0;
ULONG wszSize = 0;

ULONG PerfDiskDebug = 0;
UCHAR PerfDiskDebugBuffer[DEBUG_BUFFER_LENGTH];

#endif


BOOL
WriteNewBootTimeEntry (
    LONGLONG *pBootTime
)
{
    LONG    lStatus;
    HKEY    hKeyPerfDiskPerf;
    DWORD   dwType, dwSize;
    BOOL    bReturn = FALSE;

    // try to read the registry value of the last time
    // this error was reported 
    lStatus = RegOpenKeyExW (
        HKEY_LOCAL_MACHINE,
        cszRegKeyPath,
        (DWORD)0,
        KEY_WRITE,
        &hKeyPerfDiskPerf);
    if (lStatus == ERROR_SUCCESS) {
        // read the key value
        dwType = REG_BINARY;
        dwSize = sizeof (*pBootTime);
        lStatus = RegSetValueExW (
            hKeyPerfDiskPerf,
            (LPCWSTR)L"SystemStartTimeOfLastErrorMsg",
            0L,  // reserved 
            dwType,
            (LPBYTE)pBootTime,
            dwSize);
        if (lStatus == ERROR_SUCCESS) {
            bReturn = TRUE;
        } else {
            // the value hasn't been written and 
            SetLastError (lStatus);
        } // else assume the value hasn't been written and 
          // return FALSE
        RegCloseKey (hKeyPerfDiskPerf);
    } else {
        // assume the value hasn't been written and 
        SetLastError (lStatus);
    }

    return bReturn;
}
BOOL
NT4NamesAreDefault ()
{
    LONG    lStatus;
    HKEY    hKeyPerfDiskPerf;
    DWORD   dwType, dwSize;
    DWORD   dwValue;
    BOOL    bReturn = FALSE;

    // try to read the registry value of the last time
    // this error was reported 
    lStatus = RegOpenKeyExW (
        HKEY_LOCAL_MACHINE,
        cszRegKeyPath,
        (DWORD)0,
        KEY_READ,
        &hKeyPerfDiskPerf);
    if (lStatus == ERROR_SUCCESS) {
        // read the key value
        dwType = 0;
        dwSize = sizeof (dwValue);
        lStatus = RegQueryValueExW (
            hKeyPerfDiskPerf,
            cszNT4InstanceNames,
            0L,  // reserved 
            &dwType,
            (LPBYTE)&dwValue,
            &dwSize);
        if ((lStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            if (dwValue != 0) {
                bReturn = TRUE;
            } 
        } else {
            // the key is not present or not accessible so
            // leave default as is and 
            // return FALSE
        }
        RegCloseKey (hKeyPerfDiskPerf);
    } else {
        // the key could not be opened.
        SetLastError (lStatus);
    }

    return bReturn;
}

BOOL
SystemHasBeenRestartedSinceLastEntry (
    DWORD   dwReserved, // just in case we want to have multiple tests in the future
    LONGLONG *pBootTime // a buffer to receive the current boot time
)
{
    BOOL        bReturn = TRUE;
    NTSTATUS    ntStatus = ERROR_SUCCESS;
    SYSTEM_TIMEOFDAY_INFORMATION    SysTimeInfo;
    DWORD       dwReturnedBufferSize = 0;
    HKEY        hKeyPerfDiskPerf;
    LONG        lStatus;
    DWORD       dwType;
    DWORD       dwSize;
    LONGLONG    llLastErrorStartTime;

    DBG_UNREFERENCED_PARAMETER(dwReserved);

    // get the current system boot time (as a filetime)
    memset ((LPVOID)&SysTimeInfo, 0, sizeof(SysTimeInfo));

    ntStatus = NtQuerySystemInformation(
        SystemTimeOfDayInformation,
        &SysTimeInfo,
        sizeof(SysTimeInfo),
        &dwReturnedBufferSize
        );

    if (NT_SUCCESS(ntStatus)) {
        // try to read the registry value of the last time
        // this error was reported 
        lStatus = RegOpenKeyExW (
            HKEY_LOCAL_MACHINE,
            cszRegKeyPath,
            (DWORD)0,
            KEY_READ,
            &hKeyPerfDiskPerf);
        if (lStatus == ERROR_SUCCESS) {
            // read the key value
            dwType = 0;
            dwSize = sizeof (llLastErrorStartTime);
            lStatus = RegQueryValueExW (
                hKeyPerfDiskPerf,
                (LPCWSTR)L"SystemStartTimeOfLastErrorMsg",
                0L,  // reserved 
                &dwType,
                (LPBYTE)&llLastErrorStartTime,
                &dwSize);
            if (lStatus == ERROR_SUCCESS) {
                assert (dwType == REG_BINARY);  // this should be a binary type
                assert (dwSize == sizeof (LONGLONG)); // and it should be 8 bytes long
                // compare times
                // if the times are the same, then this message has already been
                // written since the last boot so we don't need to do it again.
                if (SysTimeInfo.BootTime.QuadPart ==
                    llLastErrorStartTime) {
                    bReturn = FALSE;
                } // else they are the different times so return FALSE
            } // else assume the value hasn't been written and 
              // return TRUE
            RegCloseKey (hKeyPerfDiskPerf);
        } // else assume the value hasn't been written and 
          // return TRUE

        // return the boot time if a buffer was passed in
        if (pBootTime != NULL) {
            // save the time
            *pBootTime = SysTimeInfo.BootTime.QuadPart;
        }
    } // else assume that it has been rebooted and return TRUE

    return bReturn;
}

static
BOOL
DllProcessAttach (
    IN  HANDLE DllHandle
)
/*++

Description:

    perform any initialization function that apply to all object
    modules
   
--*/
{
    BOOL    bReturn = TRUE;
    WCHAR   wszTempBuffer[512];
    LONG    lStatus;
    DWORD   dwBufferSize;
    HKEY    hKeyPerfDiskPerf;

    LARGE_INTEGER   liSysTick;

    UNREFERENCED_PARAMETER(DllHandle);

    // create heap for this library
    if (hLibHeap == NULL) hLibHeap = HeapCreate (0, 1, 0);
    assert (hLibHeap != NULL);

    if (hLibHeap == NULL) {
        return FALSE;
    }
    // open handle to the event log
    if (hEventLog == NULL) hEventLog = MonOpenEventLog((LPWSTR)L"PerfDisk");
    assert (hEventLog != NULL);

    lStatus = GetPerflibKeyValue (
        szTotalValue,
        REG_SZ,
        sizeof(wszTempBuffer),
        (LPVOID)&wszTempBuffer[0],
        DEFAULT_TOTAL_STRING_LEN,
        (LPVOID)&szDefaultTotalString[0]);

    if (lStatus == ERROR_SUCCESS) {
        // then a string was returned in the temp buffer
        dwBufferSize = lstrlenW (wszTempBuffer) + 1;
        dwBufferSize *= sizeof (WCHAR);
        wszTotal = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
        if (wszTotal == NULL) {
            // unable to allocate buffer so use static buffer
            wszTotal = (LPWSTR)&szDefaultTotalString[0];
        } else {
            memcpy (wszTotal, wszTempBuffer, dwBufferSize);
#if DBG
            HeapUsed += dwBufferSize;
            wszSize = dwBufferSize;
            DebugPrint((4,
                "DllAttach: wszTotal add %d to %d\n",
                dwBufferSize, HeapUsed));
#endif
        }
    } else {
        // unable to get string from registry so just use static buffer
        wszTotal = (LPWSTR)&szDefaultTotalString[0];
    }

    QueryPerformanceFrequency (&liSysTick);
    dSysTickTo100Ns = (DOUBLE)liSysTick.QuadPart;
    dSysTickTo100Ns /= 10000000.0;

    lStatus = RegOpenKeyExW (
        HKEY_LOCAL_MACHINE,
        cszRegKeyPath,
        (DWORD)0,
        KEY_READ,
        &hKeyPerfDiskPerf);
    if (lStatus == ERROR_SUCCESS) {
        DWORD dwType = REG_DWORD;
        DWORD dwSize = sizeof(DWORD);
        ULONG interval;
        lStatus = RegQueryValueExW (
                    hKeyPerfDiskPerf,
                    cszRefreshInterval,
                    0L,  // reserved
                    &dwType,
                    (LPBYTE)&interval,
                    &dwSize);
        if ((lStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            g_lRefreshInterval = interval;
        }
#if DBG
        dwSize = sizeof(DWORD);
        dwType = REG_DWORD;
        lStatus = RegQueryValueExW (
                    hKeyPerfDiskPerf,
                    cszDebugPrintLevel,
                    0L,
                    &dwType,
                    (LPBYTE) &interval,
                    &dwSize);
        if ((lStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            PerfDiskDebug = interval;
        }
#endif
        RegCloseKey (hKeyPerfDiskPerf);
    }

    return bReturn;
}

static
BOOL
DllProcessDetach (
    IN  HANDLE DllHandle
)
{
    UNREFERENCED_PARAMETER(DllHandle);

    if (dwOpenCount > 0) {
        // then close the object now, since the DLL's being discarded
        // prematurely, this is our last chance.
        // this is to insure the object is closed.
        dwOpenCount = 1;
        CloseDiskObject();
    }

    if ((wszTotal != NULL) && (wszTotal != &szDefaultTotalString[0])) {
        FREEMEM (hLibHeap, 0, wszTotal);
#if DBG
        HeapUsed -= wszSize;
        DebugPrint((4,
            "DllDetach: wsz freed %d to %d\n",
            wszSize, HeapUsed));
        wszSize = 0;
#endif
        wszTotal = NULL;
    }

    if (HeapDestroy (hLibHeap)) {
        hLibHeap = NULL;
        pVolumeList = NULL;
        pPhysDiskList = NULL;
        dwNumVolumeListEntries = 0;
        dwNumPhysDiskListEntries = 0;
    }

    if (hEventLog != NULL) {
        MonCloseEventLog ();
    }
    return TRUE;
}

BOOL
__stdcall
DllInit(
    IN HANDLE DLLHandle,
    IN DWORD  Reason,
    IN LPVOID ReservedAndUnused
)
{
    ReservedAndUnused;

    // this will prevent the DLL from getting
    // the DLL_THREAD_* messages
    DisableThreadLibraryCalls (DLLHandle);

    switch(Reason) {
        case DLL_PROCESS_ATTACH:
            return DllProcessAttach (DLLHandle);

        case DLL_PROCESS_DETACH:
            return DllProcessDetach (DLLHandle);

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        default:
            return TRUE;
    }
}

DWORD APIENTRY
MapDriveLetters()
{
    DWORD   status = ERROR_SUCCESS;
    DWORD   dwLoopCount;   
    PDRIVE_VOLUME_ENTRY pTempPtr;
    DWORD   dwDriveCount;
    DWORD   dwThisEntry;
    PVOID   pOldBuffer;
#if DBG
    LONG64 startTime, endTime;
    LONG elapsed;
#endif

    if (pPhysDiskList != NULL) {
        FREEMEM (hLibHeap, 0, pPhysDiskList);
#if DBG
        HeapUsed -= oldPLSize;
        DebugPrint((4,"MapDriveLetters: PL Freed %d to %d\n",
            oldPLSize, HeapUsed));
        oldPLSize = 0;
#endif
        pPhysDiskList = NULL;
    }
#ifdef DBG
    GetSystemTimeAsFileTime((LPFILETIME) &startTime);
    DebugPrint((1, "BEGIN MapDriveLetters:\n",
                   status));
#endif
    dwNumPhysDiskListEntries = INITIAL_NUM_VOL_LIST_ENTRIES;

    // Initially allocate enough entries for drives A through Z
    pPhysDiskList = (PDRIVE_VOLUME_ENTRY)ALLOCMEM (
        hLibHeap, HEAP_ZERO_MEMORY, 
        (dwNumPhysDiskListEntries * sizeof (DRIVE_VOLUME_ENTRY)));

#if DBG
    if (pPhysDiskList == NULL) {
        DebugPrint((2,
            "MapDriveLetters: pPhysDiskList alloc failure\n"));
    }
#endif

    if (pPhysDiskList != NULL) {
        // try until we get a big enough buffer
#if DBG
        ULONG oldsize = dwNumPhysDiskListEntries * sizeof(DRIVE_VOLUME_ENTRY);
        HeapUsed += oldsize;
        oldPLSize = oldsize;
        DebugPrint((4, "MapDriveLetter: Alloc %d to %d\n",
            oldsize, HeapUsed));
#endif
        dwLoopCount = 10;   // no more than 10 retries to get the right size
        while ((status = BuildPhysDiskList (
                hWmiDiskPerf,
                pPhysDiskList,
                &dwNumPhysDiskListEntries)) == ERROR_INSUFFICIENT_BUFFER) {

            DebugPrint ((3,
                "MapDriveLetters: BuildPhysDiskList returns: %d, requesting %d entries\n",
                status, dwNumPhysDiskListEntries));
#if DBG
            if (!HeapValidate(hLibHeap, 0, pPhysDiskList)) {
                DebugPrint((2,
                    "\tERROR! pPhysDiskList %X corrupted BuildPhysDiskList\n",
                    pPhysDiskList));
                DbgBreakPoint();
            }
#endif

            // if ERROR_INSUFFICIENT_BUFFER, then 
            // dwNumPhysDiskListEntries should contain the required size
            pOldBuffer = pPhysDiskList;
            pPhysDiskList = (PDRIVE_VOLUME_ENTRY)REALLOCMEM (
                hLibHeap, HEAP_ZERO_MEMORY, 
                pPhysDiskList, (dwNumPhysDiskListEntries * sizeof (DRIVE_VOLUME_ENTRY)));

            if (pPhysDiskList == NULL) {
                // bail if the allocation failed
                DebugPrint((2,
                    "MapDriveLetters: pPhysDiskList realloc failure\n"));
                status = ERROR_OUTOFMEMORY;
                FREEMEM(hLibHeap, 0, pOldBuffer);
                break;
            }
#if DBG
            else {
                HeapUsed -= oldsize; // subtract the old size and add new size
                oldPLSize = dwNumPhysDiskListEntries*sizeof(DRIVE_VOLUME_ENTRY);
                HeapUsed += oldPLSize;
                DebugPrint((4,
                    "MapDriveLetter: Realloc old %d new %d to %d\n",
                    oldsize, oldPLSize, HeapUsed));
            }
#endif
            dwLoopCount--;
            if (!dwLoopCount) {
                status = ERROR_OUTOFMEMORY;
                break;
            }
            DebugPrint ((3,
                "MapDriveLetters: %d retrying BuildPhysDiskList with %d entries\n",
                status, dwNumPhysDiskListEntries));
        }
    }

    else {      // do not bother going any further if no memory
        return ERROR_OUTOFMEMORY;
    }

    DebugPrint ((4,
        "MapDriveLetters: BuildPhysDiskList returns: %d\n", status));
#if DBG
    if (pPhysDiskList != NULL) {
        if (!HeapValidate(hLibHeap, 0, pPhysDiskList)) {
            DebugPrint((2, "\tERROR! pPhysDiskList %X corrupted after Builds\n",
                pPhysDiskList));
            DbgBreakPoint();
        }
    }
#endif

    if (pVolumeList != NULL) {
        // close any open handles
        dwThisEntry = dwNumVolumeListEntries;
        while (dwThisEntry != 0) {
            dwThisEntry--;
            if (pVolumeList[dwThisEntry].hVolume != NULL) {
                NtClose (pVolumeList[dwThisEntry].hVolume);
            }
        } 
#if DBG
        HeapUsed -= oldVLSize;
        DebugPrint((4,"MapDriveLetters: VL Freed %d to %d\n",
            oldVLSize, HeapUsed));
        oldVLSize = 0;
        if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
            DebugPrint((2, "\tERROR! pVolumeList %X is corrupted before free\n",
                pVolumeList));
            DbgBreakPoint();
        }
#endif
        FREEMEM (hLibHeap, 0, pVolumeList);
        pVolumeList = NULL;
    }
    dwNumVolumeListEntries = INITIAL_NUM_VOL_LIST_ENTRIES;

    // Initially allocate enough entries for letters C through Z
    pVolumeList = (PDRIVE_VOLUME_ENTRY)ALLOCMEM (
        hLibHeap, HEAP_ZERO_MEMORY, 
        (dwNumVolumeListEntries * sizeof (DRIVE_VOLUME_ENTRY)));

#if DBG
    if (pVolumeList == NULL) {
        DebugPrint((2,
            "MapDriveLetters: pPhysVolumeList alloc failure\n"));
    }
#endif

    if (pVolumeList != NULL) {
        // try until we get a big enough buffer
#if DBG
        ULONG oldsize = dwNumVolumeListEntries * sizeof (DRIVE_VOLUME_ENTRY);
        HeapUsed += oldsize;
        oldVLSize = oldsize;
        DebugPrint((4,
            "MapDriveLetter: Add %d HeapUsed %d\n", oldsize, HeapUsed));
#endif
        dwLoopCount = 10;   // no more than 10 retries to get the right size
        while ((status = BuildVolumeList (
                pVolumeList,
                &dwNumVolumeListEntries)) == ERROR_INSUFFICIENT_BUFFER) {
            // if ERROR_INSUFFICIENT_BUFFER, then 

            DebugPrint ((3,
                "MapDriveLetters: BuildVolumeList returns: %d, requesting %d entries\n",
                status, dwNumVolumeListEntries));

#if DBG
            if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
                DebugPrint((2, "\tERROR! pVolumeList %X corrupted in while\n",
                    pVolumeList));
                DbgBreakPoint();
            }
#endif
            // dwNumVolumeListEntries should contain the required size
            pOldBuffer = pVolumeList;
            pVolumeList = (PDRIVE_VOLUME_ENTRY)REALLOCMEM (
                hLibHeap, HEAP_ZERO_MEMORY, 
                pVolumeList, (dwNumVolumeListEntries * sizeof (DRIVE_VOLUME_ENTRY)));

            if (pVolumeList == NULL) {
                // bail if the allocation failed
                DebugPrint((2,
                    "MapDriveLetters: pPhysVolumeList realloc failure\n"));
                status = ERROR_OUTOFMEMORY;
                FREEMEM(hLibHeap, 0, pOldBuffer);
                break;
            }
#if DBG
            else {
                if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
                    DebugPrint((2, "\tpVolumeList %X corrupted - realloc\n",
                        pVolumeList));
                    DbgBreakPoint();
                }
                HeapUsed -= oldsize; // subtract the old size and add new size
                oldVLSize = dwNumVolumeListEntries*sizeof(DRIVE_VOLUME_ENTRY);
                HeapUsed += oldVLSize;
                DebugPrint((4,
                    "MapDriveLetter: Realloc old %d new %d to %d\n",
                    oldsize, oldVLSize, HeapUsed));
            }
#endif
            dwLoopCount--;
            if (!dwLoopCount) {
                status = ERROR_OUTOFMEMORY;
                break;
            }
            DebugPrint ((3,
                "MapDriveLetters: retrying BuildVolumeList with %d entries\n",
                status, dwNumVolumeListEntries));
        }

        DebugPrint ((4, "MapDriveLetters: BuildVolumeList returns %d\n", status));

#if DBG
        if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
            DebugPrint((2, "\tpVolumeList %X corrupted after build\n",
                pVolumeList));
            DbgBreakPoint();
        }
#endif

        if (status == ERROR_SUCCESS) {
            status = FindNewVolumes(
                        &pPhysDiskList,
                        &dwNumPhysDiskListEntries,
                        pVolumeList,
                        dwNumVolumeListEntries);
        }

        // now map the disks to their drive letters 
        if (status == ERROR_SUCCESS) {
            status = MapLoadedDisks (
                hWmiDiskPerf,
                pVolumeList,
                &dwNumVolumeListEntries,
                &dwMaxVolumeNumber,
                &dwWmiDriveCount
                );

            DebugPrint ((4,
                "MapDriveLetters: MapLoadedDisks returns status %d %d MaxVol %d WmiDrive\n",
                status, dwNumVolumeListEntries,
                dwMaxVolumeNumber, dwWmiDriveCount));
        }
        
#if DBG
        if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
            DebugPrint((2, "\tpVolumeList %X corrupted by MapLoadedDisks\n",
                pVolumeList));
            DbgBreakPoint();
        }
#endif

        if (status == ERROR_SUCCESS) {
            // now assign drive letters to the phys disk list
            dwDriveCount = 0;
            status = MakePhysDiskInstanceNames (
                    pPhysDiskList,
                    dwNumPhysDiskListEntries,
                    &dwDriveCount,
                    pVolumeList,
                    dwNumVolumeListEntries);
        
#if DBG
        if (!HeapValidate(hLibHeap, 0, pPhysDiskList)) {
            DebugPrint((2, "\tpPhysList %X corrupted by MakePhysDiskInst\n",
                pPhysDiskList));
            DbgBreakPoint();
        }
#endif
            if (status == ERROR_SUCCESS) {
                // then compress this into an indexed table
                // save original pointer
                pTempPtr = pPhysDiskList;

                // the function returns the last Drive ID
                // so we need to add 1 here to the count to include
                // the "0" drive
                dwDriveCount += 1;

                DebugPrint ((4, "\tDrive count now = %d\n",
                    dwDriveCount));

                // and allocate just enough for the actual physical drives
                pPhysDiskList = (PDRIVE_VOLUME_ENTRY)ALLOCMEM (
                    hLibHeap, HEAP_ZERO_MEMORY, 
                    (dwDriveCount * sizeof (DRIVE_VOLUME_ENTRY)));

                if (pPhysDiskList != NULL) {
                    status = CompressPhysDiskTable (
                        pTempPtr,
                        dwNumPhysDiskListEntries,
                        pPhysDiskList,
                        dwDriveCount);
        
#if DBG
        if (!HeapValidate(hLibHeap, 0, pPhysDiskList)) {
            DebugPrint((2, "\tpPhysList %X corrupted by CompressPhys\n",
                pPhysDiskList));
            DbgBreakPoint();
        }
#endif
                    if (status == ERROR_SUCCESS) {
                        dwNumPhysDiskListEntries = dwDriveCount;
                    }
                    else {  // free if cannot compress
                        FREEMEM(hLibHeap, 0, pPhysDiskList);
#if DBG
                        HeapUsed -= dwDriveCount * sizeof(DRIVE_VOLUME_ENTRY);
                        DebugPrint((4,
                            "MapDriveLetters: Compress freed %d to %d\n",
                            dwDriveCount*sizeof(DRIVE_VOLUME_ENTRY), HeapUsed));
#endif
                        pPhysDiskList = NULL;
                    }
                } else {
                    DebugPrint((2,"MapDriveLetters: pPhysDiskList alloc fail for compress\n"));
                    status = ERROR_OUTOFMEMORY;
                }
                if (pTempPtr) {     // Free the previous list
                    FREEMEM(hLibHeap, 0, pTempPtr);
#if DBG
                    HeapUsed -= oldPLSize;
                    DebugPrint((4,
                        "MapDriveLetters: tempPtr freed %d to %d\n",
                        oldPLSize, HeapUsed));
                    oldPLSize = 0;
#endif
                }
#if DBG
                if (status == ERROR_SUCCESS) {
                    oldPLSize = dwDriveCount * sizeof(DRIVE_VOLUME_ENTRY);
                    HeapUsed += oldPLSize;
                    DebugPrint((4,
                        "MapDriveLetters: Compress add %d to %d\n",
                        oldPLSize, HeapUsed));
                }
#endif
            }
        }
        if (status == ERROR_SUCCESS) {
            // clear the remap flag
            bRemapDriveLetters = FALSE;
        }
    } else {
        status = ERROR_OUTOFMEMORY;
    }
#if DBG
    GetSystemTimeAsFileTime((LPFILETIME) &endTime);
    elapsed = (LONG) ((endTime - startTime) / 10000);
    DebugPrint((1, "END MapDriveLetters: %d msec\n\n", elapsed));
#endif

    // TODO: Need to keep track of different status for PhysDisk & Volumes
    //       If Physdisk succeeds whereas Volume fails, need to log event
    //       and try and continue with Physdisk counters
    // TODO Post W2K: Free stuff if status != ERROR_SUCCESS
    return status;
}

DWORD APIENTRY
OpenDiskObject (
    LPWSTR lpDeviceNames
    )
/*++

Routine Description:

    This routine will initialize the data structures used to pass
    data back to the registry

Arguments:

    Pointer to object ID of each device to be opened (PerfGen)

Return Value:

    None.

--*/
{
    DWORD   status = ERROR_SUCCESS;
    LONGLONG    llLastBootTime;
    BOOL        bWriteMessage;
#if DBG
    LONG64 startTime, endTime;
    LONG elapsed;
#endif

    UNREFERENCED_PARAMETER (lpDeviceNames);

#ifdef DBG
    GetSystemTimeAsFileTime((LPFILETIME) &startTime);
    DebugPrint((1, "BEGIN OpenDiskObject:\n",
                   status));
#endif

    if (dwOpenCount == 0) {
        status = WmiOpenBlock (
            (GUID *)&DiskPerfGuid,
            GENERIC_READ,
            &hWmiDiskPerf);

#if DBG
        GetSystemTimeAsFileTime((LPFILETIME) &endTime);
        elapsed = (LONG) ((endTime - startTime) / 10000);
        DebugPrint((3, "WmiOpenBlock returns: %d in %d msec after BEGIN\n",
                   status, elapsed));
#endif

        if (status == ERROR_SUCCESS) {
            // build drive map
            status = MapDriveLetters();

            DebugPrint((3,
                "OpenDiskObject: MapDriveLetters returns: %d\n", status));
        }
        // determine instance name format
        bUseNT4InstanceNames = NT4NamesAreDefault();
#if DBG
        GetSystemTimeAsFileTime((LPFILETIME) &endTime);
        elapsed = (LONG) ((endTime - startTime) / 10000);
        DebugPrint((3,
            "OpenDiskObject: NT4Names - %d msec after BEGIN\n", status));
#endif

        if (status == ERROR_SUCCESS) {
            bInitOk = TRUE;
        }
    }

    if (status != ERROR_SUCCESS) {
        // check to see if this is a WMI error and if so only 
        // write the error once per boot cycle

        if (status == ERROR_WMI_GUID_NOT_FOUND) {
            bWriteMessage = SystemHasBeenRestartedSinceLastEntry (
                0, &llLastBootTime);
    
            if (bWriteMessage) {
                // update registry time
                WriteNewBootTimeEntry (&llLastBootTime);
                ReportEvent (hEventLog,
                    EVENTLOG_ERROR_TYPE,
                    0,
                    PERFDISK_UNABLE_QUERY_DISKPERF_INFO,
                    NULL,
                    0,
                    sizeof(DWORD),
                    NULL,
                    (LPVOID)&status);
            } // else it's already been written
        } else {
            // always write other messages
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,
                0,
                PERFDISK_UNABLE_OPEN,
                NULL,
                0,
                sizeof(DWORD),
                NULL,
                (LPVOID)&status);
        }
    } else {
        dwOpenCount++;
#ifndef _DONT_CHECK_FOR_VOLUME_FILTER
        if (!CheckVolumeFilter()) {
            posDataFuncInfo[0].dwCollectFunctionBit |= POS_COLLECT_IGNORE;
        }
#if DBG
        GetSystemTimeAsFileTime((LPFILETIME) &endTime);
        elapsed = (LONG) ((endTime - startTime) / 10000);
        DebugPrint((3,
            "OpenDiskObject: CheckVolFilter - %d msec after BEGIN\n", status));
#endif
#endif
    }

#if DBG
    if (status == ERROR_SUCCESS) {
        if (pPhysDiskList) {
            DebugPrint((4, "\t Validating pPhysDiskList %X at end Open\n",
                pPhysDiskList));
            if (!HeapValidate(hLibHeap, 0, pPhysDiskList)) {
                DebugPrint((2, "OpenDiskObject: PhysDiskList heap corrupt!\n"));
                DbgBreakPoint();
            }
        }
        if (pVolumeList) {
            DebugPrint((4, "\t Validating pVolumeList %X at end Open\n",
                pVolumeList));
            if (!HeapValidate(hLibHeap, 0, pVolumeList)) {
                DebugPrint((2, "OpenDiskObject: VolumeList heap corrupt!\n"));
                DbgBreakPoint();
            }
        }
        if (WmiBuffer) {
            DebugPrint((4, "\t Validating WmiBuffer %X at end Open\n",
                WmiBuffer));
            if (!HeapValidate(hLibHeap, 0, WmiBuffer)) {
                DebugPrint((2, "OpenDiskObject: WmiBuffer heap corrupt!\n"));
                DbgBreakPoint();
            }
        }
    }
    GetSystemTimeAsFileTime((LPFILETIME) &endTime);
    elapsed = (LONG) ((endTime - startTime) / 10000);
    DebugPrint((1, "END OpenDiskObject: %d msec\n\n", elapsed));
#endif
    return  status;
}

DWORD APIENTRY
CollectDiskObjectData (
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

    This routine will return the data for the processor object

Arguments:

   IN       LPWSTR   lpValueName
            pointer to a wide character string passed by registry.

   IN OUT   LPVOID   *lppData
         IN: pointer to the address of the buffer to receive the completed
            PerfDataBlock and subordinate structures. This routine will
            append its data to the buffer starting at the point referenced
            by *lppData.
         OUT: points to the first byte after the data structure added by this
            routine. This routine updated the value at lppdata after appending
            its data.

   IN OUT   LPDWORD  lpcbTotalBytes
         IN: the address of the DWORD that tells the size in bytes of the
            buffer referenced by the lppData argument
         OUT: the number of bytes added by this routine is writted to the
            DWORD pointed to by this argument

   IN OUT   LPDWORD  NumObjectTypes
         IN: the address of the DWORD to receive the number of objects added
            by this routine
         OUT: the number of objects added by this routine is writted to the
            DWORD pointed to by this argument

   Returns:

             0 if successful, else Win 32 error code of failure

--*/
{
    LONG    lReturn = ERROR_SUCCESS;

    NTSTATUS    Status;

    // build bit mask of functions to call

    DWORD       dwQueryType;
    DWORD       FunctionCallMask = 0;
    DWORD       FunctionIndex;

    DWORD       dwNumObjectsFromFunction;
    DWORD       dwOrigBuffSize;
    DWORD       dwByteSize;
#if DBG
    LONG64 startTime, endTime;
    LONG elapsed;

    GetSystemTimeAsFileTime((LPFILETIME) &startTime);
    DebugPrint((1, "BEGIN CollectDiskObject:\n"));
#endif

    if (!bInitOk) {
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        lReturn = ERROR_SUCCESS;
        bShownDiskPerfMessage = TRUE;
        goto COLLECT_BAIL_OUT;
    }

    dwQueryType = GetQueryType (lpValueName);

    switch (dwQueryType) {
        case QUERY_ITEMS:
            for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
                if (IsNumberInUnicodeList (
                    posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
                    FunctionCallMask |=
                        posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
                }
            }
            break;

        case QUERY_GLOBAL:
            FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
            break;

        case QUERY_FOREIGN:
            FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
            break;

        case QUERY_COSTLY:
            FunctionCallMask = POS_COLLECT_COSTLY_DATA;
            break;

        default:
            FunctionCallMask = POS_COLLECT_COSTLY_DATA;
            break;
    }

    // collect data 

	// if either bit is set, collect data
	if (FunctionCallMask & POS_COLLECT_GLOBAL_DATA) {
		__try {
			// read the data from the diskperf driver

			// only one call at a time is permitted. This should be 
			// throttled by the perflib, but just in case we'll test it

			assert (WmiBuffer == NULL);

			if (WmiBuffer != NULL) {
				ReportEvent (hEventLog,
					EVENTLOG_ERROR_TYPE,
					0,
					PERFDISK_BUSY,
					NULL,
					0,
					0,
					NULL,
					NULL);
				*lpcbTotalBytes = (DWORD) 0;
				*lpNumObjectTypes = (DWORD) 0;
				lReturn = ERROR_SUCCESS;
				goto COLLECT_BAIL_OUT;
			} else {
				WmiBuffer = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY, WmiAllocSize);
#if DBG
                if (WmiBuffer != NULL) {
                    HeapUsed += WmiAllocSize;
                    DebugPrint((4,
                        "CollecDiskObjectData: WmiBuffer added %d to %d\n",
                        WmiAllocSize, HeapUsed));
                }
#endif
			}

			// the buffer pointer should NOT be null if here

			if ( WmiBuffer == NULL ) {
				ReportEvent (hEventLog,
					EVENTLOG_WARNING_TYPE,
					0,
					PERFDISK_UNABLE_ALLOC_BUFFER,
					NULL,
					0,
					0,
					NULL,
					NULL);

				*lpcbTotalBytes = (DWORD) 0;
				*lpNumObjectTypes = (DWORD) 0;
				lReturn = ERROR_SUCCESS;
				goto COLLECT_BAIL_OUT;
			}

			WmiBufSize = WmiAllocSize;
			Status = WmiQueryAllDataW (
				hWmiDiskPerf,
				&WmiBufSize,
				WmiBuffer);

			// if buffer size attempted is too big or too small, resize
			if ((WmiBufSize > 0) && (WmiBufSize != WmiAllocSize)) {
				WmiBuffer = REALLOCMEM (hLibHeap,
					HEAP_ZERO_MEMORY,
					WmiBuffer, WmiBufSize);

				if (WmiBuffer == NULL) {
					// reallocation failed so bail out
					Status = ERROR_OUTOFMEMORY;
				} else {
					// if the required buffer is larger than 
					// originally planned, bump it up some
#if DBG
                    HeapUsed += (WmiBufSize - WmiAllocSize);
                    DebugPrint((4,
                        "CollectDiskObjectData: Realloc old %d new %d to %d\n",
                        WmiAllocSize, WmiBufSize, HeapUsed));
#endif
					if (WmiBufSize > WmiAllocSize) {                    
						WmiAllocSize = WmiBufSize;
					}
				}
			}

			if (Status == ERROR_INSUFFICIENT_BUFFER) {
				// if it didn't work because it was too small the first time
				// try one more time
				Status = WmiQueryAllDataW (
					hWmiDiskPerf,
					&WmiBufSize,
					WmiBuffer);
            
			} else {
				// it either worked the fisrt time or it failed because of 
				// something other than a buffer size problem
			}

		} __except (EXCEPTION_EXECUTE_HANDLER) {
            DebugPrint((2, "\tWmiBuffer %X size %d set to NULL\n",
                WmiBuffer, WmiAllocSize));
            if (WmiBuffer != NULL) {
                if (HeapValidate(hLibHeap, 0, WmiBuffer)) {
                    FREEMEM(hLibHeap, 0, WmiBuffer);
                }
            } // else we are really in trouble with WmiBuffer!!
			WmiBuffer = NULL;
			Status = ERROR_OUTOFMEMORY;
		}
        DebugPrint((3,
            "WmiQueryAllData status return: %x Buffer %d bytes\n",
            Status, WmiBufSize));

    } else {
        // no data required so these counter objects must not be in 
        // the query list
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        lReturn = ERROR_SUCCESS;
        goto COLLECT_BAIL_OUT;
    }

    if (Status == ERROR_SUCCESS) {
        *lpNumObjectTypes = 0;
        dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
        *lpcbTotalBytes = 0;

        for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
            if (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit &
                POS_COLLECT_IGNORE)
                continue;

            if (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit &
                FunctionCallMask) {
                dwNumObjectsFromFunction = 0;
                lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
                    lppData,
                    &dwByteSize,
                    &dwNumObjectsFromFunction);

                if (lReturn == ERROR_SUCCESS) {
                    *lpNumObjectTypes += dwNumObjectsFromFunction;
                    *lpcbTotalBytes += dwByteSize;
                    dwOrigBuffSize -= dwByteSize;
                    dwByteSize = dwOrigBuffSize;
                } else {
                    break;
                }
            }
#if DBG
            dwQueryType = HeapValidate(hLibHeap, 0, WmiBuffer);
            DebugPrint((4,
                "CollectDiskObjectData: Index %d HeapValid %d lReturn %d\n",
                FunctionIndex, dwQueryType, lReturn));
            if (!dwQueryType)
                DbgBreakPoint();
#endif
        }
    } else {
        ReportEvent (hEventLog,
            EVENTLOG_WARNING_TYPE,
            0,
            PERFDISK_UNABLE_QUERY_DISKPERF_INFO,
            NULL,
            0,
            sizeof(DWORD),
            NULL,
            (LPVOID)&Status);

        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        lReturn = ERROR_SUCCESS;
    }

    // *lppData is updated by each function
    // *lpcbTotalBytes is updated after each successful function
    // *lpNumObjects is updated after each successful function

COLLECT_BAIL_OUT:
    if (WmiBuffer != NULL) {
        FREEMEM (hLibHeap, 0, WmiBuffer);
#if DBG
        HeapUsed -= WmiBufSize;
        DebugPrint((4, "CollectDiskObjectData: Freed %d to %d\n",
            WmiBufSize, HeapUsed));
#endif
        WmiBuffer = NULL;
    }
    
#if DBG
    GetSystemTimeAsFileTime((LPFILETIME) &endTime);
    elapsed = (LONG) ((endTime - startTime) / 10000);
    DebugPrint((1, "END CollectDiskObject: %d msec\n\n", elapsed));
#endif
    return lReturn;
}

DWORD APIENTRY
CloseDiskObject (
)
/*++

Routine Description:

    This routine closes the open handles to the Signal Gen counters.

Arguments:

    None.


Return Value:

    ERROR_SUCCESS

--*/

{
    DWORD   status = ERROR_SUCCESS;
    DWORD   dwThisEntry;
#if DBG
    LONG64 startTime, endTime;
    LONG elapsed;

    GetSystemTimeAsFileTime((LPFILETIME) &startTime);
    DebugPrint((1, "BEGIN CloseDiskObject:\n"));
#endif

    if (--dwOpenCount == 0) {
        if (pVolumeList != NULL) {
            // close handles in volume list
            dwThisEntry = dwNumVolumeListEntries;
            while (dwThisEntry != 0) {
                dwThisEntry--;
                if (pVolumeList[dwThisEntry].hVolume != NULL) {
                    NtClose (pVolumeList[dwThisEntry].hVolume);
                }
                if (pVolumeList[dwThisEntry].DeviceName.Buffer) {
                    FREEMEM(hLibHeap, 0,
                        pVolumeList[dwThisEntry].DeviceName.Buffer);
                }
            } 
            FREEMEM (hLibHeap, 0, pVolumeList);
#if DBG
            HeapUsed -= oldVLSize;
            DebugPrint((4, "CloseDiskObject: Freed VL %d to %d\n",
                oldVLSize, HeapUsed));
            oldVLSize = 0;
#endif
            pVolumeList = NULL;
            dwNumVolumeListEntries = 0;
        }
        if (pPhysDiskList != NULL) {
            FREEMEM (hLibHeap, 0, pPhysDiskList);
#if DBG
            HeapUsed -= oldPLSize;
            DebugPrint((4, "CloseDiskObject: Freed PL %d to %d\n",
                oldVLSize, HeapUsed));
            oldPLSize = 0;
#endif
            pPhysDiskList = NULL;
            dwNumPhysDiskListEntries = 0;
        }
        // close PDisk object
        if (hWmiDiskPerf != NULL) {
            status = WmiCloseBlock (hWmiDiskPerf);
            hWmiDiskPerf = NULL;
        }
    }
#if DBG
    GetSystemTimeAsFileTime((LPFILETIME) &endTime);
    elapsed = (LONG) ((endTime - startTime) / 10000);
    DebugPrint((1, "END CloseDiskObject %d msec\n\n", elapsed));
#endif
    return  status;

}

#ifndef _DONT_CHECK_FOR_VOLUME_FILTER
ULONG
CheckVolumeFilter(
    )
/*++

Routine Description:

    This routine checks to see if diskperf is set to be an upper filter
    for Storage Volumes

Arguments:

    None.


Return Value:

    TRUE if there is a filter

--*/
{
    WCHAR Buffer[MAX_PATH+1];
    WCHAR *string = Buffer;
    DWORD dwSize = sizeof(Buffer);
    ULONG stringLength, diskperfLen, result, status;
    HKEY hKey;

    status = RegOpenKeyExW(
                HKEY_LOCAL_MACHINE,
                cszVolumeKey,
                (DWORD) 0,
                KEY_QUERY_VALUE,
                &hKey
                );
    if (status != ERROR_SUCCESS) {
        return FALSE;
    }

    status = RegQueryValueExW(
                hKey,
                (LPCWSTR)REGSTR_VAL_UPPERFILTERS,
                NULL,
                NULL,
                (LPBYTE) Buffer,
                &dwSize);
    if (status != ERROR_SUCCESS) {
        RegCloseKey(hKey);
        return FALSE;
    }

    stringLength = wcslen(string);

    diskperfLen = wcslen((LPCWSTR)DISKPERF_SERVICE_NAME);

    result = FALSE;
    while(stringLength != 0) {

        if (diskperfLen == stringLength) {
            if(_wcsicmp(string, (LPCWSTR)DISKPERF_SERVICE_NAME) == 0) {
                result = TRUE;
                break;
            }
        } else {
            string += stringLength + 1;
            stringLength = wcslen(string);
        }
    }
    RegCloseKey(hKey);
    return result;
}
#endif

#if DBG
VOID
PerfDiskDebugPrint(
    ULONG DebugPrintLevel,
    PCCHAR DebugMessage,
    ...
    )

/*++

Routine Description:

    Debug print for all PerfDisk

Arguments:

    Debug print level between 0 and 3, with 3 being the most verbose.

Return Value:

    None

--*/

{
    va_list ap;

    if ((DebugPrintLevel <= (PerfDiskDebug & 0x0000ffff)) ||
        ((1 << (DebugPrintLevel + 15)) & PerfDiskDebug)) {
        DbgPrint("%d:Perfdisk!", GetCurrentThreadId());
    }
    else
        return;

    va_start(ap, DebugMessage);


    if ((DebugPrintLevel <= (PerfDiskDebug & 0x0000ffff)) ||
        ((1 << (DebugPrintLevel + 15)) & PerfDiskDebug)) {

        _vsnprintf((LPSTR)PerfDiskDebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);

        DbgPrint((LPSTR)PerfDiskDebugBuffer);
    }

    va_end(ap);

}
#endif
