1867 lines
45 KiB
C++
1867 lines
45 KiB
C++
/*++
|
|
|
|
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
|