|
|
/*++
Copyright (C) Microsoft Corporation, 2001
Module Name:
disk.c
Abstract:
This utility adds and removes lower filter drivers for a given disk
Author: Sidhartha Environment:
User mode only
Notes:
- the filter is not checked for validity before it is added to the driver stack; if an invalid filter is added, the device may no longer be accessible. - all code works irrespective of character set (ANSI, Unicode, ...)
Revision History:
--*/
#include <stdafx.h>
#include <setupapi.h>
#include <initguid.h>
#include <ntddstor.h>
#include <ntddvol.h>
#include "verifier.h"
#include "disk.h"
#include "VrfUtil.h"
#define GETVOLUMEPATH_MAX_LEN_RETRY 1000
#ifdef __cplusplus
extern "C" { #endif //#ifdef __cplusplus
typedef BOOLEAN (*DISK_ENUMERATE_CALLBACK)( PVOID, HDEVINFO, SP_DEVINFO_DATA* );
BOOLEAN AddFilterDriver( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR Filter, IN BOOLEAN UpperFilter );
BOOLEAN RemoveFilterDriver( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR Filter, IN BOOLEAN UpperFilter );
BOOLEAN PrintFilters( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN BOOLEAN UpperFilters, IN LPTSTR FilterDriver, IN OUT LPTSTR *VerifierEnabled );
LPTSTR GetFilters( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN BOOLEAN UpperFilters );
BOOLEAN PrintDeviceName( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN OUT LPTSTR *DiskDevicesForDisplay, IN OUT LPTSTR *DiskDevicesPDOName );
BOOLEAN DeviceNameMatches( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR DeviceName );
PBYTE GetDeviceRegistryProperty( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Property, OUT PDWORD PropertyRegDataType );
BOOLEAN PrependSzToMultiSz( IN LPTSTR SzToPrepend, IN OUT LPTSTR *MultiSz );
SIZE_T GetMultiSzLength( IN LPTSTR MultiSz );
SIZE_T MultiSzSearchAndDeleteCaseInsensitive( IN LPTSTR FindThis, IN LPTSTR FindWithin, OUT SIZE_T *NewStringLength );
BOOLEAN DiskVerify( DISK_ENUMERATE_CALLBACK CallBack, PVOID Context, LPTSTR deviceName );
BOOLEAN DiskEnumerateCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData );
BOOLEAN AddCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData );
BOOLEAN DelCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData );
LPTSTR GetDriveLetters ( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData );
LPTSTR GetDriveLettersFromVolume ( IN ULONG DeviceNumber );
LPTSTR PrintDriveLetters( IN PTSTR VolumeName );
BOOLEAN StrConcatWithSpace( IN LPTSTR SzToAppend, IN OUT LPTSTR *drives );
typedef struct _DISPLAY_STRUCT{ LPTSTR Filter; LPTSTR* DiskDevicesForDisplay; LPTSTR* DiskDevicesPDOName; LPTSTR* VerifierEnabled; }DISPLAY_STRUCT, *PDISPLAY_STRUCT;
typedef struct _ADD_REMOVE_STRUCT{ LPTSTR Filter; }ADD_REMOVE_STRUCT, *PADD_REMOVE_STRUCT;
BOOLEAN DiskEnumerate( IN LPTSTR Filter, OUT LPTSTR* DiskDevicesForDisplayP, OUT LPTSTR* DiskDevicesPDONameP, OUT LPTSTR* VerifierEnabledP )
/*++
Routine Description:
This function enumerates all disk drives present on the systen. Arguments:
Filter - The name of the filter drver whose presence we want to check on any disk DiskDevicesForDisplayP - Placeholder for User Friendly Disk names DiskDevicesPDONameP - Placeholder for PDO device names VerifierEnabledP - Placeholder for Information regarding presence of Filter on a particular disk Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ DISPLAY_STRUCT Display; DISK_ENUMERATE_CALLBACK CallBack; BOOLEAN Status;
LPTSTR DiskDevicesForDisplay = NULL; LPTSTR DiskDevicesPDOName = NULL; LPTSTR VerifierEnabled = NULL; //
//Initialize the Multisz strings with \0 (i.e. an empty MultiSz String)
//
DiskDevicesForDisplay = (LPTSTR)malloc(sizeof(TCHAR)); if(DiskDevicesForDisplay == NULL ){ VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY ); goto ErrorReturn; } DiskDevicesForDisplay[0] = 0; DiskDevicesPDOName = (LPTSTR)malloc(sizeof(TCHAR)); if( DiskDevicesPDOName == NULL ){ VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
goto ErrorReturn; } DiskDevicesPDOName[0] = 0; VerifierEnabled = (LPTSTR)malloc(sizeof(TCHAR)); if( VerifierEnabled == NULL ){ VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
goto ErrorReturn; } VerifierEnabled[0] = 0; CallBack = DiskEnumerateCallback; Display.Filter = Filter; Display.DiskDevicesForDisplay = &DiskDevicesForDisplay; Display.DiskDevicesPDOName = &DiskDevicesPDOName; Display.VerifierEnabled = &VerifierEnabled; Status = DiskVerify(CallBack,(PVOID) &Display,NULL); if( !Status ) {
goto ErrorReturn; }
*DiskDevicesForDisplayP = DiskDevicesForDisplay; *DiskDevicesPDONameP = DiskDevicesPDOName; *VerifierEnabledP = VerifierEnabled; return TRUE;
ErrorReturn:
if(DiskDevicesForDisplay != NULL) {
free( DiskDevicesForDisplay ); }
if(DiskDevicesPDOName != NULL) {
free( DiskDevicesPDOName ); }
if(VerifierEnabled != NULL) {
free(VerifierEnabled); }
return FALSE; }
BOOLEAN DiskEnumerateCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData )
/*++
Routine Description:
This function is a callback to be executed whenever a disk is discovered Arguments:
Context - Points to all the relevant information needed to succesfully enumerate the disk DevInfo - The device information set which contains DeviceInfoData DevInfoData - Information needed to deal with the given device
Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ PDISPLAY_STRUCT PDisplay; BOOLEAN Status;
PDisplay = (PDISPLAY_STRUCT) Context; //
//Add newly discoverd disks to the list
//
Status = PrintDeviceName(DevInfo, DevInfoData, PDisplay->DiskDevicesForDisplay, PDisplay->DiskDevicesPDOName); if(!Status) { return Status; }
Status = PrintFilters(DevInfo, DevInfoData,FALSE, PDisplay->Filter, PDisplay->VerifierEnabled); return Status; }
BOOLEAN AddFilter( IN LPTSTR Filter, IN LPTSTR DiskDevicesPDONameP )
/*++
Routine Description:
This function adds the filter on the specified disk device Arguments:
Filter - Name of the filter to be added DiskDevicesPDONameP - PDO device name of the disk Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ ADD_REMOVE_STRUCT AddRemove; DISK_ENUMERATE_CALLBACK CallBack; BOOLEAN Status;
AddRemove.Filter = Filter; CallBack = AddCallback; Status = DiskVerify(CallBack,(PVOID) &AddRemove,DiskDevicesPDONameP); return Status; }
BOOLEAN DelFilter( IN LPTSTR Filter, IN LPTSTR DiskDevicesPDONameP )
/*++
Routine Description:
This function removes the filter on the specified disk device Arguments:
Filter - Name of the filter to be added DiskDevicesPDONameP - PDO device name of the disk Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ ADD_REMOVE_STRUCT AddRemove; DISK_ENUMERATE_CALLBACK CallBack; BOOLEAN Status;
AddRemove.Filter = Filter; CallBack = DelCallback; Status = DiskVerify(CallBack,(PVOID) &AddRemove,DiskDevicesPDONameP); return Status; }
BOOLEAN AddCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData )
/*++
Routine Description:
This function is a callback to be executed whenever a disk is matched, and a filter needs to be added/deleted Arguments:
Context - Points to all the relevant information needed to succesfully identify the disk DevInfo - The device information set which contains DeviceInfoData DevInfoData - Information needed to deal with the given device Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ PADD_REMOVE_STRUCT PAddRemove; BOOLEAN Status;
PAddRemove = (PADD_REMOVE_STRUCT) Context;
Status = AddFilterDriver(DevInfo, DevInfoData, PAddRemove->Filter, FALSE);
if( !Status ) { return FALSE; }
return TRUE; }
BOOLEAN DelCallback ( PVOID Context, HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData )
/*++
Routine Description:
This function is a callback to be executed whenever a disk is matched, and a filter needs to be added/deleted Arguments:
Context - Points to all the relevant information needed to succesfully identify the disk DevInfo - The device information set which contains DeviceInfoData DevInfoData - Information needed to deal with the given device Return Value: Returns TRUE if successful, FALSE otherwise --*/
{ PADD_REMOVE_STRUCT PAddRemove; BOOLEAN Status;
PAddRemove = (PADD_REMOVE_STRUCT) Context;
Status = RemoveFilterDriver(DevInfo, DevInfoData, PAddRemove->Filter, FALSE); if( !Status ){ return FALSE; } return TRUE; }
BOOLEAN DiskVerify( DISK_ENUMERATE_CALLBACK CallBack, PVOID Context, LPTSTR deviceName )
/*++
Routine Description:
This function enumerates all disk drives. It also is used to add/remove filter drivers. It triggers callbacks upon detecting a disk.
Arguments:
CallBack - The routine to be executed upon succesfull detection of a disk Context - Points to all the relevant information needed to succesfully identify the disk Return Value: Returns TRUE if successful, FALSE otherwise
--*/ { //
// these two constants are used to help enumerate through the list of all
// disks and volumes on the system. Adding another GUID should "just work"
//
const GUID * deviceGuids[] = { &DiskClassGuid, }; HDEVINFO devInfo = INVALID_HANDLE_VALUE; SP_DEVINFO_DATA devInfoData;
int deviceIndex;
BOOLEAN upperFilter = FALSE; BOOLEAN deviceMatch = FALSE; BOOLEAN Status; BOOLEAN Sucess;
//
// get a list of devices which support the given interface
//
devInfo = SetupDiGetClassDevs( deviceGuids[0], NULL, NULL, DIGCF_PROFILE | DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
if( devInfo == INVALID_HANDLE_VALUE ) { VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICES_LIST ); return FALSE; }
//
// step through the list of devices for this handle
// get device info at index deviceIndex, the function returns FALSE
// when there is no device at the given index.
//
deviceIndex=0; do{ //
// if a device name was specified, and it doesn't match this one,
// stop. If there is a match (or no name was specified), mark that
// there was a match.
//
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); Sucess = (SetupDiEnumDeviceInfo( devInfo, deviceIndex++, &devInfoData ) != FALSE);
if( !Sucess ) { break; }
if( deviceName != NULL && !DeviceNameMatches( devInfo, &devInfoData, deviceName )) { continue; } else { deviceMatch = TRUE; } Status = (*CallBack)(Context,devInfo,&devInfoData); if( !Status ){ return FALSE; } }while(Sucess);
if( devInfo != INVALID_HANDLE_VALUE ) { Status = ( SetupDiDestroyDeviceInfoList( devInfo ) != FALSE ); }
if( !deviceMatch ) { VrfErrorResourceFormat( IDS_NO_DEVICES_MATCH_NAME, deviceName ); return FALSE; } return TRUE; }
BOOLEAN AddFilterDriver( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR Filter, IN BOOLEAN UpperFilter )
/*++
Routine Description:
Adds the given filter driver to the list of lower filter drivers for the disk. Note: The filter is prepended to the list of drivers, which will put it at the bottom of the filter driver stack
Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device Filter - The filter to add
Return Value: If we are successful in adding the driver, as a lower driver to disk, returns TRUE, FALSE otherwise
--*/
{ SIZE_T length; SIZE_T size; LPTSTR buffer = NULL; BOOLEAN SetupDiSetDeviceRegistryPropertyReturn; BOOLEAN Success; ASSERT(DeviceInfoData != NULL); ASSERT(Filter != NULL);
Success = TRUE;
length = 0; size = 0;
buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilter ); if( buffer == NULL ){
//
// if there is no such value in the registry, then there are no upper
// filter drivers loaded, and we can just put one there
// make room for the string, string null terminator, and multisz null
// terminator
//
length = _tcslen(Filter)+1; size = (length+1)*sizeof(_TCHAR); buffer = (LPTSTR)malloc( size ); if( buffer == NULL ){
VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
Success = FALSE; goto Done; } memset(buffer, 0, size);
memcpy(buffer, Filter, length*sizeof(_TCHAR)); } else { LPTSTR buffer2; //
// remove all instances of filter from driver list
//
MultiSzSearchAndDeleteCaseInsensitive( Filter, buffer, &length ); //
// allocate a buffer large enough to add the new filter
// GetMultiSzLength already includes length of terminating NULL
// determing the new length of the string
//
length = GetMultiSzLength(buffer) + _tcslen(Filter) + 1; size = length*sizeof(_TCHAR); buffer2 = (LPTSTR)malloc( size ); if (buffer2 == NULL) { VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );
Success = FALSE;
goto Done; } memset(buffer2, 0, size); memcpy(buffer2, buffer, GetMultiSzLength(buffer)*sizeof(_TCHAR)); free(buffer); buffer = buffer2; //
// add the driver to the driver list
//
PrependSzToMultiSz(Filter, &buffer); }
//
// set the new list of filters in place
//
SetupDiSetDeviceRegistryPropertyReturn = ( SetupDiSetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS), (PBYTE)buffer, ((ULONG) GetMultiSzLength(buffer)*sizeof(_TCHAR))) != FALSE ); if(!SetupDiSetDeviceRegistryPropertyReturn){ TRACE(_T("in AddUpperFilterDriver(): ") _T("couldn't set registry value! error: %u\n"), GetLastError());
VrfErrorResourceFormat( IDS_CANNOT_SET_DEVICE_REGISTRY_PROPERTY );
Success = FALSE;
goto Done; }
Done:
if(buffer != NULL) {
free( buffer ); }
return Success; }
BOOLEAN RemoveFilterDriver( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR Filter, IN BOOLEAN UpperFilter )
/*++
Routine Description:
Removes all instances of the given filter driver from the list of lower filter drivers for the device.
Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device Filter - The filter to remove
Return Value:
returns TRUE if successful, FALSE otherwise
--*/ { SIZE_T length; SIZE_T size; LPTSTR buffer; BOOL success;
ASSERT(DeviceInfoData != NULL); ASSERT(Filter != NULL);
success = FALSE; length = 0; size = 0; buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilter );
if( buffer == NULL ){ return (TRUE); } else { //
// remove all instances of filter from driver list
//
MultiSzSearchAndDeleteCaseInsensitive( Filter, buffer, &length ); }
length = GetMultiSzLength(buffer);
ASSERT ( length > 0 );
if( length == 1 ){ //
// if the length of the list is 1, the return value from
// GetMultiSzLength() was just accounting for the trailing '\0', so we can
// delete the registry key, by setting it to NULL.
//
success = SetupDiSetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS), NULL, 0 ); } else { //
// set the new list of drivers into the registry
//
size = length*sizeof(_TCHAR); success = SetupDiSetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, (UpperFilter ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS), (PBYTE)buffer, (ULONG)size ); }
free( buffer );
if( !success ){ TRACE(_T("in RemoveUpperFilterDriver(): ") _T("couldn't set registry value! error: %i\n"), GetLastError());
VrfErrorResourceFormat( IDS_CANNOT_SET_DEVICE_REGISTRY_PROPERTY );
return FALSE; }
return TRUE; }
BOOLEAN PrintFilters( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN BOOLEAN UpperFilters, IN LPTSTR FilterDriver, IN OUT LPTSTR *VerifierEnabled )
/*++
Routine Description:
Looks at all the lower filters installed for the disk. It tells us if DiskVerifier is installed for that particular disk.
Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device FilterDriver - The name of the Filter driver for Disk Verifier DiskDevices - MultiSZ style string listing all devices VerifierEnabled - MultiSZ style string indicating if Verifier Enabled Return Value:
Returns TRUE if successful, FALSE otherwise
--*/
{ LPTSTR buffer; SIZE_T filterPosition; LPTSTR temp; int StrCmpReturn;
buffer = GetFilters( DeviceInfoSet, DeviceInfoData, UpperFilters ); if ( buffer != NULL ) { //
// go through the multisz and print out each driver
//
temp=buffer; filterPosition=0; while( *temp != _T('\0') ){ StrCmpReturn = _tcsicmp(FilterDriver,temp); if(StrCmpReturn == 0 ){ PrependSzToMultiSz( _T("1"),VerifierEnabled); free( buffer ); return TRUE; } temp = temp+_tcslen(temp)+1; filterPosition++; } free( buffer ); } PrependSzToMultiSz( _T("0"),VerifierEnabled); return TRUE; }
BOOLEAN PrintDeviceName( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN OUT LPTSTR *DiskDevicesForDisplay, IN OUT LPTSTR *DiskDevicesPDOName )
/*++
Routine Description:
Looks up the PDO device name, and user friendly name for disk. Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device DiskDevicesForDisplay - User Friendly Names for Disk DiskDevicesPDOName - PDO specified device names for disk Return Value:
Returns TRUE if successful, FALSE otherwise
--*/
{ DWORD regDataType; LPTSTR deviceName; LPTSTR driveLetters; int StrCmpReturned; BOOL bResult; deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, ®DataType ); if( deviceName == NULL ) { TRACE(_T("in PrintDeviceName(): registry key is NULL! error: %u\n"), GetLastError());
VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
return FALSE; }
if( regDataType != REG_SZ ){ TRACE(_T("in PrintDeviceName(): registry key is not an SZ!\n"));
return FALSE; } else { //
// if the device name starts with \Device, cut that off (all
// devices will start with it, so it is redundant)
//
StrCmpReturned = _tcsncmp(deviceName, _T("\\Device"), 7); if( StrCmpReturned == 0 ){ memmove(deviceName, deviceName+7, (_tcslen(deviceName)-6)*sizeof(_TCHAR) ); } PrependSzToMultiSz(deviceName,DiskDevicesPDOName); free( deviceName ); } deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, SPDRP_FRIENDLYNAME , ®DataType ); if( deviceName == NULL ){ TRACE(_T("in PrintDeviceName(): registry key is NULL! error: %u\n"), GetLastError()); //
//We could not obtain the friendly name for disk, setting it to Unknown.
//
deviceName = (LPTSTR)malloc(sizeof(TCHAR) * 64); if ( !deviceName ) { VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY ); return FALSE; } bResult = VrfLoadString(IDS_UNKNOWN, deviceName, 64 ); if ( !bResult ) { VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY ); return FALSE; } } //
// just to make sure we are getting the expected type of buffer
//
if( regDataType != REG_SZ ){ TRACE(_T("in PrintDeviceName(): registry key is not an SZ!\n")); return FALSE; }else{ driveLetters = GetDriveLetters( DeviceInfoSet, DeviceInfoData); if(driveLetters){ StrConcatWithSpace(driveLetters,&deviceName); } PrependSzToMultiSz(deviceName,DiskDevicesForDisplay); free( deviceName ); }
return TRUE; }
LPTSTR GetFilters( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN BOOLEAN UpperFilters )
/*++
Routine Description: Returns a buffer containing the list of lower filters for the device.The buffer must be freed by the caller.
Arguments: DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device UpperFilters - Switch to select Upper/Lower filter. Currently we use Lower.
Return Value:
MultiSz style string containing all lower Filters for the disk is returned. NULL is returned if there is no buffer, or an error occurs.
--*/ { DWORD regDataType; LPTSTR buffer; buffer = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, (UpperFilters ? SPDRP_UPPERFILTERS : SPDRP_LOWERFILTERS), ®DataType );
if( buffer != NULL && regDataType != REG_MULTI_SZ ){ TRACE(_T("in GetUpperFilters(): ") _T("registry key is not a MULTI_SZ!\n")); free( buffer ); return (NULL); } return (buffer); }
BOOLEAN DeviceNameMatches( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN LPTSTR DeviceName )
/*++
Routine Description: Searches if DeviceName matches the name of the device specified by DeviceInfoData
Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device DeviceName - the name to try to match Return Value:
Returns TRUE if successful, FALSE otherwise --*/
{ BOOLEAN matching = FALSE; DWORD regDataType; LPTSTR deviceName; int StrCmpReturn; deviceName = (LPTSTR) GetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, ®DataType );
if( deviceName != NULL) { if( regDataType != REG_SZ ){ TRACE(_T("in DeviceNameMatches(): registry key is not an SZ!\n")); matching = FALSE; }else{ //
// if the device name starts with \Device, cut that off (all
// devices will start with it, so it is redundant)
//
StrCmpReturn = _tcsncmp(deviceName, _T("\\Device"), 7); if( StrCmpReturn == 0 ){ memmove(deviceName, deviceName+7, (_tcslen(deviceName)-6)*sizeof(_TCHAR) ); }
matching = (_tcscmp(deviceName, DeviceName) == 0); } free( deviceName ); } else { TRACE(_T("in DeviceNameMatches(): registry key is NULL!\n")); VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
matching = FALSE; }
return (matching); }
PBYTE GetDeviceRegistryProperty( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Property, OUT PDWORD PropertyRegDataType )
/*++
Routine Description:
A wrapper around SetupDiGetDeviceRegistryProperty, so that I don't have to deal with memory allocation anywhere else.
Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device Property - which property to get (SPDRP_XXX) PropertyRegDataType - the type of registry property
Return Value:
Returns buffer for Registery property
--*/
{ DWORD length = 0; PBYTE buffer = NULL; BOOL SetupDiGetDeviceRegistryPropertyReturn;
SetupDiGetDeviceRegistryPropertyReturn = SetupDiGetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, Property, NULL, NULL, 0, &length);
if( SetupDiGetDeviceRegistryPropertyReturn ){ //
// we should not be successful at this point, so this call succeeding
// is an error condition
//
TRACE(_T("in GetDeviceRegistryProperty(): ") _T("call SetupDiGetDeviceRegistryProperty did not fail\n"), GetLastError());
VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
return (NULL); }
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){ //
// this means there are no upper filter drivers loaded, so we can just
// return.
//
return (NULL); }
//
// since we don't have a buffer yet, it is "insufficient"; we allocate
// one and try again.
//
buffer = (PBYTE)malloc( length ); if( buffer == NULL ) { VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY ); return (NULL); }
SetupDiGetDeviceRegistryPropertyReturn = SetupDiGetDeviceRegistryProperty( DeviceInfoSet, DeviceInfoData, Property, PropertyRegDataType, buffer, length, NULL);
if( !SetupDiGetDeviceRegistryPropertyReturn) { TRACE(_T("in GetDeviceRegistryProperty(): ") _T("couldn't get registry property! error: %i\n"), GetLastError());
VrfErrorResourceFormat( IDS_CANNOT_GET_DEVICE_REGISTRY_PROPERTY );
free( buffer ); return (NULL); }
return (buffer); }
BOOLEAN PrependSzToMultiSz( IN LPTSTR SzToPrepend, IN OUT LPTSTR *MultiSz )
/*++
Routine Description:
Prepends the given string to a MultiSz.Note: This WILL allocate and free memory, so don't keep pointers to the MultiSz passed in.
Arguments:
SzToPrepend - string to prepend MultiSz - pointer to a MultiSz which will be prepended-to Return Value:
Returns true if successful, false if not (will only fail in memory allocation)
--*/
{ SIZE_T szLen; SIZE_T multiSzLen; LPTSTR newMultiSz = NULL;
ASSERT(SzToPrepend != NULL); ASSERT(MultiSz != NULL);
szLen = (_tcslen(SzToPrepend)+1)*sizeof(_TCHAR); multiSzLen = GetMultiSzLength(*MultiSz)*sizeof(_TCHAR); newMultiSz = (LPTSTR)malloc( szLen+multiSzLen );
if( newMultiSz == NULL ){ return (FALSE); } //
// recopy the old MultiSz into proper position into the new buffer.
// the (char*) cast is necessary, because newMultiSz may be a wchar*, and
// szLen is in bytes.
//
memcpy( ((char*)newMultiSz) + szLen, *MultiSz, multiSzLen );
_tcscpy( newMultiSz, SzToPrepend );
free( *MultiSz ); *MultiSz = newMultiSz;
return (TRUE); }
SIZE_T GetMultiSzLength( IN LPTSTR MultiSz )
/*++
Routine Description:
Calculates the size of the buffer required to hold a particular MultiSz
Arguments:
MultiSz - the MultiSz to get the length of Return Value: Returns the length (in characters) of the buffer required to hold this MultiSz, INCLUDING the trailing null. example: GetMultiSzLength("foo\0bar\0") returns 9 note: since MultiSz cannot be null, a number >= 1 will always be returned
--*/
{ SIZE_T len = 0; SIZE_T totalLen = 0;
ASSERT( MultiSz != NULL );
while( *MultiSz != _T('\0') ){ len = _tcslen(MultiSz)+1; MultiSz += len; totalLen += len; }
return (totalLen+1); }
SIZE_T MultiSzSearchAndDeleteCaseInsensitive( IN LPTSTR FindThis, IN LPTSTR FindWithin, OUT SIZE_T *NewLength )
/*++
Routine Description: Deletes all instances of a string from within a multi-sz.
Arguments: FindThis - the string to find and remove FindWithin - the string having the instances removed NewStringLength - the new string length
Return Value: Returns the no. of string occurences deleted from MultiSz
--*/
{ LPTSTR search; SIZE_T currentOffset; DWORD instancesDeleted; SIZE_T searchLen;
ASSERT(FindThis != NULL); ASSERT(FindWithin != NULL); ASSERT(NewLength != NULL);
currentOffset = 0; instancesDeleted = 0; search = FindWithin;
*NewLength = GetMultiSzLength(FindWithin); //
// loop while the multisz null terminator is not found
//
while ( *search != _T('\0') ){ //
// length of string + null char; used in more than a couple places
//
searchLen = _tcslen(search) + 1;
if( _tcsicmp(search, FindThis) == 0 ){ //
// they match, shift the contents of the multisz, to overwrite the
// string (and terminating null), and update the length
//
instancesDeleted++; *NewLength -= searchLen; memmove( search, search + searchLen, (*NewLength - currentOffset) * sizeof(TCHAR) ); } else { currentOffset += searchLen; search += searchLen; } }
return instancesDeleted; }
BOOLEAN FreeDiskMultiSz( IN LPTSTR MultiSz ) { ASSERT( MultiSz != NULL ); free( MultiSz );
return TRUE; }
LPTSTR GetDriveLetters ( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData )
/*++
Routine Description:
Looks up the drive letters for the specified disk. Finds the device number of the disk, and passes it on to the volume code Arguments:
DeviceInfoSet - The device information set which contains DeviceInfoData DeviceInfoData - Information needed to deal with the given device Return Value:
Returns the list of drives present on the disk if successful, NULL otherwise
--*/
{ SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; ULONG cbDetail; PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail; BOOL Status; HANDLE hDisk; STORAGE_DEVICE_NUMBER devNumber; DWORD cbBytes; DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData); cbDetail = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH * sizeof(WCHAR); pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, cbDetail); if (pDetail == NULL) { return NULL; } pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); Status = SetupDiEnumDeviceInterfaces (DeviceInfoSet, DeviceInfoData, &DiskClassGuid, 0, &DeviceInterfaceData); if (! Status) { LocalFree(pDetail); return NULL; }
Status = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &DeviceInterfaceData, pDetail, cbDetail, NULL, NULL); if (! Status) { LocalFree(pDetail); return NULL; }
hDisk = CreateFile(pDetail->DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDisk == INVALID_HANDLE_VALUE) { LocalFree(pDetail); return NULL; }
Status = DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &devNumber, sizeof(devNumber), &cbBytes, NULL); if (!Status) { LocalFree(pDetail); return NULL; } LocalFree(pDetail); return GetDriveLettersFromVolume (devNumber.DeviceNumber); }
LPTSTR GetDriveLettersFromVolume ( IN ULONG DeviceNumber )
/*++
Routine Description:
Looks up the drive letter(s) and volume_label(s) for the specified disk(device number) by parsing the volumes. Arguments:
DeviceNumber - Unique Number identifying the physical disk Return Value:
Returns the list of drives present on the disk if successful, NULL otherwise
--*/ { HANDLE h = INVALID_HANDLE_VALUE; HANDLE hVol; TCHAR volumeName[MAX_PATH]; TCHAR originalVolumeName[MAX_PATH]; DWORD cbBytes; PVOLUME_DISK_EXTENTS PVolDiskExtent; LPTSTR drives; LPTSTR temp; BOOL b; BOOL First; int maxDisks; int i; int j; size_t tempLen; TCHAR OpenParan[] = TEXT(" ( "); TCHAR CloseParan[] = TEXT(")");
drives = NULL; First = TRUE;
StrConcatWithSpace(OpenParan,&drives);
for (;;) { if(First) { //
//Using FindFirstVolumeA, as it is the Non-Unicode version
//of FindFirstVolume
//
h = FindFirstVolume(volumeName, MAX_PATH); if (h == INVALID_HANDLE_VALUE) { return NULL; } First = FALSE; b = TRUE; } else { b = FindNextVolume(h, volumeName, MAX_PATH); } if (!b) { break; } tempLen = _tcslen(volumeName); _tcsncpy(originalVolumeName, volumeName, tempLen - 1); _tcscpy(originalVolumeName + tempLen - 1, volumeName + tempLen); //
//To open a handle correctly, CreateFile expects the name
//of the Volume without the trailing \ returned by
//FindFirstVolume / FindNextVolume
//
hVol = CreateFile(originalVolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hVol == INVALID_HANDLE_VALUE) { continue; } PVolDiskExtent = (PVOLUME_DISK_EXTENTS) LocalAlloc(LMEM_FIXED, sizeof(VOLUME_DISK_EXTENTS)); if(!PVolDiskExtent) { continue; } //
//This IOCTL has to be called with a minimum of
//size VOLUME_DISK_EXTENTS. If more entries are present
//it can be obtained by PVolDiskExtent->NumberOfDiskExtents
//
b = DeviceIoControl(hVol, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, PVolDiskExtent, sizeof(VOLUME_DISK_EXTENTS), &cbBytes, NULL);
if (!b) { //
//Now, we can read how much memory is actually required
//to read in the disk info
//
if(GetLastError() == ERROR_MORE_DATA){ maxDisks = PVolDiskExtent->NumberOfDiskExtents; LocalFree(PVolDiskExtent); PVolDiskExtent = (PVOLUME_DISK_EXTENTS) LocalAlloc(LMEM_FIXED, sizeof(VOLUME_DISK_EXTENTS) + (sizeof(DISK_EXTENT) * maxDisks)); if(!PVolDiskExtent) { continue; } b = DeviceIoControl(hVol, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, PVolDiskExtent, sizeof(VOLUME_DISK_EXTENTS) + (sizeof(DISK_EXTENT) * maxDisks), &cbBytes, NULL); if (!b) { continue; } for(j=0;j<maxDisks;j++){ if(PVolDiskExtent->Extents[j].DiskNumber == DeviceNumber) { temp = PrintDriveLetters(volumeName); if(temp) { StrConcatWithSpace(temp,&drives); FreeDiskMultiSz(temp); } } } } else { continue; } } else { if(PVolDiskExtent->Extents[0].DiskNumber == DeviceNumber) { temp = PrintDriveLetters(volumeName); if(temp) { StrConcatWithSpace(temp,&drives); FreeDiskMultiSz(temp); } } } CloseHandle(hVol); LocalFree(PVolDiskExtent); }
if(h != INVALID_HANDLE_VALUE) { FindVolumeClose(h); } StrConcatWithSpace(CloseParan,&drives); return drives; }
LPTSTR PrintDriveLetters( IN PTSTR VolumeName )
/*++
Routine Description:
Looks up the drive letters for the specified Volume Arguments:
VolumeName - Name of the Volume Return Value:
A list of drive letter(s) for the specified volume
--*/
{ BOOL b; DWORD len; LPTSTR volumePaths, p; LPTSTR drives; TCHAR volumeName1[MAX_PATH]; DWORD lpVolumeSerialNumber; DWORD lpMaximumComponentLength; DWORD lpFileSystemFlags;
drives = NULL; b = GetVolumePathNamesForVolumeName(VolumeName, NULL, 0, &len); if (!b) { if(GetLastError() != ERROR_MORE_DATA) { return NULL; } } volumePaths = (LPTSTR) LocalAlloc(LMEM_FIXED, len*sizeof(TCHAR)); if (!volumePaths) { return NULL; }
b = GetVolumePathNamesForVolumeName(VolumeName, volumePaths, len, NULL);
if (!b ) {
if( GetLastError() != ERROR_MORE_DATA) { LocalFree(volumePaths); return NULL; } else { //
//Warning - This is a hack. For some reason the non-unicode
//version of GetVolumePathNamesForVolumeNameA does not return
//the correct length to be used. So we hope it will not be
//greater then GETVOLUMEPATH_MAX_LEN_RETRY. But if the correct len is
//returned, we used that previosly
//
LocalFree(volumePaths); len = GETVOLUMEPATH_MAX_LEN_RETRY; volumePaths = (LPTSTR) LocalAlloc(LMEM_FIXED, GETVOLUMEPATH_MAX_LEN_RETRY*sizeof(TCHAR)); if (!volumePaths) { return NULL; } b = GetVolumePathNamesForVolumeName(VolumeName, volumePaths, len, NULL); if (!b ) { LocalFree(volumePaths); return NULL; } } }
if (!volumePaths[0]) { return NULL; } p = volumePaths;
for (;;) { if(_tcslen(p) > 2) { p[_tcslen(p) - 1] = _T(' '); } StrConcatWithSpace(p,&drives); while (*p++); //
//The drive letters returned are a collection of strings,
//and the end is marked with \0\0. If we reached the end,
//stop, else traverse the string list
//
if (*p == 0) { break; } }
LocalFree(volumePaths); return drives; }
BOOLEAN StrConcatWithSpace( IN LPTSTR SzToAppend, IN OUT LPTSTR *drives )
/*++
Routine Description:
Concatenates the given string to the drive list. Note: This WILL allocate and free memory, so don't keep pointers to the drives passed in. Do no pass uninitialized pointers. *drives should be NULL if empty
Arguments:
SzToAppend - string to prepend drives - pointer to the existing drive list Return Value:
Returns true if successful, false if not (will only fail in memory allocation)
--*/
{ SIZE_T szLen; SIZE_T driveLen; LPTSTR newdrives = NULL;
ASSERT(SzToAppend != NULL); ASSERT(drives != NULL); szLen = (_tcslen(SzToAppend))*sizeof(_TCHAR); if(*drives == NULL) { driveLen = sizeof(_TCHAR) ; } else { driveLen = (_tcslen(*drives) + 1)*sizeof(_TCHAR); } newdrives = (LPTSTR)malloc(szLen+driveLen);
if( newdrives == NULL ){ return (FALSE); } if(*drives == NULL){ _tcscpy( newdrives, SzToAppend); } else { _tcscpy(newdrives, *drives); //_tcscat(newdrives, _T(" "));
_tcscat(newdrives, SzToAppend); } free( *drives ); *drives = newdrives;
return (TRUE); }
#ifdef __cplusplus
}; //extern "C"
#endif //#ifdef __cplusplus
|